summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp21
-rw-r--r--Android.bp1
-rw-r--r--apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java5
-rw-r--r--boot/preloaded-classes1
-rw-r--r--config/preloaded-classes1
-rw-r--r--core/api/current.txt6
-rw-r--r--core/api/system-current.txt13
-rw-r--r--core/api/test-current.txt8
-rw-r--r--core/api/test-lint-baseline.txt6
-rw-r--r--core/java/android/app/Activity.java8
-rw-r--r--core/java/android/app/ActivityThread.java2
-rw-r--r--core/java/android/app/Notification.java5
-rw-r--r--core/java/android/app/UiModeManager.java4
-rw-r--r--core/java/android/app/WallpaperManager.java2
-rw-r--r--core/java/android/app/assist/AssistContent.java71
-rw-r--r--core/java/android/app/jank/AppJankStats.java17
-rw-r--r--core/java/android/companion/virtual/flags/deprecated_flags_do_not_edit.aconfig (renamed from core/java/android/companion/virtual/flags.aconfig)20
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig18
-rw-r--r--core/java/android/companion/virtual/flags/launched_flags.aconfig6
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java69
-rw-r--r--core/java/android/content/pm/flags.aconfig8
-rw-r--r--core/java/android/content/res/ApkAssets.java56
-rw-r--r--core/java/android/content/res/ResourceTimer.java56
-rw-r--r--core/java/android/hardware/SystemSensorManager.java43
-rw-r--r--core/java/android/hardware/display/DisplayTopology.java3
-rw-r--r--core/java/android/hardware/input/InputSettings.java66
-rw-r--r--core/java/android/provider/Settings.java21
-rw-r--r--core/java/android/view/InputEventConsistencyVerifier.java2
-rw-r--r--core/java/android/view/SurfaceControl.java64
-rw-r--r--core/java/android/view/WindowManagerGlobal.java1
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java3
-rw-r--r--core/java/android/view/contentprotection/flags/content_protection_flags.aconfig7
-rw-r--r--core/java/android/widget/flags/flags.aconfig2
-rw-r--r--core/java/android/window/flags/window_surfaces.aconfig8
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig9
-rw-r--r--core/java/com/android/internal/policy/IKeyguardService.aidl14
-rw-r--r--core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java13
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp66
-rw-r--r--core/jni/android_util_AssetManager.cpp2
-rw-r--r--core/jni/android_view_SurfaceControl.cpp20
-rw-r--r--core/proto/android/providers/settings/system.proto1
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--core/res/res/layout/notification_2025_expand_button.xml1
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_base.xml1
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_call.xml1
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_media.xml1
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_messaging.xml1
-rw-r--r--core/res/res/layout/notification_2025_template_conversation.xml1
-rw-r--r--core/res/res/layout/notification_2025_template_header.xml1
-rw-r--r--core/res/res/values/config.xml15
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/res/res/xml/sms_short_codes.xml52
-rw-r--r--core/tests/coretests/Android.bp1
-rw-r--r--core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java20
-rw-r--r--core/tests/systemproperties/Android.bp1
-rw-r--r--core/tests/utiltests/Android.bp1
-rw-r--r--libs/WindowManager/Shell/multivalentTests/Android.bp1
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java182
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java80
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java54
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt122
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt44
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt202
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java29
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java9
-rw-r--r--libs/androidfw/ApkAssets.cpp9
-rw-r--r--libs/androidfw/AssetsProvider.cpp82
-rw-r--r--libs/androidfw/Idmap.cpp21
-rw-r--r--libs/androidfw/ResourceTypes.cpp78
-rw-r--r--libs/androidfw/Util.cpp25
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h2
-rw-r--r--libs/androidfw/include/androidfw/AssetsProvider.h25
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h32
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h48
-rw-r--r--libs/androidfw/include/androidfw/misc.h6
-rw-r--r--libs/androidfw/misc.cpp69
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp27
-rw-r--r--media/java/android/media/MediaCodec.java28
-rw-r--r--media/java/android/media/MediaMuxer.java6
-rw-r--r--media/java/android/media/soundtrigger/SoundTriggerManager.java7
-rw-r--r--native/android/performance_hint.cpp2
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp9
-rw-r--r--packages/CredentialManager/tests/robotests/Android.bp1
-rw-r--r--packages/CredentialManager/wear/robotests/Android.bp1
-rw-r--r--packages/InputDevices/res/raw/keyboard_layout_romanian.kcm357
-rw-r--r--packages/InputDevices/res/values/strings.xml3
-rw-r--r--packages/InputDevices/res/xml/keyboard_layouts.xml7
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java3
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java31
-rw-r--r--packages/SettingsLib/DataStore/tests/Android.bp1
-rw-r--r--packages/SettingsLib/Ipc/Android.bp2
-rw-r--r--packages/SettingsLib/SettingsTheme/Android.bp5
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt4
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/Android.bp1
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt83
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt178
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig30
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java17
-rw-r--r--packages/SettingsLib/tests/robotests/Android.bp5
-rw-r--r--packages/SettingsLib/tests/robotests/fragment/Android.bp4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java36
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java82
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java12
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java95
-rw-r--r--packages/SystemUI/Android.bp6
-rw-r--r--packages/SystemUI/OWNERS1
-rw-r--r--packages/SystemUI/aconfig/predictive_back.aconfig14
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig8
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt6
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt50
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt54
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt60
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt13
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt61
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt13
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt69
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt42
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt56
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt78
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt51
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt82
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt116
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java85
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java280
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt91
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt111
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt117
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt73
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java123
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java236
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java309
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt226
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt75
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt103
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt114
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerNotificationWarningsTest.java)0
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt116
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java197
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt)0
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt8
-rw-r--r--ravenwood/tests/bivalenttest/Android.bp1
-rw-r--r--ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt31
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickController.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java44
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java2
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java6
-rw-r--r--services/core/java/com/android/server/BinaryTransparencyService.java2
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java105
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java16
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java12
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java9
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java4
-rw-r--r--services/core/java/com/android/server/appop/AttributedOp.java11
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java387
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java106
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsRegistry.java298
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java689
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsTable.java128
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java220
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java (renamed from services/core/java/com/android/server/appop/DiscreteRegistry.java)263
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java35
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java21
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java7
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateProvider.java5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java14
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java28
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java38
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java28
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java7
-rw-r--r--services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java2
-rw-r--r--services/core/java/com/android/server/input/InputSettingsObserver.java7
-rw-r--r--services/core/java/com/android/server/input/NativeInputManagerService.java5
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java71
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java341
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java4
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java20
-rw-r--r--services/core/java/com/android/server/notification/ZenModeConditions.java9
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java47
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig12
-rw-r--r--services/core/java/com/android/server/om/IdmapDaemon.java61
-rw-r--r--services/core/java/com/android/server/om/OverlayActorEnforcer.java9
-rw-r--r--services/core/java/com/android/server/om/OverlayReferenceMapper.java62
-rw-r--r--services/core/java/com/android/server/pm/ResilientAtomicFile.java22
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackageItem.java2
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java10
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java20
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java12
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java9
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java113
-rw-r--r--services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java15
-rw-r--r--services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java15
-rw-r--r--services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java15
-rw-r--r--services/core/java/com/android/server/wm/AppCompatController.java46
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOverrides.java11
-rw-r--r--services/core/java/com/android/server/wm/AppCompatResizeOverrides.java38
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java5
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp28
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/java/com/android/server/flags.aconfig7
-rw-r--r--services/robotests/Android.bp1
-rw-r--r--services/robotests/backup/Android.bp1
-rw-r--r--services/tests/InputMethodSystemServerTests/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java30
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java34
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java5
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java5
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/NetworkStatsTestUtils.java86
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java5
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java6
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java6
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java235
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java309
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java (renamed from services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java)34
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java168
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt66
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java217
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java21
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java4
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java245
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java257
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java41
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java105
-rw-r--r--services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java6
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java3
-rw-r--r--test-mock/Android.bp1
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/JankUtils.java1
-rw-r--r--tests/InputScreenshotTest/robotests/Android.bp1
-rw-r--r--tests/Internal/Android.bp1
-rw-r--r--tools/aapt2/cmd/Command.cpp169
-rw-r--r--tools/aapt2/cmd/Command.h54
-rw-r--r--tools/aapt2/cmd/Command_test.cpp41
346 files changed, 8319 insertions, 5056 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index b5e22f687a64..a60ced5835ea 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -107,6 +107,7 @@ aconfig_declarations_group {
"com.android.server.flags.services-aconfig-java",
"com.android.text.flags-aconfig-java",
"com.android.window.flags.window-aconfig-java",
+ "configinfra_framework_flags_java_lib",
"conscrypt_exported_aconfig_flags_lib",
"device_policy_aconfig_flags_lib",
"display_flags_lib",
@@ -522,7 +523,10 @@ aconfig_declarations {
package: "android.companion.virtualdevice.flags",
container: "system",
exportable: true,
- srcs: ["core/java/android/companion/virtual/flags/*.aconfig"],
+ srcs: [
+ "core/java/android/companion/virtual/flags/flags.aconfig",
+ "core/java/android/companion/virtual/flags/launched_flags.aconfig",
+ ],
}
java_aconfig_library {
@@ -547,7 +551,7 @@ aconfig_declarations {
name: "android.companion.virtual.flags-aconfig",
package: "android.companion.virtual.flags",
container: "system",
- srcs: ["core/java/android/companion/virtual/*.aconfig"],
+ srcs: ["core/java/android/companion/virtual/flags/deprecated_flags_do_not_edit.aconfig"],
}
// InputMethod
@@ -1584,6 +1588,13 @@ java_aconfig_library {
}
java_aconfig_library {
+ name: "android.app.appfunctions.flags-aconfig-java-host",
+ aconfig_declarations: "android.app.appfunctions.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ host_supported: true,
+}
+
+java_aconfig_library {
name: "android.app.appfunctions.exported-flags-aconfig-java",
aconfig_declarations: "android.app.appfunctions.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
@@ -1824,12 +1835,6 @@ java_aconfig_library {
min_sdk_version: "30",
apex_available: [
"//apex_available:platform",
- "com.android.adservices",
- "com.android.cellbroadcast",
- "com.android.devicelock",
- "com.android.extservices",
- "com.android.healthfitness",
- "com.android.mediaprovider",
"com.android.permission",
],
}
diff --git a/Android.bp b/Android.bp
index d47919c3fafb..9d8c8a69fc18 100644
--- a/Android.bp
+++ b/Android.bp
@@ -408,7 +408,6 @@ java_defaults {
"bouncycastle-repackaged-unbundled",
"com.android.sysprop.foldlockbehavior",
"com.android.sysprop.view",
- "configinfra_framework_flags_java_lib",
"framework-internal-utils",
"dynamic_instrumentation_manager_aidl-java",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java b/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java
index df6e3c836256..e790874ebc61 100644
--- a/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java
+++ b/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java
@@ -43,7 +43,7 @@ public class AconfigPackagePerfTest {
@Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- @Parameterized.Parameters(name = "isPlatform={0}")
+ @Parameterized.Parameters(name = "isPlatform_{0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {{false}, {true}});
}
@@ -60,10 +60,9 @@ public class AconfigPackagePerfTest {
}
}
- @Parameterized.Parameter(0)
-
// if this variable is true, then the test query flags from system/product/vendor
// if this variable is false, then the test query flags from updatable partitions
+ @Parameterized.Parameter(0)
public boolean mIsPlatform;
@Test
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index b83bd4e4d401..9926aef91ee1 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -6470,6 +6470,7 @@ android.os.connectivity.WifiActivityEnergyInfo
android.os.connectivity.WifiBatteryStats$1
android.os.connectivity.WifiBatteryStats
android.os.flagging.AconfigPackage
+android.os.flagging.PlatformAconfigPackage
android.os.health.HealthKeys$Constant
android.os.health.HealthKeys$Constants
android.os.health.HealthKeys$SortedIntArray
diff --git a/config/preloaded-classes b/config/preloaded-classes
index e53c78f65877..bdd95f8e67ae 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -6474,6 +6474,7 @@ android.os.connectivity.WifiActivityEnergyInfo
android.os.connectivity.WifiBatteryStats$1
android.os.connectivity.WifiBatteryStats
android.os.flagging.AconfigPackage
+android.os.flagging.PlatformAconfigPackage
android.os.health.HealthKeys$Constant
android.os.health.HealthKeys$Constants
android.os.health.HealthKeys$SortedIntArray
diff --git a/core/api/current.txt b/core/api/current.txt
index b308335126f1..c4109392d6bd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8947,18 +8947,19 @@ package android.app.assist {
method public android.content.ClipData getClipData();
method public android.os.Bundle getExtras();
method public android.content.Intent getIntent();
+ method @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") @Nullable public android.net.Uri getSessionTransferUri();
method public String getStructuredData();
method public android.net.Uri getWebUri();
method public boolean isAppProvidedIntent();
method public boolean isAppProvidedWebUri();
method public void setClipData(android.content.ClipData);
method public void setIntent(android.content.Intent);
+ method @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public void setSessionTransferUri(@Nullable android.net.Uri);
method public void setStructuredData(String);
method public void setWebUri(android.net.Uri);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXTRA_APP_FUNCTION_DATA = "android.app.assist.extra.APP_FUNCTION_DATA";
- field @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public static final String EXTRA_SESSION_TRANSFER_WEB_URI = "android.app.assist.extra.SESSION_TRANSFER_WEB_URI";
}
public class AssistStructure implements android.os.Parcelable {
@@ -9190,8 +9191,9 @@ package android.app.blob {
package android.app.jank {
@FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public final class AppJankStats {
- ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.RelativeFrameTimeHistogram);
+ ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.RelativeFrameTimeHistogram);
method public long getJankyFrameCount();
+ method @Nullable public String getNavigationComponent();
method @NonNull public android.app.jank.RelativeFrameTimeHistogram getRelativeFrameTimeHistogram();
method public long getTotalFrameCount();
method public int getUid();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f82aecbd6d44..a775c2bc1891 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1290,7 +1290,6 @@ package android.app {
public class WallpaperManager {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int);
- method @FlaggedApi("android.app.customization_packs_apis") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.util.SparseArray<android.graphics.Rect> getBitmapCrops(int);
method @FlaggedApi("android.app.customization_packs_apis") public static int getOrientation(@NonNull android.graphics.Point);
method @FloatRange(from=0.0f, to=1.0f) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount();
method @FlaggedApi("android.app.customization_packs_apis") @Nullable public android.os.ParcelFileDescriptor getWallpaperFile(int, boolean);
@@ -8095,16 +8094,16 @@ package android.media.soundtrigger {
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void deleteModel(java.util.UUID);
method public int getDetectionServiceOperationsTimeout();
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
- method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getModelState(@NonNull java.util.UUID);
+ method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int getModelState(@NonNull java.util.UUID);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModuleProperties getModuleProperties();
method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getParameter(@NonNull java.util.UUID, int);
- method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public boolean isRecognitionActive(@NonNull java.util.UUID);
- method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int loadSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.SoundModel);
+ method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public boolean isRecognitionActive(@NonNull java.util.UUID);
+ method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int loadSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.SoundModel);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModelParamRange queryParameter(@Nullable java.util.UUID, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int setParameter(@Nullable java.util.UUID, int, int);
- method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int startRecognition(@NonNull java.util.UUID, @Nullable android.os.Bundle, @NonNull android.content.ComponentName, @NonNull android.hardware.soundtrigger.SoundTrigger.RecognitionConfig);
- method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int stopRecognition(@NonNull java.util.UUID);
- method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int unloadSoundModel(@NonNull java.util.UUID);
+ method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int startRecognition(@NonNull java.util.UUID, @Nullable android.os.Bundle, @NonNull android.content.ComponentName, @NonNull android.hardware.soundtrigger.SoundTrigger.RecognitionConfig);
+ method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int stopRecognition(@NonNull java.util.UUID);
+ method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int unloadSoundModel(@NonNull java.util.UUID);
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ed8042d45243..33a06165c77a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2,7 +2,7 @@
package android {
public static final class Manifest.permission {
- field @FlaggedApi("com.android.server.accessibility.motion_event_observing") public static final String ACCESSIBILITY_MOTION_EVENT_OBSERVING = "android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING";
+ field public static final String ACCESSIBILITY_MOTION_EVENT_OBSERVING = "android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING";
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
@@ -536,6 +536,7 @@ package android.app {
method @Nullable public android.graphics.Bitmap getBitmap();
method @Nullable public android.graphics.Bitmap getBitmapAsUser(int, boolean, int);
method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull java.util.List<android.graphics.Point>, int, boolean);
+ method @FlaggedApi("android.app.customization_packs_apis") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.util.SparseArray<android.graphics.Rect> getBitmapCrops(int);
method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull android.graphics.Point, @NonNull java.util.List<android.graphics.Point>, @Nullable java.util.Map<android.graphics.Point,android.graphics.Rect>);
method public boolean isLockscreenLiveWallpaperEnabled();
method @Nullable public android.graphics.Rect peekBitmapDimensions();
@@ -2113,6 +2114,11 @@ package android.media {
method public boolean isAidlHal();
}
+ public static final class MediaMuxer.OutputFormat {
+ field public static final int MUXER_OUTPUT_FIRST = 0; // 0x0
+ field public static final int MUXER_OUTPUT_LAST = 4; // 0x4
+ }
+
public final class MediaRoute2Info implements android.os.Parcelable {
method @NonNull public String getOriginalId();
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 349b4edffc32..fe23517f77ba 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1977,6 +1977,8 @@ Todo: android.window.WindowContainerTransaction#setActivityWindowingMode(android
Documentation mentions 'TODO'
+UnflaggedApi: android.Manifest.permission#ACCESSIBILITY_MOTION_EVENT_OBSERVING:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING
UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
UnflaggedApi: android.Manifest.permission#RESERVED_FOR_TESTING_SIGNATURE:
@@ -2057,6 +2059,10 @@ UnflaggedApi: android.media.AudioManager#getFocusFadeOutDurationForTest():
New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusFadeOutDurationForTest()
UnflaggedApi: android.media.AudioManager#getFocusUnmuteDelayAfterFadeOutForTest():
New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusUnmuteDelayAfterFadeOutForTest()
+UnflaggedApi: android.media.MediaMuxer.OutputFormat#MUXER_OUTPUT_FIRST:
+ New API must be flagged with @FlaggedApi: field android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_FIRST
+UnflaggedApi: android.media.MediaMuxer.OutputFormat#MUXER_OUTPUT_LAST:
+ New API must be flagged with @FlaggedApi: field android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_LAST
UnflaggedApi: android.media.RingtoneSelection:
New API must be flagged with @FlaggedApi: class android.media.RingtoneSelection
UnflaggedApi: android.media.RingtoneSelection#DEFAULT_SELECTION_URI_STRING:
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8614bde775ad..b198811416cd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1273,8 +1273,8 @@ public class Activity extends ContextThemeWrapper
* Requests to show the “Open in browser” education. “Open in browser” is a feature
* within the app header that allows users to switch from an app to the web. The feature
* is made available when an application is opened by a user clicking a link or when a
- * link is provided by an application. Links can be provided by utilizing
- * {@link AssistContent#EXTRA_AUTHENTICATING_USER_WEB_URI} or
+ * link is provided by an application. Links can be provided by calling
+ * {@link AssistContent#setSessionTransferUri} or
* {@link AssistContent#setWebUri}.
*
* <p>This method should be utilized when an activity wants to nudge the user to switch
@@ -1287,7 +1287,7 @@ public class Activity extends ContextThemeWrapper
* disruptive to the user to show the education and when it is optimal to switch the user to a
* browser session. Before requesting to show the education, developers should assert that they
* have set a link that can be used by the "Open in browser" feature through either
- * {@link AssistContent#EXTRA_AUTHENTICATING_USER_WEB_URI} or
+ * {@link AssistContent#setSessionTransferUri} or
* {@link AssistContent#setWebUri} so that users are navigated to a relevant page if they choose
* to switch to the browser. If a URI is not set using either method, "Open in browser" will
* utilize a generic link if available which will direct users to the homepage of the site
@@ -1296,7 +1296,7 @@ public class Activity extends ContextThemeWrapper
* the user will not be provided with the option to switch to the browser and the education will
* not be shown if requested.
*
- * @see android.app.assist.AssistContent#EXTRA_SESSION_TRANSFER_WEB_URI
+ * @see android.app.assist.AssistContent#setSessionTransferUri
*/
@FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
public final void requestOpenInBrowserEducation() {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 717a2acb4b4a..1f3e6559a695 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -105,6 +105,7 @@ import android.content.pm.ServiceInfo;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.ResourceTimer;
import android.content.res.Resources;
import android.content.res.ResourcesImpl;
import android.content.res.loader.ResourcesLoader;
@@ -5253,6 +5254,7 @@ public final class ActivityThread extends ClientTransactionHandler
Resources.dumpHistory(pw, "");
pw.flush();
+ ResourceTimer.dumpTimers(info.fd.getFileDescriptor(), "-refresh");
if (info.finishCallback != null) {
info.finishCallback.sendResult(null);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 24594ab41100..614e2aaf42e8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -774,8 +774,9 @@ public class Notification implements Parcelable
/**
* Bit to be bitwise-ored into the {@link #flags} field that should be
- * set by the system if this notification is a promoted ongoing notification, either via a
- * user setting or allowlist.
+ * set by the system if this notification is a promoted ongoing notification, both because it
+ * {@link #hasPromotableCharacteristics()} and the user has not disabled the feature for this
+ * app.
*
* Applications cannot set this flag directly, but the posting app and
* {@link android.service.notification.NotificationListenerService} can read it.
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 2e6f3e1c7f0a..57549847f05d 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -753,7 +753,7 @@ public class UiModeManager {
* <p>
* The mode can be one of:
* <ul>
- * <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
+ * <li><em>{@link #MODE_NIGHT_NO}</em> sets the device into
* {@code notnight} mode</li>
* <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
* {@code night} mode</li>
@@ -889,7 +889,7 @@ public class UiModeManager {
* <p>
* The mode can be one of:
* <ul>
- * <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
+ * <li><em>{@link #MODE_NIGHT_NO}</em> sets the device into
* {@code notnight} mode</li>
* <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
* {@code night} mode</li>
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 360376da618c..73ecc7199686 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1690,7 +1690,7 @@ public class WallpaperManager {
* @hide
*/
@FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS)
- @SystemApi
+ @TestApi
@RequiresPermission(READ_WALLPAPER_INTERNAL)
@NonNull
public SparseArray<Rect> getBitmapCrops(@SetWallpaperFlags int which) {
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
index 3e3ca2488bd3..adf8c94ff8d5 100644
--- a/core/java/android/app/assist/AssistContent.java
+++ b/core/java/android/app/assist/AssistContent.java
@@ -1,6 +1,7 @@
package android.app.assist;
import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.Intent;
@@ -30,31 +31,6 @@ public class AssistContent implements Parcelable {
public static final String EXTRA_APP_FUNCTION_DATA =
"android.app.assist.extra.APP_FUNCTION_DATA";
- /**
- * This extra can be optionally supplied in the {@link #getExtras} bundle to provide a
- * {@link Uri} which will be utilized when transitioning a user's session to another surface.
- *
- * <p>If provided, instead of using the URI provided in {@link #setWebUri}, the
- * "Open in browser" feature will use this URI to transition the current session from one
- * surface to the other. Apps may choose to encode session or user information into this
- * URI in order to provide a better session transfer experience.
- *
- * <p>Unlike {@link #setWebUri}, this URI will not be used for features where the user might
- * accidentally share it with another user. However, developers should not encode
- * authentication credentials into this URI, because it will be surfaced in the browser URL
- * bar and may be copied and shared from there.
- *
- * <p>When providing this extra, developers should still continue to provide
- * {@link #setWebUri} for backwards compatibility with features such as
- * <a href="https://developer.android.com/guide/components/activities/recents#url-sharing">
- * recents URL sharing</a> which do not benefit from a session-transfer web URI.
- *
- * @see android.app.Activity#requestOpenInBrowserEducation()
- */
- @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
- public static final String EXTRA_SESSION_TRANSFER_WEB_URI =
- "android.app.assist.extra.SESSION_TRANSFER_WEB_URI";
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private boolean mIsAppProvidedIntent = false;
private boolean mIsAppProvidedWebUri = false;
@@ -66,6 +42,7 @@ public class AssistContent implements Parcelable {
private ClipData mClipData;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Uri mUri;
+ private Uri mSessionTransferUri;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final Bundle mExtras;
@@ -200,6 +177,41 @@ public class AssistContent implements Parcelable {
}
/**
+ * This method can be used to provide a {@link Uri} which will be utilized when transitioning a
+ * user's session to another surface.
+ *
+ * <p>If provided, instead of using the URI provided in {@link #setWebUri}, the
+ * "Open in browser" feature will use this URI to transition the current session from one
+ * surface to the other. Apps may choose to encode session or user information into this
+ * URI in order to provide a better session transfer experience. However, while this URI will
+ * only be available to the system and not other applications, developers should not encode
+ * authentication credentials into this URI, because it will be surfaced in the browser URL bar
+ * and may be copied and shared from there.
+ *
+ * <p>When providing this URI, developers should still continue to provide
+ * {@link #setWebUri} for backwards compatibility with features such as
+ * <a href="https://developer.android.com/guide/components/activities/recents#url-sharing">
+ * recents URL sharing</a> which facilitate link sharing with other users and would not benefit
+ * from a session-transfer URI.
+ *
+ * @see android.app.Activity#requestOpenInBrowserEducation()
+ */
+ @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
+ public void setSessionTransferUri(@Nullable Uri uri) {
+ mSessionTransferUri = uri;
+ }
+
+ /**
+ * Return the content's session transfer web URI as per
+ * {@link #setSessionTransferUri(android.net.Uri)}, or null if there is none.
+ */
+ @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
+ @Nullable
+ public Uri getSessionTransferUri() {
+ return mSessionTransferUri;
+ }
+
+ /**
* Return Bundle for extra vendor-specific data that can be modified and examined.
*/
public Bundle getExtras() {
@@ -218,6 +230,9 @@ public class AssistContent implements Parcelable {
mUri = Uri.CREATOR.createFromParcel(in);
}
if (in.readInt() != 0) {
+ mSessionTransferUri = Uri.CREATOR.createFromParcel(in);
+ }
+ if (in.readInt() != 0) {
mStructuredData = in.readString();
}
mIsAppProvidedIntent = in.readInt() == 1;
@@ -245,6 +260,12 @@ public class AssistContent implements Parcelable {
} else {
dest.writeInt(0);
}
+ if (mSessionTransferUri != null) {
+ dest.writeInt(1);
+ mSessionTransferUri.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
if (mStructuredData != null) {
dest.writeInt(1);
dest.writeString(mStructuredData);
diff --git a/core/java/android/app/jank/AppJankStats.java b/core/java/android/app/jank/AppJankStats.java
index 6ef6a44ddfbb..a8ebc383b7b5 100644
--- a/core/java/android/app/jank/AppJankStats.java
+++ b/core/java/android/app/jank/AppJankStats.java
@@ -57,6 +57,8 @@ public final class AppJankStats {
// Histogram of relative frame times encoded in predetermined buckets.
private RelativeFrameTimeHistogram mRelativeFrameTimeHistogram;
+ // Navigation component associated to this stat.
+ private String mNavigationComponent;
/** Used to indicate no widget category has been set. */
public static final String WIDGET_CATEGORY_UNSPECIFIED = "unspecified";
@@ -158,6 +160,8 @@ public final class AppJankStats {
*
* @param appUid the Uid of the App that is collecting jank stats.
* @param widgetId the widget id that frames will be associated to.
+ * @param navigationComponent the intended navigation target within the activity, this could be
+ * a navigation destination, screen and/or pane.
* @param widgetCategory a category used to organize widgets in a structured way that indicates
* they serve a similar purpose or perform related functions. Must be
* prefixed with WIDGET_CATEGORY_ and have a suffix of one of the
@@ -172,14 +176,14 @@ public final class AppJankStats {
* @param jankyFrames the total number of janky frames that were counted for this stat.
* @param relativeFrameTimeHistogram the histogram with predefined buckets. See
* {@link #getRelativeFrameTimeHistogram()} for details.
- *
*/
- public AppJankStats(int appUid, @NonNull String widgetId,
+ public AppJankStats(int appUid, @NonNull String widgetId, @Nullable String navigationComponent,
@Nullable @WidgetCategory String widgetCategory,
@Nullable @WidgetState String widgetState, long totalFrames, long jankyFrames,
@NonNull RelativeFrameTimeHistogram relativeFrameTimeHistogram) {
mUid = appUid;
mWidgetId = widgetId;
+ mNavigationComponent = navigationComponent;
mWidgetCategory = widgetCategory != null ? widgetCategory : WIDGET_CATEGORY_UNSPECIFIED;
mWidgetState = widgetState != null ? widgetState : WIDGET_STATE_UNSPECIFIED;
mTotalFrames = totalFrames;
@@ -254,4 +258,13 @@ public final class AppJankStats {
public @NonNull RelativeFrameTimeHistogram getRelativeFrameTimeHistogram() {
return mRelativeFrameTimeHistogram;
}
+
+ /**
+ * Returns the navigation component if it exists that this stat applies to.
+ *
+ * @return the navigation component if it exists that this stat applies to.
+ */
+ public @Nullable String getNavigationComponent() {
+ return mNavigationComponent;
+ }
}
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags/deprecated_flags_do_not_edit.aconfig
index 46da4a3d99bc..eae50624539e 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/deprecated_flags_do_not_edit.aconfig
@@ -1,24 +1,18 @@
-# Do not add new flags to this file.
+# Do not modify this file.
#
-# Due to "virtual" keyword in the package name flags
-# added to this file cannot be accessed from C++
-# code.
+# Due to "virtual" keyword in the package name flags added to this file cannot
+# be accessed from C++ code.
#
# Use frameworks/base/core/java/android/companion/virtual/flags/flags.aconfig
-# instead.
+# instead for new flags.
+#
+# All of the remaining flags here have been used for API flagging and are
+# therefore exported and should not be deleted.
package: "android.companion.virtual.flags"
container: "system"
flag {
- name: "enable_native_vdm"
- namespace: "virtual_devices"
- description: "Enable native VDM service"
- bug: "303535376"
- is_fixed_read_only: true
-}
-
-flag {
name: "dynamic_policy"
is_exported: true
namespace: "virtual_devices"
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index de01280f293f..84af84072f1b 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -1,17 +1,11 @@
+# VirtualDeviceManager flags
#
-# Copyright (C) 2023 The Android Open Source Project
+# This file contains flags guarding features that are in development.
#
-# 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.
+# Once a flag is launched or abandoned and there are no more references to it in
+# the codebase, it should be either:
+# - deleted, or
+# - moved to launched_flags.aconfig if it was launched and used for API flagging.
package: "android.companion.virtualdevice.flags"
container: "system"
diff --git a/core/java/android/companion/virtual/flags/launched_flags.aconfig b/core/java/android/companion/virtual/flags/launched_flags.aconfig
new file mode 100644
index 000000000000..ee896319bb72
--- /dev/null
+++ b/core/java/android/companion/virtual/flags/launched_flags.aconfig
@@ -0,0 +1,6 @@
+# This file contains the launched VirtualDeviceManager flags from the
+# "android.companion.virtualdevice.flags" package that cannot be deleted because
+# they have been used for API flagging.
+
+package: "android.companion.virtualdevice.flags"
+container: "system"
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 0333942b7f3e..9d11710a2cad 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.Manifest;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -30,6 +31,7 @@ import android.os.Environment;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.IntArray;
@@ -45,11 +47,11 @@ import com.android.internal.util.ArrayUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
-import libcore.io.IoUtils;
-
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -94,6 +96,9 @@ public abstract class RegisteredServicesCache<V> {
@GuardedBy("mServicesLock")
private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
+ @GuardedBy("mServicesLock")
+ private final ArrayMap<String, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>();
+
private static class UserServices<V> {
@GuardedBy("mServicesLock")
final Map<V, Integer> persistentServices = Maps.newHashMap();
@@ -323,13 +328,16 @@ public abstract class RegisteredServicesCache<V> {
public final ComponentName componentName;
@UnsupportedAppUsage
public final int uid;
+ public final long lastUpdateTime;
/** @hide */
- public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName) {
+ public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName,
+ long lastUpdateTime) {
this.type = type;
this.componentInfo = componentInfo;
this.componentName = componentName;
this.uid = (componentInfo != null) ? componentInfo.applicationInfo.uid : -1;
+ this.lastUpdateTime = lastUpdateTime;
}
@Override
@@ -490,7 +498,7 @@ public abstract class RegisteredServicesCache<V> {
final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
for (ResolveInfo resolveInfo : resolveInfos) {
try {
- ServiceInfo<V> info = parseServiceInfo(resolveInfo);
+ ServiceInfo<V> info = parseServiceInfo(resolveInfo, userId);
if (info == null) {
Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
continue;
@@ -638,13 +646,31 @@ public abstract class RegisteredServicesCache<V> {
}
@VisibleForTesting
- protected ServiceInfo<V> parseServiceInfo(ResolveInfo service)
+ protected ServiceInfo<V> parseServiceInfo(ResolveInfo service, int userId)
throws XmlPullParserException, IOException {
android.content.pm.ServiceInfo si = service.serviceInfo;
ComponentName componentName = new ComponentName(si.packageName, si.name);
PackageManager pm = mContext.getPackageManager();
+ // Check if the service has been in the service cache.
+ long lastUpdateTime = -1;
+ if (Flags.optimizeParsingInRegisteredServicesCache()) {
+ try {
+ PackageInfo packageInfo = pm.getPackageInfoAsUser(si.packageName,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ lastUpdateTime = packageInfo.lastUpdateTime;
+
+ ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(si, lastUpdateTime);
+ if (serviceInfo != null) {
+ return serviceInfo;
+ }
+ } catch (NameNotFoundException | SecurityException e) {
+ Slog.d(TAG, "Fail to get the PackageInfo in parseServiceInfo: " + e);
+ }
+ }
+
XmlResourceParser parser = null;
try {
parser = si.loadXmlMetaData(pm, mMetaDataName);
@@ -670,8 +696,13 @@ public abstract class RegisteredServicesCache<V> {
if (v == null) {
return null;
}
- final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
- return new ServiceInfo<V>(v, serviceInfo, componentName);
+ ServiceInfo<V> serviceInfo = new ServiceInfo<V>(v, si, componentName, lastUpdateTime);
+ if (Flags.optimizeParsingInRegisteredServicesCache()) {
+ synchronized (mServicesLock) {
+ mServiceInfoCaches.put(getServiceCacheKey(si), serviceInfo);
+ }
+ }
+ return serviceInfo;
} catch (NameNotFoundException e) {
throw new XmlPullParserException(
"Unable to load resources for pacakge " + si.packageName);
@@ -841,4 +872,28 @@ public abstract class RegisteredServicesCache<V> {
mContext.unregisterReceiver(mExternalReceiver);
mContext.unregisterReceiver(mUserRemovedReceiver);
}
+
+ private static String getServiceCacheKey(@NonNull android.content.pm.ServiceInfo serviceInfo) {
+ StringBuilder sb = new StringBuilder(serviceInfo.packageName);
+ sb.append('-');
+ sb.append(serviceInfo.name);
+ return sb.toString();
+ }
+
+ private ServiceInfo<V> getServiceInfoFromServiceCache(
+ @NonNull android.content.pm.ServiceInfo serviceInfo, long lastUpdateTime) {
+ String serviceCacheKey = getServiceCacheKey(serviceInfo);
+ synchronized (mServicesLock) {
+ ServiceInfo<V> serviceCache = mServiceInfoCaches.get(serviceCacheKey);
+ if (serviceCache == null) {
+ return null;
+ }
+ if (serviceCache.lastUpdateTime == lastUpdateTime) {
+ return serviceCache;
+ }
+ // The service is not latest, remove it from the cache.
+ mServiceInfoCaches.remove(serviceCacheKey);
+ return null;
+ }
+ }
}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 7bba06c87813..e4b8c90d381d 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -383,3 +383,11 @@ flag {
bug: "334024639"
description: "Feature flag to check whether a given UID can access a content provider"
}
+
+flag {
+ name: "optimize_parsing_in_registered_services_cache"
+ namespace: "package_manager_service"
+ description: "Feature flag to optimize RegisteredServicesCache ServiceInfo parsing by using caches."
+ bug: "319137634"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 908999b64961..b938aac811fd 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -25,6 +25,7 @@ import android.content.res.loader.ResourcesProvider;
import android.ravenwood.annotation.RavenwoodClassLoadHook;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -50,6 +51,7 @@ import java.util.Objects;
@RavenwoodKeepWholeClass
@RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
public final class ApkAssets {
+ private static final boolean DEBUG = false;
/**
* The apk assets contains framework resource values specified by the system.
@@ -134,6 +136,17 @@ public final class ApkAssets {
@Nullable
private final AssetsProvider mAssets;
+ @NonNull
+ private String mName;
+
+ private static final int UPTODATE_FALSE = 0;
+ private static final int UPTODATE_TRUE = 1;
+ private static final int UPTODATE_ALWAYS_TRUE = 2;
+
+ // Start with the only value that may change later and would force a native call to
+ // double check it.
+ private int mPreviousUpToDateResult = UPTODATE_TRUE;
+
/**
* Creates a new ApkAssets instance from the given path on disk.
*
@@ -304,7 +317,7 @@ public final class ApkAssets {
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
- this(format, flags, assets);
+ this(format, flags, assets, path);
Objects.requireNonNull(path, "path");
mNativePtr = nativeLoad(format, path, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
@@ -313,7 +326,7 @@ public final class ApkAssets {
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
throws IOException {
- this(format, flags, assets);
+ this(format, flags, assets, friendlyName);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
@@ -323,7 +336,7 @@ public final class ApkAssets {
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
- this(format, flags, assets);
+ this(format, flags, assets, friendlyName);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
@@ -331,16 +344,17 @@ public final class ApkAssets {
}
private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
- this(FORMAT_APK, flags, assets);
+ this(FORMAT_APK, flags, assets, "empty");
mNativePtr = nativeLoadEmpty(flags, assets);
mStringBlock = null;
}
private ApkAssets(@FormatType int format, @PropertyFlags int flags,
- @Nullable AssetsProvider assets) {
+ @Nullable AssetsProvider assets, @NonNull String name) {
mFlags = flags;
mAssets = assets;
mIsOverlay = format == FORMAT_IDMAP;
+ if (DEBUG) mName = name;
}
@UnsupportedAppUsage
@@ -421,13 +435,41 @@ public final class ApkAssets {
}
}
+ private static double intervalMs(long beginNs, long endNs) {
+ return (endNs - beginNs) / 1000000.0;
+ }
+
/**
* Returns false if the underlying APK was changed since this ApkAssets was loaded.
*/
public boolean isUpToDate() {
+ // This function is performance-critical - it's called multiple times on every Resources
+ // object creation, and on few other cache accesses - so it's important to avoid the native
+ // call when we know for sure what it will return (which is the case for both ALWAYS_TRUE
+ // and FALSE).
+ if (mPreviousUpToDateResult != UPTODATE_TRUE) {
+ return mPreviousUpToDateResult == UPTODATE_ALWAYS_TRUE;
+ }
+ final long beforeTs, afterLockTs, afterNativeTs, afterUnlockTs;
+ if (DEBUG) beforeTs = System.nanoTime();
+ final int res;
synchronized (this) {
- return nativeIsUpToDate(mNativePtr);
+ if (DEBUG) afterLockTs = System.nanoTime();
+ res = nativeIsUpToDate(mNativePtr);
+ if (DEBUG) afterNativeTs = System.nanoTime();
+ }
+ if (DEBUG) {
+ afterUnlockTs = System.nanoTime();
+ if (afterUnlockTs - beforeTs >= 10L * 1000000) {
+ Log.d("ApkAssets", "isUpToDate(" + mName + ") took "
+ + intervalMs(beforeTs, afterUnlockTs)
+ + " ms: " + intervalMs(beforeTs, afterLockTs)
+ + " / " + intervalMs(afterLockTs, afterNativeTs)
+ + " / " + intervalMs(afterNativeTs, afterUnlockTs));
+ }
}
+ mPreviousUpToDateResult = res;
+ return res != UPTODATE_FALSE;
}
public boolean isSystem() {
@@ -487,7 +529,7 @@ public final class ApkAssets {
private static native @NonNull String nativeGetAssetPath(long ptr);
private static native @NonNull String nativeGetDebugName(long ptr);
private static native long nativeGetStringBlock(long ptr);
- @CriticalNative private static native boolean nativeIsUpToDate(long ptr);
+ @CriticalNative private static native int nativeIsUpToDate(long ptr);
private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
private static native @Nullable OverlayableInfo nativeGetOverlayableInfo(long ptr,
String overlayableName) throws IOException;
diff --git a/core/java/android/content/res/ResourceTimer.java b/core/java/android/content/res/ResourceTimer.java
index d51f64ce8106..2d1bf4d9d296 100644
--- a/core/java/android/content/res/ResourceTimer.java
+++ b/core/java/android/content/res/ResourceTimer.java
@@ -17,13 +17,10 @@
package android.content.res;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-
import android.app.AppProtoEnums;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
import android.text.TextUtils;
@@ -33,6 +30,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
+import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -277,38 +275,40 @@ public final class ResourceTimer {
* Update the metrics information and dump it.
* @hide
*/
- public static void dumpTimers(@NonNull ParcelFileDescriptor pfd, @Nullable String[] args) {
- FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
- PrintWriter pw = new FastPrintWriter(fout);
- synchronized (sLock) {
- if (!sEnabled || (sConfig == null)) {
+ public static void dumpTimers(@NonNull FileDescriptor fd, String... args) {
+ try (PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd))) {
+ pw.println("\nDumping ResourceTimers");
+
+ final boolean enabled;
+ synchronized (sLock) {
+ enabled = sEnabled && sConfig != null;
+ }
+ if (!enabled) {
pw.println(" Timers are not enabled in this process");
- pw.flush();
return;
}
- }
- // Look for the --refresh switch. If the switch is present, then sTimers is updated.
- // Otherwise, the current value of sTimers is displayed.
- boolean refresh = Arrays.asList(args).contains("-refresh");
-
- synchronized (sLock) {
- update(refresh);
- long runtime = sLastUpdated - sProcessStart;
- pw.format(" config runtime=%d proc=%s\n", runtime, Process.myProcessName());
- for (int i = 0; i < sTimers.length; i++) {
- Timer t = sTimers[i];
- if (t.count != 0) {
- String name = sConfig.timers[i];
- pw.format(" stats timer=%s cnt=%d avg=%d min=%d max=%d pval=%s "
- + "largest=%s\n",
- name, t.count, t.total / t.count, t.mintime, t.maxtime,
- packedString(t.percentile),
- packedString(t.largest));
+ // Look for the --refresh switch. If the switch is present, then sTimers is updated.
+ // Otherwise, the current value of sTimers is displayed.
+ boolean refresh = Arrays.asList(args).contains("-refresh");
+
+ synchronized (sLock) {
+ update(refresh);
+ long runtime = sLastUpdated - sProcessStart;
+ pw.format(" config runtime=%d proc=%s\n", runtime, Process.myProcessName());
+ for (int i = 0; i < sTimers.length; i++) {
+ Timer t = sTimers[i];
+ if (t.count != 0) {
+ String name = sConfig.timers[i];
+ pw.format(" stats timer=%s cnt=%d avg=%d min=%d max=%d pval=%s "
+ + "largest=%s\n",
+ name, t.count, t.total / t.count, t.mintime, t.maxtime,
+ packedString(t.percentile),
+ packedString(t.largest));
+ }
}
}
}
- pw.flush();
}
// Enable (or disabled) the runtime timers. Note that timers are disabled by default. This
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 2d3d25217357..868429c30631 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -16,8 +16,6 @@
package android.hardware;
-import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
-import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
import static android.content.Context.DEVICE_ID_DEFAULT;
@@ -164,11 +162,7 @@ public class SystemSensorManager extends SensorManager {
// initialize the sensor list
for (int index = 0;; ++index) {
Sensor sensor = new Sensor();
- if (android.companion.virtual.flags.Flags.enableNativeVdm()) {
- if (!nativeGetDefaultDeviceSensorAtIndex(mNativeInstance, sensor, index)) break;
- } else {
- if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
- }
+ if (!nativeGetDefaultDeviceSensorAtIndex(mNativeInstance, sensor, index)) break;
mFullSensorsList.add(sensor);
mHandleToSensor.put(sensor.getHandle(), sensor);
}
@@ -555,11 +549,7 @@ public class SystemSensorManager extends SensorManager {
}
private List<Sensor> createRuntimeSensorListLocked(int deviceId) {
- if (android.companion.virtual.flags.Flags.vdmPublicApis()) {
- setupVirtualDeviceListener();
- } else {
- setupRuntimeSensorBroadcastReceiver();
- }
+ setupVirtualDeviceListener();
List<Sensor> list = new ArrayList<>();
nativeGetRuntimeSensors(mNativeInstance, deviceId, list);
mFullRuntimeSensorListByDevice.put(deviceId, list);
@@ -570,35 +560,6 @@ public class SystemSensorManager extends SensorManager {
return list;
}
- private void setupRuntimeSensorBroadcastReceiver() {
- if (mRuntimeSensorBroadcastReceiver == null) {
- mRuntimeSensorBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(ACTION_VIRTUAL_DEVICE_REMOVED)) {
- synchronized (mFullRuntimeSensorListByDevice) {
- final int deviceId = intent.getIntExtra(
- EXTRA_VIRTUAL_DEVICE_ID, DEVICE_ID_DEFAULT);
- List<Sensor> removedSensors =
- mFullRuntimeSensorListByDevice.removeReturnOld(deviceId);
- if (removedSensors != null) {
- for (Sensor s : removedSensors) {
- cleanupSensorConnection(s);
- }
- }
- mRuntimeSensorListByDeviceByType.remove(deviceId);
- }
- }
- }
- };
-
- IntentFilter filter = new IntentFilter("virtual_device_removed");
- filter.addAction(ACTION_VIRTUAL_DEVICE_REMOVED);
- mContext.registerReceiver(mRuntimeSensorBroadcastReceiver, filter,
- Context.RECEIVER_NOT_EXPORTED);
- }
- }
-
private void setupVirtualDeviceListener() {
if (mVirtualDeviceListener != null) {
return;
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index 0e2c05f92e7c..1d2f133ee759 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -679,8 +679,7 @@ public final class DisplayTopology implements Parcelable {
}
/**
- * Tests whether two brightness float values are within a small enough tolerance
- * of each other.
+ * Tests whether two float values are within a small enough tolerance of each other.
* @param a first float to compare
* @param b second float to compare
* @return whether the two values are within a small enough tolerance value
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 8da630c95135..b380e259577c 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -78,6 +78,24 @@ public class InputSettings {
public static final int DEFAULT_POINTER_SPEED = 0;
/**
+ * Pointer Speed: The minimum (slowest) mouse scrolling speed (-7).
+ * @hide
+ */
+ public static final int MIN_MOUSE_SCROLLING_SPEED = -7;
+
+ /**
+ * Pointer Speed: The maximum (fastest) mouse scrolling speed (7).
+ * @hide
+ */
+ public static final int MAX_MOUSE_SCROLLING_SPEED = 7;
+
+ /**
+ * Pointer Speed: The default mouse scrolling speed (0).
+ * @hide
+ */
+ public static final int DEFAULT_MOUSE_SCROLLING_SPEED = 0;
+
+ /**
* Bounce Keys Threshold: The default value of the threshold (500 ms).
*
* @hide
@@ -650,6 +668,54 @@ public class InputSettings {
}
/**
+ * Gets the mouse scrolling speed.
+ *
+ * The returned value only applies when mouse scrolling acceleration is not enabled.
+ *
+ * @param context The application context.
+ * @return The mouse scrolling speed as a value between {@link #MIN_MOUSE_SCROLLING_SPEED} and
+ * {@link #MAX_MOUSE_SCROLLING_SPEED}, or the default value
+ * {@link #DEFAULT_MOUSE_SCROLLING_SPEED}.
+ *
+ * @hide
+ */
+ public static int getMouseScrollingSpeed(@NonNull Context context) {
+ if (!isMouseScrollingAccelerationFeatureFlagEnabled()) {
+ return 0;
+ }
+
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.MOUSE_SCROLLING_SPEED, DEFAULT_MOUSE_SCROLLING_SPEED,
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Sets the mouse scrolling speed, and saves it in the settings.
+ *
+ * The new speed will only apply when mouse scrolling acceleration is not enabled.
+ *
+ * @param context The application context.
+ * @param speed The mouse scrolling speed as a value between {@link #MIN_MOUSE_SCROLLING_SPEED}
+ * and {@link #MAX_MOUSE_SCROLLING_SPEED}, or the default value
+ * {@link #DEFAULT_MOUSE_SCROLLING_SPEED}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setMouseScrollingSpeed(@NonNull Context context, int speed) {
+ if (isMouseScrollingAccelerationEnabled(context)) {
+ return;
+ }
+
+ if (speed < MIN_MOUSE_SCROLLING_SPEED || speed > MAX_MOUSE_SCROLLING_SPEED) {
+ throw new IllegalArgumentException("speed out of range");
+ }
+
+ Settings.System.putIntForUser(context.getContentResolver(),
+ Settings.System.MOUSE_SCROLLING_SPEED, speed, UserHandle.USER_CURRENT);
+ }
+
+ /**
* Whether mouse vertical scrolling is reversed. This applies only to connected mice.
*
* @param context The application context.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c94526bcdcd7..2656a7b58c8d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6372,6 +6372,19 @@ public final class Settings {
"mouse_pointer_acceleration_enabled";
/**
+ * Mouse scrolling speed setting.
+ *
+ * This is an integer value in a range between -7 and +7, so there are 15 possible values.
+ * The setting only applies when mouse scrolling acceleration is not enabled.
+ * -7 = slowest
+ * 0 = default speed
+ * +7 = fastest
+ *
+ * @hide
+ */
+ public static final String MOUSE_SCROLLING_SPEED = "mouse_scrolling_speed";
+
+ /**
* Pointer fill style, specified by
* {@link android.view.PointerIcon.PointerIconVectorStyleFill} constants.
*
@@ -6623,6 +6636,7 @@ public final class Settings {
PRIVATE_SETTINGS.add(MOUSE_POINTER_ACCELERATION_ENABLED);
PRIVATE_SETTINGS.add(PREFERRED_REGION);
PRIVATE_SETTINGS.add(MOUSE_SCROLLING_ACCELERATION);
+ PRIVATE_SETTINGS.add(MOUSE_SCROLLING_SPEED);
}
/**
@@ -17395,13 +17409,6 @@ public final class Settings {
/**
- * Whether back preview animations are played when user does a back gesture or presses
- * the back button.
- * @hide
- */
- public static final String ENABLE_BACK_ANIMATION = "enable_back_animation";
-
- /**
* An allow list of packages for which the user has granted the permission to communicate
* across profiles.
*
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index 5c38a1597433..195896dc8edf 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -81,7 +81,7 @@ public final class InputEventConsistencyVerifier {
// Bitfield of pointer ids that are currently down.
// Assumes that the largest possible pointer id is 31, which is potentially subject to change.
- // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+ // (See MAX_POINTER_ID in frameworks/native/include/input/input.h)
private int mTouchEventStreamPointers;
// The device id and source of the current stream of touch events.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 833f2d98554e..e665c08c63e4 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -160,6 +160,10 @@ public final class SurfaceControl implements Parcelable {
float l, float t, float r, float b);
private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
float cornerRadius);
+ private static native void nativeSetClientDrawnCornerRadius(long transactionObj,
+ long nativeObject, float clientDrawnCornerRadius);
+ private static native void nativeSetClientDrawnShadows(long transactionObj,
+ long nativeObject, float clientDrawnShadows);
private static native void nativeSetBackgroundBlurRadius(long transactionObj, long nativeObject,
int blurRadius);
private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
@@ -3654,6 +3658,66 @@ public final class SurfaceControl implements Parcelable {
return this;
}
+
+ /**
+ * Disables corner radius of a {@link SurfaceControl}. When the radius set by
+ * {@link Transaction#setCornerRadius(SurfaceControl, float)} is equal to
+ * clientDrawnCornerRadius the corner radius drawn by SurfaceFlinger is disabled.
+ *
+ * @param sc SurfaceControl
+ * @param clientDrawnCornerRadius Corner radius drawn by the client
+ * @return Itself.
+ * @hide
+ */
+ @NonNull
+ public Transaction setClientDrawnCornerRadius(@NonNull SurfaceControl sc,
+ float clientDrawnCornerRadius) {
+ checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setClientDrawnCornerRadius", this, sc, "clientDrawnCornerRadius="
+ + clientDrawnCornerRadius);
+ }
+ if (Flags.ignoreCornerRadiusAndShadows()) {
+ nativeSetClientDrawnCornerRadius(mNativeObject, sc.mNativeObject,
+ clientDrawnCornerRadius);
+ } else {
+ Log.w(TAG, "setClientDrawnCornerRadius was called but"
+ + "ignore_corner_radius_and_shadows flag is disabled");
+ }
+
+ return this;
+ }
+
+ /**
+ * Disables shadows of a {@link SurfaceControl}. When the radius set by
+ * {@link Transaction#setClientDrawnShadows(SurfaceControl, float)} is equal to
+ * clientDrawnShadowRadius the shadows drawn by SurfaceFlinger is disabled.
+ *
+ * @param sc SurfaceControl
+ * @param clientDrawnShadowRadius Shadow radius drawn by the client
+ * @return Itself.
+ * @hide
+ */
+ @NonNull
+ public Transaction setClientDrawnShadows(@NonNull SurfaceControl sc,
+ float clientDrawnShadowRadius) {
+ checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setClientDrawnShadows", this, sc,
+ "clientDrawnShadowRadius=" + clientDrawnShadowRadius);
+ }
+ if (Flags.ignoreCornerRadiusAndShadows()) {
+ nativeSetClientDrawnShadows(mNativeObject, sc.mNativeObject,
+ clientDrawnShadowRadius);
+ } else {
+ Log.w(TAG, "setClientDrawnShadows was called but"
+ + "ignore_corner_radius_and_shadows flag is disabled");
+ }
+ return this;
+ }
+
/**
* Sets the background blur radius of the {@link SurfaceControl}.
*
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index f50ea9106a61..25bd713d9191 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -453,6 +453,7 @@ public final class WindowManagerGlobal {
try {
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
+ Log.e(TAG, "Couldn't add view: " + view, e);
final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
// BadTokenException or InvalidDisplayException, clean up.
if (viewIndex >= 0) {
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 1f341caa8ed3..6d2c0d0061dd 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -30,7 +30,8 @@ import java.lang.annotation.RetentionPolicy;
* @hide
*/
public interface WindowManagerPolicyConstants {
- // Policy flags. These flags are also defined in frameworks/base/include/ui/Input.h and
+ // Policy flags. These flags are also defined in
+ // frameworks/native/include/input/Input.h and
// frameworks/native/libs/input/android/os/IInputConstants.aidl
int FLAG_WAKE = 0x00000001;
int FLAG_VIRTUAL = 0x00000002;
diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
index b3bd92b37357..c871d568e625 100644
--- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
+++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
@@ -45,3 +45,10 @@ flag {
description: "If true, the APIs to manage content protection device policy will be enabled."
bug: "319477846"
}
+
+flag {
+ name: "exported_settings_activity_enabled"
+ namespace: "content_protection"
+ description: "If true, the content protection Settings Activity will be exported for launching externally."
+ bug: "385310141"
+}
diff --git a/core/java/android/widget/flags/flags.aconfig b/core/java/android/widget/flags/flags.aconfig
index 88eb0438ec4b..83a4c8676a38 100644
--- a/core/java/android/widget/flags/flags.aconfig
+++ b/core/java/android/widget/flags/flags.aconfig
@@ -2,7 +2,7 @@ package: "android.widget.flags"
container: "system"
flag {
name: "enable_fading_view_group"
- namespace: "system_performance"
+ namespace: "wear_systems"
description: "FRP screen during OOBE must have fading and scaling animation in Wear Watches"
bug: "348515581"
metadata {
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index bb4770768cb1..8ff2e6aebdd0 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -91,6 +91,14 @@ flag {
}
flag {
+ name: "ignore_corner_radius_and_shadows"
+ namespace: "window_surfaces"
+ description: "Ignore the corner radius and shadows of a SurfaceControl"
+ bug: "375624570"
+ is_fixed_read_only: true
+} # ignore_corner_radius_and_shadows
+
+flag {
name: "jank_api"
namespace: "window_surfaces"
description: "Adds the jank data listener to AttachedSurfaceControl"
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 091f86ec9234..de3e0d3faf43 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -113,13 +113,6 @@ flag {
}
flag {
- name: "predictive_back_system_anims"
- namespace: "systemui"
- description: "Predictive back for system animations"
- bug: "320510464"
-}
-
-flag {
name: "remove_activity_starter_dream_callback"
namespace: "windowing_frontend"
description: "Avoid a race with DreamManagerService callbacks for isDreaming by checking Activity state directly"
@@ -423,7 +416,7 @@ flag {
flag {
name: "port_window_size_animation"
- namespace: "systemui"
+ namespace: "windowing_frontend"
description: "Port window-resize animation from legacy to shell"
bug: "384976265"
}
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index d62c8f378af0..73c2265d5f6e 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -53,21 +53,21 @@ oneway interface IKeyguardService {
*
* @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the specific reason
* we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT.
- * @param cameraGestureTriggered whether the camera gesture was triggered between
- * {@link #onStartedGoingToSleep} and this method; if it's been
- * triggered, we shouldn't lock the device.
+ * @param powerButtonLaunchGestureTriggered whether the power button double tap gesture was
+ * triggered between {@link #onStartedGoingToSleep} and this
+ * method; if it's been triggered, we shouldn't lock the device.
*/
- void onFinishedGoingToSleep(int pmSleepReason, boolean cameraGestureTriggered);
+ void onFinishedGoingToSleep(int pmSleepReason, boolean powerButtonLaunchGestureTriggered);
/**
* Called when the device has started waking up.
* @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the reason we're waking up,
* such as WAKE_REASON_POWER_BUTTON or WAKE_REASON_GESTURE.
- * @param cameraGestureTriggered Whether we're waking up due to a power button double tap
- * gesture.
+ * @param powerButtonLaunchGestureTriggered Whether we're waking up due to a power button
+ * double tap gesture.
*/
- void onStartedWakingUp(int pmWakeReason, boolean cameraGestureTriggered);
+ void onStartedWakingUp(int pmWakeReason, boolean powerButtonLaunchGestureTriggered);
/**
* Called when the device has finished waking up.
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index 8df3f2abcafd..e522b508b44b 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -94,14 +94,21 @@ public final class RavenwoodEnvironment {
/** Used for testing */
@Disabled
- @ChangeId public static final long TEST_COMPAT_ID_2 = 368131701L;
+ @ChangeId
+ public static final long TEST_COMPAT_ID_2 = 368131701L;
/** Used for testing */
@EnabledAfter(targetSdkVersion = S)
- @ChangeId public static final long TEST_COMPAT_ID_3 = 368131659L;
+ @ChangeId
+ public static final long TEST_COMPAT_ID_3 = 368131659L;
/** Used for testing */
@EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
- @ChangeId public static final long TEST_COMPAT_ID_4 = 368132057L;
+ @ChangeId
+ public static final long TEST_COMPAT_ID_4 = 368132057L;
+
+ /** Used for testing */
+ @ChangeId
+ public static final long TEST_COMPAT_ID_5 = 387558811L;
}
}
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 1e7bfe32ba79..e6364a96bd9f 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -111,9 +111,8 @@ static void DeleteGuardedApkAssets(Guarded<AssetManager2::ApkAssetsPtr>& apk_ass
class LoaderAssetsProvider : public AssetsProvider {
public:
static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
- return (!assets_provider) ? EmptyAssetsProvider::Create()
- : std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider(
- env, assets_provider));
+ return std::unique_ptr<AssetsProvider>{
+ assets_provider ? new LoaderAssetsProvider(env, assets_provider) : nullptr};
}
bool ForEachFile(const std::string& /* root_path */,
@@ -129,8 +128,8 @@ class LoaderAssetsProvider : public AssetsProvider {
return debug_name_;
}
- bool IsUpToDate() const override {
- return true;
+ UpToDate IsUpToDate() const override {
+ return UpToDate::Always;
}
~LoaderAssetsProvider() override {
@@ -212,7 +211,7 @@ class LoaderAssetsProvider : public AssetsProvider {
auto string_result = static_cast<jstring>(env->CallObjectMethod(
assets_provider_, gAssetsProviderOffsets.toString));
ScopedUtfChars str(env, string_result);
- debug_name_ = std::string(str.c_str(), str.size());
+ debug_name_ = std::string(str.c_str());
}
// The global reference to the AssetsProvider
@@ -243,10 +242,10 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma
apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
break;
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()),
- std::move(loader_assets),
- property_flags);
- break;
+ apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()),
+ MultiAssetsProvider::Create(std::move(loader_assets)),
+ property_flags);
+ break;
case FORMAT_DIRECTORY: {
auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
DirectoryAssetsProvider::Create(path.c_str()));
@@ -316,10 +315,11 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t
break;
}
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTable(
- AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */),
- std::move(loader_assets), property_flags);
- break;
+ apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFd(std::move(dup_fd),
+ nullptr /* path */),
+ MultiAssetsProvider::Create(std::move(loader_assets)),
+ property_flags);
+ break;
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
@@ -386,12 +386,15 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_
break;
}
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTable(
- AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */,
- static_cast<off64_t>(offset),
- static_cast<off64_t>(length)),
- std::move(loader_assets), property_flags);
- break;
+ apk_assets =
+ ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFd(std::move(dup_fd),
+ nullptr /* path */,
+ static_cast<off64_t>(offset),
+ static_cast<off64_t>(
+ length)),
+ MultiAssetsProvider::Create(std::move(loader_assets)),
+ property_flags);
+ break;
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
@@ -408,13 +411,16 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_
}
static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
- auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
- if (apk_assets == nullptr) {
- const std::string error_msg =
- base::StringPrintf("Failed to load empty assets with provider %p", (void*)assets_provider);
- jniThrowException(env, "java/io/IOException", error_msg.c_str());
- return 0;
- }
+ auto apk_assets = ApkAssets::Load(MultiAssetsProvider::Create(
+ LoaderAssetsProvider::Create(env, assets_provider)),
+ flags);
+ if (apk_assets == nullptr) {
+ const std::string error_msg =
+ base::StringPrintf("Failed to load empty assets with provider %p",
+ (void*)assets_provider);
+ jniThrowException(env, "java/io/IOException", error_msg.c_str());
+ return 0;
+ }
return CreateGuardedApkAssets(std::move(apk_assets));
}
@@ -443,10 +449,10 @@ static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr)
return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
}
-static jboolean NativeIsUpToDate(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+static jint NativeIsUpToDate(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
auto apk_assets = scoped_apk_assets->get();
- return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
+ return (jint)apk_assets->IsUpToDate();
}
static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
@@ -558,7 +564,7 @@ static const JNINativeMethod gApkAssetsMethods[] = {
{"nativeGetDebugName", "(J)Ljava/lang/String;", (void*)NativeGetDebugName},
{"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
// @CriticalNative
- {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
+ {"nativeIsUpToDate", "(J)I", (void*)NativeIsUpToDate},
{"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
{"nativeGetOverlayableInfo", "(JLjava/lang/String;)Landroid/content/om/OverlayableInfo;",
(void*)NativeGetOverlayableInfo},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 57bfc7086ed5..b2649a471b8a 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -198,7 +198,7 @@ static jobject NativeGetOverlayableMap(JNIEnv* env, jclass /*clazz*/, jlong ptr,
auto assetmanager = LockAndStartAssetManager(ptr);
const ScopedUtfChars package_name_utf8(env, package_name);
CHECK(package_name_utf8.c_str() != nullptr);
- const std::string std_package_name(package_name_utf8.c_str());
+ const std::string_view std_package_name(package_name_utf8.c_str());
const std::unordered_map<std::string, std::string>* map = nullptr;
assetmanager->ForEachPackage([&](const std::string& this_package_name, uint8_t package_id) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0c243d1dc185..6f69e4005b80 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1113,6 +1113,22 @@ static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionOb
transaction->setCornerRadius(ctrl, cornerRadius);
}
+static void nativeSetClientDrawnCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jfloat clientDrawnCornerRadius) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setClientDrawnCornerRadius(ctrl, clientDrawnCornerRadius);
+}
+
+static void nativeSetClientDrawnShadows(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jfloat clientDrawnShadowRadius) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setClientDrawnShadowRadius(ctrl, clientDrawnShadowRadius);
+}
+
static void nativeSetBackgroundBlurRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint blurRadius) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2547,6 +2563,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetCrop },
{"nativeSetCornerRadius", "(JJF)V",
(void*)nativeSetCornerRadius },
+ {"nativeSetClientDrawnCornerRadius", "(JJF)V",
+ (void*) nativeSetClientDrawnCornerRadius },
+ {"nativeSetClientDrawnShadows", "(JJF)V",
+ (void*) nativeSetClientDrawnShadows },
{"nativeSetBackgroundBlurRadius", "(JJI)V",
(void*)nativeSetBackgroundBlurRadius },
{"nativeSetLayerStack", "(JJI)V",
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 0d99200f4e6f..64c9f540a97b 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -229,6 +229,7 @@ message SystemSettingsProto {
optional SettingProto swap_primary_button = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto scrolling_acceleration = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto pointer_acceleration_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto scrolling_speed = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Mouse mouse = 38;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dc954718d623..ed021b64f7a0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5405,10 +5405,9 @@
<permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME"
android:protectionLevel="signature" />
- <!-- @FlaggedApi("com.android.server.accessibility.motion_event_observing")
- @hide
- @TestApi
- Allows an accessibility service to observe motion events without consuming them. -->
+ <!-- @TestApi Allows an accessibility service to observe motion events
+ without consuming them.
+ @hide -->
<permission android:name="android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING"
android:protectionLevel="signature" />
diff --git a/core/res/res/layout/notification_2025_expand_button.xml b/core/res/res/layout/notification_2025_expand_button.xml
index c8263c26f38f..1c367544c90a 100644
--- a/core/res/res/layout/notification_2025_expand_button.xml
+++ b/core/res/res/layout/notification_2025_expand_button.xml
@@ -22,6 +22,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:contentDescription="@string/expand_button_content_description_collapsed"
+ android:padding="@dimen/notification_2025_margin"
>
<LinearLayout
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index c827dcb16584..f108ce5bd1b9 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -168,7 +168,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
- android:layout_margin="@dimen/notification_2025_margin"
/>
</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index ce38c1645fb1..6f3c15adb082 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -70,7 +70,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
- android:layout_margin="@dimen/notification_2025_margin"
/>
</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 0021b8384847..bd17a3a0a74e 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -189,7 +189,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
- android:layout_margin="@dimen/notification_2025_margin"
/>
</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index f3e4ce13ff4b..edbebb17f825 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -193,7 +193,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
- android:layout_margin="@dimen/notification_2025_margin"
/>
</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_conversation.xml b/core/res/res/layout/notification_2025_template_conversation.xml
index 6be5a1cee7f9..24b6ad09d3f7 100644
--- a/core/res/res/layout/notification_2025_template_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_conversation.xml
@@ -152,7 +152,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
- android:layout_margin="@dimen/notification_2025_margin"
/>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml
index 3f34eb3cbf0c..0c07053d428a 100644
--- a/core/res/res/layout/notification_2025_template_header.xml
+++ b/core/res/res/layout/notification_2025_template_header.xml
@@ -85,7 +85,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
- android:layout_margin="@dimen/notification_2025_margin"
android:layout_alignParentEnd="true" />
<include layout="@layout/notification_close_button"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 45a5d85a097d..c50c5e9d3341 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4244,12 +4244,19 @@
is non-interactive. -->
<bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
- <!-- Allow the gesture to double tap the power button to trigger a target action. -->
- <bool name="config_doubleTapPowerGestureEnabled">true</bool>
- <!-- Default target action for double tap of the power button gesture.
+ <!-- Controls the double tap power button gesture to trigger a target action.
+ 0: Gesture is disabled
+ 1: Launch camera mode, allowing the user to disable/enable the double tap power gesture
+ from launching the camera application.
+ 2: Multi target mode, allowing the user to select one of the targets defined in
+ config_doubleTapPowerGestureMultiTargetDefaultAction and to disable/enable the double
+ tap power gesture from triggering the selected target action.
+ -->
+ <integer name="config_doubleTapPowerGestureMode">2</integer>
+ <!-- Default target action for double tap of the power button gesture in multi target mode.
0: Launch camera
1: Launch wallet -->
- <integer name="config_defaultDoubleTapPowerGestureAction">0</integer>
+ <integer name="config_doubleTapPowerGestureMultiTargetDefaultAction">0</integer>
<!-- Allow the gesture to quick tap the power button multiple times to start the emergency sos
experience while the device is non-interactive. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a18f923d625b..24e7057320ff 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3167,8 +3167,8 @@
<java-symbol type="integer" name="config_cameraLiftTriggerSensorType" />
<java-symbol type="string" name="config_cameraLiftTriggerSensorStringType" />
<java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
- <java-symbol type="bool" name="config_doubleTapPowerGestureEnabled" />
- <java-symbol type="integer" name="config_defaultDoubleTapPowerGestureAction" />
+ <java-symbol type="integer" name="config_doubleTapPowerGestureMode" />
+ <java-symbol type="integer" name="config_doubleTapPowerGestureMultiTargetDefaultAction" />
<java-symbol type="bool" name="config_emergencyGestureEnabled" />
<java-symbol type="bool" name="config_defaultEmergencyGestureEnabled" />
<java-symbol type="bool" name="config_defaultEmergencyGestureSoundEnabled" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index bb5380e1312d..06cd44e6544d 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
<!-- Arab Emirates -->
- <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253|6568" />
+ <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253|6568|999" />
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -63,8 +63,8 @@
<!-- Burkina Faso: 1-4 digits (standard system default, not country specific) -->
<shortcode country="bf" pattern="\\d{1,4}" free="3558" />
- <!-- Bulgaria: 4-5 digits, plus EU -->
- <shortcode country="bg" pattern="\\d{4,5}" premium="18(?:16|423)|19(?:1[56]|35)" free="116\\d{3}|1988|1490" />
+ <!-- Bulgaria: 4-6 digits, plus EU -->
+ <shortcode country="bg" pattern="\\d{4,6}" premium="18(?:16|423)|19(?:1[56]|35)" free="116\\d{3}|1988|1490|162055" />
<!-- Bahrain: 1-5 digits (standard system default, not country specific) -->
<shortcode country="bh" pattern="\\d{1,5}" free="81181|85999" />
@@ -81,18 +81,21 @@
<!-- Canada: 5-6 digits -->
<shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" free="455677|24470" />
+ <!-- DR Congo: 1-6 digits, known premium codes listed -->
+ <shortcode country="cd" pattern="\\d{1,6}" free="444123" />
+
<!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
<shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075|30047" />
<!-- Chile: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="cl" pattern="\\d{4,5}" free="9963|9240|1038" />
+ <shortcode country="cl" pattern="\\d{4,5}" free="9963|9240|1038|4848" />
<!-- China: premium shortcodes start with "1066", free shortcodes start with "1065":
http://clients.txtnation.com/entries/197192-china-premium-sms-short-code-requirements -->
<shortcode country="cn" premium="1066.*" free="1065.*" />
<!-- Colombia: 1-6 digits (not confirmed) -->
- <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517|491289" />
+ <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517|491289|890119" />
<!-- Costa Rica -->
<shortcode country="cr" pattern="\\d{1,6}" free="466453" />
@@ -116,6 +119,9 @@
<!-- Dominican Republic: 1-6 digits (standard system default, not country specific) -->
<shortcode country="do" pattern="\\d{1,6}" free="912892|912" />
+ <!-- Algeria: 1-5 digits, known premium codes listed -->
+ <shortcode country="dz" pattern="\\d{1,5}" free="63071" />
+
<!-- Ecuador: 1-6 digits (standard system default, not country specific) -->
<shortcode country="ec" pattern="\\d{1,6}" free="466453|18512" />
@@ -123,20 +129,23 @@
http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht -->
<shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}|95034" />
- <!-- Egypt: 4-5 digits, known codes listed -->
- <shortcode country="eg" pattern="\\d{4,5}" free="1499|10020" />
+ <!-- Egypt: 4-6 digits, known codes listed -->
+ <shortcode country="eg" pattern="\\d{4,6}" free="1499|10020|100158" />
<!-- Spain: 5-6 digits: 25xxx, 27xxx, 280xx, 35xxx, 37xxx, 795xxx, 797xxx, 995xxx, 997xxx, plus EU.
http://www.legallink.es/?q=en/content/which-current-regulatory-status-premium-rate-services-spain -->
<shortcode country="es" premium="[23][57]\\d{3}|280\\d{2}|[79]9[57]\\d{3}" free="116\\d{3}|22791|222145|22189" />
+ <!-- Ethiopia: 1-4 digits, known codes listed -->
+ <shortcode country="et" pattern="\\d{1,4}" free="8527" />
+
<!-- Finland: 5-6 digits, premium 0600, 0700: http://en.wikipedia.org/wiki/Telephone_numbers_in_Finland -->
<shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}|14789|17110" />
<!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU:
http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements,
visual voicemail code for Orange: 21101 -->
- <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}|21101|20366|555|2051|33033" />
+ <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}|21101|20366|555|2051|33033|21727" />
<!-- United Kingdom (Great Britain): 4-6 digits, common codes [5-8]xxxx, plus EU:
http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf,
@@ -179,17 +188,17 @@
<shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477|6681" />
<!-- Iran: 4-8 digits, known premium codes listed -->
- <shortcode country="ir" pattern="\\d{4,8}" free="700791|700792|100016|30008360" />
+ <shortcode country="ir" pattern="\\d{4,8}" free="700792|100016|30008360" />
<!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU:
https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
<shortcode country="it" pattern="\\d{5}" premium="44[0-4]\\d{2}|47[0-4]\\d{2}|48[0-4]\\d{2}|44[5-9]\\d{4}|47[5-9]\\d{4}|48[5-9]\\d{4}|455\\d{2}|499\\d{2}" free="116\\d{3}|4112503|40\\d{0,12}" standard="430\\d{2}|431\\d{2}|434\\d{4}|435\\d{4}|439\\d{7}" />
<!-- Jordan: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="jo" pattern="\\d{1,5}" free="99066" />
+ <shortcode country="jo" pattern="\\d{1,5}" free="99066|99390" />
<!-- Japan: 8083 used by SOFTBANK_DCB_2 -->
- <shortcode country="jp" pattern="\\d{1,5}" free="8083" />
+ <shortcode country="jp" pattern="\\d{1,9}" free="8083|00050320" />
<!-- Kenya: 5 digits, known premium codes listed -->
<shortcode country="ke" pattern="\\d{5}" free="21725|21562|40520|23342|40023|24088|23054" />
@@ -206,6 +215,9 @@
<!-- Kuwait: 1-5 digits (standard system default, not country specific) -->
<shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006|55991|50976|7112" />
+ <!-- Lesotho: 4-5 digits, known codes listed -->
+ <shortcode country="ls" pattern="\\d{4,5}" free="32012" />
+
<!-- Lithuania: 3-5 digits, known premium codes listed, plus EU -->
<shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}|1399|1324" />
@@ -222,11 +234,14 @@
<!-- Macedonia: 1-6 digits (not confirmed), known premium codes listed -->
<shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
+ <!-- Mali: 1-5 digits, known codes listed -->
+ <shortcode country="ml" pattern="\\d{1,5}" free="36098" />
+
<!-- Mongolia : 1-6 digits (standard system default, not country specific) -->
<shortcode country="mn" pattern="\\d{1,6}" free="44444|45678|445566" />
<!-- Malawi: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="mw" pattern="\\d{1,5}" free="4276|4305" />
+ <shortcode country="mw" pattern="\\d{1,5}" free="4276|4305|4326" />
<!-- Mozambique: 1-5 digits (standard system default, not country specific) -->
<shortcode country="mz" pattern="\\d{1,5}" free="1714" />
@@ -323,11 +338,14 @@
<!-- Tajikistan: 4 digits, known premium codes listed -->
<shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
+ <!-- Timor-Leste 1-5 digits, known codes listed -->
+ <shortcode country="tl" pattern="\\d{1,5}" free="46645" />
+
<!-- Tanzania: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="tz" pattern="\\d{1,5}" free="15046|15234|15324|15610" />
+ <shortcode country="tz" pattern="\\d{1,5}" free="15046|15324|15610" />
- <!-- Tunisia: 5 digits, known premium codes listed -->
- <shortcode country="tn" pattern="\\d{5}" free="85799" />
+ <!-- Tunisia: 1-6 digits, known premium codes listed -->
+ <shortcode country="tn" pattern="\\d{1,6}" free="85799|772024" />
<!-- Turkey -->
<shortcode country="tr" pattern="\\d{1,5}" free="7529|5528|6493|3193" />
@@ -336,7 +354,7 @@
<shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
<!-- Uganda(UG): 4 digits (standard system default, not country specific) -->
- <shortcode country="ug" pattern="\\d{4}" free="8000|8009" />
+ <shortcode country="ug" pattern="\\d{4}" free="8009" />
<!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
visual voicemail code for T-Mobile: 122 -->
@@ -349,7 +367,7 @@
<shortcode country="ve" pattern="\\d{1,6}" free="538352" />
<!-- Vietnam: 1-6 digits (standard system default, not country specific) -->
- <shortcode country="vn" pattern="\\d{1,6}" free="5001|9055|8079|90002|118989" />
+ <shortcode country="vn" pattern="\\d{1,6}" free="5001|9055|90002|118989|46645" />
<!-- Mayotte (French Territory): 1-5 digits (not confirmed) -->
<shortcode country="yt" pattern="\\d{1,5}" free="38600,36300,36303,959" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 3bbb9519d016..1b6746ca4b63 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -313,6 +313,7 @@ android_ravenwood_test {
"res/xml/power_profile_test_modem.xml",
],
auto_gen_config: true,
+ team: "trendy_team_ravenwood",
}
test_module_config {
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index 37ef6cba8814..939bf2ec4b0a 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -207,7 +207,8 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
final ComponentInfo info = new ComponentInfo();
info.applicationInfo = new ApplicationInfo();
info.applicationInfo.uid = uid;
- return new RegisteredServicesCache.ServiceInfo<>(type, info, null);
+ return new RegisteredServicesCache.ServiceInfo<>(type, info, null /* componentName */,
+ 0 /* lastUpdateTime */);
}
private void assertNotEmptyFileCreated(TestServicesCache cache, int userId) {
@@ -301,7 +302,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
@Override
protected ServiceInfo<TestServiceType> parseServiceInfo(
- ResolveInfo resolveInfo) throws XmlPullParserException, IOException {
+ ResolveInfo resolveInfo, int userId) throws XmlPullParserException, IOException {
int size = mServices.size();
for (int i = 0; i < size; i++) {
Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
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 5613caf4427e..d26bb35e5481 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -120,7 +120,9 @@ public class NotificationProgressBarTest {
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
segments, points, progress, progressMax, isStyledByProgress);
- int fadedRed = 0x7FFF0000;
+ // Colors with 40% opacity
+ int fadedRed = 0x66FF0000;
+
List<Part> expected = new ArrayList<>(List.of(new Segment(1f, fadedRed, true)));
assertThat(parts).isEqualTo(expected);
@@ -199,8 +201,8 @@ public class NotificationProgressBarTest {
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
segments, points, progress, progressMax, isStyledByProgress);
- // Colors with 50% opacity
- int fadedGreen = 0x7F00FF00;
+ // Colors with 40% opacity
+ int fadedGreen = 0x6600FF00;
List<Part> expected = new ArrayList<>(List.of(
new Segment(0.50f, Color.RED),
@@ -223,9 +225,9 @@ public class NotificationProgressBarTest {
int progressMax = 100;
boolean isStyledByProgress = true;
- // Colors with 50% opacity
- int fadedBlue = 0x7F0000FF;
- int fadedYellow = 0x7FFFFF00;
+ // Colors with 40% opacity
+ int fadedBlue = 0x660000FF;
+ int fadedYellow = 0x66FFFF00;
List<Part> expected = new ArrayList<>(List.of(
new Segment(0.15f, Color.BLUE),
@@ -261,9 +263,9 @@ public class NotificationProgressBarTest {
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
segments, points, progress, progressMax, isStyledByProgress);
- // Colors with 50% opacity
- int fadedGreen = 0x7F00FF00;
- int fadedYellow = 0x7FFFFF00;
+ // Colors with 40% opacity
+ int fadedGreen = 0x6600FF00;
+ int fadedYellow = 0x66FFFF00;
List<Part> expected = new ArrayList<>(List.of(
new Segment(0.15f, Color.RED),
diff --git a/core/tests/systemproperties/Android.bp b/core/tests/systemproperties/Android.bp
index ed99a1f5cc4a..9197dec00d25 100644
--- a/core/tests/systemproperties/Android.bp
+++ b/core/tests/systemproperties/Android.bp
@@ -44,4 +44,5 @@ android_ravenwood_test {
"src/**/*.java",
],
auto_gen_config: true,
+ team: "trendy_team_ravenwood",
}
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index 7cf49ab5c376..5011f7ac1e7c 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -69,4 +69,5 @@ android_ravenwood_test {
"src/com/android/internal/util/**/*.java",
],
auto_gen_config: true,
+ team: "trendy_team_ravenwood",
}
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
index eecf199a3ec2..03076c0940a4 100644
--- a/libs/WindowManager/Shell/multivalentTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -35,7 +35,6 @@ android_app {
android_robolectric_test {
name: "WMShellRobolectricTests",
instrumentation_for: "WindowManagerShellRobolectric",
- upstream: true,
java_resource_dirs: [
"robolectric/config",
],
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
index 4300e84e8044..2ca011bfe000 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
@@ -16,10 +16,12 @@
package com.android.wm.shell.shared;
+import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+
import android.annotation.IntDef;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.TaskInfo;
-import android.app.WindowConfiguration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,11 +30,14 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.shared.split.SplitBounds;
+import kotlin.collections.CollectionsKt;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Simple container for recent tasks which should be presented as a single task within the
@@ -43,11 +48,13 @@ public class GroupedTaskInfo implements Parcelable {
public static final int TYPE_FULLSCREEN = 1;
public static final int TYPE_SPLIT = 2;
public static final int TYPE_FREEFORM = 3;
+ public static final int TYPE_MIXED = 4;
@IntDef(prefix = {"TYPE_"}, value = {
TYPE_FULLSCREEN,
TYPE_SPLIT,
- TYPE_FREEFORM
+ TYPE_FREEFORM,
+ TYPE_MIXED
})
public @interface GroupType {}
@@ -64,7 +71,7 @@ public class GroupedTaskInfo implements Parcelable {
* TYPE_SPLIT: Contains the two split roots of each side
* TYPE_FREEFORM: Contains the set of tasks currently in freeform mode
*/
- @NonNull
+ @Nullable
protected final List<TaskInfo> mTasks;
/**
@@ -85,6 +92,14 @@ public class GroupedTaskInfo implements Parcelable {
protected final int[] mMinimizedTaskIds;
/**
+ * Only set for TYPE_MIXED.
+ *
+ * The mixed set of task infos in this group.
+ */
+ @Nullable
+ protected final List<GroupedTaskInfo> mGroupedTasks;
+
+ /**
* Create new for a stack of fullscreen tasks
*/
public static GroupedTaskInfo forFullscreenTasks(@NonNull TaskInfo task) {
@@ -111,18 +126,41 @@ public class GroupedTaskInfo implements Parcelable {
minimizedFreeformTasks.stream().mapToInt(i -> i).toArray());
}
+ /**
+ * Create new for a group of grouped task infos, those grouped task infos may not be mixed
+ * themselves (ie. multiple depths of mixed grouped task infos are not allowed).
+ */
+ public static GroupedTaskInfo forMixed(@NonNull List<GroupedTaskInfo> groupedTasks) {
+ if (groupedTasks.isEmpty()) {
+ throw new IllegalArgumentException("Expected non-empty grouped task list");
+ }
+ if (groupedTasks.stream().anyMatch(task -> task.mType == TYPE_MIXED)) {
+ throw new IllegalArgumentException("Unexpected grouped task list");
+ }
+ return new GroupedTaskInfo(groupedTasks);
+ }
+
private GroupedTaskInfo(
@NonNull List<TaskInfo> tasks,
@Nullable SplitBounds splitBounds,
@GroupType int type,
@Nullable int[] minimizedFreeformTaskIds) {
mTasks = tasks;
+ mGroupedTasks = null;
mSplitBounds = splitBounds;
mType = type;
mMinimizedTaskIds = minimizedFreeformTaskIds;
ensureAllMinimizedIdsPresent(tasks, minimizedFreeformTaskIds);
}
+ private GroupedTaskInfo(@NonNull List<GroupedTaskInfo> groupedTasks) {
+ mTasks = null;
+ mGroupedTasks = groupedTasks;
+ mSplitBounds = null;
+ mType = TYPE_MIXED;
+ mMinimizedTaskIds = null;
+ }
+
private void ensureAllMinimizedIdsPresent(
@NonNull List<TaskInfo> tasks,
@Nullable int[] minimizedFreeformTaskIds) {
@@ -141,26 +179,47 @@ public class GroupedTaskInfo implements Parcelable {
for (int i = 0; i < numTasks; i++) {
mTasks.add(new TaskInfo(parcel));
}
+ mGroupedTasks = parcel.createTypedArrayList(GroupedTaskInfo.CREATOR);
mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
mType = parcel.readInt();
mMinimizedTaskIds = parcel.createIntArray();
}
/**
- * Get primary {@link RecentTaskInfo}
+ * If TYPE_MIXED, returns the root of the grouped tasks
+ * For all other types, returns this task itself
+ */
+ @NonNull
+ public GroupedTaskInfo getBaseGroupedTask() {
+ if (mType == TYPE_MIXED) {
+ return mGroupedTasks.getFirst();
+ }
+ return this;
+ }
+
+ /**
+ * Get primary {@link TaskInfo}.
+ *
+ * @throws IllegalStateException if the group is TYPE_MIXED.
*/
@NonNull
public TaskInfo getTaskInfo1() {
+ if (mType == TYPE_MIXED) {
+ throw new IllegalStateException("No indexed tasks for a mixed task");
+ }
return mTasks.getFirst();
}
/**
- * Get secondary {@link RecentTaskInfo}.
+ * Get secondary {@link TaskInfo}, used primarily for TYPE_SPLIT.
*
- * Used in split screen.
+ * @throws IllegalStateException if the group is TYPE_MIXED.
*/
@Nullable
public TaskInfo getTaskInfo2() {
+ if (mType == TYPE_MIXED) {
+ throw new IllegalStateException("No indexed tasks for a mixed task");
+ }
if (mTasks.size() > 1) {
return mTasks.get(1);
}
@@ -172,9 +231,7 @@ public class GroupedTaskInfo implements Parcelable {
*/
@Nullable
public TaskInfo getTaskById(int taskId) {
- return mTasks.stream()
- .filter(task -> task.taskId == taskId)
- .findFirst().orElse(null);
+ return CollectionsKt.firstOrNull(getTaskInfoList(), taskInfo -> taskInfo.taskId == taskId);
}
/**
@@ -182,35 +239,59 @@ public class GroupedTaskInfo implements Parcelable {
*/
@NonNull
public List<TaskInfo> getTaskInfoList() {
- return mTasks;
+ if (mType == TYPE_MIXED) {
+ return CollectionsKt.flatMap(mGroupedTasks, groupedTaskInfo -> groupedTaskInfo.mTasks);
+ } else {
+ return mTasks;
+ }
}
/**
* @return Whether this grouped task contains a task with the given {@code taskId}.
*/
public boolean containsTask(int taskId) {
- return mTasks.stream()
- .anyMatch((task -> task.taskId == taskId));
+ return getTaskById(taskId) != null;
}
/**
- * Return {@link SplitBounds} if this is a split screen entry or {@code null}
+ * Returns whether the group is of the given type, if this is a TYPE_MIXED group, then returns
+ * whether the root task info is of the given type.
+ */
+ public boolean isBaseType(@GroupType int type) {
+ return getBaseGroupedTask().mType == type;
+ }
+
+ /**
+ * Return {@link SplitBounds} if this is a split screen entry or {@code null}. Only valid for
+ * TYPE_SPLIT.
*/
@Nullable
public SplitBounds getSplitBounds() {
+ if (mType == TYPE_MIXED) {
+ throw new IllegalStateException("No split bounds for a mixed task");
+ }
return mSplitBounds;
}
/**
- * Get type of this recents entry. One of {@link GroupType}
+ * Get type of this recents entry. One of {@link GroupType}.
+ * Note: This is deprecated, callers should use `isBaseType()` and not make assumptions about
+ * specific group types
*/
+ @Deprecated
@GroupType
public int getType() {
return mType;
}
+ /**
+ * Returns the set of minimized task ids, only valid for TYPE_FREEFORM.
+ */
@Nullable
public int[] getMinimizedTaskIds() {
+ if (mType == TYPE_MIXED) {
+ throw new IllegalStateException("No minimized task ids for a mixed task");
+ }
return mMinimizedTaskIds;
}
@@ -222,67 +303,64 @@ public class GroupedTaskInfo implements Parcelable {
GroupedTaskInfo other = (GroupedTaskInfo) obj;
return mType == other.mType
&& Objects.equals(mTasks, other.mTasks)
+ && Objects.equals(mGroupedTasks, other.mGroupedTasks)
&& Objects.equals(mSplitBounds, other.mSplitBounds)
&& Arrays.equals(mMinimizedTaskIds, other.mMinimizedTaskIds);
}
@Override
public int hashCode() {
- return Objects.hash(mType, mTasks, mSplitBounds, Arrays.hashCode(mMinimizedTaskIds));
+ return Objects.hash(mType, mTasks, mGroupedTasks, mSplitBounds,
+ Arrays.hashCode(mMinimizedTaskIds));
}
@Override
public String toString() {
StringBuilder taskString = new StringBuilder();
- for (int i = 0; i < mTasks.size(); i++) {
- if (i == 0) {
- taskString.append("Task");
- } else {
- taskString.append(", Task");
+ if (mType == TYPE_MIXED) {
+ taskString.append("GroupedTasks=" + mGroupedTasks.stream()
+ .map(GroupedTaskInfo::toString)
+ .collect(Collectors.joining(",\n\t", "[\n\t", "\n]")));
+ } else {
+ taskString.append("Tasks=" + mTasks.stream()
+ .map(taskInfo -> getTaskInfoDumpString(taskInfo))
+ .collect(Collectors.joining(", ", "[", "]")));
+ if (mSplitBounds != null) {
+ taskString.append(", SplitBounds=").append(mSplitBounds);
}
- taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks.get(i)));
+ taskString.append(", Type=" + typeToString(mType));
+ taskString.append(", Minimized Task IDs=" + Arrays.toString(mMinimizedTaskIds));
}
- if (mSplitBounds != null) {
- taskString.append(", SplitBounds: ").append(mSplitBounds);
- }
- taskString.append(", Type=");
- switch (mType) {
- case TYPE_FULLSCREEN:
- taskString.append("TYPE_FULLSCREEN");
- break;
- case TYPE_SPLIT:
- taskString.append("TYPE_SPLIT");
- break;
- case TYPE_FREEFORM:
- taskString.append("TYPE_FREEFORM");
- break;
- }
- taskString.append(", Minimized Task IDs: ");
- taskString.append(Arrays.toString(mMinimizedTaskIds));
return taskString.toString();
}
- private String getTaskInfo(TaskInfo taskInfo) {
+ private String getTaskInfoDumpString(TaskInfo taskInfo) {
if (taskInfo == null) {
return null;
}
+ final boolean isExcluded = (taskInfo.baseIntent.getFlags()
+ & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
return "id=" + taskInfo.taskId
- + " baseIntent=" +
- (taskInfo.baseIntent != null && taskInfo.baseIntent.getComponent() != null
- ? taskInfo.baseIntent.getComponent().flattenToString()
- : "null")
- + " winMode=" + WindowConfiguration.windowingModeToString(
- taskInfo.getWindowingMode());
+ + " winMode=" + windowingModeToString(taskInfo.getWindowingMode())
+ + " visReq=" + taskInfo.isVisibleRequested
+ + " vis=" + taskInfo.isVisible
+ + " excluded=" + isExcluded
+ + " baseIntent="
+ + (taskInfo.baseIntent != null && taskInfo.baseIntent.getComponent() != null
+ ? taskInfo.baseIntent.getComponent().flattenToShortString()
+ : "null");
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
// We don't use the parcel list methods because we want to only write the TaskInfo state
// and not the subclasses (Recents/RunningTaskInfo) whose fields are all deprecated
- parcel.writeInt(mTasks.size());
- for (int i = 0; i < mTasks.size(); i++) {
+ final int tasksSize = mTasks != null ? mTasks.size() : 0;
+ parcel.writeInt(tasksSize);
+ for (int i = 0; i < tasksSize; i++) {
mTasks.get(i).writeTaskToParcel(parcel, flags);
}
+ parcel.writeTypedList(mGroupedTasks);
parcel.writeTypedObject(mSplitBounds, flags);
parcel.writeInt(mType);
parcel.writeIntArray(mMinimizedTaskIds);
@@ -293,6 +371,16 @@ public class GroupedTaskInfo implements Parcelable {
return 0;
}
+ private String typeToString(@GroupType int type) {
+ return switch (type) {
+ case TYPE_FULLSCREEN -> "FULLSCREEN";
+ case TYPE_SPLIT -> "SPLIT";
+ case TYPE_FREEFORM -> "FREEFORM";
+ case TYPE_MIXED -> "MIXED";
+ default -> "UNKNOWN";
+ };
+ }
+
public static final Creator<GroupedTaskInfo> CREATOR = new Creator() {
@Override
public GroupedTaskInfo createFromParcel(Parcel in) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
index 06a55d3dbbd0..08079d94fcee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
@@ -19,7 +19,6 @@
package com.android.wm.shell.apptoweb
import android.app.assist.AssistContent
-import android.app.assist.AssistContent.EXTRA_SESSION_TRANSFER_WEB_URI
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_VIEW
@@ -113,5 +112,5 @@ fun getDomainVerificationUserState(
* Returns the web uri from the given [AssistContent].
*/
fun AssistContent.getSessionWebUri(): Uri? {
- return extras.getParcelable(EXTRA_SESSION_TRANSFER_WEB_URI) ?: webUri
+ return sessionTransferUri ?: webUri
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index e96bc02c1198..8dabd54a01ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -28,7 +28,6 @@ import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME;
-import static com.android.window.flags.Flags.predictiveBackSystemAnims;
import static com.android.window.flags.Flags.unifyBackNavigationTransition;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
@@ -40,23 +39,17 @@ import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.TaskInfo;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
-import android.database.ContentObserver;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.input.InputManager;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.provider.Settings.Global;
import android.util.Log;
import android.view.IRemoteAnimationRunner;
import android.view.InputDevice;
@@ -92,7 +85,6 @@ import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.TransitionUtil;
-import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -102,7 +94,6 @@ import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
/**
@@ -111,14 +102,7 @@ import java.util.function.Predicate;
public class BackAnimationController implements RemoteCallable<BackAnimationController>,
ConfigurationChangeListener {
private static final String TAG = "ShellBackPreview";
- private static final int SETTING_VALUE_OFF = 0;
- private static final int SETTING_VALUE_ON = 1;
- public static final boolean IS_ENABLED =
- SystemProperties.getInt("persist.wm.debug.predictive_back",
- SETTING_VALUE_ON) == SETTING_VALUE_ON;
-
- /** Predictive back animation developer option */
- private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
+
/**
* Max duration to wait for an animation to finish before triggering the real back.
*/
@@ -148,11 +132,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private boolean mReceivedNullNavigationInfo = false;
private final IActivityTaskManager mActivityTaskManager;
private final Context mContext;
- private final ContentResolver mContentResolver;
private final ShellController mShellController;
private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mShellExecutor;
- private final Handler mBgHandler;
private final WindowManager mWindowManager;
private final Transitions mTransitions;
@VisibleForTesting
@@ -245,7 +227,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@NonNull @ShellMainThread ShellExecutor shellExecutor,
- @NonNull @ShellBackgroundThread Handler backgroundHandler,
Context context,
@NonNull BackAnimationBackground backAnimationBackground,
ShellBackAnimationRegistry shellBackAnimationRegistry,
@@ -256,10 +237,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
shellInit,
shellController,
shellExecutor,
- backgroundHandler,
ActivityTaskManager.getService(),
context,
- context.getContentResolver(),
backAnimationBackground,
shellBackAnimationRegistry,
shellCommandHandler,
@@ -272,10 +251,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@NonNull @ShellMainThread ShellExecutor shellExecutor,
- @NonNull @ShellBackgroundThread Handler bgHandler,
@NonNull IActivityTaskManager activityTaskManager,
Context context,
- ContentResolver contentResolver,
@NonNull BackAnimationBackground backAnimationBackground,
ShellBackAnimationRegistry shellBackAnimationRegistry,
ShellCommandHandler shellCommandHandler,
@@ -285,10 +262,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShellExecutor = shellExecutor;
mActivityTaskManager = activityTaskManager;
mContext = context;
- mContentResolver = contentResolver;
mRequirePointerPilfer =
context.getResources().getBoolean(R.bool.config_backAnimationRequiresPointerPilfer);
- mBgHandler = bgHandler;
shellInit.addInitCallback(this::onInit, this);
mAnimationBackground = backAnimationBackground;
mShellBackAnimationRegistry = shellBackAnimationRegistry;
@@ -305,8 +280,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
private void onInit() {
- setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
- updateEnableAnimationFromFlags();
createAdapter();
mShellController.addExternalInterface(IBackAnimation.DESCRIPTOR,
this::createExternalInterface, this);
@@ -314,42 +287,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShellController.addConfigurationChangeListener(this);
}
- private void setupAnimationDeveloperSettingsObserver(
- @NonNull ContentResolver contentResolver,
- @NonNull @ShellBackgroundThread final Handler backgroundHandler) {
- if (predictiveBackSystemAnims()) {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation aconfig flag is enabled, therefore "
- + "developer settings flag is ignored and no content observer registered");
- return;
- }
- ContentObserver settingsObserver = new ContentObserver(backgroundHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- updateEnableAnimationFromFlags();
- }
- };
- contentResolver.registerContentObserver(
- Global.getUriFor(Global.ENABLE_BACK_ANIMATION),
- false, settingsObserver, UserHandle.USER_SYSTEM
- );
- }
-
- /**
- * Updates {@link BackAnimationController#mEnableAnimations} based on the current values of the
- * aconfig flag and the developer settings flag
- */
- @ShellBackgroundThread
- private void updateEnableAnimationFromFlags() {
- boolean isEnabled = predictiveBackSystemAnims() || isDeveloperSettingEnabled();
- mEnableAnimations.set(isEnabled);
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s", isEnabled);
- }
-
- private boolean isDeveloperSettingEnabled() {
- return Global.getInt(mContext.getContentResolver(),
- Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF) == SETTING_VALUE_ON;
- }
-
public BackAnimation getBackAnimationImpl() {
return mBackAnimation;
}
@@ -617,14 +554,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private void startBackNavigation(@NonNull BackTouchTracker touchTracker) {
try {
startLatencyTracking();
- final BackAnimationAdapter adapter = mEnableAnimations.get()
- ? mBackAnimationAdapter : null;
- if (adapter != null && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) {
- adapter.updateSupportedAnimators(
+ if (mBackAnimationAdapter != null
+ && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) {
+ mBackAnimationAdapter.updateSupportedAnimators(
mShellBackAnimationRegistry.getSupportedAnimators());
}
mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
- mNavigationObserver, adapter);
+ mNavigationObserver, mBackAnimationAdapter);
onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker);
} catch (RemoteException remoteException) {
Log.e(TAG, "Failed to initAnimation", remoteException);
@@ -696,9 +632,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
private boolean shouldDispatchToAnimator() {
- return mEnableAnimations.get()
- && mBackNavigationInfo != null
- && mBackNavigationInfo.isPrepareRemoteAnimation();
+ return mBackNavigationInfo != null && mBackNavigationInfo.isPrepareRemoteAnimation();
}
private void tryPilferPointers() {
@@ -1093,7 +1027,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
() -> mShellExecutor.execute(this::onBackAnimationFinished));
if (mApps.length >= 1) {
- mCurrentTracker.updateStartLocation();
BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
dispatchOnBackStarted(mActiveCallback, startEvent);
if (startEvent.getSwipeEdge() == EDGE_NONE) {
@@ -1194,7 +1127,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
*/
private void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "BackAnimationController state:");
- pw.println(prefix + " mEnableAnimations=" + mEnableAnimations.get());
pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted);
pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress);
pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 1408b6efc7f9..2600bcc18f72 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -108,7 +108,6 @@ import com.android.wm.shell.recents.TaskStackTransitionObserver;
import com.android.wm.shell.shared.ShellTransitions;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.annotations.ShellAnimationThread;
-import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.annotations.ShellSplashscreenThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -438,29 +437,24 @@ public abstract class WMShellBaseModule {
ShellInit shellInit,
ShellController shellController,
@ShellMainThread ShellExecutor shellExecutor,
- @ShellBackgroundThread Handler backgroundHandler,
BackAnimationBackground backAnimationBackground,
Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry,
ShellCommandHandler shellCommandHandler,
Transitions transitions,
@ShellMainThread Handler handler
) {
- if (BackAnimationController.IS_ENABLED) {
return shellBackAnimationRegistry.map(
(animations) ->
new BackAnimationController(
shellInit,
shellController,
shellExecutor,
- backgroundHandler,
context,
backAnimationBackground,
animations,
shellCommandHandler,
transitions,
handler));
- }
- return Optional.empty();
}
@BindsOptionalOf
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 03f388c9f1c9..9e2b9b20be16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -117,6 +117,7 @@ public abstract class Pip2Module {
PipTouchHandler pipTouchHandler,
PipAppOpsListener pipAppOpsListener,
PhonePipMenuController pipMenuController,
+ PipUiEventLogger pipUiEventLogger,
@ShellMainThread ShellExecutor mainExecutor) {
if (!PipUtils.isPip2ExperimentEnabled()) {
return Optional.empty();
@@ -126,7 +127,7 @@ public abstract class Pip2Module {
displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
- mainExecutor));
+ pipUiEventLogger, mainExecutor));
}
}
@@ -188,11 +189,11 @@ public abstract class Pip2Module {
FloatingContentCoordinator floatingContentCoordinator,
PipScheduler pipScheduler,
Optional<PipPerfHintController> pipPerfHintControllerOptional,
- PipBoundsAlgorithm pipBoundsAlgorithm,
- PipTransitionState pipTransitionState) {
+ PipTransitionState pipTransitionState,
+ PipUiEventLogger pipUiEventLogger) {
return new PipMotionHelper(context, pipBoundsState, menuController, pipSnapAlgorithm,
floatingContentCoordinator, pipScheduler, pipPerfHintControllerOptional,
- pipBoundsAlgorithm, pipTransitionState);
+ pipTransitionState, pipUiEventLogger);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
index 536dc2a58534..a4620d5a4dfe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
@@ -339,7 +339,7 @@ class DesktopImmersiveController(
.setWindowCrop(leash, endBounds.width(), endBounds.height())
.apply()
onTaskResizeAnimationListener?.onAnimationEnd(taskId)
- finishCallback.onTransitionFinished(null /* wct */)
+ finishCallback.onTransitionFinished(/* wct= */ null)
}
)
addUpdateListener { animation ->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index ceef69969d9a..e8f9a789bb98 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -306,7 +306,7 @@ class DesktopModeEventLogger {
fun logTaskInfoStateInit() {
logTaskUpdate(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD,
- /* session_id */ 0,
+ sessionId = 0,
TaskUpdate(
visibleTaskCount = 0,
instanceId = 0,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index cd37113666bb..32ee319a053b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -274,7 +274,7 @@ public class DesktopModeVisualIndicator {
lp.inputFeatures |= INPUT_FEATURE_NO_INPUT_CHANNEL;
final WindowlessWindowManager windowManager = new WindowlessWindowManager(
mTaskInfo.configuration, mLeash,
- null /* hostInputToken */);
+ /* hostInputToken= */ null);
mViewHost = new SurfaceControlViewHost(mContext,
mDisplayController.getDisplay(mTaskInfo.displayId), windowManager,
"DesktopModeVisualIndicator");
@@ -338,7 +338,7 @@ public class DesktopModeVisualIndicator {
if (mCurrentType == NO_INDICATOR) {
fadeInIndicator(newType);
} else if (newType == NO_INDICATOR) {
- fadeOutIndicator(null /* finishCallback */);
+ fadeOutIndicator(/* finishCallback= */ null);
} else {
final VisualIndicatorAnimator animator = VisualIndicatorAnimator.animateIndicatorType(
mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType,
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 d180ea7b79ff..ee817b34b24a 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
@@ -762,7 +762,7 @@ class DesktopTasksController(
return
}
val wct = WindowContainerTransaction()
- wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */)
+ wct.reorder(taskInfo.token, /* onTop= */ true, /* includingParents= */ true)
startLaunchTransition(
transitionType = TRANSIT_TO_FRONT,
wct = wct,
@@ -884,7 +884,7 @@ class DesktopTasksController(
} else if (Flags.enableMoveToNextDisplayShortcut()) {
applyFreeformDisplayChange(wct, task, displayId)
}
- wct.reparent(task.token, displayAreaInfo.token, true /* onTop */)
+ wct.reparent(task.token, displayAreaInfo.token, /* onTop= */ true)
if (Flags.enableDisplayFocusInShellTransitions()) {
// Bring the destination display to top with includingParents=true, so that the
// destination display gains the display focus, which makes the top task in the display
@@ -896,7 +896,7 @@ class DesktopTasksController(
performDesktopExitCleanupIfNeeded(task.taskId, task.displayId, wct)
}
- transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
+ transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
}
/**
@@ -1672,7 +1672,7 @@ class DesktopTasksController(
requestedTaskId,
splitPosition,
options.toBundle(),
- null, /* hideTaskToken */
+ /* hideTaskToken= */ null,
)
}
}
@@ -1709,8 +1709,8 @@ class DesktopTasksController(
fillIn,
splitPosition,
options.toBundle(),
- null /* hideTaskToken */,
- true /* forceLaunchNewTask */,
+ /* hideTaskToken= */ null,
+ /* forceLaunchNewTask= */ true,
splitIndex,
)
}
@@ -1961,7 +1961,7 @@ class DesktopTasksController(
wct.setBounds(taskInfo.token, initialBounds)
}
wct.setWindowingMode(taskInfo.token, targetWindowingMode)
- wct.reorder(taskInfo.token, true /* onTop */)
+ wct.reorder(taskInfo.token, /* onTop= */ true)
if (useDesktopOverrideDensity()) {
wct.setDensityDpi(taskInfo.token, DESKTOP_DENSITY_OVERRIDE)
}
@@ -2796,7 +2796,7 @@ class DesktopTasksController(
controller,
"visibleTaskCount",
{ controller -> result[0] = controller.visibleTaskCount(displayId) },
- true, /* blocking */
+ /* blocking= */ true,
)
return result[0]
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 0330a5f0c4e7..c2dd4d28305b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -234,7 +234,7 @@ class DesktopTasksLimiter(
// If it's a running task, reorder it to back.
taskIdToMinimize
?.let { shellTaskOrganizer.getRunningTaskInfo(it) }
- ?.let { wct.reorder(it.token, false /* onTop */) }
+ ?.let { wct.reorder(it.token, /* onTop= */ false) }
return taskIdToMinimize
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 72c064248988..1380a9ca164f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -131,7 +131,7 @@ sealed class DragToDesktopTransitionHandler(
val pendingIntent =
PendingIntent.getActivityAsUser(
context.createContextAsUser(taskUser, /* flags= */ 0),
- 0 /* requestCode */,
+ /* requestCode= */ 0,
launchHomeIntent,
FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT,
options.toBundle(),
@@ -234,7 +234,7 @@ sealed class DragToDesktopTransitionHandler(
val wct = WindowContainerTransaction()
restoreWindowOrder(wct, state)
state.startTransitionFinishTransaction?.apply()
- state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
+ state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
requestSplitFromScaledTask(splitPosition, wct)
clearState()
} else {
@@ -440,7 +440,7 @@ sealed class DragToDesktopTransitionHandler(
val wct = WindowContainerTransaction()
restoreWindowOrder(wct)
state.startTransitionFinishTransaction?.apply()
- state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
+ state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
requestSplitSelect(wct, taskInfo, splitPosition)
}
return true
@@ -492,7 +492,7 @@ sealed class DragToDesktopTransitionHandler(
finishTransaction = startTransactionFinishT,
)
// Call finishCallback to merge animation before startTransitionFinishCb is called
- finishCallback.onTransitionFinished(null /* wct */)
+ finishCallback.onTransitionFinished(/* wct= */ null)
animateEndDragToDesktop(startTransaction = t, startTransitionFinishCb)
} else if (isCancelTransition) {
info.changes.forEach { change ->
@@ -500,8 +500,8 @@ sealed class DragToDesktopTransitionHandler(
startTransactionFinishT.show(change.leash)
}
t.apply()
- finishCallback.onTransitionFinished(null /* wct */)
- startTransitionFinishCb.onTransitionFinished(null /* wct */)
+ finishCallback.onTransitionFinished(/* wct= */ null)
+ startTransitionFinishCb.onTransitionFinished(/* wct= */ null)
clearState()
}
}
@@ -653,7 +653,7 @@ sealed class DragToDesktopTransitionHandler(
interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
} else if (state.cancelTransitionToken == transition) {
state.draggedTaskChange?.leash?.let { state.startTransitionFinishTransaction?.show(it) }
- state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
+ state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
clearState()
} else {
// This transition being aborted is neither the start, nor the cancel transition, so
@@ -741,19 +741,19 @@ sealed class DragToDesktopTransitionHandler(
// TODO(b/322852244): investigate why even though these "other" tasks are
// reordered in front of home and behind the translucent dragged task, its
// surface is not visible on screen.
- wct.reorder(wc, true /* toTop */)
+ wct.reorder(wc, /* onTop= */ true)
}
val wc =
state.draggedTaskChange?.container
?: error("Dragged task should be non-null before cancelling")
// Then the dragged task a the very top.
- wct.reorder(wc, true /* toTop */)
+ wct.reorder(wc, /* onTop= */ true)
}
is TransitionState.FromSplit -> {
val wc =
state.splitRootChange?.container
?: error("Split root should be non-null before cancelling")
- wct.reorder(wc, true /* toTop */)
+ wct.reorder(wc, /* onTop= */ true)
}
}
val homeWc =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 8c6d5f5c6660..562b26014bf3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -59,6 +59,7 @@ import com.android.wm.shell.common.pip.PipAppOpsListener;
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.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -98,6 +99,7 @@ public class PipController implements ConfigurationChangeListener,
private final PipTouchHandler mPipTouchHandler;
private final PipAppOpsListener mPipAppOpsListener;
private final PhonePipMenuController mPipMenuController;
+ private final PipUiEventLogger mPipUiEventLogger;
private final ShellExecutor mMainExecutor;
private final PipImpl mImpl;
private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>();
@@ -143,6 +145,7 @@ public class PipController implements ConfigurationChangeListener,
PipTouchHandler pipTouchHandler,
PipAppOpsListener pipAppOpsListener,
PhonePipMenuController pipMenuController,
+ PipUiEventLogger pipUiEventLogger,
ShellExecutor mainExecutor) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
@@ -160,6 +163,7 @@ public class PipController implements ConfigurationChangeListener,
mPipTouchHandler = pipTouchHandler;
mPipAppOpsListener = pipAppOpsListener;
mPipMenuController = pipMenuController;
+ mPipUiEventLogger = pipUiEventLogger;
mMainExecutor = mainExecutor;
mImpl = new PipImpl();
@@ -187,6 +191,7 @@ public class PipController implements ConfigurationChangeListener,
PipTouchHandler pipTouchHandler,
PipAppOpsListener pipAppOpsListener,
PhonePipMenuController pipMenuController,
+ PipUiEventLogger pipUiEventLogger,
ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -197,7 +202,7 @@ public class PipController implements ConfigurationChangeListener,
displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
- mainExecutor);
+ pipUiEventLogger, mainExecutor);
}
public PipImpl getPipImpl() {
@@ -238,18 +243,6 @@ public class PipController implements ConfigurationChangeListener,
});
mPipAppOpsListener.setCallback(mPipTouchHandler.getMotionHelper());
- mPipTransitionState.addPipTransitionStateChangedListener(
- (oldState, newState, extra) -> {
- if (newState == PipTransitionState.ENTERED_PIP) {
- final TaskInfo taskInfo = mPipTransitionState.getPipTaskInfo();
- if (taskInfo != null && taskInfo.topActivity != null) {
- mPipAppOpsListener.onActivityPinned(
- taskInfo.topActivity.getPackageName());
- }
- } else if (newState == PipTransitionState.EXITED_PIP) {
- mPipAppOpsListener.onActivityUnpinned();
- }
- });
}
private ExternalInterfaceBinder createExternalInterface() {
@@ -446,14 +439,25 @@ public class PipController implements ConfigurationChangeListener,
mPipTransitionState.setSwipePipToHomeState(overlay, appBounds);
break;
case PipTransitionState.ENTERED_PIP:
+ final TaskInfo taskInfo = mPipTransitionState.getPipTaskInfo();
+ if (taskInfo != null && taskInfo.topActivity != null) {
+ mPipAppOpsListener.onActivityPinned(taskInfo.topActivity.getPackageName());
+ mPipUiEventLogger.setTaskInfo(taskInfo);
+ }
if (mPipTransitionState.isInSwipePipToHomeTransition()) {
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_AUTO_ENTER);
mPipTransitionState.resetSwipePipToHomeState();
+ } else {
+ mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
}
for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) {
listener.accept(true /* inPip */);
}
break;
case PipTransitionState.EXITED_PIP:
+ mPipAppOpsListener.onActivityUnpinned();
+ mPipUiEventLogger.setTaskInfo(null);
for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) {
listener.accept(false /* inPip */);
}
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 37296531ee34..9babe9e9e4eb 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
@@ -43,20 +43,20 @@ import com.android.wm.shell.R;
import com.android.wm.shell.animation.FloatProperties;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.pip.PipAppOpsListener;
-import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip2.animation.PipResizeAnimator;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
+import java.util.Optional;
+
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
-import java.util.Optional;
-
/**
* A helper to animate and manipulate the PiP.
*/
@@ -80,12 +80,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private static final float DISMISS_CIRCLE_PERCENT = 0.85f;
private final Context mContext;
- private @NonNull PipBoundsState mPipBoundsState;
- private @NonNull PipBoundsAlgorithm mPipBoundsAlgorithm;
- private @NonNull PipScheduler mPipScheduler;
- private @NonNull PipTransitionState mPipTransitionState;
- private PhonePipMenuController mMenuController;
- private PipSnapAlgorithm mSnapAlgorithm;
+ @NonNull private final PipBoundsState mPipBoundsState;
+ @NonNull private final PipScheduler mPipScheduler;
+ @NonNull private final PipTransitionState mPipTransitionState;
+ @NonNull private final PipUiEventLogger mPipUiEventLogger;
+ private final PhonePipMenuController mMenuController;
+ private final PipSnapAlgorithm mSnapAlgorithm;
/** The region that all of PIP must stay within. */
private final Rect mFloatingAllowedArea = new Rect();
@@ -168,10 +168,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm,
FloatingContentCoordinator floatingContentCoordinator, PipScheduler pipScheduler,
Optional<PipPerfHintController> pipPerfHintControllerOptional,
- PipBoundsAlgorithm pipBoundsAlgorithm, PipTransitionState pipTransitionState) {
+ PipTransitionState pipTransitionState, PipUiEventLogger pipUiEventLogger) {
mContext = context;
mPipBoundsState = pipBoundsState;
- mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipScheduler = pipScheduler;
mMenuController = menuController;
mSnapAlgorithm = snapAlgorithm;
@@ -185,6 +184,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
};
mPipTransitionState = pipTransitionState;
mPipTransitionState.addPipTransitionStateChangedListener(this);
+ mPipUiEventLogger = pipUiEventLogger;
}
void init() {
@@ -850,9 +850,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
if (mPipBoundsState.getBounds().left < 0
&& mPipBoundsState.getStashedState() != STASH_TYPE_LEFT) {
mPipBoundsState.setStashed(STASH_TYPE_LEFT);
+ mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_LEFT);
} else if (mPipBoundsState.getBounds().left >= 0
&& mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT) {
mPipBoundsState.setStashed(STASH_TYPE_RIGHT);
+ mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_RIGHT);
}
mMenuController.hideMenu();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl
index 964e5fd62a5f..af1679f2d175 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl
@@ -36,12 +36,6 @@ import com.android.internal.os.IResultReceiver;
interface IRecentsAnimationController {
/**
- * Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the
- * current set of task ids provided to the handler.
- */
- TaskSnapshot screenshotTask(int taskId);
-
- /**
* Sets the final surface transaction on a Task. This is used by Launcher to notify the system
* that animating Activity to PiP has completed and the associated task surface should be
* updated accordingly. This should be called before `finish`
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 032dac9ff3a2..76496b06a4dd 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
@@ -1227,19 +1227,6 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
}
@Override
- public TaskSnapshot screenshotTask(int taskId) {
- try {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "[%d] RecentsController.screenshotTask: taskId=%d", mInstanceId, taskId);
- return ActivityTaskManager.getService().takeTaskSnapshot(taskId,
- true /* updateCache */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to screenshot task", e);
- }
- return null;
- }
-
- @Override
public void setInputConsumerEnabled(boolean enabled) {
mExecutor.execute(() -> {
if (mFinishCB == null || !enabled) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 3f65d9318692..1264c013faf5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -231,6 +231,7 @@ internal class AppHandleViewHolder(
fun disposeStatusBarInputLayer() {
if (!statusBarInputLayerExists) return
statusBarInputLayerExists = false
+ statusBarInputLayer?.view?.setOnTouchListener(null)
handler.post {
statusBarInputLayer?.releaseView()
statusBarInputLayer = null
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index 40ecdecde4e7..805f4c2fe7f8 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -67,6 +67,7 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
+@FlakyTest(bugId = 386333280)
open class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) :
EnterPipTransition(flicker) {
override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 47ee7bb20199..bbdb90f0a37c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -61,9 +61,7 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableContentResolver;
import android.testing.TestableLooper;
import android.view.IRemoteAnimationRunner;
import android.view.KeyEvent;
@@ -84,7 +82,6 @@ import android.window.WindowContainerToken;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
-import com.android.internal.util.test.FakeSettingsProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -109,7 +106,6 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
public class BackAnimationControllerTest extends ShellTestCase {
- private static final String ANIMATION_ENABLED = "1";
private final TestShellExecutor mShellExecutor = new TestShellExecutor();
private ShellInit mShellInit;
@@ -148,8 +144,6 @@ public class BackAnimationControllerTest extends ShellTestCase {
private Transitions.TransitionHandler mTakeoverHandler;
private BackAnimationController mController;
- private TestableContentResolver mContentResolver;
- private TestableLooper mTestableLooper;
private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation;
private CrossTaskBackAnimation mCrossTaskBackAnimation;
@@ -166,11 +160,6 @@ public class BackAnimationControllerTest extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mContext.addMockSystemService(InputManager.class, mInputManager);
mContext.getApplicationInfo().privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
- mContentResolver = new TestableContentResolver(mContext);
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION,
- ANIMATION_ENABLED);
- mTestableLooper = TestableLooper.get(this);
mShellInit = spy(new ShellInit(mShellExecutor));
mDefaultCrossActivityBackAnimation = new DefaultCrossActivityBackAnimation(mContext,
mAnimationBackground, mRootTaskDisplayAreaOrganizer, mHandler);
@@ -187,10 +176,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
mShellInit,
mShellController,
mShellExecutor,
- new Handler(mTestableLooper.getLooper()),
mActivityTaskManager,
mContext,
- mContentResolver,
mAnimationBackground,
mShellBackAnimationRegistry,
mShellCommandHandler,
@@ -342,47 +329,6 @@ public class BackAnimationControllerTest extends ShellTestCase {
}
@Test
- public void animationDisabledFromSettings() throws RemoteException {
- // Toggle the setting off
- Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0");
- ShellInit shellInit = new ShellInit(mShellExecutor);
- mController =
- new BackAnimationController(
- shellInit,
- mShellController,
- mShellExecutor,
- new Handler(mTestableLooper.getLooper()),
- mActivityTaskManager,
- mContext,
- mContentResolver,
- mAnimationBackground,
- mShellBackAnimationRegistry,
- mShellCommandHandler,
- mTransitions,
- mHandler);
- shellInit.init();
- registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
-
- ArgumentCaptor<BackMotionEvent> backEventCaptor =
- ArgumentCaptor.forClass(BackMotionEvent.class);
-
- createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME,
- /* enableAnimation = */ false,
- /* isAnimationCallback = */ false);
-
- triggerBackGesture();
- releaseBackGesture();
-
- verify(mAppCallback, times(1)).onBackInvoked();
-
- verify(mAnimatorCallback, never()).onBackStarted(any());
- verify(mAnimatorCallback, never()).onBackProgressed(backEventCaptor.capture());
- verify(mAnimatorCallback, never()).onBackInvoked();
- verify(mBackAnimationRunner, never()).onAnimationStart(
- anyInt(), any(), any(), any(), any());
- }
-
- @Test
public void gestureQueued_WhenPreviousTransitionHasNotYetEnded() throws RemoteException {
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
index db00f41f723b..04f9ada8a9d7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
@@ -142,7 +142,7 @@ class CloseDesktopTaskTransitionHandlerTest : ShellTestCase() {
changeMode: Int = WindowManager.TRANSIT_CLOSE,
task: RunningTaskInfo,
): TransitionInfo =
- TransitionInfo(type, 0 /* flags */).apply {
+ TransitionInfo(type, /* flags= */ 0).apply {
addChange(
TransitionInfo.Change(mock(), closingTaskLeash).apply {
mode = changeMode
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
index d14c6402982d..c705f5a5ac87 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
@@ -153,7 +153,7 @@ class DesktopBackNavigationTransitionHandlerTest : ShellTestCase() {
changeMode: Int = WindowManager.TRANSIT_TO_BACK,
task: RunningTaskInfo,
): TransitionInfo =
- TransitionInfo(type, 0 /* flags */).apply {
+ TransitionInfo(type, /* flags= */ 0).apply {
addChange(
TransitionInfo.Change(mock(), closingTaskLeash).apply {
mode = changeMode
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index 3cf84d92a625..372e47ce6a17 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -777,7 +777,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
task: RunningTaskInfo,
withWallpaper: Boolean = false,
): TransitionInfo =
- TransitionInfo(WindowManager.TRANSIT_CLOSE, 0 /* flags */).apply {
+ TransitionInfo(WindowManager.TRANSIT_CLOSE, /* flags= */ 0).apply {
addChange(
TransitionInfo.Change(mock(), closingTaskLeash).apply {
mode = changeMode
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 e032616e7d43..da27c08920dc 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
@@ -1267,7 +1267,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
// Set task as systemUI package
val systemUIPackageName =
context.resources.getString(com.android.internal.R.string.config_systemUi)
- val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
val task =
setUpFullscreenTask().apply {
baseActivity = baseComponent
@@ -1284,7 +1284,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
// Set task as systemUI package
val systemUIPackageName =
context.resources.getString(com.android.internal.R.string.config_systemUi)
- val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
val task =
setUpFullscreenTask().apply {
baseActivity = baseComponent
@@ -1757,12 +1757,12 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(type = TRANSIT_CHANGE)) {
- val wallpaperChange =
- hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() }
- assertThat(wallpaperChange).isNotNull()
- assertThat(wallpaperChange!!.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
- }
+ val wallpaperChange =
+ getLatestWct(type = TRANSIT_CHANGE).hierarchyOps.find { op ->
+ op.container == wallpaperToken.asBinder()
+ }
+ assertNotNull(wallpaperChange)
+ assertThat(wallpaperChange.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
}
@Test
@@ -1792,15 +1792,13 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(type = TRANSIT_CHANGE)) {
- val taskChange = changes[task.token.asBinder()]
- assertThat(taskChange).isNotNull()
- // To preserve DP size, pixel size is changed to 320x240. The ratio of the left margin
- // to the right margin and the ratio of the top margin to bottom margin are also
- // preserved.
- assertThat(taskChange!!.configuration.windowConfiguration.bounds)
- .isEqualTo(Rect(240, 160, 560, 400))
- }
+ val taskChange = getLatestWct(type = TRANSIT_CHANGE).changes[task.token.asBinder()]
+ assertNotNull(taskChange)
+ // To preserve DP size, pixel size is changed to 320x240. The ratio of the left margin
+ // to the right margin and the ratio of the top margin to bottom margin are also
+ // preserved.
+ assertThat(taskChange.configuration.windowConfiguration.bounds)
+ .isEqualTo(Rect(240, 160, 560, 400))
}
@Test
@@ -1831,12 +1829,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(type = TRANSIT_CHANGE)) {
- val taskChange = changes[task.token.asBinder()]
- assertThat(taskChange).isNotNull()
- assertThat(taskChange!!.configuration.windowConfiguration.bounds)
- .isEqualTo(Rect(960, 480, 1280, 720))
- }
+ val taskChange = getLatestWct(type = TRANSIT_CHANGE).changes[task.token.asBinder()]
+ assertNotNull(taskChange)
+ assertThat(taskChange.configuration.windowConfiguration.bounds)
+ .isEqualTo(Rect(960, 480, 1280, 720))
}
@Test
@@ -1864,13 +1860,11 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(type = TRANSIT_CHANGE)) {
- val taskChange = changes[task.token.asBinder()]
- assertThat(taskChange).isNotNull()
- // DP size is preserved. The window is centered in the destination display.
- assertThat(taskChange!!.configuration.windowConfiguration.bounds)
- .isEqualTo(Rect(320, 120, 960, 600))
- }
+ val taskChange = getLatestWct(type = TRANSIT_CHANGE).changes[task.token.asBinder()]
+ assertNotNull(taskChange)
+ // DP size is preserved. The window is centered in the destination display.
+ assertThat(taskChange.configuration.windowConfiguration.bounds)
+ .isEqualTo(Rect(320, 120, 960, 600))
}
@Test
@@ -1903,14 +1897,12 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(type = TRANSIT_CHANGE)) {
- val taskChange = changes[task.token.asBinder()]
- assertThat(taskChange).isNotNull()
- assertThat(taskChange!!.configuration.windowConfiguration.bounds.left).isAtLeast(0)
- assertThat(taskChange.configuration.windowConfiguration.bounds.top).isAtLeast(0)
- assertThat(taskChange.configuration.windowConfiguration.bounds.right).isAtMost(640)
- assertThat(taskChange.configuration.windowConfiguration.bounds.bottom).isAtMost(480)
- }
+ val taskChange = getLatestWct(type = TRANSIT_CHANGE).changes[task.token.asBinder()]
+ assertNotNull(taskChange)
+ assertThat(taskChange.configuration.windowConfiguration.bounds.left).isAtLeast(0)
+ assertThat(taskChange.configuration.windowConfiguration.bounds.top).isAtLeast(0)
+ assertThat(taskChange.configuration.windowConfiguration.bounds.right).isAtMost(640)
+ assertThat(taskChange.configuration.windowConfiguration.bounds.bottom).isAtMost(480)
}
@Test
@@ -2722,7 +2714,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
// Set task as systemUI package
val systemUIPackageName =
context.resources.getString(com.android.internal.R.string.config_systemUi)
- val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
val task =
setUpFreeformTask().apply {
baseActivity = baseComponent
@@ -2743,7 +2735,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
// Set task as systemUI package
val systemUIPackageName =
context.resources.getString(com.android.internal.R.string.config_systemUi)
- val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
val task =
setUpFullscreenTask().apply {
baseActivity = baseComponent
@@ -3376,11 +3368,11 @@ class DesktopTasksControllerTest : ShellTestCase() {
spyController.onDragPositioningEnd(
task,
mockSurface,
- Point(100, -100), /* position */
- PointF(200f, -200f), /* inputCoordinate */
- Rect(100, -100, 500, 1000), /* currentDragBounds */
- Rect(0, 50, 2000, 2000), /* validDragArea */
- Rect() /* dragStartBounds */,
+ position = Point(100, -100),
+ inputCoordinate = PointF(200f, -200f),
+ currentDragBounds = Rect(100, -100, 500, 1000),
+ validDragArea = Rect(0, 50, 2000, 2000),
+ dragStartBounds = Rect(),
motionEvent,
desktopWindowDecoration,
)
@@ -3415,11 +3407,11 @@ class DesktopTasksControllerTest : ShellTestCase() {
spyController.onDragPositioningEnd(
task,
mockSurface,
- Point(100, 200), /* position */
- PointF(200f, 300f), /* inputCoordinate */
- currentDragBounds, /* currentDragBounds */
- Rect(0, 50, 2000, 2000) /* validDragArea */,
- Rect() /* dragStartBounds */,
+ position = Point(100, 200),
+ inputCoordinate = PointF(200f, 300f),
+ currentDragBounds = currentDragBounds,
+ validDragArea = Rect(0, 50, 2000, 2000),
+ dragStartBounds = Rect(),
motionEvent,
desktopWindowDecoration,
)
@@ -3459,11 +3451,11 @@ class DesktopTasksControllerTest : ShellTestCase() {
spyController.onDragPositioningEnd(
task,
mockSurface,
- Point(100, 50), /* position */
- PointF(200f, 300f), /* inputCoordinate */
- Rect(100, 50, 500, 1000), /* currentDragBounds */
- Rect(0, 50, 2000, 2000) /* validDragArea */,
- Rect() /* dragStartBounds */,
+ position = Point(100, 50),
+ inputCoordinate = PointF(200f, 300f),
+ currentDragBounds = Rect(100, 50, 500, 1000),
+ validDragArea = Rect(0, 50, 2000, 2000),
+ dragStartBounds = Rect(),
motionEvent,
desktopWindowDecoration,
)
@@ -3498,11 +3490,11 @@ class DesktopTasksControllerTest : ShellTestCase() {
spyController.onDragPositioningEnd(
task,
mockSurface,
- Point(100, 50), /* position */
- PointF(200f, 300f), /* inputCoordinate */
+ position = Point(100, 50),
+ inputCoordinate = PointF(200f, 300f),
currentDragBounds,
- Rect(0, 50, 2000, 2000) /* validDragArea */,
- Rect() /* dragStartBounds */,
+ validDragArea = Rect(0, 50, 2000, 2000),
+ dragStartBounds = Rect(),
motionEvent,
desktopWindowDecoration,
)
@@ -3555,11 +3547,11 @@ class DesktopTasksControllerTest : ShellTestCase() {
spyController.onDragPositioningEnd(
task,
mockSurface,
- Point(100, 50), /* position */
- PointF(200f, 300f), /* inputCoordinate */
- currentDragBounds, /* currentDragBounds */
- Rect(0, 50, 2000, 2000) /* validDragArea */,
- Rect() /* dragStartBounds */,
+ position = Point(100, 50),
+ inputCoordinate = PointF(200f, 300f),
+ currentDragBounds = currentDragBounds,
+ validDragArea = Rect(0, 50, 2000, 2000),
+ dragStartBounds = Rect(),
motionEvent,
desktopWindowDecoration,
)
@@ -5053,7 +5045,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
task: RunningTaskInfo?,
@WindowManager.TransitionType type: Int = TRANSIT_OPEN,
): TransitionRequestInfo {
- return TransitionRequestInfo(type, task, null /* remoteTransition */)
+ return TransitionRequestInfo(type, task, /* remoteTransition= */ null)
}
private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 52602f22fd4b..c8214b3838e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -193,10 +193,10 @@ class DesktopTasksLimiterTest : ShellTestCase() {
desktopTasksLimiter
.getTransitionObserver()
.onTransitionReady(
- Binder() /* transition */,
+ /* transition= */ Binder(),
TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
@@ -217,10 +217,10 @@ class DesktopTasksLimiterTest : ShellTestCase() {
desktopTasksLimiter
.getTransitionObserver()
.onTransitionReady(
- taskTransition /* transition */,
+ /* transition= */ taskTransition,
TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
@@ -242,8 +242,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
.onTransitionReady(
transition,
TransitionInfoBuilder(TRANSIT_OPEN).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
@@ -265,8 +265,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
.onTransitionReady(
transition,
TransitionInfoBuilder(TRANSIT_OPEN).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
@@ -287,8 +287,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
.onTransitionReady(
transition,
TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
@@ -316,8 +316,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
.onTransitionReady(
transition,
TransitionInfo(TRANSIT_OPEN, TransitionInfo.FLAG_NONE).apply { addChange(change) },
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
@@ -344,8 +344,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
.onTransitionReady(
newTransition,
TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
@@ -552,8 +552,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
.onTransitionReady(
transition,
TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
@@ -584,8 +584,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
.onTransitionReady(
transition,
TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
@@ -617,8 +617,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
.onTransitionReady(
mergedTransition,
TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- StubTransaction() /* startTransaction */,
- StubTransaction(), /* finishTransaction */
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
)
desktopTasksLimiter.getTransitionObserver().onTransitionStarting(mergedTransition)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index d491d445458d..3cc30cb491b3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -305,7 +305,7 @@ class DesktopTasksTransitionObserverTest {
type: Int = TRANSIT_TO_BACK,
withWallpaper: Boolean = false,
): TransitionInfo {
- return TransitionInfo(type, 0 /* flags */).apply {
+ return TransitionInfo(type, /* flags= */ 0).apply {
addChange(
Change(mock(), mock()).apply {
mode = type
@@ -331,7 +331,7 @@ class DesktopTasksTransitionObserverTest {
task: RunningTaskInfo?,
type: Int = TRANSIT_OPEN,
): TransitionInfo {
- return TransitionInfo(TRANSIT_OPEN, 0 /* flags */).apply {
+ return TransitionInfo(TRANSIT_OPEN, /* flags= */ 0).apply {
addChange(
Change(mock(), mock()).apply {
mode = TRANSIT_OPEN
@@ -344,7 +344,7 @@ class DesktopTasksTransitionObserverTest {
}
private fun createCloseTransition(task: RunningTaskInfo?): TransitionInfo {
- return TransitionInfo(TRANSIT_CLOSE, 0 /* flags */).apply {
+ return TransitionInfo(TRANSIT_CLOSE, /* flags= */ 0).apply {
addChange(
Change(mock(), mock()).apply {
mode = TRANSIT_CLOSE
@@ -357,7 +357,7 @@ class DesktopTasksTransitionObserverTest {
}
private fun createToBackTransition(task: RunningTaskInfo?): TransitionInfo {
- return TransitionInfo(TRANSIT_TO_BACK, 0 /* flags */).apply {
+ return TransitionInfo(TRANSIT_TO_BACK, /* flags= */ 0).apply {
addChange(
Change(mock(), mock()).apply {
mode = TRANSIT_TO_BACK
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 2216d5452ce5..341df0299a97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -677,7 +677,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
}
private fun createTransitionInfo(type: Int, draggedTask: RunningTaskInfo): TransitionInfo {
- return TransitionInfo(type, 0 /* flags */).apply {
+ return TransitionInfo(type, /* flags= */ 0).apply {
addChange( // Home.
TransitionInfo.Change(mock(), homeTaskLeash).apply {
parent = null
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
index fd3adabfd44b..32096645aea7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
@@ -40,6 +40,7 @@ import org.mockito.Mockito.mock
/**
* Tests for [GroupedTaskInfo]
+ * Build & Run: atest WMShellUnitTests:GroupedTaskInfoTest
*/
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -47,7 +48,7 @@ class GroupedTaskInfoTest : ShellTestCase() {
@Test
fun testSingleTask_hasCorrectType() {
- assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_FULLSCREEN)
+ assertThat(singleTaskGroupInfo().isBaseType(TYPE_FULLSCREEN)).isTrue()
}
@Test
@@ -66,7 +67,7 @@ class GroupedTaskInfoTest : ShellTestCase() {
@Test
fun testSplitTasks_hasCorrectType() {
- assertThat(splitTasksGroupInfo().type).isEqualTo(TYPE_SPLIT)
+ assertThat(splitTasksGroupInfo().isBaseType(TYPE_SPLIT)).isTrue()
}
@Test
@@ -87,8 +88,8 @@ class GroupedTaskInfoTest : ShellTestCase() {
@Test
fun testFreeformTasks_hasCorrectType() {
- assertThat(freeformTasksGroupInfo(freeformTaskIds = arrayOf(1)).type)
- .isEqualTo(TYPE_FREEFORM)
+ assertThat(freeformTasksGroupInfo(freeformTaskIds = arrayOf(1)).isBaseType(TYPE_FREEFORM))
+ .isTrue()
}
@Test
@@ -111,83 +112,155 @@ class GroupedTaskInfoTest : ShellTestCase() {
}
@Test
+ fun testMixedWithFullscreenBase_hasCorrectType() {
+ assertThat(mixedTaskGroupInfoWithFullscreenBase().isBaseType(TYPE_FULLSCREEN)).isTrue()
+ }
+
+ @Test
+ fun testMixedWithSplitBase_hasCorrectType() {
+ assertThat(mixedTaskGroupInfoWithSplitBase().isBaseType(TYPE_SPLIT)).isTrue()
+ }
+
+ @Test
+ fun testMixedWithFreeformBase_hasCorrectType() {
+ assertThat(mixedTaskGroupInfoWithFreeformBase().isBaseType(TYPE_FREEFORM)).isTrue()
+ }
+
+ @Test
+ fun testMixed_disallowEmptyMixed() {
+ assertThrows(IllegalArgumentException::class.java) {
+ GroupedTaskInfo.forMixed(listOf())
+ }
+ }
+
+ @Test
+ fun testMixed_disallowNestedMixed() {
+ assertThrows(IllegalArgumentException::class.java) {
+ GroupedTaskInfo.forMixed(listOf(
+ GroupedTaskInfo.forMixed(listOf(singleTaskGroupInfo()))))
+ }
+ }
+
+ @Test
+ fun testMixed_disallowNonMixedAccessors() {
+ val mixed = mixedTaskGroupInfoWithFullscreenBase()
+ assertThrows(IllegalStateException::class.java) {
+ mixed.taskInfo1
+ }
+ assertThrows(IllegalStateException::class.java) {
+ mixed.taskInfo2
+ }
+ assertThrows(IllegalStateException::class.java) {
+ mixed.splitBounds
+ }
+ assertThrows(IllegalStateException::class.java) {
+ mixed.minimizedTaskIds
+ }
+ }
+
+ @Test
fun testParcelling_singleTask() {
- val recentTaskInfo = singleTaskGroupInfo()
+ val taskInfo = singleTaskGroupInfo()
val parcel = Parcel.obtain()
- recentTaskInfo.writeToParcel(parcel, 0)
+ taskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel: GroupedTaskInfo =
+ val taskInfoFromParcel: GroupedTaskInfo =
GroupedTaskInfo.CREATOR.createFromParcel(parcel)
- assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FULLSCREEN)
- assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
- assertThat(recentTaskInfoParcel.taskInfo2).isNull()
+ assertThat(taskInfoFromParcel.isBaseType(TYPE_FULLSCREEN)).isTrue()
+ assertThat(taskInfoFromParcel.taskInfo1.taskId).isEqualTo(1)
+ assertThat(taskInfoFromParcel.taskInfo2).isNull()
}
@Test
fun testParcelling_splitTasks() {
- val recentTaskInfo = splitTasksGroupInfo()
+ val taskInfo = splitTasksGroupInfo()
val parcel = Parcel.obtain()
- recentTaskInfo.writeToParcel(parcel, 0)
+ taskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel: GroupedTaskInfo =
+ val taskInfoFromParcel: GroupedTaskInfo =
GroupedTaskInfo.CREATOR.createFromParcel(parcel)
- assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SPLIT)
- assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
- assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
- assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2)
- assertThat(recentTaskInfoParcel.splitBounds).isNotNull()
- assertThat(recentTaskInfoParcel.splitBounds!!.snapPosition).isEqualTo(SNAP_TO_2_50_50)
+ assertThat(taskInfoFromParcel.isBaseType(TYPE_SPLIT)).isTrue()
+ assertThat(taskInfoFromParcel.taskInfo1.taskId).isEqualTo(1)
+ assertThat(taskInfoFromParcel.taskInfo2).isNotNull()
+ assertThat(taskInfoFromParcel.taskInfo2!!.taskId).isEqualTo(2)
+ assertThat(taskInfoFromParcel.splitBounds).isNotNull()
+ assertThat(taskInfoFromParcel.splitBounds!!.snapPosition).isEqualTo(SNAP_TO_2_50_50)
}
@Test
fun testParcelling_freeformTasks() {
- val recentTaskInfo = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3))
+ val taskInfo = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3))
val parcel = Parcel.obtain()
- recentTaskInfo.writeToParcel(parcel, 0)
+ taskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel: GroupedTaskInfo =
+ val taskInfoFromParcel: GroupedTaskInfo =
GroupedTaskInfo.CREATOR.createFromParcel(parcel)
- assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
- assertThat(recentTaskInfoParcel.taskInfoList).hasSize(3)
+ assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue()
+ assertThat(taskInfoFromParcel.taskInfoList).hasSize(3)
// Only compare task ids
val taskIdComparator = Correspondence.transforming<TaskInfo, Int>(
{ it?.taskId }, "has taskId of"
)
- assertThat(recentTaskInfoParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
- .containsExactly(1, 2, 3)
+ assertThat(taskInfoFromParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
+ .containsExactly(1, 2, 3).inOrder()
}
@Test
fun testParcelling_freeformTasks_minimizedTasks() {
- val recentTaskInfo = freeformTasksGroupInfo(
+ val taskInfo = freeformTasksGroupInfo(
freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(2))
val parcel = Parcel.obtain()
- recentTaskInfo.writeToParcel(parcel, 0)
+ taskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel: GroupedTaskInfo =
+ val taskInfoFromParcel: GroupedTaskInfo =
GroupedTaskInfo.CREATOR.createFromParcel(parcel)
- assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
- assertThat(recentTaskInfoParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray())
+ assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue()
+ assertThat(taskInfoFromParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray())
}
@Test
- fun testGetTaskById_singleTasks() {
+ fun testParcelling_mixedTasks() {
+ val taskInfo = GroupedTaskInfo.forMixed(listOf(
+ freeformTasksGroupInfo(freeformTaskIds = arrayOf(4, 5, 6),
+ minimizedTaskIds = arrayOf(5)),
+ splitTasksGroupInfo(firstId = 2, secondId = 3),
+ singleTaskGroupInfo(id = 1)))
+
+ val parcel = Parcel.obtain()
+ taskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+
+ // Read the object back from the parcel
+ val taskInfoFromParcel: GroupedTaskInfo =
+ GroupedTaskInfo.CREATOR.createFromParcel(parcel)
+ assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue()
+ assertThat(taskInfoFromParcel.baseGroupedTask.minimizedTaskIds).isEqualTo(
+ arrayOf(5).toIntArray())
+ for (i in 1..6) {
+ assertThat(taskInfoFromParcel.containsTask(i)).isTrue()
+ }
+ assertThat(taskInfoFromParcel.taskInfoList).hasSize(taskInfo.taskInfoList.size)
+ }
+
+ @Test
+ fun testTaskProperties_singleTasks() {
val task1 = createTaskInfo(id = 1234)
val taskInfo = GroupedTaskInfo.forFullscreenTasks(task1)
assertThat(taskInfo.getTaskById(1234)).isEqualTo(task1)
assertThat(taskInfo.containsTask(1234)).isTrue()
+ assertThat(taskInfo.taskInfoList).isEqualTo(listOf(task1))
}
@Test
- fun testGetTaskById_multipleTasks() {
+ fun testTaskProperties_splitTasks() {
val task1 = createTaskInfo(id = 1)
val task2 = createTaskInfo(id = 2)
val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_2_50_50)
@@ -198,6 +271,41 @@ class GroupedTaskInfoTest : ShellTestCase() {
assertThat(taskInfo.getTaskById(2)).isEqualTo(task2)
assertThat(taskInfo.containsTask(1)).isTrue()
assertThat(taskInfo.containsTask(2)).isTrue()
+ assertThat(taskInfo.taskInfoList).isEqualTo(listOf(task1, task2))
+ }
+
+ @Test
+ fun testTaskProperties_freeformTasks() {
+ val task1 = createTaskInfo(id = 1)
+ val task2 = createTaskInfo(id = 2)
+
+ val taskInfo = GroupedTaskInfo.forFreeformTasks(listOf(task1, task2), setOf())
+
+ assertThat(taskInfo.getTaskById(1)).isEqualTo(task1)
+ assertThat(taskInfo.getTaskById(2)).isEqualTo(task2)
+ assertThat(taskInfo.containsTask(1)).isTrue()
+ assertThat(taskInfo.containsTask(2)).isTrue()
+ assertThat(taskInfo.taskInfoList).isEqualTo(listOf(task1, task2))
+ }
+
+ @Test
+ fun testTaskProperties_mixedTasks() {
+ val task1 = createTaskInfo(id = 1)
+ val task2 = createTaskInfo(id = 2)
+ val task3 = createTaskInfo(id = 3)
+ val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_2_50_50)
+
+ val splitTasks = GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds)
+ val fullscreenTasks = GroupedTaskInfo.forFullscreenTasks(task3)
+ val mixedTasks = GroupedTaskInfo.forMixed(listOf(splitTasks, fullscreenTasks))
+
+ assertThat(mixedTasks.getTaskById(1)).isEqualTo(task1)
+ assertThat(mixedTasks.getTaskById(2)).isEqualTo(task2)
+ assertThat(mixedTasks.getTaskById(3)).isEqualTo(task3)
+ assertThat(mixedTasks.containsTask(1)).isTrue()
+ assertThat(mixedTasks.containsTask(2)).isTrue()
+ assertThat(mixedTasks.containsTask(3)).isTrue()
+ assertThat(mixedTasks.taskInfoList).isEqualTo(listOf(task1, task2, task3))
}
private fun createTaskInfo(id: Int) = ActivityManager.RecentTaskInfo().apply {
@@ -205,14 +313,14 @@ class GroupedTaskInfoTest : ShellTestCase() {
token = WindowContainerToken(mock(IWindowContainerToken::class.java))
}
- private fun singleTaskGroupInfo(): GroupedTaskInfo {
- val task = createTaskInfo(id = 1)
+ private fun singleTaskGroupInfo(id: Int = 1): GroupedTaskInfo {
+ val task = createTaskInfo(id)
return GroupedTaskInfo.forFullscreenTasks(task)
}
- private fun splitTasksGroupInfo(): GroupedTaskInfo {
- val task1 = createTaskInfo(id = 1)
- val task2 = createTaskInfo(id = 2)
+ private fun splitTasksGroupInfo(firstId: Int = 1, secondId: Int = 2): GroupedTaskInfo {
+ val task1 = createTaskInfo(firstId)
+ val task2 = createTaskInfo(secondId)
val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_2_50_50)
return GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds)
}
@@ -225,4 +333,22 @@ class GroupedTaskInfoTest : ShellTestCase() {
freeformTaskIds.map { createTaskInfo(it) }.toList(),
minimizedTaskIds.toSet())
}
+
+ private fun mixedTaskGroupInfoWithFullscreenBase(): GroupedTaskInfo {
+ return GroupedTaskInfo.forMixed(listOf(
+ singleTaskGroupInfo(id = 1),
+ singleTaskGroupInfo(id = 2)))
+ }
+
+ private fun mixedTaskGroupInfoWithSplitBase(): GroupedTaskInfo {
+ return GroupedTaskInfo.forMixed(listOf(
+ splitTasksGroupInfo(firstId = 2, secondId = 3),
+ singleTaskGroupInfo(id = 1)))
+ }
+
+ private fun mixedTaskGroupInfoWithFreeformBase(): GroupedTaskInfo {
+ return GroupedTaskInfo.forMixed(listOf(
+ freeformTasksGroupInfo(freeformTaskIds = arrayOf(2, 3, 4)),
+ singleTaskGroupInfo(id = 1)))
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 22b45e8c63af..7e5d6ce38c5a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -24,6 +24,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.launcher3.Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE;
+import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM;
+import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN;
+import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
import static org.junit.Assert.assertEquals;
@@ -346,9 +349,9 @@ public class RecentTasksControllerTest extends ShellTestCase {
GroupedTaskInfo singleGroup2 = recentTasks.get(2);
// Check that groups have expected types
- assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
- assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup1.getType());
- assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup2.getType());
+ assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM));
+ assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN));
+ assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN));
// Check freeform group entries
assertEquals(t1, freeformGroup.getTaskInfoList().get(0));
@@ -385,9 +388,9 @@ public class RecentTasksControllerTest extends ShellTestCase {
GroupedTaskInfo singleGroup = recentTasks.get(2);
// Check that groups have expected types
- assertEquals(GroupedTaskInfo.TYPE_SPLIT, splitGroup.getType());
- assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
- assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup.getType());
+ assertTrue(splitGroup.isBaseType(TYPE_SPLIT));
+ assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM));
+ assertTrue(singleGroup.isBaseType(TYPE_FULLSCREEN));
// Check freeform group entries
assertEquals(t3, freeformGroup.getTaskInfoList().get(0));
@@ -420,10 +423,10 @@ public class RecentTasksControllerTest extends ShellTestCase {
// Expect no grouping of tasks
assertEquals(4, recentTasks.size());
- assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(0).getType());
- assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(1).getType());
- assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(2).getType());
- assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(3).getType());
+ assertTrue(recentTasks.get(0).isBaseType(TYPE_FULLSCREEN));
+ assertTrue(recentTasks.get(1).isBaseType(TYPE_FULLSCREEN));
+ assertTrue(recentTasks.get(2).isBaseType(TYPE_FULLSCREEN));
+ assertTrue(recentTasks.get(3).isBaseType(TYPE_FULLSCREEN));
assertEquals(t1, recentTasks.get(0).getTaskInfo1());
assertEquals(t2, recentTasks.get(1).getTaskInfo1());
@@ -457,9 +460,9 @@ public class RecentTasksControllerTest extends ShellTestCase {
GroupedTaskInfo singleGroup2 = recentTasks.get(2);
// Check that groups have expected types
- assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
- assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup1.getType());
- assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup2.getType());
+ assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM));
+ assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN));
+ assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN));
// Check freeform group entries
assertEquals(3, freeformGroup.getTaskInfoList().size());
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 7dac0859b7e9..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
@@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.assist.AssistContent.EXTRA_SESSION_TRANSFER_WEB_URI;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
@@ -1176,7 +1175,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void webUriLink_webUriLinkUsedWhenWhenAvailable() {
+ public void sessionTransferUri_sessionTransferUriUsedWhenWhenAvailable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
@@ -1188,7 +1187,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void webUriLink_webUriLinkUsedWhenSessionTransferUriUnavailable() {
+ public void webUri_webUriUsedWhenSessionTransferUriUnavailable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
@@ -1200,7 +1199,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void genericLink_genericLinkUsedWhenCapturedLinkAndWebUriUnavailable() {
+ public void genericLink_genericLinkUsedWhenCapturedLinkAndAssistContentUriUnavailable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, null /* captured link */, null /* web uri */,
@@ -1490,7 +1489,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
taskInfo.capturedLink = capturedLink;
taskInfo.capturedLinkTimestamp = System.currentTimeMillis();
mAssistContent.setWebUri(webUri);
- mAssistContent.getExtras().putObject(EXTRA_SESSION_TRANSFER_WEB_URI, sessionTransferUri);
+ mAssistContent.setSessionTransferUri(sessionTransferUri);
final String genericLinkString = genericLink == null ? null : genericLink.toString();
doReturn(genericLinkString).when(mMockGenericLinksParser).getGenericLink(any());
// Relayout to set captured link
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index dbb891455ddd..e693fcfd3918 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -162,10 +162,13 @@ const std::string& ApkAssets::GetDebugName() const {
return assets_provider_->GetDebugName();
}
-bool ApkAssets::IsUpToDate() const {
+UpToDate ApkAssets::IsUpToDate() const {
// Loaders are invalidated by the app, not the system, so assume they are up to date.
- return IsLoader() || ((!loaded_idmap_ || loaded_idmap_->IsUpToDate())
- && assets_provider_->IsUpToDate());
+ if (IsLoader()) {
+ return UpToDate::Always;
+ }
+ const auto idmap_res = loaded_idmap_ ? loaded_idmap_->IsUpToDate() : UpToDate::Always;
+ return combine(idmap_res, [this] { return assets_provider_->IsUpToDate(); });
}
} // namespace android
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index 2d3c06506a1f..11b12eb030a6 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -24,9 +24,8 @@
#include <ziparchive/zip_archive.h>
namespace android {
-namespace {
-constexpr const char* kEmptyDebugString = "<empty>";
-} // namespace
+
+static constexpr std::string_view kEmptyDebugString = "<empty>";
std::unique_ptr<Asset> AssetsProvider::Open(const std::string& path, Asset::AccessMode mode,
bool* file_exists) const {
@@ -86,11 +85,9 @@ void ZipAssetsProvider::ZipCloser::operator()(ZipArchive* a) const {
}
ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
- package_property_t flags, time_t last_mod_time)
- : zip_handle_(handle),
- name_(std::move(path)),
- flags_(flags),
- last_mod_time_(last_mod_time) {}
+ package_property_t flags, ModDate last_mod_time)
+ : zip_handle_(handle), name_(std::move(path)), flags_(flags), last_mod_time_(last_mod_time) {
+}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
package_property_t flags,
@@ -104,10 +101,10 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
return {};
}
- struct stat sb{.st_mtime = -1};
+ ModDate mod_date = kInvalidModDate;
// Skip all up-to-date checks if the file won't ever change.
- if (!isReadonlyFilesystem(path.c_str())) {
- if ((released_fd < 0 ? stat(path.c_str(), &sb) : fstat(released_fd, &sb)) < 0) {
+ if (isKnownWritablePath(path.c_str()) || !isReadonlyFilesystem(GetFileDescriptor(handle))) {
+ if (mod_date = getFileModDate(GetFileDescriptor(handle)); mod_date == kInvalidModDate) {
// Stat requires execute permissions on all directories path to the file. If the process does
// not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
// always have to return true.
@@ -116,7 +113,7 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
}
return std::unique_ptr<ZipAssetsProvider>(
- new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, sb.st_mtime));
+ new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, mod_date));
}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
@@ -137,10 +134,10 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
return {};
}
- struct stat sb{.st_mtime = -1};
+ ModDate mod_date = kInvalidModDate;
// Skip all up-to-date checks if the file won't ever change.
if (!isReadonlyFilesystem(released_fd)) {
- if (fstat(released_fd, &sb) < 0) {
+ if (mod_date = getFileModDate(released_fd); mod_date == kInvalidModDate) {
// Stat requires execute permissions on all directories path to the file. If the process does
// not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
// always have to return true.
@@ -150,7 +147,7 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
}
return std::unique_ptr<ZipAssetsProvider>(new ZipAssetsProvider(
- handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, sb.st_mtime));
+ handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, mod_date));
}
std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
@@ -282,21 +279,16 @@ const std::string& ZipAssetsProvider::GetDebugName() const {
return name_.GetDebugName();
}
-bool ZipAssetsProvider::IsUpToDate() const {
- if (last_mod_time_ == -1) {
- return true;
- }
- struct stat sb{};
- if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
- // If fstat fails on the zip archive, return true so the zip archive the resource system does
- // attempt to refresh the ApkAsset.
- return true;
+UpToDate ZipAssetsProvider::IsUpToDate() const {
+ if (last_mod_time_ == kInvalidModDate) {
+ return UpToDate::Always;
}
- return last_mod_time_ == sb.st_mtime;
+ return fromBool(last_mod_time_ == getFileModDate(GetFileDescriptor(zip_handle_.get())));
}
-DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
- : dir_(std::move(path)), last_mod_time_(last_mod_time) {}
+DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, ModDate last_mod_time)
+ : dir_(std::move(path)), last_mod_time_(last_mod_time) {
+}
std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
struct stat sb;
@@ -317,7 +309,7 @@ std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::st
const bool isReadonly = isReadonlyFilesystem(path.c_str());
return std::unique_ptr<DirectoryAssetsProvider>(
- new DirectoryAssetsProvider(std::move(path), isReadonly ? -1 : sb.st_mtime));
+ new DirectoryAssetsProvider(std::move(path), isReadonly ? kInvalidModDate : getModDate(sb)));
}
std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
@@ -346,17 +338,11 @@ const std::string& DirectoryAssetsProvider::GetDebugName() const {
return dir_;
}
-bool DirectoryAssetsProvider::IsUpToDate() const {
- if (last_mod_time_ == -1) {
- return true;
- }
- struct stat sb;
- if (stat(dir_.c_str(), &sb) < 0) {
- // If stat fails on the zip archive, return true so the zip archive the resource system does
- // attempt to refresh the ApkAsset.
- return true;
+UpToDate DirectoryAssetsProvider::IsUpToDate() const {
+ if (last_mod_time_ == kInvalidModDate) {
+ return UpToDate::Always;
}
- return last_mod_time_ == sb.st_mtime;
+ return fromBool(last_mod_time_ == getFileModDate(dir_.c_str()));
}
MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
@@ -369,8 +355,14 @@ MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& prima
std::unique_ptr<AssetsProvider> MultiAssetsProvider::Create(
std::unique_ptr<AssetsProvider>&& primary, std::unique_ptr<AssetsProvider>&& secondary) {
- if (primary == nullptr || secondary == nullptr) {
- return nullptr;
+ if (primary == nullptr && secondary == nullptr) {
+ return EmptyAssetsProvider::Create();
+ }
+ if (!primary) {
+ return secondary;
+ }
+ if (!secondary) {
+ return primary;
}
return std::unique_ptr<MultiAssetsProvider>(new MultiAssetsProvider(std::move(primary),
std::move(secondary)));
@@ -397,8 +389,8 @@ const std::string& MultiAssetsProvider::GetDebugName() const {
return debug_name_;
}
-bool MultiAssetsProvider::IsUpToDate() const {
- return primary_->IsUpToDate() && secondary_->IsUpToDate();
+UpToDate MultiAssetsProvider::IsUpToDate() const {
+ return combine(primary_->IsUpToDate(), [this] { return secondary_->IsUpToDate(); });
}
EmptyAssetsProvider::EmptyAssetsProvider(std::optional<std::string>&& path) :
@@ -438,12 +430,12 @@ const std::string& EmptyAssetsProvider::GetDebugName() const {
if (path_.has_value()) {
return *path_;
}
- const static std::string kEmpty = kEmptyDebugString;
+ constexpr static std::string kEmpty{kEmptyDebugString};
return kEmpty;
}
-bool EmptyAssetsProvider::IsUpToDate() const {
- return true;
+UpToDate EmptyAssetsProvider::IsUpToDate() const {
+ return UpToDate::Always;
}
} // namespace android
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 3ecd82b074a1..262e7df185b7 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -22,9 +22,10 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "android-base/utf8.h"
-#include "androidfw/misc.h"
+#include "androidfw/AssetManager.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/Util.h"
+#include "androidfw/misc.h"
#include "utils/ByteOrder.h"
#include "utils/Trace.h"
@@ -268,11 +269,16 @@ LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* head
configurations_(configs),
overlay_entries_(overlay_entries),
string_pool_(std::move(string_pool)),
- idmap_fd_(
- android::base::utf8::open(idmap_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY | O_PATH)),
overlay_apk_path_(overlay_apk_path),
target_apk_path_(target_apk_path),
- idmap_last_mod_time_(getFileModDate(idmap_fd_.get())) {
+ idmap_last_mod_time_(kInvalidModDate) {
+ if (!isReadonlyFilesystem(std::string(overlay_apk_path_).c_str()) ||
+ !(target_apk_path_ == AssetManager::TARGET_APK_PATH ||
+ isReadonlyFilesystem(std::string(target_apk_path_).c_str()))) {
+ idmap_fd_.reset(
+ android::base::utf8::open(idmap_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY | O_PATH));
+ idmap_last_mod_time_ = getFileModDate(idmap_fd_);
+ }
}
std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
@@ -381,8 +387,11 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie
overlay_entries, std::move(idmap_string_pool), *overlay_path, *target_path));
}
-bool LoadedIdmap::IsUpToDate() const {
- return idmap_last_mod_time_ == getFileModDate(idmap_fd_.get());
+UpToDate LoadedIdmap::IsUpToDate() const {
+ if (idmap_last_mod_time_ == kInvalidModDate) {
+ return UpToDate::Always;
+ }
+ return fromBool(idmap_last_mod_time_ == getFileModDate(idmap_fd_.get()));
}
} // namespace android
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index de9991a8be5e..a8eb062a2ece 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -152,12 +152,11 @@ static void fill9patchOffsets(Res_png_9patch* patch) {
patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t));
}
-void Res_value::copyFrom_dtoh(const Res_value& src)
-{
- size = dtohs(src.size);
- res0 = src.res0;
- dataType = src.dataType;
- data = dtohl(src.data);
+void Res_value::copyFrom_dtoh_slow(const Res_value& src) {
+ size = dtohs(src.size);
+ res0 = src.res0;
+ dataType = src.dataType;
+ data = dtohl(src.data);
}
void Res_png_9patch::deviceToFile()
@@ -2031,16 +2030,6 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const
// --------------------------------------------------------------------
// --------------------------------------------------------------------
-void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) {
- const size_t size = dtohl(o.size);
- if (size >= sizeof(ResTable_config)) {
- *this = o;
- } else {
- memcpy(this, &o, size);
- memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size);
- }
-}
-
/* static */ size_t unpackLanguageOrRegion(const char in[2], const char base,
char out[4]) {
if (in[0] & 0x80) {
@@ -2105,34 +2094,33 @@ size_t ResTable_config::unpackRegion(char region[4]) const {
return unpackLanguageOrRegion(this->country, '0', region);
}
-
-void ResTable_config::copyFromDtoH(const ResTable_config& o) {
- copyFromDeviceNoSwap(o);
- size = sizeof(ResTable_config);
- mcc = dtohs(mcc);
- mnc = dtohs(mnc);
- density = dtohs(density);
- screenWidth = dtohs(screenWidth);
- screenHeight = dtohs(screenHeight);
- sdkVersion = dtohs(sdkVersion);
- minorVersion = dtohs(minorVersion);
- smallestScreenWidthDp = dtohs(smallestScreenWidthDp);
- screenWidthDp = dtohs(screenWidthDp);
- screenHeightDp = dtohs(screenHeightDp);
-}
-
-void ResTable_config::swapHtoD() {
- size = htodl(size);
- mcc = htods(mcc);
- mnc = htods(mnc);
- density = htods(density);
- screenWidth = htods(screenWidth);
- screenHeight = htods(screenHeight);
- sdkVersion = htods(sdkVersion);
- minorVersion = htods(minorVersion);
- smallestScreenWidthDp = htods(smallestScreenWidthDp);
- screenWidthDp = htods(screenWidthDp);
- screenHeightDp = htods(screenHeightDp);
+void ResTable_config::copyFromDtoH_slow(const ResTable_config& o) {
+ copyFromDeviceNoSwap(o);
+ size = sizeof(ResTable_config);
+ mcc = dtohs(mcc);
+ mnc = dtohs(mnc);
+ density = dtohs(density);
+ screenWidth = dtohs(screenWidth);
+ screenHeight = dtohs(screenHeight);
+ sdkVersion = dtohs(sdkVersion);
+ minorVersion = dtohs(minorVersion);
+ smallestScreenWidthDp = dtohs(smallestScreenWidthDp);
+ screenWidthDp = dtohs(screenWidthDp);
+ screenHeightDp = dtohs(screenHeightDp);
+}
+
+void ResTable_config::swapHtoD_slow() {
+ size = htodl(size);
+ mcc = htods(mcc);
+ mnc = htods(mnc);
+ density = htods(density);
+ screenWidth = htods(screenWidth);
+ screenHeight = htods(screenHeight);
+ sdkVersion = htods(sdkVersion);
+ minorVersion = htods(minorVersion);
+ smallestScreenWidthDp = htods(smallestScreenWidthDp);
+ screenWidthDp = htods(screenWidthDp);
+ screenHeightDp = htods(screenHeightDp);
}
/* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) {
@@ -2145,7 +2133,7 @@ void ResTable_config::swapHtoD() {
// systems should happen very infrequently (if at all.)
// The comparison code relies on memcmp low-level optimizations that make it
// more efficient than strncmp.
- const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
+ static constexpr char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript;
const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript;
diff --git a/libs/androidfw/Util.cpp b/libs/androidfw/Util.cpp
index be55fe8b4bb6..86c459fb4647 100644
--- a/libs/androidfw/Util.cpp
+++ b/libs/androidfw/Util.cpp
@@ -32,13 +32,18 @@ namespace android {
namespace util {
void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out) {
- char buf[5];
- while (*src && len != 0) {
- char16_t c = static_cast<char16_t>(dtohs(*src));
- utf16_to_utf8(&c, 1, buf, sizeof(buf));
- out->append(buf, strlen(buf));
- ++src;
- --len;
+ static constexpr bool kDeviceEndiannessSame = dtohs(0x1001) == 0x1001;
+ if constexpr (kDeviceEndiannessSame) {
+ *out = Utf16ToUtf8({(const char16_t*)src, strnlen16((const char16_t*)src, len)});
+ } else {
+ char buf[5];
+ while (*src && len != 0) {
+ char16_t c = static_cast<char16_t>(dtohs(*src));
+ utf16_to_utf8(&c, 1, buf, sizeof(buf));
+ out->append(buf, strlen(buf));
+ ++src;
+ --len;
+ }
}
}
@@ -63,8 +68,10 @@ std::string Utf16ToUtf8(StringPiece16 utf16) {
}
std::string utf8;
- utf8.resize(utf8_length);
- utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8_length + 1);
+ utf8.resize_and_overwrite(utf8_length, [&utf16](char* data, size_t size) {
+ utf16_to_utf8(utf16.data(), utf16.length(), data, size + 1);
+ return size;
+ });
return utf8;
}
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 231808beb718..3f6f4661f2f7 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -116,7 +116,7 @@ class ApkAssets : public RefBase {
return resources_asset_ != nullptr && resources_asset_->isAllocated();
}
- bool IsUpToDate() const;
+ UpToDate IsUpToDate() const;
// DANGER!
// This is a destructive method that rips the assets provider out of ApkAssets object.
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index d33c325ff369..e3b3ae41f7f4 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROIDFW_ASSETSPROVIDER_H
-#define ANDROIDFW_ASSETSPROVIDER_H
+#pragma once
#include <memory>
#include <string>
@@ -58,7 +57,7 @@ struct AssetsProvider {
WARN_UNUSED virtual const std::string& GetDebugName() const = 0;
// Returns whether the interface provides the most recent version of its files.
- WARN_UNUSED virtual bool IsUpToDate() const = 0;
+ WARN_UNUSED virtual UpToDate IsUpToDate() const = 0;
// Creates an Asset from a file on disk.
static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
@@ -95,7 +94,7 @@ struct ZipAssetsProvider : public AssetsProvider {
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
- WARN_UNUSED bool IsUpToDate() const override;
+ WARN_UNUSED UpToDate IsUpToDate() const override;
WARN_UNUSED std::optional<uint32_t> GetCrc(std::string_view path) const;
~ZipAssetsProvider() override = default;
@@ -106,7 +105,7 @@ struct ZipAssetsProvider : public AssetsProvider {
private:
struct PathOrDebugName;
ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, package_property_t flags,
- time_t last_mod_time);
+ ModDate last_mod_time);
struct PathOrDebugName {
static PathOrDebugName Path(std::string value) {
@@ -135,7 +134,7 @@ struct ZipAssetsProvider : public AssetsProvider {
std::unique_ptr<ZipArchive, ZipCloser> zip_handle_;
PathOrDebugName name_;
package_property_t flags_;
- time_t last_mod_time_;
+ ModDate last_mod_time_;
};
// Supplies assets from a root directory.
@@ -147,7 +146,7 @@ struct DirectoryAssetsProvider : public AssetsProvider {
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
- WARN_UNUSED bool IsUpToDate() const override;
+ WARN_UNUSED UpToDate IsUpToDate() const override;
~DirectoryAssetsProvider() override = default;
protected:
@@ -156,23 +155,23 @@ struct DirectoryAssetsProvider : public AssetsProvider {
bool* file_exists) const override;
private:
- explicit DirectoryAssetsProvider(std::string&& path, time_t last_mod_time);
+ explicit DirectoryAssetsProvider(std::string&& path, ModDate last_mod_time);
std::string dir_;
- time_t last_mod_time_;
+ ModDate last_mod_time_;
};
// Supplies assets from a `primary` asset provider and falls back to supplying assets from the
// `secondary` asset provider if the asset cannot be found in the `primary`.
struct MultiAssetsProvider : public AssetsProvider {
static std::unique_ptr<AssetsProvider> Create(std::unique_ptr<AssetsProvider>&& primary,
- std::unique_ptr<AssetsProvider>&& secondary);
+ std::unique_ptr<AssetsProvider>&& secondary = {});
bool ForEachFile(const std::string& root_path,
base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
- WARN_UNUSED bool IsUpToDate() const override;
+ WARN_UNUSED UpToDate IsUpToDate() const override;
~MultiAssetsProvider() override = default;
protected:
@@ -199,7 +198,7 @@ struct EmptyAssetsProvider : public AssetsProvider {
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
- WARN_UNUSED bool IsUpToDate() const override;
+ WARN_UNUSED UpToDate IsUpToDate() const override;
~EmptyAssetsProvider() override = default;
protected:
@@ -212,5 +211,3 @@ struct EmptyAssetsProvider : public AssetsProvider {
};
} // namespace android
-
-#endif /* ANDROIDFW_ASSETSPROVIDER_H */
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ac75eb3bb98c..87f3c9df9a91 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef IDMAP_H_
-#define IDMAP_H_
+#pragma once
#include <memory>
#include <string>
@@ -32,6 +31,31 @@
namespace android {
+// An enum that tracks more states than just 'up to date' or 'not' for a resources container:
+// there are several cases where we know for sure that the object can't change and won't get
+// out of date. Reporting those states to the managed layer allows it to stop checking here
+// completely, speeding up the cache lookups by dozens of milliseconds.
+enum class UpToDate : int { False, True, Always };
+
+// Combines two UpToDate values, and only accesses the second one if it matters to the result.
+template <class Getter>
+UpToDate combine(UpToDate first, Getter secondGetter) {
+ switch (first) {
+ case UpToDate::False:
+ return UpToDate::False;
+ case UpToDate::True: {
+ const auto second = secondGetter();
+ return second == UpToDate::False ? UpToDate::False : UpToDate::True;
+ }
+ case UpToDate::Always:
+ return secondGetter();
+ }
+}
+
+inline UpToDate fromBool(bool value) {
+ return value ? UpToDate::True : UpToDate::False;
+}
+
class LoadedIdmap;
class IdmapResMap;
struct Idmap_header;
@@ -196,7 +220,7 @@ class LoadedIdmap {
// Returns whether the idmap file on disk has not been modified since the construction of this
// LoadedIdmap.
- bool IsUpToDate() const;
+ UpToDate IsUpToDate() const;
protected:
// Exposed as protected so that tests can subclass and mock this class out.
@@ -231,5 +255,3 @@ class LoadedIdmap {
};
} // namespace android
-
-#endif // IDMAP_H_
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index e330410ed1a0..819fe4b38c87 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -47,6 +47,8 @@
namespace android {
+constexpr const bool kDeviceEndiannessSame = dtohs(0x1001) == 0x1001;
+
constexpr const uint32_t kIdmapMagic = 0x504D4449u;
constexpr const uint32_t kIdmapCurrentVersion = 0x0000000Au;
@@ -408,7 +410,16 @@ struct Res_value
typedef uint32_t data_type;
data_type data;
- void copyFrom_dtoh(const Res_value& src);
+ void copyFrom_dtoh(const Res_value& src) {
+ if constexpr (kDeviceEndiannessSame) {
+ *this = src;
+ } else {
+ copyFrom_dtoh_slow(src);
+ }
+ }
+
+ private:
+ void copyFrom_dtoh_slow(const Res_value& src);
};
/**
@@ -1254,11 +1265,32 @@ struct ResTable_config
// Varies in length from 3 to 8 chars. Zero-filled value.
char localeNumberingSystem[8];
- void copyFromDeviceNoSwap(const ResTable_config& o);
-
- void copyFromDtoH(const ResTable_config& o);
-
- void swapHtoD();
+ void copyFromDeviceNoSwap(const ResTable_config& o) {
+ const auto o_size = dtohl(o.size);
+ if (o_size >= sizeof(ResTable_config)) [[likely]] {
+ *this = o;
+ } else {
+ memcpy(this, &o, o_size);
+ memset(((uint8_t*)this) + o_size, 0, sizeof(ResTable_config) - o_size);
+ }
+ this->size = sizeof(*this);
+ }
+
+ void copyFromDtoH(const ResTable_config& o) {
+ if constexpr (kDeviceEndiannessSame) {
+ copyFromDeviceNoSwap(o);
+ } else {
+ copyFromDtoH_slow(o);
+ }
+ }
+
+ void swapHtoD() {
+ if constexpr (kDeviceEndiannessSame) {
+ ; // noop
+ } else {
+ swapHtoD_slow();
+ }
+ }
int compare(const ResTable_config& o) const;
int compareLogical(const ResTable_config& o) const;
@@ -1384,6 +1416,10 @@ struct ResTable_config
bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const;
String8 toString() const;
+
+ private:
+ void copyFromDtoH_slow(const ResTable_config& o);
+ void swapHtoD_slow();
};
/**
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index c9ba8a01a5e9..d8ca64a174a2 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -15,6 +15,7 @@
*/
#pragma once
+#include <sys/stat.h>
#include <time.h>
//
@@ -64,10 +65,15 @@ ModDate getFileModDate(const char* fileName);
/* same, but also returns -1 if the file has already been deleted */
ModDate getFileModDate(int fd);
+// Extract the modification date from the stat structure.
+ModDate getModDate(const struct ::stat& st);
+
// Check if |path| or |fd| resides on a readonly filesystem.
bool isReadonlyFilesystem(const char* path);
bool isReadonlyFilesystem(int fd);
+bool isKnownWritablePath(const char* path);
+
} // namespace android
// Whoever uses getFileModDate() will need this as well
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 32f3624a3aee..26eb320805c9 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -16,10 +16,10 @@
#define LOG_TAG "misc"
-//
-// Miscellaneous utility functions.
-//
-#include <androidfw/misc.h>
+#include "androidfw/misc.h"
+
+#include <errno.h>
+#include <sys/stat.h>
#include "android-base/logging.h"
@@ -28,9 +28,7 @@
#include <sys/vfs.h>
#endif // __linux__
-#include <errno.h>
-#include <sys/stat.h>
-
+#include <array>
#include <cstdio>
#include <cstring>
#include <tuple>
@@ -40,28 +38,26 @@ namespace android {
/*
* Get a file's type.
*/
-FileType getFileType(const char* fileName)
-{
- struct stat sb;
-
- if (stat(fileName, &sb) < 0) {
- if (errno == ENOENT || errno == ENOTDIR)
- return kFileTypeNonexistent;
- else {
- PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed";
- return kFileTypeUnknown;
- }
- } else {
- if (S_ISREG(sb.st_mode))
- return kFileTypeRegular;
- else if (S_ISDIR(sb.st_mode))
- return kFileTypeDirectory;
- else if (S_ISCHR(sb.st_mode))
- return kFileTypeCharDev;
- else if (S_ISBLK(sb.st_mode))
- return kFileTypeBlockDev;
- else if (S_ISFIFO(sb.st_mode))
- return kFileTypeFifo;
+FileType getFileType(const char* fileName) {
+ struct stat sb;
+ if (stat(fileName, &sb) < 0) {
+ if (errno == ENOENT || errno == ENOTDIR)
+ return kFileTypeNonexistent;
+ else {
+ PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed";
+ return kFileTypeUnknown;
+ }
+ } else {
+ if (S_ISREG(sb.st_mode))
+ return kFileTypeRegular;
+ else if (S_ISDIR(sb.st_mode))
+ return kFileTypeDirectory;
+ else if (S_ISCHR(sb.st_mode))
+ return kFileTypeCharDev;
+ else if (S_ISBLK(sb.st_mode))
+ return kFileTypeBlockDev;
+ else if (S_ISFIFO(sb.st_mode))
+ return kFileTypeFifo;
#if defined(S_ISLNK)
else if (S_ISLNK(sb.st_mode))
return kFileTypeSymlink;
@@ -75,7 +71,7 @@ FileType getFileType(const char* fileName)
}
}
-static ModDate getModDate(const struct stat& st) {
+ModDate getModDate(const struct stat& st) {
#ifdef _WIN32
return st.st_mtime;
#elif defined(__APPLE__)
@@ -113,8 +109,14 @@ bool isReadonlyFilesystem(const char*) {
bool isReadonlyFilesystem(int) {
return false;
}
+bool isKnownWritablePath(const char*) {
+ return false;
+}
#else // __linux__
bool isReadonlyFilesystem(const char* path) {
+ if (isKnownWritablePath(path)) {
+ return false;
+ }
struct statfs sfs;
if (::statfs(path, &sfs)) {
PLOG(ERROR) << "isReadonlyFilesystem(): statfs(" << path << ") failed";
@@ -131,6 +133,13 @@ bool isReadonlyFilesystem(int fd) {
}
return (sfs.f_flags & ST_RDONLY) != 0;
}
+
+bool isKnownWritablePath(const char* path) {
+ // We know that all paths in /data/ are writable.
+ static constexpr char kRwPrefix[] = "/data/";
+ return strncmp(kRwPrefix, path, std::size(kRwPrefix) - 1) == 0;
+}
+
#endif // __linux__
} // namespace android
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index cb2e56f5f5e4..22b9e69500d9 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -218,10 +218,11 @@ TEST_F(IdmapTest, OverlayAssetsIsUpToDate) {
auto apk_assets = ApkAssets::LoadOverlay(temp_file.path);
ASSERT_NE(nullptr, apk_assets);
- ASSERT_TRUE(apk_assets->IsUpToDate());
+ ASSERT_TRUE(apk_assets->IsOverlay());
+ ASSERT_EQ(UpToDate::True, apk_assets->IsUpToDate());
unlink(temp_file.path);
- ASSERT_FALSE(apk_assets->IsUpToDate());
+ ASSERT_EQ(UpToDate::False, apk_assets->IsUpToDate());
const auto sleep_duration =
std::chrono::nanoseconds(std::max(kModDateResolutionNs, 1'000'000ull));
@@ -230,7 +231,27 @@ TEST_F(IdmapTest, OverlayAssetsIsUpToDate) {
base::WriteStringToFile("hello", temp_file.path);
std::this_thread::sleep_for(sleep_duration);
- ASSERT_FALSE(apk_assets->IsUpToDate());
+ ASSERT_EQ(UpToDate::False, apk_assets->IsUpToDate());
+}
+
+TEST(IdmapTestUpToDate, Combine) {
+ ASSERT_EQ(UpToDate::False, combine(UpToDate::False, [] {
+ ADD_FAILURE(); // Shouldn't get called at all.
+ return UpToDate::False;
+ }));
+
+ ASSERT_EQ(UpToDate::False, combine(UpToDate::True, [] { return UpToDate::False; }));
+
+ ASSERT_EQ(UpToDate::True, combine(UpToDate::True, [] { return UpToDate::True; }));
+ ASSERT_EQ(UpToDate::True, combine(UpToDate::True, [] { return UpToDate::Always; }));
+ ASSERT_EQ(UpToDate::True, combine(UpToDate::Always, [] { return UpToDate::True; }));
+
+ ASSERT_EQ(UpToDate::Always, combine(UpToDate::Always, [] { return UpToDate::Always; }));
+}
+
+TEST(IdmapTestUpToDate, FromBool) {
+ ASSERT_EQ(UpToDate::False, fromBool(false));
+ ASSERT_EQ(UpToDate::True, fromBool(true));
}
} // namespace
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 36f62da651db..c9625c405faa 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -5866,19 +5866,31 @@ final public class MediaCodec {
@NonNull MediaCodec codec, @NonNull MediaFormat format);
/**
- * Called when the metrics for this codec have been flushed due to the
- * start of a new subsession.
+ * Called when the metrics for this codec have been flushed "mid-stream"
+ * due to the start of a new subsession during execution.
* <p>
- * This can happen when the codec is reconfigured after stop(), or
- * mid-stream e.g. if the video size changes. When this happens, the
- * metrics for the previous subsession are flushed, and
- * {@link MediaCodec#getMetrics} will return the metrics for the
- * new subsession. This happens just before the {@link Callback#onOutputFormatChanged}
+ * A new codec subsession normally starts when the codec is reconfigured
+ * after stop(), but it can also happen mid-stream e.g. if the video size
+ * changes. When this happens, the metrics for the previous subsession
+ * are flushed, and {@link MediaCodec#getMetrics} will return the metrics
+ * for the new subsession.
+ * <p>
+ * For subsessions that begin due to a reconfiguration, the metrics for
+ * the prior subsession can be retrieved via {@link MediaCodec#getMetrics}
+ * prior to calling {@link #configure}.
+ * <p>
+ * When a new subsession begins "mid-stream", the metrics for the prior
+ * subsession are flushed just before the {@link Callback#onOutputFormatChanged}
* event, so this <b>optional</b> callback is provided to be able to
* capture the final metrics for the previous subsession.
*
* @param codec The MediaCodec object.
- * @param metrics The flushed metrics for this codec.
+ * @param metrics The flushed metrics for this codec. This is a
+ * {@link PersistableBundle} containing the set of
+ * attributes and values available for the media being
+ * handled by this instance of MediaCodec. The attributes
+ * are described in {@link MetricsConstants}. Additional
+ * vendor-specific fields may also be present.
*/
@FlaggedApi(FLAG_SUBSESSION_METRICS)
public void onMetricsFlushed(
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 678150b9f3a1..4c5efc1cf24b 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -18,6 +18,8 @@ package android.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.media.MediaCodec.BufferInfo;
import android.os.Build;
@@ -257,6 +259,8 @@ final public class MediaMuxer {
*/
private OutputFormat() {}
/** @hide */
+ @SuppressLint("UnflaggedApi")
+ @TestApi
public static final int MUXER_OUTPUT_FIRST = 0;
/** MPEG4 media file format*/
public static final int MUXER_OUTPUT_MPEG_4 = MUXER_OUTPUT_FIRST;
@@ -269,6 +273,8 @@ final public class MediaMuxer {
/** Ogg media file format*/
public static final int MUXER_OUTPUT_OGG = MUXER_OUTPUT_FIRST + 4;
/** @hide */
+ @SuppressLint("UnflaggedApi")
+ @TestApi
public static final int MUXER_OUTPUT_LAST = MUXER_OUTPUT_OGG;
};
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 3d0c4069e782..213bc0673da6 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -27,6 +27,7 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.WorkerThread;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -475,6 +476,7 @@ public final class SoundTriggerManager {
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
@FlaggedApi(Flags.FLAG_MANAGER_API)
+ @WorkerThread
public int loadSoundModel(@NonNull SoundModel soundModel) {
if (mSoundTriggerSession == null) {
throw new IllegalStateException("No underlying SoundTriggerModule available");
@@ -518,6 +520,7 @@ public final class SoundTriggerManager {
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
@FlaggedApi(Flags.FLAG_MANAGER_API)
+ @WorkerThread
public int startRecognition(@NonNull UUID soundModelId, @Nullable Bundle params,
@NonNull ComponentName detectionService, @NonNull RecognitionConfig config) {
Objects.requireNonNull(soundModelId);
@@ -544,6 +547,7 @@ public final class SoundTriggerManager {
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
@FlaggedApi(Flags.FLAG_MANAGER_API)
+ @WorkerThread
public int stopRecognition(@NonNull UUID soundModelId) {
if (mSoundTriggerSession == null) {
throw new IllegalStateException("No underlying SoundTriggerModule available");
@@ -568,6 +572,7 @@ public final class SoundTriggerManager {
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
@FlaggedApi(Flags.FLAG_MANAGER_API)
+ @WorkerThread
public int unloadSoundModel(@NonNull UUID soundModelId) {
if (mSoundTriggerSession == null) {
throw new IllegalStateException("No underlying SoundTriggerModule available");
@@ -587,6 +592,7 @@ public final class SoundTriggerManager {
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
@FlaggedApi(Flags.FLAG_MANAGER_API)
+ @WorkerThread
public boolean isRecognitionActive(@NonNull UUID soundModelId) {
if (soundModelId == null || mSoundTriggerSession == null) {
return false;
@@ -624,6 +630,7 @@ public final class SoundTriggerManager {
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
@FlaggedApi(Flags.FLAG_MANAGER_API)
+ @WorkerThread
public int getModelState(@NonNull UUID soundModelId) {
if (mSoundTriggerSession == null) {
throw new IllegalStateException("No underlying SoundTriggerModule available");
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 68c1983825a2..1e6a7b7f2810 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -859,7 +859,7 @@ void APerformanceHintManager::layersFromNativeSurfaces(ANativeWindow** windows,
std::vector<ANativeWindow*> windowVec(windows, windows + numWindows);
for (auto&& window : windowVec) {
Surface* surface = static_cast<Surface*>(window);
- if (Surface::isValid(surface)) {
+ if (surface != nullptr) {
const sp<IBinder>& handle = surface->getSurfaceControlHandle();
if (handle != nullptr) {
out.push_back(handle);
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 3b34fa335cec..0fa92eab4c0a 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -605,6 +605,15 @@ TEST_F(PerformanceHintTest, TestASessionCreationConfig) {
ASSERT_NE(config, nullptr);
}
+TEST_F(PerformanceHintTest, TestSessionCreationWithNullLayers) {
+ EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)).Times(1);
+ auto&& config = configFromCreator(
+ {.tids = mTids, .nativeWindows = {nullptr}, .surfaceControls = {nullptr}});
+ APerformanceHintManager* manager = createManager();
+ auto&& session = createSessionUsingConfig(manager, config);
+ ASSERT_TRUE(session);
+}
+
TEST_F(PerformanceHintTest, TestSupportObject) {
// Disable GPU and Power Efficiency support to test partial enabling
mClientData.supportInfo.sessionModes &= ~(1 << (int)hal::SessionMode::AUTO_GPU);
diff --git a/packages/CredentialManager/tests/robotests/Android.bp b/packages/CredentialManager/tests/robotests/Android.bp
index 27afaaa49fdd..01f403d3719d 100644
--- a/packages/CredentialManager/tests/robotests/Android.bp
+++ b/packages/CredentialManager/tests/robotests/Android.bp
@@ -53,7 +53,6 @@ android_robolectric_test {
"android.test.mock.stubs.system",
"truth",
],
- upstream: true,
java_resource_dirs: ["config"],
instrumentation_for: "CredentialManagerRobo",
}
diff --git a/packages/CredentialManager/wear/robotests/Android.bp b/packages/CredentialManager/wear/robotests/Android.bp
index 589a3d6cc103..db3c36355dde 100644
--- a/packages/CredentialManager/wear/robotests/Android.bp
+++ b/packages/CredentialManager/wear/robotests/Android.bp
@@ -24,6 +24,5 @@ android_robolectric_test {
"framework_graphics_flags_java_lib",
],
java_resource_dirs: ["config"],
- upstream: true,
strict_mode: false,
}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_romanian.kcm b/packages/InputDevices/res/raw/keyboard_layout_romanian.kcm
new file mode 100644
index 000000000000..b384a2418ff2
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_romanian.kcm
@@ -0,0 +1,357 @@
+# 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.
+
+#
+# Romanian keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '\u201e'
+ base: '\u201e'
+ shift: '\u201d'
+ ralt: '`'
+ ralt+shift: '~'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+ ralt: '\u0303'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '@'
+ ralt: '\u030C'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+ ralt: '\u0302'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+ ralt: '\u0306'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+ ralt: '\u030A'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '^'
+ ralt: '\u0328'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '&'
+ ralt: '\u0300'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '*'
+ ralt: '\u0307'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+ ralt: '\u0301'
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+ ralt: '\u030B'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+ ralt: '\u0308'
+ ralt+shift: '\u2013'
+}
+
+key EQUALS {
+ label: '='
+ base: '='
+ shift: '+'
+ ralt: '\u0327'
+ ralt+shift: '\u00b1'
+}
+
+### ROW 2
+
+key Q {
+ label: 'Q'
+ base, capslock+shift: 'q'
+ shift, capslock: 'Q'
+}
+
+key W {
+ label: 'W'
+ base, capslock+shift: 'w'
+ shift, capslock: 'W'
+}
+
+key E {
+ label: 'E'
+ base, capslock+shift: 'e'
+ shift, capslock: 'E'
+ ralt: '\u20ac'
+}
+
+key R {
+ label: 'R'
+ base, capslock+shift: 'r'
+ shift, capslock: 'R'
+}
+
+key T {
+ label: 'T'
+ base, capslock+shift: 't'
+ shift, capslock: 'T'
+}
+
+key Y {
+ label: 'Y'
+ base, capslock+shift: 'y'
+ shift, capslock: 'Y'
+}
+
+key U {
+ label: 'U'
+ base, capslock+shift: 'u'
+ shift, capslock: 'U'
+}
+
+key I {
+ label: 'I'
+ base, capslock+shift: 'i'
+ shift, capslock: 'I'
+}
+
+key O {
+ label: 'O'
+ base, capslock+shift: 'o'
+ shift, capslock: 'O'
+}
+
+key P {
+ label: 'P'
+ base, capslock+shift: 'p'
+ shift, capslock: 'P'
+ ralt: '\u00a7'
+}
+
+key LEFT_BRACKET {
+ label: '\u0102'
+ base, capslock+shift: '\u0103'
+ shift, capslock: '\u0102'
+ ralt: '['
+ ralt+shift: '{'
+}
+
+key RIGHT_BRACKET {
+ label: '\u00ce'
+ base, capslock+shift: '\u00ee'
+ shift, capslock: '\u00ce'
+ ralt: ']'
+ ralt+shift: '}'
+}
+
+### ROW 3
+
+key A {
+ label: 'A'
+ base, capslock+shift: 'a'
+ shift, capslock: 'A'
+}
+
+key S {
+ label: 'S'
+ base, capslock+shift: 's'
+ shift, capslock: 'S'
+ ralt: '\u00df'
+}
+
+key D {
+ label: 'D'
+ base, capslock+shift: 'd'
+ shift, capslock: 'D'
+ ralt: '\u0111'
+ ralt+shift, ralt+capslock: '\u0110'
+ ralt+shift+capslock: '\u0111'
+}
+
+key F {
+ label: 'F'
+ base, capslock+shift: 'f'
+ shift, capslock: 'F'
+}
+
+key G {
+ label: 'G'
+ base, capslock+shift: 'g'
+ shift, capslock: 'G'
+}
+
+key H {
+ label: 'H'
+ base, capslock+shift: 'h'
+ shift, capslock: 'H'
+}
+
+key J {
+ label: 'J'
+ base, capslock+shift: 'j'
+ shift, capslock: 'J'
+}
+
+key K {
+ label: 'K'
+ base, capslock+shift: 'k'
+ shift, capslock: 'K'
+}
+
+key L {
+ label: 'L'
+ base, capslock+shift: 'l'
+ shift, capslock: 'L'
+ ralt: '\u0142'
+ ralt+shift, ralt+capslock: '\u0141'
+ ralt+shift+capslock: '\u0142'
+}
+
+key SEMICOLON {
+ label: '\u0218'
+ base, capslock+shift: '\u0219'
+ shift, capslock: '\u0218'
+ ralt: ';'
+ ralt+shift: ':'
+}
+
+key APOSTROPHE {
+ label: '\u021a'
+ base, capslock+shift: '\u021b'
+ shift, capslock: '\u021a'
+ ralt: '\''
+ ralt+shift: '\u0022'
+}
+
+key BACKSLASH {
+ label: '\u00c2'
+ base, capslock+shift: '\u00e2'
+ shift, capslock: '\u00c2'
+ ralt: '\\'
+ ralt+shift: '|'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '\\'
+ base: '\\'
+ shift: '|'
+}
+
+key Z {
+ label: 'Z'
+ base, capslock+shift: 'z'
+ shift, capslock: 'Z'
+}
+
+key X {
+ label: 'X'
+ base, capslock+shift: 'x'
+ shift, capslock: 'X'
+}
+
+key C {
+ label: 'C'
+ base, capslock+shift: 'c'
+ shift, capslock: 'C'
+ ralt: '\u00a9'
+}
+
+key V {
+ label: 'V'
+ base, capslock+shift: 'v'
+ shift, capslock: 'V'
+}
+
+key B {
+ label: 'B'
+ base, capslock+shift: 'b'
+ shift, capslock: 'B'
+}
+
+key N {
+ label: 'N'
+ base, capslock+shift: 'n'
+ shift, capslock: 'N'
+}
+
+key M {
+ label: 'M'
+ base, capslock+shift: 'm'
+ shift, capslock: 'M'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift: ';'
+ ralt: '<'
+ ralt+shift: '\u00ab'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift: ':'
+ ralt: '>'
+ ralt+shift: '\u00bb'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift: '?'
+}
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 5a911256d9be..bd7cdc481524 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -164,4 +164,7 @@
<!-- Montenegrin (Cyrillic) keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_montenegrin_cyrillic">Montenegrin (Cyrillic)</string>
+
+ <!-- Romanian keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_romanian">Romanian</string>
</resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 93094890418d..9ce9a87a1f9f 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -360,4 +360,11 @@
android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_cyrillic"
android:keyboardLocale="cnr-Cyrl-ME"
android:keyboardLayoutType="extended" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_romanian"
+ android:label="@string/keyboard_layout_romanian"
+ android:keyboardLayout="@raw/keyboard_layout_romanian"
+ android:keyboardLocale="ro-Latn-RO"
+ android:keyboardLayoutType="qwerty" />
</keyboard-layouts>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
index b20117d78230..c99d37bb6ce6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
@@ -19,6 +19,7 @@ package com.android.packageinstaller;
import static android.Manifest.permission;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ARCHIVED_PACKAGES;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.app.Activity;
import android.app.DialogFragment;
@@ -53,6 +54,8 @@ public class UnarchiveActivity extends Activity {
@Override
public void onCreate(Bundle icicle) {
+ getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
super.onCreate(null);
int callingUid = getLaunchedFromUid();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
index 42dd382b98bc..fbb0fa4d6a57 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
@@ -21,10 +21,14 @@ import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
+import android.widget.Button;
public class UnarchiveFragment extends DialogFragment implements
DialogInterface.OnClickListener {
+ private Dialog mDialog;
+ private Button mRestoreButton;
+
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
String appTitle = getArguments().getString(UnarchiveActivity.APP_TITLE);
@@ -40,7 +44,32 @@ public class UnarchiveFragment extends DialogFragment implements
dialogBuilder.setPositiveButton(R.string.restore, this);
dialogBuilder.setNegativeButton(android.R.string.cancel, this);
- return dialogBuilder.create();
+ mDialog = dialogBuilder.create();
+ return mDialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (mDialog != null) {
+ mRestoreButton = ((AlertDialog) mDialog).getButton(DialogInterface.BUTTON_POSITIVE);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (mRestoreButton != null) {
+ mRestoreButton.setEnabled(false);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (mRestoreButton != null) {
+ mRestoreButton.setEnabled(true);
+ }
}
@Override
diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp
index 2e3b42de5b9d..6044eaba5f89 100644
--- a/packages/SettingsLib/DataStore/tests/Android.bp
+++ b/packages/SettingsLib/DataStore/tests/Android.bp
@@ -25,6 +25,5 @@ android_robolectric_test {
java_resource_dirs: ["config"],
instrumentation_for: "SettingsLibDataStoreShell",
coverage_libs: ["SettingsLibDataStore"],
- upstream: true,
strict_mode: false,
}
diff --git a/packages/SettingsLib/Ipc/Android.bp b/packages/SettingsLib/Ipc/Android.bp
index 2c7209a48bbd..bc5a9364279d 100644
--- a/packages/SettingsLib/Ipc/Android.bp
+++ b/packages/SettingsLib/Ipc/Android.bp
@@ -25,7 +25,7 @@ android_library {
name: "SettingsLibIpc-testutils",
srcs: ["testutils/**/*.kt"],
static_libs: [
- "Robolectric_all-target_upstream",
+ "Robolectric_all-target",
"SettingsLibIpc",
"androidx.test.core",
"flag-junit",
diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp
index 3e109f13ae7b..1661dfb2a86b 100644
--- a/packages/SettingsLib/SettingsTheme/Android.bp
+++ b/packages/SettingsLib/SettingsTheme/Android.bp
@@ -16,7 +16,6 @@ android_library {
],
resource_dirs: ["res"],
static_libs: [
- "aconfig_settingslib_exported_flags_java_lib",
"androidx.preference_preference",
"com.google.android.material_material",
],
@@ -24,12 +23,12 @@ android_library {
min_sdk_version: "21",
apex_available: [
"//apex_available:platform",
- "com.android.adservices",
"com.android.cellbroadcast",
"com.android.devicelock",
"com.android.extservices",
+ "com.android.permission",
+ "com.android.adservices",
"com.android.healthfitness",
"com.android.mediaprovider",
- "com.android.permission",
],
}
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
index 8223eff1e024..74f5441f6760 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
@@ -18,7 +18,6 @@ package com.android.settingslib.widget
import android.content.Context
import android.os.Build
-import com.android.settingslib.flags.Flags
object SettingsThemeHelper {
private const val IS_EXPRESSIVE_DESIGN_ENABLED = "is_expressive_design_enabled"
@@ -50,8 +49,7 @@ object SettingsThemeHelper {
expressiveThemeState =
if (
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) &&
- (getPropBoolean(context, IS_EXPRESSIVE_DESIGN_ENABLED, false) ||
- Flags.isExpressiveDesignEnabled())
+ getPropBoolean(context, IS_EXPRESSIVE_DESIGN_ENABLED, false)
) {
ExpressiveThemeState.ENABLED
} else {
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/Android.bp b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
index f6477e2f052a..dd6743b8595c 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
+++ b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
@@ -68,7 +68,6 @@ android_robolectric_test {
"android.test.mock.stubs.system",
"truth",
],
- upstream: true,
java_resource_dirs: ["config"],
instrumentation_for: "SpaRoboApp",
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
new file mode 100644
index 000000000000..5b7e2a86135a
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.framework.common
+
+import android.content.Context
+import android.content.res.Resources
+import android.icu.text.DecimalFormat
+import android.icu.text.MeasureFormat
+import android.icu.text.NumberFormat
+import android.icu.text.UnicodeSet
+import android.icu.text.UnicodeSetSpanner
+import android.icu.util.Measure
+import android.text.format.Formatter
+import android.text.format.Formatter.RoundedBytesResult
+import java.math.BigDecimal
+
+class BytesFormatter(resources: Resources) {
+
+ enum class UseCase(val flag: Int) {
+ FileSize(Formatter.FLAG_SI_UNITS),
+ DataUsage(Formatter.FLAG_IEC_UNITS),
+ }
+
+ data class Result(val number: String, val units: String)
+
+ constructor(context: Context) : this(context.resources)
+
+ private val locale = resources.configuration.locales[0]
+
+ fun format(bytes: Long, useCase: UseCase): String {
+ val rounded = RoundedBytesResult.roundBytes(bytes, useCase.flag)
+ val numberFormatter = getNumberFormatter(rounded.fractionDigits)
+ return numberFormatter.formatRoundedBytesResult(rounded)
+ }
+
+ fun formatWithUnits(bytes: Long, useCase: UseCase): Result {
+ val rounded = RoundedBytesResult.roundBytes(bytes, useCase.flag)
+ val numberFormatter = getNumberFormatter(rounded.fractionDigits)
+ val formattedString = numberFormatter.formatRoundedBytesResult(rounded)
+ val formattedNumber = numberFormatter.format(rounded.value)
+ return Result(
+ number = formattedNumber,
+ units = formattedString.removeFirst(formattedNumber),
+ )
+ }
+
+ private fun NumberFormat.formatRoundedBytesResult(rounded: RoundedBytesResult): String {
+ val measureFormatter =
+ MeasureFormat.getInstance(locale, MeasureFormat.FormatWidth.SHORT, this)
+ return measureFormatter.format(Measure(rounded.value, rounded.units))
+ }
+
+ private fun getNumberFormatter(fractionDigits: Int) =
+ NumberFormat.getInstance(locale).apply {
+ minimumFractionDigits = fractionDigits
+ maximumFractionDigits = fractionDigits
+ isGroupingUsed = false
+ if (this is DecimalFormat) {
+ setRoundingMode(BigDecimal.ROUND_HALF_UP)
+ }
+ }
+
+ private companion object {
+ fun String.removeFirst(removed: String): String =
+ SPACES_AND_CONTROLS.trim(replaceFirst(removed, "")).toString()
+
+ val SPACES_AND_CONTROLS = UnicodeSetSpanner(UnicodeSet("[[:Zs:][:Cf:]]").freeze())
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt
new file mode 100644
index 000000000000..7220848eebff
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt
@@ -0,0 +1,178 @@
+/*
+ * 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.framework.common
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class BytesFormatterTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val formatter = BytesFormatter(context)
+
+ @Test
+ fun `Zero bytes`() {
+ // Given a byte value of 0, the formatted output should be "0 byte" for both FileSize
+ // and DataUsage UseCases. This verifies special handling of zero values.
+
+ val fileSizeResult = formatter.format(0, BytesFormatter.UseCase.FileSize)
+ assertThat(fileSizeResult).isEqualTo("0 byte")
+
+ val dataUsageResult = formatter.format(0, BytesFormatter.UseCase.DataUsage)
+ assertThat(dataUsageResult).isEqualTo("0 byte")
+ }
+
+ @Test
+ fun `Positive bytes`() {
+ // Given a positive byte value (e.g., 1000), the formatted output should be correctly
+ // displayed with appropriate units (e.g., '1.00 kB') for both UseCases.
+
+ val fileSizeResult = formatter.format(1000, BytesFormatter.UseCase.FileSize)
+ assertThat(fileSizeResult).isEqualTo("1.00 kB")
+
+ val dataUsageResult = formatter.format(1024, BytesFormatter.UseCase.DataUsage)
+ assertThat(dataUsageResult).isEqualTo("1.00 kB")
+ }
+
+ @Test
+ fun `Large bytes`() {
+ // Given a very large byte value (e.g., Long.MAX_VALUE), the formatted output should be
+ // correctly displayed with the largest unit (e.g., 'PB') for both UseCases.
+
+ val fileSizeResult = formatter.format(Long.MAX_VALUE, BytesFormatter.UseCase.FileSize)
+ assertThat(fileSizeResult).isEqualTo("9223 PB")
+
+ val dataUsageResult = formatter.format(Long.MAX_VALUE, BytesFormatter.UseCase.DataUsage)
+ assertThat(dataUsageResult).isEqualTo("8192 PB")
+ }
+
+ @Test
+ fun `Bytes requiring rounding`() {
+ // Given byte values that require rounding (e.g., 1512), the formatted output should be
+ // rounded to the appropriate number of decimal places (e.g., '1.51 kB').
+
+ val fileSizeResult = formatter.format(1512, BytesFormatter.UseCase.FileSize)
+ assertThat(fileSizeResult).isEqualTo("1.51 kB")
+
+ val dataUsageResult = formatter.format(1512, BytesFormatter.UseCase.DataUsage)
+ assertThat(dataUsageResult).isEqualTo("1.48 kB")
+ }
+
+ @Test
+ fun `FileSize UseCase`() {
+ // When the UseCase is FileSize, the correct units (byte, KB, kB, GB, TB, PB) should
+ // be used.
+ val values =
+ listOf(
+ 1L,
+ 1024L,
+ 1024L * 1024L,
+ 1024L * 1024L * 1024L,
+ 1024L * 1024L * 1024L * 1024L,
+ 1024L * 1024L * 1024L * 1024L * 1024L,
+ 1024L * 1024L * 1024L * 1024L * 1024L * 1024L,
+ )
+ val expectedUnits = listOf("byte", "kB", "MB", "GB", "TB", "PB", "PB")
+
+ values.zip(expectedUnits).forEach { (value, expectedUnit) ->
+ val result = formatter.format(value, BytesFormatter.UseCase.FileSize)
+ assertThat(result).contains(expectedUnit)
+ }
+ }
+
+ @Test
+ fun `DataUsage UseCase`() {
+ // When the UseCase is DataUsage, the correct units (byte, kB, MB, GB, TB, PB) should
+ // be used.
+ val values =
+ listOf(
+ 1L,
+ 1024L,
+ 1024L * 1024L,
+ 1024L * 1024L * 1024L,
+ 1024L * 1024L * 1024L * 1024L,
+ 1024L * 1024L * 1024L * 1024L * 1024L,
+ 1024L * 1024L * 1024L * 1024L * 1024L * 1024L,
+ )
+ val expectedUnits = listOf("byte", "kB", "MB", "GB", "TB", "PB", "PB")
+
+ values.zip(expectedUnits).forEach { (value, expectedUnit) ->
+ val result = formatter.format(value, BytesFormatter.UseCase.DataUsage)
+ assertThat(result).contains(expectedUnit)
+ }
+ }
+
+ @Test
+ fun `Fraction digits`() {
+ // The number of fraction digits in the output should be correctly determined based on
+ // the rounded byte value.
+
+ assertThat(formatter.format(1500, BytesFormatter.UseCase.FileSize)).isEqualTo("1.50 kB")
+ assertThat(formatter.format(1050, BytesFormatter.UseCase.FileSize)).isEqualTo("1.05 kB")
+ assertThat(formatter.format(999, BytesFormatter.UseCase.FileSize)).isEqualTo("1.00 kB")
+ }
+
+ @Test
+ fun `Rounding mode`() {
+ // The rounding mode used for formatting should be ROUND_HALF_UP.
+
+ val result = formatter.format(1006, BytesFormatter.UseCase.FileSize)
+
+ assertThat(result).isEqualTo("1.01 kB") // Ensure rounding mode is effective
+ }
+
+ @Test
+ fun `Grouping separator`() {
+ // Grouping separators should not be used in the formatted output.
+
+ val result = formatter.format(Long.MAX_VALUE, BytesFormatter.UseCase.FileSize)
+
+ assertThat(result).isEqualTo("9223 PB")
+ }
+
+ @Test
+ fun `Format with units`() {
+ // Verify that the `formatWithUnits` method correctly formats the given bytes with the
+ // specified units.
+
+ val resultByte = formatter.formatWithUnits(0, BytesFormatter.UseCase.FileSize)
+ assertThat(resultByte).isEqualTo(BytesFormatter.Result("0", "byte"))
+
+ val resultKb = formatter.formatWithUnits(1000, BytesFormatter.UseCase.FileSize)
+ assertThat(resultKb).isEqualTo(BytesFormatter.Result("1.00", "kB"))
+
+ val resultMb = formatter.formatWithUnits(479_999_999, BytesFormatter.UseCase.FileSize)
+ assertThat(resultMb).isEqualTo(BytesFormatter.Result("480", "MB"))
+
+ val resultGb = formatter.formatWithUnits(20_100_000_000, BytesFormatter.UseCase.FileSize)
+ assertThat(resultGb).isEqualTo(BytesFormatter.Result("20.10", "GB"))
+
+ val resultTb =
+ formatter.formatWithUnits(300_100_000_000_000, BytesFormatter.UseCase.FileSize)
+ assertThat(resultTb).isEqualTo(BytesFormatter.Result("300", "TB"))
+
+ val resultPb =
+ formatter.formatWithUnits(1000_000_000_000_000, BytesFormatter.UseCase.FileSize)
+ assertThat(resultPb).isEqualTo(BytesFormatter.Result("1.00", "PB"))
+ }
+}
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 71cf8f6ee53b..bbe08f254283 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -6,7 +6,6 @@ flag {
namespace: "systemui"
description: "Enable new status bar system icons"
bug: "314812750"
- is_exported: true
}
flag {
@@ -14,7 +13,6 @@ flag {
namespace: "bluetooth"
description: "Displays the auto on toggle in the bluetooth QS tile dialog"
bug: "316985153"
- is_exported: true
}
flag {
@@ -22,7 +20,6 @@ flag {
namespace: "pixel_cross_device_control"
description: "Gates the legacy le audio sharing UI."
bug: "322295262"
- is_exported: true
}
flag {
@@ -30,7 +27,6 @@ flag {
namespace: "pixel_cross_device_control"
description: "Gates whether to enable LE audio sharing"
bug: "323125723"
- is_exported: true
}
flag {
@@ -38,7 +34,6 @@ flag {
namespace: "pixel_cross_device_control"
description: "Gates whether to enable LE audio private broadcast sharing via QR code"
bug: "323125723"
- is_exported: true
}
flag {
@@ -46,7 +41,6 @@ flag {
namespace: "dck_framework"
description: "Hide exclusively managed Bluetooth devices in BT settings menu."
bug: "324475542"
- is_exported: true
}
flag {
@@ -54,7 +48,6 @@ flag {
namespace: "bluetooth"
description: "Enable setting preferred transport for Le Audio device"
bug: "330581926"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -65,7 +58,6 @@ flag {
namespace: "pixel_cross_device_control"
description: "Use metadata instead of device type to determine whether a bluetooth device should use advanced details header."
bug: "328556903"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -76,7 +68,6 @@ flag {
namespace: "cross_device_experiences"
description: "Use bluetooth profile connection policy to determine spatial audio attributes"
bug: "341005211"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -87,7 +78,6 @@ flag {
namespace: "cross_device_experiences"
description: "Gates whether to show separate volume bars during audio sharing"
bug: "336716411"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -127,7 +117,6 @@ flag {
namespace: "accessibility"
description: "Changes the return value of HearingAidProfile.accessProfileEnabled() to true"
bug: "356530795"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -138,7 +127,6 @@ flag {
namespace: "cross_device_experiences"
description: "Gates whether to enable fix for hysteresis mode"
bug: "355222285"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -149,7 +137,6 @@ flag {
namespace: "cross_device_experiences"
description: "Gates whether to enable fix for member device active state sync on lea profile"
bug: "364201289"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -160,7 +147,6 @@ flag {
namespace: "cross_device_experiences"
description: "Gates whether to enable audio sharing qs dialog improvement"
bug: "360759048"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -171,7 +157,6 @@ flag {
namespace: "cross_device_experiences"
description: "Gates whether to enable audio sharing developer option"
bug: "368401233"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -182,7 +167,6 @@ flag {
namespace: "accessibility"
description: "Enable the ambient volume control in device details and hearing devices dialog."
bug: "357878944"
- is_exported: true
}
flag {
@@ -190,7 +174,6 @@ flag {
namespace: "android_settings"
description: "Enable the user consent prompt before writing sensitive preferences via service"
bug: "378552675"
- is_exported: true
}
flag {
@@ -198,7 +181,6 @@ flag {
namespace: "accessibility"
description: "Enable the input routing control in device details and hearing devices dialog."
bug: "349255906"
- is_exported: true
}
flag {
@@ -206,7 +188,6 @@ flag {
namespace: "accessibility"
description: "Enable the connection status report for a set of hearing device."
bug: "357882387"
- is_exported: true
}
flag {
@@ -214,7 +195,6 @@ flag {
namespace: "cross_device_experiences"
description: "Do not show problem connecting message when Android Auto disconnect A2DP"
bug: "381981752"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -225,7 +205,6 @@ flag {
namespace: "cross_device_experiences"
description: "Do not auto pick audio sharing fallback device in UI"
bug: "383469911"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -236,16 +215,7 @@ flag {
namespace: "cross_device_experiences"
description: "UI changes for temporary bond devices in audio sharing."
bug: "362859132"
- is_exported: true
metadata {
purpose: PURPOSE_BUGFIX
}
}
-
-flag {
- name: "is_expressive_design_enabled"
- namespace: "android_settings"
- description: "enable expressive design in Settings"
- bug: "386013400"
- is_exported: true
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 145b62cd12b5..68e9fe703090 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -73,6 +73,10 @@ public class BluetoothUtils {
private static final Set<Integer> SA_PROFILES =
ImmutableSet.of(
BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID);
+ private static final List<Integer> BLUETOOTH_DEVICE_CLASS_HEADSET =
+ List.of(
+ BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES,
+ BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET);
private static final String TEMP_BOND_TYPE = "TEMP_BOND_TYPE";
private static final String TEMP_BOND_DEVICE_METADATA_VALUE = "le_audio_sharing";
@@ -390,6 +394,19 @@ public class BluetoothUtils {
return false;
}
+ /** Checks whether the bluetooth device is a headset. */
+ public static boolean isHeadset(@NonNull BluetoothDevice bluetoothDevice) {
+ String deviceType =
+ BluetoothUtils.getStringMetaData(
+ bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE);
+ if (!TextUtils.isEmpty(deviceType)) {
+ return BluetoothDevice.DEVICE_TYPE_HEADSET.equals(deviceType)
+ || BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.equals(deviceType);
+ }
+ BluetoothClass btClass = bluetoothDevice.getBluetoothClass();
+ return btClass != null && BLUETOOTH_DEVICE_CLASS_HEADSET.contains(btClass.getDeviceClass());
+ }
+
/** Create an Icon pointing to a drawable. */
public static IconCompat createIconWithDrawable(Drawable drawable) {
Bitmap bitmap;
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 81358ca168d0..117ca85c2761 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -65,7 +65,6 @@ android_robolectric_test {
test_options: {
timeout: 36000,
},
- upstream: true,
strict_mode: false,
}
@@ -100,10 +99,10 @@ java_library {
plugins: [
"auto_value_plugin_1.9",
"auto_value_builder_plugin_1.9",
- "Robolectric_processor_upstream",
+ "Robolectric_processor",
],
libs: [
- "Robolectric_all-target_upstream",
+ "Robolectric_all-target",
"mockito-robolectric-prebuilt",
"truth",
],
diff --git a/packages/SettingsLib/tests/robotests/fragment/Android.bp b/packages/SettingsLib/tests/robotests/fragment/Android.bp
index 3e67156af0c4..0214874979f3 100644
--- a/packages/SettingsLib/tests/robotests/fragment/Android.bp
+++ b/packages/SettingsLib/tests/robotests/fragment/Android.bp
@@ -28,13 +28,13 @@ java_library {
//"-J-verbose",
],
libs: [
- "Robolectric_all-target_upstream",
+ "Robolectric_all-target",
"androidx.fragment_fragment",
],
plugins: [
"auto_value_plugin_1.9",
"auto_value_builder_plugin_1.9",
- "Robolectric_processor_upstream",
+ "Robolectric_processor",
],
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index d49447f05011..cafe19ff9a9b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -80,7 +80,9 @@ public class BluetoothUtilsTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock private BluetoothDevice mBluetoothDevice;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private BluetoothDevice mBluetoothDevice;
+
@Mock private AudioManager mAudioManager;
@Mock private PackageManager mPackageManager;
@Mock private LeAudioProfile mA2dpProfile;
@@ -399,6 +401,38 @@ public class BluetoothUtilsTest {
}
@Test
+ public void isHeadset_metadataMatched_returnTrue() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+ .thenReturn(BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
+
+ assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isTrue();
+ }
+
+ @Test
+ public void isHeadset_metadataNotMatched_returnFalse() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+ .thenReturn(BluetoothDevice.DEVICE_TYPE_CARKIT.getBytes());
+
+ assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isFalse();
+ }
+
+ @Test
+ public void isHeadset_btClassMatched_returnTrue() {
+ when(mBluetoothDevice.getBluetoothClass().getDeviceClass())
+ .thenReturn(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+
+ assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isTrue();
+ }
+
+ @Test
+ public void isHeadset_btClassNotMatched_returnFalse() {
+ when(mBluetoothDevice.getBluetoothClass().getDeviceClass())
+ .thenReturn(BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER);
+
+ assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isFalse();
+ }
+
+ @Test
public void isAvailableMediaBluetoothDevice_isConnectedLeAudioDevice_returnTrue() {
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 5ddf005d9468..dafcc729b8f1 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -322,9 +322,6 @@
<!-- Whether vibrate icon is shown in the status bar by default. -->
<integer name="def_statusBarVibrateIconEnabled">0</integer>
- <!-- Whether predictive back animation is enabled by default. -->
- <bool name="def_enable_back_animation">false</bool>
-
<!-- Whether wifi is always requested by default. -->
<bool name="def_enable_wifi_always_requested">false</bool>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 5b4ee8bdb339..1f56f10cca7d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -109,6 +109,7 @@ public class SystemSettings {
Settings.System.LOCALE_PREFERENCES,
Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING,
Settings.System.MOUSE_SCROLLING_ACCELERATION,
+ Settings.System.MOUSE_SCROLLING_SPEED,
Settings.System.MOUSE_SWAP_PRIMARY_BUTTON,
Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED,
Settings.System.TOUCHPAD_POINTER_SPEED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 0432eeacec4d..4d98a11bdfe7 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -227,6 +227,7 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.MOUSE_SWAP_PRIMARY_BUTTON, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.MOUSE_SCROLLING_ACCELERATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.MOUSE_POINTER_ACCELERATION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.MOUSE_SCROLLING_SPEED, new InclusiveIntegerRangeValidator(-7, 7));
VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7));
VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index a2cc008843a4..ef0bc3b100e0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -193,6 +193,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
"power_button_instantly_locks";
private static final String KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY =
"pin_enhanced_privacy";
+ private static final int NUM_LOCK_SETTINGS = 5;
// Error messages for logging metrics.
private static final String ERROR_COULD_NOT_READ_FROM_CURSOR =
@@ -208,6 +209,13 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private static final String ERROR_SKIPPED_DUE_TO_LARGE_SCREEN =
"skipped_due_to_large_screen";
private static final String ERROR_DID_NOT_PASS_VALIDATION = "did_not_pass_validation";
+ private static final String ERROR_IO_EXCEPTION = "io_exception";
+ private static final String ERROR_FAILED_TO_RESTORE_SOFTAP_CONFIG =
+ "failed_to_restore_softap_config";
+ private static final String ERROR_FAILED_TO_CONVERT_NETWORK_POLICIES =
+ "failed_to_convert_network_policies";
+ private static final String ERROR_UNKNOWN_BACKUP_SERIALIZATION_VERSION =
+ "unknown_backup_serialization_version";
// Name of the temporary file we use during full backup/restore. This is
@@ -794,29 +802,44 @@ public class SettingsBackupAgent extends BackupAgentHelper {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
+ int backedUpSettingsCount = 0;
try {
out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED);
out.writeUTF(ownerInfoEnabled ? "1" : "0");
+ backedUpSettingsCount++;
if (ownerInfo != null) {
out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO);
out.writeUTF(ownerInfo != null ? ownerInfo : "");
+ backedUpSettingsCount++;
}
if (lockPatternUtils.isVisiblePatternEverChosen(userId)) {
out.writeUTF(KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED);
out.writeUTF(visiblePatternEnabled ? "1" : "0");
+ backedUpSettingsCount++;
}
if (lockPatternUtils.isPowerButtonInstantlyLocksEverChosen(userId)) {
out.writeUTF(KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS);
out.writeUTF(powerButtonInstantlyLocks ? "1" : "0");
+ backedUpSettingsCount++;
}
if (lockPatternUtils.isPinEnhancedPrivacyEverChosen(userId)) {
out.writeUTF(KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY);
out.writeUTF(lockPatternUtils.isPinEnhancedPrivacyEnabled(userId) ? "1" : "0");
+ backedUpSettingsCount++;
}
// End marker
out.writeUTF("");
out.flush();
+ if (areAgentMetricsEnabled) {
+ numberOfSettingsPerKey.put(KEY_LOCK_SETTINGS, backedUpSettingsCount);
+ }
} catch (IOException ioe) {
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsBackupFailed(
+ KEY_LOCK_SETTINGS,
+ NUM_LOCK_SETTINGS - backedUpSettingsCount,
+ ERROR_IO_EXCEPTION);
+ }
}
return baos.toByteArray();
}
@@ -1162,6 +1185,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, nBytes);
DataInputStream in = new DataInputStream(bais);
+ int restoredLockSettingsCount = 0;
try {
String key;
// Read until empty string marker
@@ -1187,9 +1211,20 @@ public class SettingsBackupAgent extends BackupAgentHelper {
lockPatternUtils.setPinEnhancedPrivacyEnabled("1".equals(value), userId);
break;
}
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestored(KEY_LOCK_SETTINGS, /* count= */ 1);
+ restoredLockSettingsCount++;
+ }
+
}
in.close();
} catch (IOException ioe) {
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ KEY_LOCK_SETTINGS,
+ NUM_LOCK_SETTINGS - restoredLockSettingsCount,
+ ERROR_IO_EXCEPTION);
+ }
}
}
@@ -1309,12 +1344,31 @@ public class SettingsBackupAgent extends BackupAgentHelper {
mWifiManager.restoreSupplicantBackupData(supplicant_bytes, ipconfig_bytes);
}
- private byte[] getSoftAPConfiguration() {
- return mWifiManager.retrieveSoftApBackupData();
+ @VisibleForTesting
+ byte[] getSoftAPConfiguration() {
+ byte[] data = mWifiManager.retrieveSoftApBackupData();
+ if (areAgentMetricsEnabled) {
+ // We're unable to determine how many settings this includes, so we'll just log 1.
+ numberOfSettingsPerKey.put(KEY_SOFTAP_CONFIG, 1);
+ }
+ return data;
}
- private void restoreSoftApConfiguration(byte[] data) {
- SoftApConfiguration configInCloud = mWifiManager.restoreSoftApBackupData(data);
+ @VisibleForTesting
+ void restoreSoftApConfiguration(byte[] data) {
+ SoftApConfiguration configInCloud;
+ if (areAgentMetricsEnabled) {
+ try {
+ configInCloud = mWifiManager.restoreSoftApBackupData(data);
+ mBackupRestoreEventLogger.logItemsRestored(KEY_SOFTAP_CONFIG, /* count= */ 1);
+ } catch (Exception e) {
+ configInCloud = null;
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ KEY_SOFTAP_CONFIG, /* count= */ 1, ERROR_FAILED_TO_RESTORE_SOFTAP_CONFIG);
+ }
+ } else {
+ configInCloud = mWifiManager.restoreSoftApBackupData(data);
+ }
if (configInCloud != null) {
if (DEBUG) Log.d(TAG, "Successfully unMarshaled SoftApConfiguration ");
// Depending on device hardware, we may need to notify the user of a setting change
@@ -1384,6 +1438,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
try {
out.writeInt(NETWORK_POLICIES_BACKUP_VERSION);
out.writeInt(policies.length);
+ int numberOfPoliciesBackedUp = 0;
for (NetworkPolicy policy : policies) {
// We purposefully only backup policies that the user has
// defined; any inferred policies might include
@@ -1393,13 +1448,23 @@ public class SettingsBackupAgent extends BackupAgentHelper {
out.writeByte(BackupUtils.NOT_NULL);
out.writeInt(marshaledPolicy.length);
out.write(marshaledPolicy);
+ if (areAgentMetricsEnabled) {
+ numberOfPoliciesBackedUp++;
+ }
} else {
out.writeByte(BackupUtils.NULL);
}
}
+ if (areAgentMetricsEnabled) {
+ numberOfSettingsPerKey.put(KEY_NETWORK_POLICIES, numberOfPoliciesBackedUp);
+ }
} catch (IOException ioe) {
Log.e(TAG, "Failed to convert NetworkPolicies to byte array " + ioe.getMessage());
baos.reset();
+ mBackupRestoreEventLogger.logItemsBackupFailed(
+ KEY_NETWORK_POLICIES,
+ policies.length,
+ ERROR_FAILED_TO_CONVERT_NETWORK_POLICIES);
}
}
return baos.toByteArray();
@@ -1433,6 +1498,10 @@ public class SettingsBackupAgent extends BackupAgentHelper {
try {
int version = in.readInt();
if (version < 1 || version > NETWORK_POLICIES_BACKUP_VERSION) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ KEY_NETWORK_POLICIES,
+ /* count= */ 1,
+ ERROR_UNKNOWN_BACKUP_SERIALIZATION_VERSION);
throw new BackupUtils.BadVersionException(
"Unknown Backup Serialization Version");
}
@@ -1449,10 +1518,15 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
// Only set the policies if there was no error in the restore operation
networkPolicyManager.setNetworkPolicies(policies);
+ mBackupRestoreEventLogger.logItemsRestored(KEY_NETWORK_POLICIES, policies.length);
} catch (NullPointerException | IOException | BackupUtils.BadVersionException
| DateTimeException e) {
// NPE can be thrown when trying to instantiate a NetworkPolicy
Log.e(TAG, "Failed to convert byte array to NetworkPolicies " + e.getMessage());
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ KEY_NETWORK_POLICIES,
+ /* count= */ 1,
+ ERROR_FAILED_TO_CONVERT_NETWORK_POLICIES);
}
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ed193515b382..cb656bdd5d54 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -6122,17 +6122,7 @@ public class SettingsProvider extends ContentProvider {
}
if (currentVersion == 220) {
- final SettingsState globalSettings = getGlobalSettingsLocked();
- final Setting enableBackAnimation =
- globalSettings.getSettingLocked(Global.ENABLE_BACK_ANIMATION);
- if (enableBackAnimation.isNull()) {
- final boolean defEnableBackAnimation =
- getContext()
- .getResources()
- .getBoolean(R.bool.def_enable_back_animation);
- initGlobalSettingsDefaultValLocked(
- Settings.Global.ENABLE_BACK_ANIMATION, defEnableBackAnimation);
- }
+ // Version 221: Removed
currentVersion = 221;
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c88a7fd834d6..cbdb36fff98c 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -564,7 +564,6 @@ public class SettingsBackupTest {
Settings.Global.WATCHDOG_TIMEOUT_MILLIS,
Settings.Global.MANAGED_PROVISIONING_DEFER_PROVISIONING_TO_ROLE_HOLDER,
Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
- Settings.Global.ENABLE_BACK_ANIMATION, // Temporary for T, dev option only
Settings.Global.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, // cache per hearing device
Settings.Global.HEARING_DEVICE_LOCAL_NOTIFICATION, // cache per hearing device
Settings.Global.Wearable.COMBINED_LOCATION_ENABLE,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index 18c43a704bcc..95dd0db40c0e 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -16,6 +16,8 @@
package com.android.providers.settings;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@@ -28,6 +30,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.app.backup.BackupAnnotations.BackupDestination;
import android.app.backup.BackupAnnotations.OperationType;
import android.app.backup.BackupDataInput;
@@ -42,6 +45,8 @@ import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
@@ -126,6 +131,7 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
@Mock private BackupDataInput mBackupDataInput;
@Mock private BackupDataOutput mBackupDataOutput;
+ @Mock private static WifiManager mWifiManager;
private TestFriendlySettingsBackupAgent mAgentUnderTest;
private Context mContext;
@@ -754,6 +760,80 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
}
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void getSoftAPConfiguration_flagIsEnabled_numberOfSettingsInKeyAreRecorded() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ when(mWifiManager.retrieveSoftApBackupData()).thenReturn(null);
+
+ mAgentUnderTest.getSoftAPConfiguration();
+
+ assertEquals(mAgentUnderTest.getNumberOfSettingsPerKey(KEY_SOFTAP_CONFIG), 1);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void getSoftAPConfiguration_flagIsNotEnabled_numberOfSettingsInKeyAreNotRecorded() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ when(mWifiManager.retrieveSoftApBackupData()).thenReturn(null);
+
+ mAgentUnderTest.getSoftAPConfiguration();
+
+ assertEquals(mAgentUnderTest.getNumberOfSettingsPerKey(KEY_SOFTAP_CONFIG), 0);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void
+ restoreSoftApConfiguration_flagIsEnabled_restoreIsSuccessful_successMetricsAreLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid("test").build();
+ byte[] data = config.toString().getBytes();
+ when(mWifiManager.restoreSoftApBackupData(any())).thenReturn(null);
+
+ mAgentUnderTest.restoreSoftApConfiguration(data);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(KEY_SOFTAP_CONFIG, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void
+ restoreSoftApConfiguration_flagIsEnabled_restoreIsNotSuccessful_failureMetricsAreLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid("test").build();
+ byte[] data = config.toString().getBytes();
+ when(mWifiManager.restoreSoftApBackupData(any())).thenThrow(new RuntimeException());
+
+ mAgentUnderTest.restoreSoftApConfiguration(data);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(KEY_SOFTAP_CONFIG, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSoftApConfiguration_flagIsNotEnabled_metricsAreNotLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid("test").build();
+ byte[] data = config.toString().getBytes();
+ when(mWifiManager.restoreSoftApBackupData(any())).thenReturn(null);
+
+ mAgentUnderTest.restoreSoftApConfiguration(data);
+
+ assertNull(getLoggingResultForDatatype(KEY_SOFTAP_CONFIG, mAgentUnderTest));
+ }
+
private byte[] generateBackupData(Map<String, String> keyValueData) {
int totalBytes = 0;
for (String key : keyValueData.keySet()) {
@@ -890,6 +970,13 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
this.numberOfSettingsPerKey.put(key, numberOfSettings);
}
}
+
+ int getNumberOfSettingsPerKey(String key) {
+ if (numberOfSettingsPerKey == null || !numberOfSettingsPerKey.containsKey(key)) {
+ return 0;
+ }
+ return numberOfSettingsPerKey.get(key);
+ }
}
/** The TestSettingsHelper tracks which values have been backed up and/or restored. */
@@ -944,6 +1031,14 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
public ContentResolver getContentResolver() {
return mContentResolver;
}
+
+ @Override
+ public Object getSystemService(String name) {
+ if (name.equals(Context.WIFI_SERVICE)) {
+ return mWifiManager;
+ }
+ return super.getSystemService(name);
+ }
}
/** ContentProvider which returns a set of known test values. */
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b88ae3751d22..227fff59d327 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -85,6 +85,9 @@ filegroup {
filegroup {
name: "SystemUI-tests-broken-robofiles-run",
srcs: [
+ "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
+ "tests/src/**/systemui/power/PowerNotificationWarningsTest.java",
+ "tests/src/**/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt",
"tests/src/**/systemui/dreams/touch/CommunalTouchHandlerTest.java",
"tests/src/**/systemui/shade/NotificationShadeWindowViewControllerTest.kt",
"tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt",
@@ -841,7 +844,6 @@ android_robolectric_test {
"androidx.test.ext.truth",
],
- upstream: true,
instrumentation_for: "SystemUIRobo-stub",
java_resource_dirs: ["tests/robolectric/config"],
@@ -879,7 +881,6 @@ android_robolectric_test {
"androidx.test.ext.truth",
],
- upstream: true,
instrumentation_for: "SystemUIRobo-stub",
java_resource_dirs: ["tests/robolectric/config"],
@@ -916,6 +917,7 @@ android_ravenwood_test {
"android.test.mock.impl",
],
auto_gen_config: true,
+ team: "trendy_team_ravenwood",
plugins: [
"dagger2-compiler",
],
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 795b39576391..c6cc9a975191 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -119,4 +119,5 @@ xuqiu@google.com
yeinj@google.com
yuandizhou@google.com
yurilin@google.com
+yuzhechen@google.com
zakcohen@google.com
diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
index ad4a02764176..ee918c275b7b 100644
--- a/packages/SystemUI/aconfig/predictive_back.aconfig
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -7,17 +7,3 @@ flag {
description: "Enable Shade Animations"
bug: "327732946"
}
-
-flag {
- name: "predictive_back_animate_bouncer"
- namespace: "systemui"
- description: "Enable Predictive Back Animation in Bouncer"
- bug: "327733487"
-}
-
-flag {
- name: "predictive_back_animate_dialogs"
- namespace: "systemui"
- description: "Enable Predictive Back Animation for SysUI dialogs"
- bug: "327721544"
-}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 715d22328f2b..7d5fd903c01b 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -133,14 +133,6 @@ flag {
}
flag {
- name: "notifications_footer_view_refactor"
- namespace: "systemui"
- description: "Enables the refactored version of the footer view in the notification shade "
- "(containing the \"Clear all\" button). Should not bring any behavior changes"
- bug: "293167744"
-}
-
-flag {
name: "notifications_icon_container_refactor"
namespace: "systemui"
description: "Enables the refactored version of the notification icon container in StatusBar, "
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
deleted file mode 100644
index 1c9dabbb0e07..000000000000
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.android.systemui.animation
-
-interface AnimationFeatureFlags {
- val isPredictiveBackQsDialogAnim: Boolean
- get() = false
-}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
index 907c39d842ce..c88c4ebb1a8d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
@@ -59,13 +59,8 @@ constructor(
private val mainExecutor: Executor,
private val callback: Callback,
private val interactionJankMonitor: InteractionJankMonitor,
- private val featureFlags: AnimationFeatureFlags,
private val transitionAnimator: TransitionAnimator =
- TransitionAnimator(
- mainExecutor,
- TIMINGS,
- INTERPOLATORS,
- ),
+ TransitionAnimator(mainExecutor, TIMINGS, INTERPOLATORS),
private val isForTesting: Boolean = false,
) {
private companion object {
@@ -219,7 +214,7 @@ constructor(
dialog: Dialog,
view: View,
cuj: DialogCuj? = null,
- animateBackgroundBoundsChange: Boolean = false
+ animateBackgroundBoundsChange: Boolean = false,
) {
val controller = Controller.fromView(view, cuj)
if (controller == null) {
@@ -245,7 +240,7 @@ constructor(
fun show(
dialog: Dialog,
controller: Controller,
- animateBackgroundBoundsChange: Boolean = false
+ animateBackgroundBoundsChange: Boolean = false,
) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw IllegalStateException(
@@ -263,15 +258,14 @@ constructor(
val controller =
animatedParent?.dialogContentWithBackground?.let {
Controller.fromView(it, controller.cuj)
- }
- ?: controller
+ } ?: controller
// Make sure we don't run the launch animation from the same source twice at the same time.
if (openedDialogs.any { it.controller.sourceIdentity == controller.sourceIdentity }) {
Log.e(
TAG,
"Not running dialog launch animation from source as it is already expanded into a" +
- " dialog"
+ " dialog",
)
dialog.show()
return
@@ -288,7 +282,6 @@ constructor(
animateBackgroundBoundsChange = animateBackgroundBoundsChange,
parentAnimatedDialog = animatedParent,
forceDisableSynchronization = isForTesting,
- featureFlags = featureFlags,
)
openedDialogs.add(animatedDialog)
@@ -305,7 +298,7 @@ constructor(
dialog: Dialog,
animateFrom: Dialog,
cuj: DialogCuj? = null,
- animateBackgroundBoundsChange: Boolean = false
+ animateBackgroundBoundsChange: Boolean = false,
) {
val view =
openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground
@@ -313,7 +306,7 @@ constructor(
Log.w(
TAG,
"Showing dialog $dialog normally as the dialog it is shown from was not shown " +
- "using DialogTransitionAnimator"
+ "using DialogTransitionAnimator",
)
dialog.show()
return
@@ -323,7 +316,7 @@ constructor(
dialog,
view,
animateBackgroundBoundsChange = animateBackgroundBoundsChange,
- cuj = cuj
+ cuj = cuj,
)
}
@@ -346,8 +339,7 @@ constructor(
val animatedDialog =
openedDialogs.firstOrNull {
it.dialog.window?.decorView?.viewRootImpl == view.viewRootImpl
- }
- ?: return null
+ } ?: return null
return createActivityTransitionController(animatedDialog, cujType)
}
@@ -373,7 +365,7 @@ constructor(
private fun createActivityTransitionController(
animatedDialog: AnimatedDialog,
- cujType: Int? = null
+ cujType: Int? = null,
): ActivityTransitionAnimator.Controller? {
// At this point, we know that the intent of the caller is to dismiss the dialog to show
// an app, so we disable the exit animation into the source because we will never want to
@@ -440,7 +432,7 @@ constructor(
}
private fun disableDialogDismiss() {
- dialog.setDismissOverride { /* Do nothing */}
+ dialog.setDismissOverride { /* Do nothing */ }
}
private fun enableDialogDismiss() {
@@ -530,7 +522,6 @@ private class AnimatedDialog(
* Whether synchronization should be disabled, which can be useful if we are running in a test.
*/
private val forceDisableSynchronization: Boolean,
- private val featureFlags: AnimationFeatureFlags,
) {
/**
* The DecorView of this dialog window.
@@ -643,8 +634,7 @@ private class AnimatedDialog(
originalDialogBackgroundColor =
GhostedViewTransitionAnimatorController.findGradientDrawable(background)
?.color
- ?.defaultColor
- ?: Color.BLACK
+ ?.defaultColor ?: Color.BLACK
// Make the background view invisible until we start the animation. We use the transition
// visibility like GhostView does so that we don't mess up with the accessibility tree (see
@@ -700,7 +690,7 @@ private class AnimatedDialog(
oldLeft: Int,
oldTop: Int,
oldRight: Int,
- oldBottom: Int
+ oldBottom: Int,
) {
dialogContentWithBackground.removeOnLayoutChangeListener(this)
@@ -717,9 +707,7 @@ private class AnimatedDialog(
// the dialog.
dialog.setDismissOverride(this::onDialogDismissed)
- if (featureFlags.isPredictiveBackQsDialogAnim) {
- dialog.registerAnimationOnBackInvoked(targetView = dialogContentWithBackground)
- }
+ dialog.registerAnimationOnBackInvoked(targetView = dialogContentWithBackground)
// Show the dialog.
dialog.show()
@@ -815,7 +803,7 @@ private class AnimatedDialog(
if (hasInstrumentedJank) {
interactionJankMonitor.end(controller.cuj!!.cujType)
}
- }
+ },
)
}
@@ -888,14 +876,14 @@ private class AnimatedDialog(
onAnimationFinished(true /* instantDismiss */)
onDialogDismissed(this@AnimatedDialog)
}
- }
+ },
)
}
private fun startAnimation(
isLaunching: Boolean,
onLaunchAnimationStart: () -> Unit = {},
- onLaunchAnimationEnd: () -> Unit = {}
+ onLaunchAnimationEnd: () -> Unit = {},
) {
// Create 2 controllers to animate both the dialog and the source.
val startController =
@@ -969,7 +957,7 @@ private class AnimatedDialog(
override fun onTransitionAnimationProgress(
state: TransitionAnimator.State,
progress: Float,
- linearProgress: Float
+ linearProgress: Float,
) {
startController.onTransitionAnimationProgress(state, progress, linearProgress)
@@ -1026,7 +1014,7 @@ private class AnimatedDialog(
oldLeft: Int,
oldTop: Int,
oldRight: Int,
- oldBottom: Int
+ oldBottom: Int,
) {
// Don't animate if bounds didn't actually change.
if (left == oldLeft && top == oldTop && right == oldRight && bottom == oldBottom) {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index e02e8b483543..5f1f588bb2b5 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -37,6 +37,7 @@ import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
@@ -52,7 +53,6 @@ import androidx.compose.ui.node.currentValueOf
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
-import androidx.compose.ui.util.fastSumBy
import com.android.compose.modifiers.thenIf
import kotlin.math.sign
import kotlinx.coroutines.CompletableDeferred
@@ -81,7 +81,13 @@ interface NestedDraggable {
* in the direction given by [sign], with the given number of [pointersDown] when the touch slop
* was detected.
*/
- fun onDragStarted(position: Offset, sign: Float, pointersDown: Int): Controller
+ fun onDragStarted(
+ position: Offset,
+ sign: Float,
+ pointersDown: Int,
+ // TODO(b/382665591): Make this non-nullable.
+ pointerType: PointerType?,
+ ): Controller
/**
* Whether this draggable should consume any scroll amount with the given [sign] coming from a
@@ -184,8 +190,8 @@ private class NestedDraggableNode(
*/
private var lastFirstDown: Offset? = null
- /** The number of pointers down. */
- private var pointersDownCount = 0
+ /** The pointers currently down, in order of which they were done and mapping to their type. */
+ private val pointersDown = linkedMapOf<PointerId, PointerType>()
init {
delegate(nestedScrollModifierNode(this, nestedScrollDispatcher))
@@ -256,7 +262,9 @@ private class NestedDraggableNode(
check(down.position == lastFirstDown) {
"Position from detectDrags() is not the same as position in trackDownPosition()"
}
- check(pointersDownCount == 1) { "pointersDownCount is equal to $pointersDownCount" }
+ check(pointersDown.size == 1 && pointersDown.keys.first() == down.id) {
+ "pointersDown should only contain $down but it contains $pointersDown"
+ }
var overSlop = 0f
val onTouchSlopReached = { change: PointerInputChange, over: Float ->
@@ -295,8 +303,9 @@ private class NestedDraggableNode(
}
}
- check(pointersDownCount > 0) { "pointersDownCount is equal to $pointersDownCount" }
- val controller = draggable.onDragStarted(down.position, sign, pointersDownCount)
+ check(pointersDown.size > 0) { "pointersDown is empty" }
+ val controller =
+ draggable.onDragStarted(down.position, sign, pointersDown.size, drag.type)
if (overSlop != 0f) {
onDrag(controller, drag, overSlop, velocityTracker)
}
@@ -450,20 +459,24 @@ private class NestedDraggableNode(
private suspend fun PointerInputScope.trackDownPosition() {
awaitEachGesture {
- val down = awaitFirstDown(requireUnconsumed = false)
- lastFirstDown = down.position
- pointersDownCount = 1
+ try {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ lastFirstDown = down.position
+ pointersDown[down.id] = down.type
- do {
- pointersDownCount +=
- awaitPointerEvent().changes.fastSumBy { change ->
+ do {
+ awaitPointerEvent().changes.forEach { change ->
when {
- change.changedToDownIgnoreConsumed() -> 1
- change.changedToUpIgnoreConsumed() -> -1
- else -> 0
+ change.changedToDownIgnoreConsumed() -> {
+ pointersDown[change.id] = change.type
+ }
+ change.changedToUpIgnoreConsumed() -> pointersDown.remove(change.id)
}
}
- } while (pointersDownCount > 0)
+ } while (pointersDown.size > 0)
+ } finally {
+ pointersDown.clear()
+ }
}
}
@@ -491,12 +504,13 @@ private class NestedDraggableNode(
if (nestedScrollController == null && draggable.shouldConsumeNestedScroll(sign)) {
val startedPosition = checkNotNull(lastFirstDown) { "lastFirstDown is not set" }
- // TODO(b/382665591): Replace this by check(pointersDownCount > 0).
- val pointersDown = pointersDownCount.coerceAtLeast(1)
+ // TODO(b/382665591): Ensure that there is at least one pointer down.
+ val pointersDownCount = pointersDown.size.coerceAtLeast(1)
+ val pointerType = pointersDown.entries.firstOrNull()?.value
nestedScrollController =
NestedScrollController(
overscrollEffect,
- draggable.onDragStarted(startedPosition, sign, pointersDown),
+ draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
)
}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index 9c49090916e3..7f70e97411f4 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -33,10 +33,12 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performMouseInput
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import androidx.compose.ui.test.swipeLeft
@@ -653,6 +655,61 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
assertThat(flingIsDone).isTrue()
}
+ @Test
+ fun pointerType() {
+ val draggable = TestDraggable()
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedPointerType).isEqualTo(PointerType.Touch)
+ }
+
+ @Test
+ fun pointerType_mouse() {
+ val draggable = TestDraggable()
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+
+ rule.onRoot().performMouseInput {
+ moveTo(center)
+ press()
+ moveBy(touchSlop.toOffset())
+ release()
+ }
+
+ assertThat(draggable.onDragStartedPointerType).isEqualTo(PointerType.Mouse)
+ }
+
+ @Test
+ fun pointersDown_clearedWhenDisabled() {
+ val draggable = TestDraggable()
+ var enabled by mutableStateOf(true)
+ rule.setContent {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation, enabled = enabled))
+ }
+
+ rule.onRoot().performTouchInput { down(center) }
+
+ enabled = false
+ rule.waitForIdle()
+
+ rule.onRoot().performTouchInput { up() }
+
+ enabled = true
+ rule.waitForIdle()
+
+ rule.onRoot().performTouchInput { down(center) }
+ }
+
private fun ComposeContentTestRule.setContentWithTouchSlop(
content: @Composable () -> Unit
): Float {
@@ -688,6 +745,7 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
var onDragStartedPosition = Offset.Zero
var onDragStartedSign = 0f
var onDragStartedPointersDown = 0
+ var onDragStartedPointerType: PointerType? = null
var onDragDelta = 0f
override fun shouldStartDrag(change: PointerInputChange): Boolean = shouldStartDrag
@@ -696,11 +754,13 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
position: Offset,
sign: Float,
pointersDown: Int,
+ pointerType: PointerType?,
): NestedDraggable.Controller {
onDragStartedCalled = true
onDragStartedPosition = position
onDragStartedSign = sign
onDragStartedPointersDown = pointersDown
+ onDragStartedPointerType = pointerType
onDragDelta = 0f
onDragStarted.invoke(position, sign)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index c704a3e96467..de428a7d3548 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -35,6 +35,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
+import com.android.compose.gesture.NestedScrollableBound
import com.android.compose.gesture.effect.ContentOverscrollEffect
/**
@@ -238,6 +239,18 @@ interface BaseContentScope : ElementStateScope {
fun Modifier.noResizeDuringTransitions(): Modifier
/**
+ * Temporarily disable this content swipe actions when any scrollable below this modifier has
+ * consumed any amount of scroll delta, until the scroll gesture is finished.
+ *
+ * This can for instance be used to ensure that a scrollable list is overscrolled once it
+ * reached its bounds instead of directly starting a scene transition from the same scroll
+ * gesture.
+ */
+ fun Modifier.disableSwipesWhenScrolling(
+ bounds: NestedScrollableBound = NestedScrollableBound.Any
+ ): Modifier
+
+ /**
* A [NestedSceneTransitionLayout] will share its elements with its ancestor STLs therefore
* enabling sharedElement transitions between them.
*/
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 607e4fadc256..ba92f9bea07d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -315,16 +315,10 @@ internal class SwipeAnimation<T : ContentKey>(
val skipAnimation =
hasReachedTargetContent && !contentTransition.isWithinProgressRange(initialProgress)
- val targetOffset =
- if (targetContent == fromContent) {
- 0f
- } else {
- val distance = distance()
- check(distance != DistanceUnspecified) {
- "distance is equal to $DistanceUnspecified"
- }
- distance
- }
+ val distance = distance()
+ check(distance != DistanceUnspecified) { "distance is equal to $DistanceUnspecified" }
+
+ val targetOffset = if (targetContent == fromContent) 0f else distance
// If the effective current content changed, it should be reflected right now in the
// current state, even before the settle animation is ongoing. That way all the
@@ -343,7 +337,16 @@ internal class SwipeAnimation<T : ContentKey>(
}
val animatable =
- Animatable(initialOffset, OffsetVisibilityThreshold).also { offsetAnimation = it }
+ Animatable(initialOffset, OffsetVisibilityThreshold).also {
+ offsetAnimation = it
+
+ // We should animate when the progress value is between [0, 1].
+ if (distance > 0) {
+ it.updateBounds(0f, distance)
+ } else {
+ it.updateBounds(distance, 0f)
+ }
+ }
check(isAnimatingOffset())
@@ -370,42 +373,26 @@ internal class SwipeAnimation<T : ContentKey>(
val velocityConsumed = CompletableDeferred<Float>()
offsetAnimationRunnable.complete {
- try {
+ val result =
animatable.animateTo(
targetValue = targetOffset,
animationSpec = swipeSpec,
initialVelocity = initialVelocity,
- ) {
- // Immediately stop this transition if we are bouncing on a content that
- // does not bounce.
- if (!contentTransition.isWithinProgressRange(progress)) {
- // We are no longer able to consume the velocity, the rest can be
- // consumed by another component in the hierarchy.
- velocityConsumed.complete(initialVelocity - velocity)
- throw SnapException()
- }
- }
- } catch (_: SnapException) {
- /* Ignore. */
- } finally {
- if (!velocityConsumed.isCompleted) {
- // The animation consumed the whole available velocity
- velocityConsumed.complete(initialVelocity)
- }
+ )
- // Wait for overscroll to finish so that the transition is removed from the STLState
- // only after the overscroll is done, to avoid dropping frame right when the user
- // lifts their finger and overscroll is animated to 0.
- overscrollCompletable?.await()
- }
+ // We are no longer able to consume the velocity, the rest can be consumed by another
+ // component in the hierarchy.
+ velocityConsumed.complete(initialVelocity - result.endState.velocity)
+
+ // Wait for overscroll to finish so that the transition is removed from the STLState
+ // only after the overscroll is done, to avoid dropping frame right when the user
+ // lifts their finger and overscroll is animated to 0.
+ overscrollCompletable?.await()
}
return velocityConsumed.await()
}
- /** An exception thrown during the animation to stop it immediately. */
- private class SnapException : Exception()
-
private fun canChangeContent(targetContent: ContentKey): Boolean {
return when (val transition = contentTransition) {
is TransitionState.Transition.ChangeScene ->
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index c5b3df222855..3f6bce724b1b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -54,7 +54,7 @@ private fun DraggableHandlerImpl.contentForSwipes(): Content {
/** Whether swipe should be enabled in the given [orientation]. */
internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
- if (userActions.isEmpty()) {
+ if (userActions.isEmpty() || !areSwipesAllowed()) {
return false
}
@@ -69,6 +69,10 @@ internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
* @return The best matching [UserActionResult], or `null` if no match is found.
*/
internal fun Content.findActionResultBestMatch(swipe: Swipe.Resolved): UserActionResult? {
+ if (!areSwipesAllowed()) {
+ return null
+ }
+
var bestPoints = Int.MIN_VALUE
var bestMatch: UserActionResult? = null
userActions.forEach { (actionSwipe, actionResult) ->
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 4c15f7a4534f..59b4a09385f5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -56,7 +56,10 @@ import com.android.compose.animation.scene.effect.GestureEffect
import com.android.compose.animation.scene.effect.VisualEffect
import com.android.compose.animation.scene.element
import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions
+import com.android.compose.gesture.NestedScrollControlState
+import com.android.compose.gesture.NestedScrollableBound
import com.android.compose.gesture.effect.OffsetOverscrollEffect
+import com.android.compose.gesture.nestedScrollController
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.ContainerState
import com.android.compose.ui.graphics.container
@@ -70,7 +73,8 @@ internal sealed class Content(
actions: Map<UserAction.Resolved, UserActionResult>,
zIndex: Float,
) {
- internal val scope = ContentScopeImpl(layoutImpl, content = this)
+ private val nestedScrollControlState = NestedScrollControlState()
+ internal val scope = ContentScopeImpl(layoutImpl, content = this, nestedScrollControlState)
val containerState = ContainerState()
var content by mutableStateOf(content)
@@ -101,11 +105,14 @@ internal sealed class Content(
scope.content()
}
}
+
+ fun areSwipesAllowed(): Boolean = nestedScrollControlState.isOuterScrollAllowed
}
internal class ContentScopeImpl(
private val layoutImpl: SceneTransitionLayoutImpl,
private val content: Content,
+ private val nestedScrollControlState: NestedScrollControlState,
) : ContentScope, ElementStateScope by layoutImpl.elementStateScope {
override val contentKey: ContentKey
get() = content.key
@@ -176,6 +183,10 @@ internal class ContentScopeImpl(
return noResizeDuringTransitions(layoutState = layoutImpl.state)
}
+ override fun Modifier.disableSwipesWhenScrolling(bounds: NestedScrollableBound): Modifier {
+ return nestedScrollController(nestedScrollControlState, bounds)
+ }
+
@Composable
override fun NestedSceneTransitionLayout(
state: SceneTransitionLayoutState,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt
new file mode 100644
index 000000000000..06a9735d97e2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ContentTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun disableSwipesWhenScrolling() {
+ lateinit var layoutImpl: SceneTransitionLayoutImpl
+ rule.setContent {
+ SceneTransitionLayoutForTesting(
+ remember { MutableSceneTransitionLayoutState(SceneA) },
+ onLayoutImpl = { layoutImpl = it },
+ ) {
+ scene(SceneA) {
+ Box(
+ Modifier.fillMaxSize()
+ .disableSwipesWhenScrolling()
+ .scrollable(rememberScrollableState { it }, Orientation.Vertical)
+ )
+ }
+ }
+ }
+
+ val content = layoutImpl.content(SceneA)
+ assertThat(content.areSwipesAllowed()).isTrue()
+ rule.onRoot().performTouchInput {
+ down(topLeft)
+ moveBy(bottomLeft)
+ }
+
+ assertThat(content.areSwipesAllowed()).isFalse()
+ rule.onRoot().performTouchInput { up() }
+ assertThat(content.areSwipesAllowed()).isTrue()
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 7c8c6e5f6c12..e580e3c40690 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -21,6 +21,7 @@ import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
@@ -33,6 +34,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.assertHeightIsEqualTo
@@ -43,6 +45,9 @@ import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onChild
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeDown
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntOffset
@@ -469,4 +474,41 @@ class SceneTransitionLayoutTest {
assertThat(layoutImpl.overlaysOrNullForTest()).isNull()
}
+
+ @Test
+ fun transitionProgressBoundedBetween0And1() {
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(initialScene = SceneA) }
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(Modifier.fillMaxSize())
+ }
+ scene(SceneB) { Spacer(Modifier.fillMaxSize()) }
+ }
+ }
+ assertThat(state.transitionState).isIdle()
+
+ rule.mainClock.autoAdvance = false
+
+ // Swipe the verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ swipeDown(endY = bottom + touchSlop, durationMillis = 50)
+ }
+
+ rule.mainClock.advanceTimeBy(16)
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).isNotNull()
+ assertThat(transition).hasProgress(1f, tolerance = 0.01f)
+
+ rule.mainClock.advanceTimeBy(16)
+ // Fling animation, we are overscrolling now. Progress should always be between [0, 1].
+ assertThat(transition).hasProgress(1f)
+ }
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 0f8ca947479b..2b0825f39243 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -30,7 +30,6 @@ import android.util.AttributeSet
import android.util.Log
import android.util.MathUtils
import android.util.TypedValue
-import android.view.View.MeasureSpec.AT_MOST
import android.view.View.MeasureSpec.EXACTLY
import android.view.animation.Interpolator
import android.widget.TextView
@@ -77,7 +76,6 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
var maxSingleDigitWidth = -1
var digitTranslateAnimator: DigitTranslateAnimator? = null
var aodFontSizePx: Float = -1F
- var isVertical: Boolean = false
// Store the font size when there's no height constraint as a reference when adjusting font size
private var lastUnconstrainedTextSize: Float = Float.MAX_VALUE
@@ -148,16 +146,7 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
logger.d("onMeasure()")
- if (isVertical) {
- // use at_most to avoid apply measuredWidth from last measuring to measuredHeight
- // cause we use max to setMeasuredDimension
- super.onMeasure(
- MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), AT_MOST),
- MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), AT_MOST),
- )
- } else {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val layout = this.layout
if (layout != null) {
@@ -213,18 +202,10 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
)
}
- if (isVertical) {
- expectedWidth = expectedHeight.also { expectedHeight = expectedWidth }
- }
setMeasuredDimension(expectedWidth, expectedHeight)
}
override fun onDraw(canvas: Canvas) {
- if (isVertical) {
- canvas.save()
- canvas.translate(0F, measuredHeight.toFloat())
- canvas.rotate(-90F)
- }
logger.d({ "onDraw(); ls: $str1" }) { str1 = textAnimator.textInterpolator.shapedText }
val translation = getLocalTranslation()
canvas.translate(translation.x.toFloat(), translation.y.toFloat())
@@ -238,9 +219,6 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
canvas.translate(-it.updatedTranslate.x.toFloat(), -it.updatedTranslate.y.toFloat())
}
canvas.translate(-translation.x.toFloat(), -translation.y.toFloat())
- if (isVertical) {
- canvas.restore()
- }
}
override fun invalidate() {
@@ -353,18 +331,20 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
}
private fun updateXtranslation(inPoint: Point, interpolatedTextBounds: Rect): Point {
- val viewWidth = if (isVertical) measuredHeight else measuredWidth
when (horizontalAlignment) {
HorizontalAlignment.LEFT -> {
inPoint.x = lockScreenPaint.strokeWidth.toInt() - interpolatedTextBounds.left
}
HorizontalAlignment.RIGHT -> {
inPoint.x =
- viewWidth - interpolatedTextBounds.right - lockScreenPaint.strokeWidth.toInt()
+ measuredWidth -
+ interpolatedTextBounds.right -
+ lockScreenPaint.strokeWidth.toInt()
}
HorizontalAlignment.CENTER -> {
inPoint.x =
- (viewWidth - interpolatedTextBounds.width()) / 2 - interpolatedTextBounds.left
+ (measuredWidth - interpolatedTextBounds.width()) / 2 -
+ interpolatedTextBounds.left
}
}
return inPoint
@@ -373,7 +353,6 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
// translation of reference point of text
// used for translation when calling textInterpolator
private fun getLocalTranslation(): Point {
- val viewHeight = if (isVertical) measuredWidth else measuredHeight
val interpolatedTextBounds = updateInterpolatedTextBounds()
val localTranslation = Point(0, 0)
val correctedBaseline = if (baseline != -1) baseline else baselineFromMeasure
@@ -381,7 +360,7 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
when (verticalAlignment) {
VerticalAlignment.CENTER -> {
localTranslation.y =
- ((viewHeight - interpolatedTextBounds.height()) / 2 -
+ ((measuredHeight - interpolatedTextBounds.height()) / 2 -
interpolatedTextBounds.top -
correctedBaseline)
}
@@ -392,7 +371,7 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
}
VerticalAlignment.BOTTOM -> {
localTranslation.y =
- viewHeight -
+ measuredHeight -
interpolatedTextBounds.bottom -
lockScreenPaint.strokeWidth.toInt() -
correctedBaseline
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 2c1dacdfae73..4d2a6d9bd57a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -232,7 +232,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Test
fun testOnViewAttached_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() {
val pinViewController = constructPinViewController(mockKeyguardPinView)
- `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3)
@@ -249,7 +248,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Test
fun testOnViewAttached_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() {
val pinViewController = constructPinViewController(mockKeyguardPinView)
- `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6)
@@ -275,7 +273,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Test
fun onUserInput_autoConfirmation_attemptsUnlock() {
val pinViewController = constructPinViewController(mockKeyguardPinView)
- whenever(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
whenever(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
whenever(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
whenever(passwordTextView.text).thenReturn("000000")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
index e659ef274980..698fac107a1d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
@@ -18,7 +18,9 @@ package com.android.systemui.keyboard.shortcut.data.repository
import android.content.Context
import android.content.Context.INPUT_SERVICE
+import android.content.Intent
import android.hardware.input.InputGestureData
+import android.hardware.input.InputManager
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
import android.hardware.input.fakeInputManager
import android.platform.test.annotations.EnableFlags
@@ -27,9 +29,12 @@ import androidx.test.filters.SmallTest
import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES
import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER
import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.customInputGesturesRepository
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.kosmos.testScope
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.userTracker
@@ -48,18 +53,41 @@ import org.mockito.kotlin.whenever
@EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
class CustomInputGesturesRepositoryTest : SysuiTestCase() {
- private val mockUserContext: Context = mock()
+ private val primaryUserContext: Context = mock()
+ private val secondaryUserContext: Context = mock()
+ private var activeUserContext: Context = primaryUserContext
+
private val kosmos = testKosmos().also {
- it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
+ it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { activeUserContext })
}
private val inputManager = kosmos.fakeInputManager.inputManager
+ private val broadcastDispatcher = kosmos.broadcastDispatcher
+ private val inputManagerForSecondaryUser: InputManager = mock()
private val testScope = kosmos.testScope
+ private val testHelper = kosmos.shortcutHelperTestHelper
private val customInputGesturesRepository = kosmos.customInputGesturesRepository
@Before
- fun setup(){
- whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
+ fun setup() {
+ activeUserContext = primaryUserContext
+ whenever(primaryUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
+ whenever(secondaryUserContext.getSystemService(INPUT_SERVICE))
+ .thenReturn(inputManagerForSecondaryUser)
+ }
+
+ @Test
+ fun customInputGestures_emitsNewUsersInputGesturesWhenUserIsSwitch() {
+ testScope.runTest {
+ setCustomInputGesturesForPrimaryUser(allAppsInputGestureData)
+ setCustomInputGesturesForSecondaryUser(goHomeInputGestureData)
+
+ val inputGestures by collectLastValue(customInputGesturesRepository.customInputGestures)
+ assertThat(inputGestures).containsExactly(allAppsInputGestureData)
+
+ switchToSecondaryUser()
+ assertThat(inputGestures).containsExactly(goHomeInputGestureData)
+ }
}
@Test
@@ -115,4 +143,24 @@ class CustomInputGesturesRepositoryTest : SysuiTestCase() {
}
}
+ private fun setCustomInputGesturesForPrimaryUser(vararg inputGesture: InputGestureData) {
+ whenever(
+ inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+ ).thenReturn(inputGesture.toList())
+ }
+
+ private fun setCustomInputGesturesForSecondaryUser(vararg inputGesture: InputGestureData) {
+ whenever(
+ inputManagerForSecondaryUser.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+ ).thenReturn(inputGesture.toList())
+ }
+
+ private fun switchToSecondaryUser() {
+ activeUserContext = secondaryUserContext
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_USER_SWITCHED)
+ )
+ }
+
} \ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index b29a5f4e456f..9e8713be3f5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -34,6 +34,7 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat as assertThatRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.data.repository.FlingInfo
import com.android.systemui.shade.data.repository.fakeShadeRepository
@@ -47,7 +48,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
-import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat as assertThatRepository
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -55,9 +55,8 @@ import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject
class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository(
- testScope = testScope,
- ))
+ this.fakeKeyguardTransitionRepository =
+ spy(FakeKeyguardTransitionRepository(testScope = testScope))
}
private val testScope = kosmos.testScope
@@ -181,6 +180,12 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
underTest.start()
assertThatRepository(transitionRepository).noTransitionsStarted()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
+
keyguardRepository.setKeyguardDismissible(true)
runCurrent()
shadeRepository.setCurrentFling(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
index 2e9d6e85d0aa..49cbb5a924f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
@@ -53,7 +53,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -365,7 +364,6 @@ public class QuickSettingsControllerImplTest extends QuickSettingsControllerImpl
}
@Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
public void updateExpansion_partiallyExpanded_fullscreenFalse() {
// WHEN QS are only partially expanded
mQsController.setExpanded(true);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index b5043ce700f1..fe44c3e31e66 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -39,7 +39,7 @@ class ShadeRepositoryImplTest : SysuiTestCase() {
@Before
fun setUp() {
- underTest = ShadeRepositoryImpl()
+ underTest = ShadeRepositoryImpl(testScope)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
index 83fb14aaf792..6b2c4b260806 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
@@ -9,9 +9,8 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -25,7 +24,7 @@ class ConditionExtensionsTest : SysuiTestCase() {
@Before
fun setUp() {
- testScope = TestScope(StandardTestDispatcher())
+ testScope = TestScope(UnconfinedTestDispatcher())
}
@Test
@@ -34,11 +33,9 @@ class ConditionExtensionsTest : SysuiTestCase() {
val flow = flowOf(true)
val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
- runCurrent()
assertThat(condition.isConditionSet).isFalse()
condition.start()
- runCurrent()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isTrue()
}
@@ -49,11 +46,9 @@ class ConditionExtensionsTest : SysuiTestCase() {
val flow = flowOf(false)
val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
- runCurrent()
assertThat(condition.isConditionSet).isFalse()
condition.start()
- runCurrent()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isFalse()
}
@@ -65,7 +60,6 @@ class ConditionExtensionsTest : SysuiTestCase() {
val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
condition.start()
- runCurrent()
assertThat(condition.isConditionSet).isFalse()
assertThat(condition.isConditionMet).isFalse()
}
@@ -78,11 +72,10 @@ class ConditionExtensionsTest : SysuiTestCase() {
flow.toCondition(
scope = this,
strategy = Condition.START_EAGERLY,
- initialValue = true
+ initialValue = true,
)
condition.start()
- runCurrent()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isTrue()
}
@@ -95,11 +88,10 @@ class ConditionExtensionsTest : SysuiTestCase() {
flow.toCondition(
scope = this,
strategy = Condition.START_EAGERLY,
- initialValue = false
+ initialValue = false,
)
condition.start()
- runCurrent()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isFalse()
}
@@ -111,16 +103,13 @@ class ConditionExtensionsTest : SysuiTestCase() {
val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY)
condition.start()
- runCurrent()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isFalse()
flow.value = true
- runCurrent()
assertThat(condition.isConditionMet).isTrue()
flow.value = false
- runCurrent()
assertThat(condition.isConditionMet).isFalse()
condition.stop()
@@ -131,15 +120,12 @@ class ConditionExtensionsTest : SysuiTestCase() {
testScope.runTest {
val flow = MutableSharedFlow<Boolean>()
val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY)
- runCurrent()
assertThat(flow.subscriptionCount.value).isEqualTo(0)
condition.start()
- runCurrent()
assertThat(flow.subscriptionCount.value).isEqualTo(1)
condition.stop()
- runCurrent()
assertThat(flow.subscriptionCount.value).isEqualTo(0)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index a62d9d5ce62f..0061c4142e8b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -132,7 +132,7 @@ class CallChipViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.chip)
repo.setOngoingCallState(
- inCallModel(startTimeMs = 1000, notificationIcon = mock<StatusBarIconView>())
+ inCallModel(startTimeMs = 1000, notificationIcon = createStatusBarIconViewOrNull())
)
assertThat((latest as OngoingActivityChipModel.Shown).icon)
@@ -147,11 +147,12 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_positiveStartTime_notifIconFlagOn_iconIsNotifIcon() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- val notifIcon = mock<StatusBarIconView>()
+ val notifIcon = createStatusBarIconViewOrNull()
repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = notifIcon))
assertThat((latest as OngoingActivityChipModel.Shown).icon)
@@ -165,6 +166,24 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ fun chip_positiveStartTime_notifIconFlagOn_cdFlagOn_iconIsNotifKeyIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(
+ inCallModel(
+ startTimeMs = 1000,
+ notificationIcon = createStatusBarIconViewOrNull(),
+ notificationKey = "notifKey",
+ )
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey"))
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
fun chip_positiveStartTime_notifIconAndConnectedDisplaysFlagOn_iconIsNotifIcon() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -192,7 +211,7 @@ class CallChipViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.chip)
repo.setOngoingCallState(
- inCallModel(startTimeMs = 0, notificationIcon = mock<StatusBarIconView>())
+ inCallModel(startTimeMs = 0, notificationIcon = createStatusBarIconViewOrNull())
)
assertThat((latest as OngoingActivityChipModel.Shown).icon)
@@ -207,11 +226,12 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun chip_zeroStartTime_notifIconFlagOn_iconIsNotifIcon() =
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun chip_zeroStartTime_notifIconFlagOn_cdFlagOff_iconIsNotifIcon() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- val notifIcon = mock<StatusBarIconView>()
+ val notifIcon = createStatusBarIconViewOrNull()
repo.setOngoingCallState(inCallModel(startTimeMs = 0, notificationIcon = notifIcon))
assertThat((latest as OngoingActivityChipModel.Shown).icon)
@@ -224,8 +244,27 @@ class CallChipViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ fun chip_zeroStartTime_notifIconFlagOn_cdFlagOn_iconIsNotifKeyIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(
+ inCallModel(
+ startTimeMs = 0,
+ notificationIcon = createStatusBarIconViewOrNull(),
+ notificationKey = "notifKey",
+ )
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey"))
+ }
+
+ @Test
@EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun chip_notifIconFlagOn_butNullNotifIcon_iconIsPhone() =
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun chip_notifIconFlagOn_butNullNotifIcon_cdFlagOff_iconIsPhone() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -242,6 +281,24 @@ class CallChipViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ fun chip_notifIconFlagOn_butNullNotifIcon_iconNotifKey() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(
+ inCallModel(
+ startTimeMs = 1000,
+ notificationIcon = null,
+ notificationKey = "notifKey",
+ )
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey"))
+ }
+
+ @Test
fun chip_positiveStartTime_colorsAreThemed() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -330,4 +387,13 @@ class CallChipViewModelTest : SysuiTestCase() {
verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(intent, null)
}
+
+ companion object {
+ fun createStatusBarIconViewOrNull(): StatusBarIconView? =
+ if (StatusBarConnectedDisplays.isEnabled) {
+ null
+ } else {
+ mock<StatusBarIconView>()
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
index 0d033a4098ec..fe15eac46e2d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.chips.notification.domain.interactor
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -148,7 +149,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
}
@Test
- fun notificationChip_missingStatusBarIconChipView_inConstructor_emitsNull() =
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChip_missingStatusBarIconChipView_cdFlagDisabled_inConstructor_emitsNull() =
kosmos.runTest {
val underTest =
factory.create(
@@ -167,6 +169,25 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChip_missingStatusBarIconChipView_cdFlagEnabled_inConstructor_emitsNotNull() =
+ kosmos.runTest {
+ val underTest =
+ factory.create(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = null,
+ promotedContent = PROMOTED_CONTENT,
+ ),
+ 32L,
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ assertThat(latest).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun notificationChip_cdEnabled_missingStatusBarIconChipView_inConstructor_emitsNotNull() =
kosmos.runTest {
val underTest =
@@ -186,7 +207,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
}
@Test
- fun notificationChip_missingStatusBarIconChipView_inSet_emitsNull() =
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChip_cdFlagDisabled_missingStatusBarIconChipView_inSet_emitsNull() =
kosmos.runTest {
val startingNotif =
activeNotificationModel(
@@ -211,6 +233,31 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChip_cdFlagEnabled_missingStatusBarIconChipView_inSet_emitsNotNull() =
+ kosmos.runTest {
+ val startingNotif =
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = mock(),
+ promotedContent = PROMOTED_CONTENT,
+ )
+ val underTest = factory.create(startingNotif, 123L)
+ val latest by collectLastValue(underTest.notificationChip)
+ assertThat(latest).isNotNull()
+
+ underTest.setNotification(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = null,
+ promotedContent = PROMOTED_CONTENT,
+ )
+ )
+
+ assertThat(latest).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun notificationChip_missingStatusBarIconChipView_inSet_cdEnabled_emitsNotNull() =
kosmos.runTest {
val startingNotif =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
index f703d785ceac..ee4a52d35d68 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
@@ -83,7 +84,8 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun notificationChips_notifMissingStatusBarChipIconView_empty() =
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChips_notifMissingStatusBarChipIconView_cdFlagOff_empty() =
kosmos.runTest {
val latest by collectLastValue(underTest.notificationChips)
@@ -101,6 +103,25 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME, StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChips_notifMissingStatusBarChipIconView_cdFlagOn_notEmpty() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = null,
+ promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ )
+ )
+ )
+
+ assertThat(latest).isNotEmpty()
+ }
+
+ @Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun notificationChips_onePromotedNotif_statusBarIconViewMatches() =
kosmos.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 17076b4d7505..e561e3ea27d7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -23,7 +23,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.kosmos.collectLastValue
@@ -31,6 +30,7 @@ import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
@@ -48,7 +48,6 @@ import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -84,8 +83,8 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
- fun chips_notifMissingStatusBarChipIconView_empty() =
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY, StatusBarConnectedDisplays.FLAG_NAME)
+ fun chips_notifMissingStatusBarChipIconView_cdFlagDisabled_empty() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -104,11 +103,31 @@ class NotifChipsViewModelTest : SysuiTestCase() {
@Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun chips_notifMissingStatusBarChipIconView_cdFlagEnabled_notEmpty() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = null,
+ promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ )
+ )
+ )
+
+ assertThat(latest).isNotEmpty()
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_onePromotedNotif_statusBarIconViewMatches() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
- val icon = mock<StatusBarIconView>()
+ val icon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -121,8 +140,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
assertThat(latest).hasSize(1)
val chip = latest!![0]
- assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
+ assertIsNotifChip(chip, icon, "notif")
}
@Test
@@ -168,7 +186,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -187,8 +205,8 @@ class NotifChipsViewModelTest : SysuiTestCase() {
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
- val firstIcon = mock<StatusBarIconView>()
- val secondIcon = mock<StatusBarIconView>()
+ val firstIcon = createStatusBarIconViewOrNull()
+ val secondIcon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -203,15 +221,15 @@ class NotifChipsViewModelTest : SysuiTestCase() {
),
activeNotificationModel(
key = "notif3",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = null,
),
)
)
assertThat(latest).hasSize(2)
- assertIsNotifChip(latest!![0], firstIcon)
- assertIsNotifChip(latest!![1], secondIcon)
+ assertIsNotifChip(latest!![0], firstIcon, "notif1")
+ assertIsNotifChip(latest!![1], secondIcon, "notif2")
}
@Test
@@ -269,7 +287,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -293,7 +311,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -323,7 +341,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -353,7 +371,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -382,7 +400,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -411,7 +429,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -439,7 +457,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -467,7 +485,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -499,7 +517,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
@@ -531,7 +549,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "clickTest",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent =
PromotedNotificationContentModel.Builder("clickTest").build(),
)
@@ -552,9 +570,21 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
companion object {
- fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
- assertThat((latest as OngoingActivityChipModel.Shown).icon)
- .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
+ fun assertIsNotifChip(
+ latest: OngoingActivityChipModel?,
+ expectedIcon: StatusBarIconView?,
+ notificationKey: String,
+ ) {
+ val shown = latest as OngoingActivityChipModel.Shown
+ if (StatusBarConnectedDisplays.isEnabled) {
+ assertThat(shown.icon)
+ .isEqualTo(
+ OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(notificationKey)
+ )
+ } else {
+ assertThat(latest.icon)
+ .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon!!))
+ }
}
fun assertIsNotifKey(latest: OngoingActivityChipModel?, expectedKey: String) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 4fb42e94adb2..42358cce59a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.Me
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -169,29 +170,35 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() =
testScope.runTest {
+ val notificationKey = "call"
screenRecordState.value = ScreenRecordModel.DoingNothing
// MediaProjection covers both share-to-app and cast-to-other-device
mediaProjectionState.value = MediaProjectionState.NotProjecting
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = notificationKey)
+ )
val latest by collectLastValue(underTest.primaryChip)
- assertIsCallChip(latest)
+ assertIsCallChip(latest, notificationKey)
}
@Test
fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
testScope.runTest {
// Start with just the lowest priority chip shown
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ val callNotificationKey = "call"
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
// And everything else hidden
mediaProjectionState.value = MediaProjectionState.NotProjecting
screenRecordState.value = ScreenRecordModel.DoingNothing
val latest by collectLastValue(underTest.primaryChip)
- assertIsCallChip(latest)
+ assertIsCallChip(latest, callNotificationKey)
// WHEN the higher priority media projection chip is added
mediaProjectionState.value =
@@ -218,7 +225,10 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ val callNotificationKey = "call"
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
val latest by collectLastValue(underTest.primaryChip)
@@ -235,7 +245,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
mediaProjectionState.value = MediaProjectionState.NotProjecting
// THEN the lower priority call is used
- assertIsCallChip(latest)
+ assertIsCallChip(latest, callNotificationKey)
}
/** Regression test for b/347726238. */
@@ -364,13 +374,27 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
}
- fun assertIsCallChip(latest: OngoingActivityChipModel?) {
- assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ fun assertIsCallChip(latest: OngoingActivityChipModel?, notificationKey: String) {
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
+ if (StatusBarConnectedDisplays.isEnabled) {
+ assertNotificationIcon(latest, notificationKey)
+ return
+ }
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
}
+
+ private fun assertNotificationIcon(
+ latest: OngoingActivityChipModel?,
+ notificationKey: String,
+ ) {
+ val shown = latest as OngoingActivityChipModel.Shown
+ val notificationIcon =
+ shown.icon as OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
+ assertThat(notificationIcon.notificationKey).isEqualTo(notificationKey)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 0050ebee64d6..0f42f29e76ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -34,7 +34,7 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
-import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
@@ -186,13 +186,16 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
@Test
fun chips_screenRecordShowAndCallShow_primaryIsScreenRecordSecondaryIsCall() =
testScope.runTest {
+ val callNotificationKey = "call"
screenRecordState.value = ScreenRecordModel.Recording
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
val latest by collectLastValue(underTest.chips)
assertIsScreenRecordChip(latest!!.primary)
- assertIsCallChip(latest!!.secondary)
+ assertIsCallChip(latest!!.secondary, callNotificationKey)
}
@Test
@@ -240,15 +243,18 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
@Test
fun chips_shareToAppShowAndCallShow_primaryIsShareToAppSecondaryIsCall() =
testScope.runTest {
+ val callNotificationKey = "call"
screenRecordState.value = ScreenRecordModel.DoingNothing
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
val latest by collectLastValue(underTest.chips)
assertIsShareToAppChip(latest!!.primary)
- assertIsCallChip(latest!!.secondary)
+ assertIsCallChip(latest!!.secondary, callNotificationKey)
}
@Test
@@ -258,25 +264,31 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// MediaProjection covers both share-to-app and cast-to-other-device
mediaProjectionState.value = MediaProjectionState.NotProjecting
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ val callNotificationKey = "call"
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
val latest by collectLastValue(underTest.primaryChip)
- assertIsCallChip(latest)
+ assertIsCallChip(latest, callNotificationKey)
}
@Test
fun chips_onlyCallShown_primaryIsCallSecondaryIsHidden() =
testScope.runTest {
+ val callNotificationKey = "call"
screenRecordState.value = ScreenRecordModel.DoingNothing
// MediaProjection covers both share-to-app and cast-to-other-device
mediaProjectionState.value = MediaProjectionState.NotProjecting
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
val latest by collectLastValue(underTest.chips)
- assertIsCallChip(latest!!.primary)
+ assertIsCallChip(latest!!.primary, callNotificationKey)
assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@@ -285,7 +297,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.chips)
- val icon = mock<StatusBarIconView>()
+ val icon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -296,7 +308,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
)
)
- assertIsNotifChip(latest!!.primary, icon)
+ assertIsNotifChip(latest!!.primary, icon, "notif")
assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@@ -305,8 +317,8 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.chips)
- val firstIcon = mock<StatusBarIconView>()
- val secondIcon = mock<StatusBarIconView>()
+ val firstIcon = createStatusBarIconViewOrNull()
+ val secondIcon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -324,8 +336,8 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
)
)
- assertIsNotifChip(latest!!.primary, firstIcon)
- assertIsNotifChip(latest!!.secondary, secondIcon)
+ assertIsNotifChip(latest!!.primary, firstIcon, "firstNotif")
+ assertIsNotifChip(latest!!.secondary, secondIcon, "secondNotif")
}
@Test
@@ -333,9 +345,9 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.chips)
- val firstIcon = mock<StatusBarIconView>()
- val secondIcon = mock<StatusBarIconView>()
- val thirdIcon = mock<StatusBarIconView>()
+ val firstIcon = createStatusBarIconViewOrNull()
+ val secondIcon = createStatusBarIconViewOrNull()
+ val thirdIcon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -359,8 +371,8 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
)
)
- assertIsNotifChip(latest!!.primary, firstIcon)
- assertIsNotifChip(latest!!.secondary, secondIcon)
+ assertIsNotifChip(latest!!.primary, firstIcon, "firstNotif")
+ assertIsNotifChip(latest!!.secondary, secondIcon, "secondNotif")
}
@Test
@@ -368,8 +380,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.chips)
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- val firstIcon = mock<StatusBarIconView>()
+ val callNotificationKey = "call"
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
+
+ val firstIcon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -380,43 +396,47 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
),
activeNotificationModel(
key = "secondNotif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent =
PromotedNotificationContentModel.Builder("secondNotif").build(),
),
)
)
- assertIsCallChip(latest!!.primary)
- assertIsNotifChip(latest!!.secondary, firstIcon)
+ assertIsCallChip(latest!!.primary, callNotificationKey)
+ assertIsNotifChip(latest!!.secondary, firstIcon, "firstNotif")
}
@Test
fun chips_screenRecordAndCallAndPromotedNotifs_notifsNotShown() =
testScope.runTest {
+ val callNotificationKey = "call"
val latest by collectLastValue(underTest.chips)
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
screenRecordState.value = ScreenRecordModel.Recording
setNotifs(
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
)
)
)
assertIsScreenRecordChip(latest!!.primary)
- assertIsCallChip(latest!!.secondary)
+ assertIsCallChip(latest!!.secondary, callNotificationKey)
}
@Test
fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
testScope.runTest {
+ val callNotificationKey = "call"
// Start with just the lowest priority chip shown
- val notifIcon = mock<StatusBarIconView>()
+ val notifIcon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -433,13 +453,15 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.primaryChip)
- assertIsNotifChip(latest, notifIcon)
+ assertIsNotifChip(latest, notifIcon, "notif")
// WHEN the higher priority call chip is added
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
// THEN the higher priority call chip is used
- assertIsCallChip(latest)
+ assertIsCallChip(latest, callNotificationKey)
// WHEN the higher priority media projection chip is added
mediaProjectionState.value =
@@ -462,12 +484,15 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_highestPriorityChipRemoved_showsNextPriorityChip() =
testScope.runTest {
+ val callNotificationKey = "call"
// WHEN all chips are active
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- val notifIcon = mock<StatusBarIconView>()
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
+ val notifIcon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -493,20 +518,21 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
mediaProjectionState.value = MediaProjectionState.NotProjecting
// THEN the lower priority call is used
- assertIsCallChip(latest)
+ assertIsCallChip(latest, callNotificationKey)
// WHEN the higher priority call is removed
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
// THEN the lower priority notif is used
- assertIsNotifChip(latest, notifIcon)
+ assertIsNotifChip(latest, notifIcon, "notif")
}
@Test
fun chips_movesChipsAroundAccordingToPriority() =
testScope.runTest {
+ val callNotificationKey = "call"
// Start with just the lowest priority chip shown
- val notifIcon = mock<StatusBarIconView>()
+ val notifIcon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -523,16 +549,18 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.chips)
- assertIsNotifChip(latest!!.primary, notifIcon)
+ assertIsNotifChip(latest!!.primary, notifIcon, "notif")
assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
// WHEN the higher priority call chip is added
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ callRepo.setOngoingCallState(
+ inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+ )
// THEN the higher priority call chip is used as primary and notif is demoted to
// secondary
- assertIsCallChip(latest!!.primary)
- assertIsNotifChip(latest!!.secondary, notifIcon)
+ assertIsCallChip(latest!!.primary, callNotificationKey)
+ assertIsNotifChip(latest!!.secondary, notifIcon, "notif")
// WHEN the higher priority media projection chip is added
mediaProjectionState.value =
@@ -545,7 +573,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// THEN the higher priority media projection chip is used as primary and call is demoted
// to secondary (and notif is dropped altogether)
assertIsShareToAppChip(latest!!.primary)
- assertIsCallChip(latest!!.secondary)
+ assertIsCallChip(latest!!.secondary, callNotificationKey)
// WHEN the higher priority screen record chip is added
screenRecordState.value = ScreenRecordModel.Recording
@@ -559,13 +587,13 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// THEN media projection and notif remain
assertIsShareToAppChip(latest!!.primary)
- assertIsNotifChip(latest!!.secondary, notifIcon)
+ assertIsNotifChip(latest!!.secondary, notifIcon, "notif")
// WHEN media projection is dropped
mediaProjectionState.value = MediaProjectionState.NotProjecting
// THEN notif is promoted to primary
- assertIsNotifChip(latest!!.primary, notifIcon)
+ assertIsNotifChip(latest!!.primary, notifIcon, "notif")
assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
index a70d24efada7..912633c874ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
@@ -28,11 +28,11 @@ import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
+import com.android.systemui.util.mockito.withArgCaptor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
-import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.inOrder
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -59,10 +59,9 @@ class RenderStageManagerTest : SysuiTestCase() {
fun setUp() {
renderStageManager = RenderStageManager()
renderStageManager.attach(shadeListBuilder)
-
- val captor = argumentCaptor<ShadeListBuilder.OnRenderListListener>()
- verify(shadeListBuilder).setOnRenderListListener(captor.capture())
- onRenderListListener = captor.lastValue
+ onRenderListListener = withArgCaptor {
+ verify(shadeListBuilder).setOnRenderListListener(capture())
+ }
}
private fun setUpRenderer() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
index 34f46088ad79..3d5d1eddf581 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
@@ -33,7 +33,6 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRepository
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -48,7 +47,6 @@ import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(ParameterizedAndroidJunit4::class)
@SmallTest
-@EnableFlags(FooterViewRefactor.FLAG_NAME)
class EmptyShadeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
index 615f4b01df9b..daa1db2d49fa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.footer.ui.view;
-import static com.android.systemui.log.LogAssertKt.assertLogsWtf;
-
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertFalse;
@@ -34,7 +32,6 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.view.LayoutInflater;
import android.view.View;
@@ -44,7 +41,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.res.R;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
import org.junit.Before;
@@ -62,8 +58,7 @@ public class FooterViewTest extends SysuiTestCase {
@Parameters(name = "{0}")
public static List<FlagsParameterization> getFlags() {
- return FlagsParameterization.progressionOf(FooterViewRefactor.FLAG_NAME,
- NotifRedesignFooter.FLAG_NAME);
+ return FlagsParameterization.allCombinationsOf(NotifRedesignFooter.FLAG_NAME);
}
public FooterViewTest(FlagsParameterization flags) {
@@ -106,24 +101,6 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void setHistoryShown() {
- mView.showHistory(true);
- assertTrue(mView.isHistoryShown());
- assertTrue(((TextView) mView.findViewById(R.id.manage_text))
- .getText().toString().contains("History"));
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void setHistoryNotShown() {
- mView.showHistory(false);
- assertFalse(mView.isHistoryShown());
- assertTrue(((TextView) mView.findViewById(R.id.manage_text))
- .getText().toString().contains("Manage"));
- }
-
- @Test
public void testPerformVisibilityAnimation() {
mView.setVisible(false /* visible */, false /* animate */);
assertFalse(mView.isVisible());
@@ -140,7 +117,6 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
@DisableFlags(NotifRedesignFooter.FLAG_NAME)
public void testSetManageOrHistoryButtonText_resourceOnlyFetchedOnce() {
int resId = R.string.manage_notifications_history_text;
@@ -160,16 +136,6 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testSetManageOrHistoryButtonText_expectsFlagEnabled() {
- clearInvocations(mSpyContext);
- int resId = R.string.manage_notifications_history_text;
- assertLogsWtf(() -> mView.setManageOrHistoryButtonText(resId));
- verify(mSpyContext, never()).getString(anyInt());
- }
-
- @Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
@DisableFlags(NotifRedesignFooter.FLAG_NAME)
public void testSetManageOrHistoryButtonDescription_resourceOnlyFetchedOnce() {
int resId = R.string.manage_notifications_history_text;
@@ -189,16 +155,6 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testSetManageOrHistoryButtonDescription_expectsFlagEnabled() {
- clearInvocations(mSpyContext);
- int resId = R.string.accessibility_clear_all;
- assertLogsWtf(() -> mView.setManageOrHistoryButtonDescription(resId));
- verify(mSpyContext, never()).getString(anyInt());
- }
-
- @Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetClearAllButtonText_resourceOnlyFetchedOnce() {
int resId = R.string.clear_all_notifications_text;
mView.setClearAllButtonText(resId);
@@ -217,16 +173,6 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testSetClearAllButtonText_expectsFlagEnabled() {
- clearInvocations(mSpyContext);
- int resId = R.string.clear_all_notifications_text;
- assertLogsWtf(() -> mView.setClearAllButtonText(resId));
- verify(mSpyContext, never()).getString(anyInt());
- }
-
- @Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetClearAllButtonDescription_resourceOnlyFetchedOnce() {
int resId = R.string.accessibility_clear_all;
mView.setClearAllButtonDescription(resId);
@@ -245,16 +191,6 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testSetClearAllButtonDescription_expectsFlagEnabled() {
- clearInvocations(mSpyContext);
- int resId = R.string.accessibility_clear_all;
- assertLogsWtf(() -> mView.setClearAllButtonDescription(resId));
- verify(mSpyContext, never()).getString(anyInt());
- }
-
- @Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetMessageString_resourceOnlyFetchedOnce() {
int resId = R.string.unlock_to_see_notif_text;
mView.setMessageString(resId);
@@ -273,16 +209,6 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testSetMessageString_expectsFlagEnabled() {
- clearInvocations(mSpyContext);
- int resId = R.string.unlock_to_see_notif_text;
- assertLogsWtf(() -> mView.setMessageString(resId));
- verify(mSpyContext, never()).getString(anyInt());
- }
-
- @Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetMessageIcon_resourceOnlyFetchedOnce() {
int resId = R.drawable.ic_friction_lock_closed;
mView.setMessageIcon(resId);
@@ -298,15 +224,6 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testSetMessageIcon_expectsFlagEnabled() {
- clearInvocations(mSpyContext);
- int resId = R.drawable.ic_friction_lock_closed;
- assertLogsWtf(() -> mView.setMessageIcon(resId));
- verify(mSpyContext, never()).getDrawable(anyInt());
- }
-
- @Test
public void testSetFooterLabelVisible() {
mView.setFooterLabelVisible(true);
assertThat(mView.findViewById(R.id.unlock_prompt_footer).getVisibility())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 1adfc2b72214..06b1c432955a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -40,7 +40,6 @@ import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRe
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
import com.android.systemui.testKosmos
import com.android.systemui.util.ui.isAnimating
@@ -57,7 +56,6 @@ import platform.test.runner.parameterized.Parameters
@RunWith(ParameterizedAndroidJunit4::class)
@SmallTest
-@EnableFlags(FooterViewRefactor.FLAG_NAME)
class FooterViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index c6cffa9da13b..20cd6c7517e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -25,14 +25,10 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -45,7 +41,6 @@ import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.MotionEvent;
-import android.view.View;
import android.view.ViewTreeObserver;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -57,15 +52,12 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.ExpandHelper;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
@@ -78,23 +70,18 @@ import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
-import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
-import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -106,11 +93,8 @@ import com.android.systemui.statusbar.notification.stack.ui.viewbinder.Notificat
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
-import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor;
@@ -145,16 +129,13 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private Provider<IStatusBarService> mStatusBarService;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private TunerService mTunerService;
- @Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private DynamicPrivacyController mDynamicPrivacyController;
@Mock private ConfigurationController mConfigurationController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
- @Mock private ZenModeController mZenModeController;
@Mock private KeyguardMediaController mKeyguardMediaController;
@Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private PowerInteractor mPowerInteractor;
- @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock private WallpaperInteractor mWallpaperInteractor;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private MetricsLogger mMetricsLogger;
@@ -164,12 +145,10 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private GroupExpansionManager mGroupExpansionManager;
- @Mock private SectionHeaderController mSilentHeaderController;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NotifCollection mNotifCollection;
@Mock private UiEventLogger mUiEventLogger;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
@Mock private ShadeController mShadeController;
@Mock private Provider<WindowRootView> mWindowRootView;
@@ -193,9 +172,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
- private final SeenNotificationsInteractor mSeenNotificationsInteractor =
- mKosmos.getSeenNotificationsInteractor();
-
private NotificationStackScrollLayoutController mController;
private NotificationTestHelper mNotificationTestHelper;
@@ -279,114 +255,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
}
@Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() {
- when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
- initController(/* viewIsAttached= */ true);
-
- setupShowEmptyShadeViewState(true);
- reset(mNotificationStackScrollLayout);
- mController.updateShowEmptyShadeView();
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- /* visible= */ true,
- /* notifVisibleInShade= */ true);
-
- setupShowEmptyShadeViewState(false);
- reset(mNotificationStackScrollLayout);
- mController.updateShowEmptyShadeView();
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- /* visible= */ false,
- /* notifVisibleInShade= */ true);
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() {
- when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
- initController(/* viewIsAttached= */ true);
-
- setupShowEmptyShadeViewState(true);
- reset(mNotificationStackScrollLayout);
- mController.updateShowEmptyShadeView();
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- /* visible= */ true,
- /* notifVisibleInShade= */ false);
-
- setupShowEmptyShadeViewState(false);
- reset(mNotificationStackScrollLayout);
- mController.updateShowEmptyShadeView();
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- /* visible= */ false,
- /* notifVisibleInShade= */ false);
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() {
- when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
- initController(/* viewIsAttached= */ true);
-
- verify(mSysuiStatusBarStateController).addCallback(
- mStateListenerArgumentCaptor.capture(), anyInt());
- StatusBarStateController.StateListener stateListener =
- mStateListenerArgumentCaptor.getValue();
- stateListener.onStateChanged(SHADE);
- mController.getView().removeAllViews();
-
- mController.setQsFullScreen(false);
- reset(mNotificationStackScrollLayout);
- mController.updateShowEmptyShadeView();
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- /* visible= */ true,
- /* notifVisibleInShade= */ false);
-
- mController.setQsFullScreen(true);
- reset(mNotificationStackScrollLayout);
- mController.updateShowEmptyShadeView();
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- /* visible= */ true,
- /* notifVisibleInShade= */ false);
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void testUpdateEmptyShadeView_bouncerShowing_hideEmptyView() {
- when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
- initController(/* viewIsAttached= */ true);
-
- when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(true);
-
- setupShowEmptyShadeViewState(true);
- reset(mNotificationStackScrollLayout);
- mController.updateShowEmptyShadeView();
-
- // THEN the PrimaryBouncerInteractor value is used. Since the bouncer is showing, we
- // hide the empty view.
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- /* visible= */ false,
- /* areNotificationsHiddenInShade= */ false);
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void testUpdateEmptyShadeView_bouncerNotShowing_showEmptyView() {
- when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
- initController(/* viewIsAttached= */ true);
-
- when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(false);
-
- setupShowEmptyShadeViewState(true);
- reset(mNotificationStackScrollLayout);
- mController.updateShowEmptyShadeView();
-
- // THEN the PrimaryBouncerInteractor value is used. Since the bouncer isn't showing, we
- // can show the empty view.
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- /* visible= */ true,
- /* areNotificationsHiddenInShade= */ false);
- }
-
- @Test
public void testOnUserChange_verifyNotSensitive() {
when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -788,31 +656,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
}
@Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void testUpdateFooter_remoteInput() {
- ArgumentCaptor<RemoteInputController.Callback> callbackCaptor =
- ArgumentCaptor.forClass(RemoteInputController.Callback.class);
- doNothing().when(mRemoteInputManager).addControllerCallback(callbackCaptor.capture());
- when(mRemoteInputManager.isRemoteInputActive()).thenReturn(false);
- initController(/* viewIsAttached= */ true);
- verify(mNotificationStackScrollLayout).setIsRemoteInputActive(false);
- RemoteInputController.Callback callback = callbackCaptor.getValue();
- callback.onRemoteInputActive(true);
- verify(mNotificationStackScrollLayout).setIsRemoteInputActive(true);
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
- initController(/* viewIsAttached= */ true);
- mSeenNotificationsInteractor.setHasFilteredOutSeenNotifications(true);
- mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
- verify(mNotificationStackScrollLayout).setHasFilteredOutSeenNotifications(true);
- verify(mNotificationStackScrollLayout).updateFooter();
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(anyBoolean(), anyBoolean());
- }
-
- @Test
public void testAttach_updatesViewStatusBarState() {
// GIVEN: Controller is attached
initController(/* viewIsAttached= */ true);
@@ -844,98 +687,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
}
@Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void updateImportantForAccessibility_noChild_onKeyGuard_notImportantForA11y() {
- // GIVEN: Controller is attached, active notifications is empty,
- // and mNotificationStackScrollLayout.onKeyguard() is true
- initController(/* viewIsAttached= */ true);
- when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true);
- mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
-
- // THEN: mNotificationStackScrollLayout should not be important for A11y
- verify(mNotificationStackScrollLayout)
- .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void updateImportantForAccessibility_hasChild_onKeyGuard_importantForA11y() {
- // GIVEN: Controller is attached, active notifications is not empty,
- // and mNotificationStackScrollLayout.onKeyguard() is true
- initController(/* viewIsAttached= */ true);
- when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true);
- mController.getNotifStackController().setNotifStats(
- new NotifStats(
- /* numActiveNotifs = */ 1,
- /* hasNonClearableAlertingNotifs = */ false,
- /* hasClearableAlertingNotifs = */ false,
- /* hasNonClearableSilentNotifs = */ false,
- /* hasClearableSilentNotifs = */ false)
- );
-
- // THEN: mNotificationStackScrollLayout should be important for A11y
- verify(mNotificationStackScrollLayout)
- .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void updateImportantForAccessibility_hasChild_notOnKeyGuard_importantForA11y() {
- // GIVEN: Controller is attached, active notifications is not empty,
- // and mNotificationStackScrollLayout.onKeyguard() is false
- initController(/* viewIsAttached= */ true);
- when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false);
- mController.getNotifStackController().setNotifStats(
- new NotifStats(
- /* numActiveNotifs = */ 1,
- /* hasNonClearableAlertingNotifs = */ false,
- /* hasClearableAlertingNotifs = */ false,
- /* hasNonClearableSilentNotifs = */ false,
- /* hasClearableSilentNotifs = */ false)
- );
-
- // THEN: mNotificationStackScrollLayout should be important for A11y
- verify(mNotificationStackScrollLayout)
- .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void updateImportantForAccessibility_noChild_notOnKeyGuard_importantForA11y() {
- // GIVEN: Controller is attached, active notifications is empty,
- // and mNotificationStackScrollLayout.onKeyguard() is false
- initController(/* viewIsAttached= */ true);
- when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false);
- mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
-
- // THEN: mNotificationStackScrollLayout should be important for A11y
- verify(mNotificationStackScrollLayout)
- .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void updateEmptyShadeView_onKeyguardTransitionToAod_hidesView() {
- initController(/* viewIsAttached= */ true);
- mController.onKeyguardTransitionChanged(
- new TransitionStep(
- /* from= */ KeyguardState.GONE,
- /* to= */ KeyguardState.AOD));
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean());
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- public void updateEmptyShadeView_onKeyguardOccludedTransitionToAod_hidesView() {
- initController(/* viewIsAttached= */ true);
- mController.onKeyguardTransitionChanged(
- new TransitionStep(
- /* from= */ KeyguardState.OCCLUDED,
- /* to= */ KeyguardState.AOD));
- verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean());
- }
-
- @Test
@DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
public void sensitiveNotificationProtectionControllerListenerNotRegistered() {
initController(/* viewIsAttached= */ true);
@@ -996,24 +747,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
return argThat(new LogMatcher(category, type));
}
- private void setupShowEmptyShadeViewState(boolean toShow) {
- if (toShow) {
- mController.onKeyguardTransitionChanged(
- new TransitionStep(
- /* from= */ KeyguardState.LOCKSCREEN,
- /* to= */ KeyguardState.GONE));
- mController.setQsFullScreen(false);
- mController.getView().removeAllViews();
- } else {
- mController.onKeyguardTransitionChanged(
- new TransitionStep(
- /* from= */ KeyguardState.GONE,
- /* to= */ KeyguardState.AOD));
- mController.setQsFullScreen(true);
- mController.getView().addContainerView(mock(ExpandableNotificationRow.class));
- }
- }
-
private void initController(boolean viewIsAttached) {
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(viewIsAttached);
ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class);
@@ -1033,16 +766,12 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mStatusBarService,
mNotificationRoundnessManager,
mTunerService,
- mDeviceProvisionedController,
mDynamicPrivacyController,
mConfigurationController,
mSysuiStatusBarStateController,
mKeyguardMediaController,
mKeyguardBypassController,
mPowerInteractor,
- mPrimaryBouncerInteractor,
- mKeyguardTransitionRepo,
- mZenModeController,
mNotificationLockscreenUserManager,
mMetricsLogger,
mColorUpdateLogger,
@@ -1051,14 +780,11 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
new FalsingManagerFake(),
mNotificationSwipeHelperBuilder,
mGroupExpansionManager,
- mSilentHeaderController,
mNotifPipeline,
mNotifCollection,
mLockscreenShadeTransitionController,
mUiEventLogger,
- mRemoteInputManager,
mVisibilityLocationProviderDelegator,
- mSeenNotificationsInteractor,
mViewBinder,
mShadeController,
mWindowRootView,
@@ -1076,7 +802,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
}
static class LogMatcher implements ArgumentMatcher<LogMaker> {
- private int mCategory, mType;
+ private final int mCategory, mType;
LogMatcher(int category, int type) {
mCategory = category;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index dcac2941b48b..39cff63f363e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -2,12 +2,10 @@ package com.android.systemui.statusbar.notification.stack
import android.annotation.DimenRes
import android.content.pm.PackageManager
-import android.platform.test.annotations.DisableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
@@ -740,20 +738,6 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat((footerView.viewState as FooterViewState).hideContent).isTrue()
}
- @DisableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR)
- @Test
- fun resetViewStates_clearAllInProgress_allRowsRemoved_emptyShade_footerHidden() {
- ambientState.isClearAllInProgress = true
- ambientState.isShadeExpanded = true
- ambientState.stackEndHeight = maxPanelHeight // plenty space for the footer in the stack
- hostView.removeAllViews() // remove all rows
- hostView.addView(footerView)
-
- stackScrollAlgorithm.resetViewStates(ambientState, 0)
-
- assertThat((footerView.viewState as FooterViewState).hideContent).isTrue()
- }
-
@Test
fun getGapForLocation_onLockscreen_returnsSmallGap() {
val gap =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index e592e4b319e3..1b4f9a79557d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -18,7 +18,6 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -41,7 +40,6 @@ import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRo
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository
@@ -63,7 +61,6 @@ import platform.test.runner.parameterized.Parameters
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-@EnableFlags(FooterViewRefactor.FLAG_NAME)
class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index d174484219ff..2e12336f6e93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -40,7 +40,6 @@ import static org.mockito.Mockito.when;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.TestableLooper;
@@ -610,7 +609,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
public void testPredictiveBackCallback_registration() {
/* verify that a predictive back callback is registered when the bouncer becomes visible */
mBouncerExpansionCallback.onVisibilityChanged(true);
@@ -625,7 +623,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
public void testPredictiveBackCallback_invocationHidesBouncer() {
mBouncerExpansionCallback.onVisibilityChanged(true);
/* capture the predictive back callback during registration */
@@ -643,7 +640,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
.thenReturn(KeyguardSecurityModel.SecurityMode.SimPin);
@@ -663,7 +659,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
public void testPredictiveBackCallback_forwardsBackDispatches() {
mBouncerExpansionCallback.onVisibilityChanged(true);
/* capture the predictive back callback during registration */
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 0652a835cb7c..650fa7ce46de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -31,7 +31,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
@@ -41,7 +40,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Dependency;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.animation.back.BackAnimationSpec;
@@ -137,7 +135,6 @@ public class SystemUIDialogTest extends SysuiTestCase {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_DIALOGS)
public void usePredictiveBackAnimFlag() {
final SystemUIDialog dialog = new SystemUIDialog(mContext);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
index 3d6882c3fdaf..c6bae197ad76 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -18,9 +18,8 @@ package com.android.systemui.statusbar.policy.domain.interactor
import android.app.AutomaticZenRule
import android.app.Flags
-import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
-import android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY
import android.app.NotificationManager.Policy
+import android.media.AudioManager
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.provider.Settings.Secure.ZEN_DURATION
@@ -34,6 +33,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.settingslib.notification.data.repository.updateNotificationPolicy
import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.settingslib.volume.shared.model.AudioStream
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -402,115 +402,124 @@ class ZenModeInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(Flags.FLAG_MODES_UI)
- fun activeModesBlockingEverything_hasModesWithFilterNone() =
+ fun activeModesBlockingMedia_hasModesWithPolicyBlockingMedia() =
testScope.runTest {
- val blockingEverything by collectLastValue(underTest.activeModesBlockingEverything)
+ val blockingMedia by
+ collectLastValue(
+ underTest.activeModesBlockingStream(AudioStream(AudioManager.STREAM_MUSIC))
+ )
zenModeRepository.addModes(
listOf(
TestModeBuilder()
- .setName("Filter=None, Not active")
- .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+ .setName("Blocks media, Not active")
+ .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
.setActive(false)
.build(),
TestModeBuilder()
- .setName("Filter=Priority, Active")
- .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setName("Allows media, Active")
+ .setZenPolicy(ZenPolicy.Builder().allowMedia(true).build())
.setActive(true)
.build(),
TestModeBuilder()
- .setName("Filter=None, Active")
- .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+ .setName("Blocks media, Active")
+ .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
.setActive(true)
.build(),
TestModeBuilder()
- .setName("Filter=None, Active Too")
- .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+ .setName("Blocks media, Active Too")
+ .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
.setActive(true)
.build(),
)
)
runCurrent()
- assertThat(blockingEverything!!.mainMode!!.name).isEqualTo("Filter=None, Active")
- assertThat(blockingEverything!!.modeNames)
- .containsExactly("Filter=None, Active", "Filter=None, Active Too")
+ assertThat(blockingMedia!!.mainMode!!.name).isEqualTo("Blocks media, Active")
+ assertThat(blockingMedia!!.modeNames)
+ .containsExactly("Blocks media, Active", "Blocks media, Active Too")
.inOrder()
}
@Test
@EnableFlags(Flags.FLAG_MODES_UI)
- fun activeModesBlockingMedia_hasModesWithPolicyBlockingMedia() =
+ fun activeModesBlockingAlarms_hasModesWithPolicyBlockingAlarms() =
testScope.runTest {
- val blockingMedia by collectLastValue(underTest.activeModesBlockingMedia)
+ val blockingAlarms by
+ collectLastValue(
+ underTest.activeModesBlockingStream(AudioStream(AudioManager.STREAM_ALARM))
+ )
zenModeRepository.addModes(
listOf(
TestModeBuilder()
- .setName("Blocks media, Not active")
- .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
+ .setName("Blocks alarms, Not active")
+ .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
.setActive(false)
.build(),
TestModeBuilder()
- .setName("Allows media, Active")
- .setZenPolicy(ZenPolicy.Builder().allowMedia(true).build())
+ .setName("Allows alarms, Active")
+ .setZenPolicy(ZenPolicy.Builder().allowAlarms(true).build())
.setActive(true)
.build(),
TestModeBuilder()
- .setName("Blocks media, Active")
- .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
+ .setName("Blocks alarms, Active")
+ .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
.setActive(true)
.build(),
TestModeBuilder()
- .setName("Blocks media, Active Too")
- .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
+ .setName("Blocks alarms, Active Too")
+ .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
.setActive(true)
.build(),
)
)
runCurrent()
- assertThat(blockingMedia!!.mainMode!!.name).isEqualTo("Blocks media, Active")
- assertThat(blockingMedia!!.modeNames)
- .containsExactly("Blocks media, Active", "Blocks media, Active Too")
+ assertThat(blockingAlarms!!.mainMode!!.name).isEqualTo("Blocks alarms, Active")
+ assertThat(blockingAlarms!!.modeNames)
+ .containsExactly("Blocks alarms, Active", "Blocks alarms, Active Too")
.inOrder()
}
@Test
@EnableFlags(Flags.FLAG_MODES_UI)
- fun activeModesBlockingAlarms_hasModesWithPolicyBlockingAlarms() =
+ fun activeModesBlockingAlarms_hasModesWithPolicyBlockingSystem() =
testScope.runTest {
- val blockingAlarms by collectLastValue(underTest.activeModesBlockingAlarms)
+ val blockingSystem by
+ collectLastValue(
+ underTest.activeModesBlockingStream(AudioStream(AudioManager.STREAM_SYSTEM))
+ )
zenModeRepository.addModes(
listOf(
TestModeBuilder()
- .setName("Blocks alarms, Not active")
- .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
+ .setName("Blocks system, Not active")
+ .setZenPolicy(ZenPolicy.Builder().allowSystem(false).build())
.setActive(false)
.build(),
TestModeBuilder()
- .setName("Allows alarms, Active")
- .setZenPolicy(ZenPolicy.Builder().allowAlarms(true).build())
+ .setName("Allows system, Active")
+ .setZenPolicy(ZenPolicy.Builder().allowSystem(true).build())
.setActive(true)
.build(),
TestModeBuilder()
- .setName("Blocks alarms, Active")
- .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
+ .setName("Blocks system, Active")
+ .setZenPolicy(ZenPolicy.Builder().allowSystem(false).build())
.setActive(true)
.build(),
TestModeBuilder()
- .setName("Blocks alarms, Active Too")
- .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
+ .setName("Blocks system, Active Too")
+ .setZenPolicy(ZenPolicy.Builder().allowSystem(false).build())
.setActive(true)
.build(),
)
)
runCurrent()
- assertThat(blockingAlarms!!.mainMode!!.name).isEqualTo("Blocks alarms, Active")
- assertThat(blockingAlarms!!.modeNames)
- .containsExactly("Blocks alarms, Active", "Blocks alarms, Active Too")
+ assertThat(blockingSystem!!.mainMode!!.name).isEqualTo("Blocks system, Active")
+ assertThat(blockingSystem!!.modeNames)
+ .containsExactly("Blocks system, Active", "Blocks system, Active Too")
.inOrder()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt
deleted file mode 100644
index 2ad1124d72d4..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.touchpad.tutorial.ui
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
-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.touchpad.tutorial.ui.composable.toGestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.toTutorialActionState
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class StateTransitionsTest : SysuiTestCase() {
-
- companion object {
- private const val START_MARKER = "startMarker"
- private const val END_MARKER = "endMarker"
- private const val SUCCESS_ANIMATION = 0
- }
-
- // needed to simulate caching last state as it's used to create new state
- private var lastState: TutorialActionState = NotStarted
-
- private fun GestureState.toTutorialActionState(): TutorialActionState {
- val newState =
- this.toGestureUiState(
- progressStartMarker = START_MARKER,
- progressEndMarker = END_MARKER,
- successAnimation = SUCCESS_ANIMATION,
- )
- .toTutorialActionState(lastState)
- lastState = newState
- return lastState
- }
-
- @Test
- fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() {
- val happyPath =
- listOf(
- GestureState.NotStarted,
- GestureState.InProgress(0f),
- GestureState.InProgress(0.5f),
- GestureState.InProgress(1f),
- GestureState.Finished,
- )
-
- val resultingStates = mutableListOf<TutorialActionState>()
- happyPath.forEach { resultingStates.add(it.toTutorialActionState()) }
-
- assertThat(resultingStates)
- .containsExactly(
- NotStarted,
- InProgress(0f, START_MARKER, END_MARKER),
- InProgress(0.5f, START_MARKER, END_MARKER),
- InProgress(1f, START_MARKER, END_MARKER),
- Finished(SUCCESS_ANIMATION),
- )
- .inOrder()
- }
-
- @Test
- fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() {
- val errorPath =
- listOf(
- GestureState.NotStarted,
- GestureState.InProgress(0f),
- GestureState.Error,
- GestureState.InProgress(0.5f),
- GestureState.InProgress(1f),
- GestureState.Finished,
- )
-
- val resultingStates = mutableListOf<TutorialActionState>()
- errorPath.forEach { resultingStates.add(it.toTutorialActionState()) }
-
- assertThat(resultingStates)
- .containsExactly(
- NotStarted,
- InProgress(0f, START_MARKER, END_MARKER),
- Error,
- InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)),
- InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)),
- Finished(SUCCESS_ANIMATION),
- )
- .inOrder()
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt
index 4aec88e8497b..d752046f4791 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt
@@ -23,16 +23,16 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+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.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture
import com.android.systemui.touchpad.ui.gesture.touchpadGestureResources
@@ -71,8 +71,8 @@ class BackGestureScreenViewModelTest : SysuiTestCase() {
expected =
InProgress(
progress = 1f,
- progressStartMarker = "gesture to L",
- progressEndMarker = "end progress L",
+ startMarker = "gesture to L",
+ endMarker = "end progress L",
),
)
}
@@ -85,8 +85,8 @@ class BackGestureScreenViewModelTest : SysuiTestCase() {
expected =
InProgress(
progress = 1f,
- progressStartMarker = "gesture to R",
- progressEndMarker = "end progress R",
+ startMarker = "gesture to R",
+ endMarker = "end progress R",
),
)
}
@@ -114,7 +114,7 @@ class BackGestureScreenViewModelTest : SysuiTestCase() {
kosmos.runTest {
fun performBackGesture() =
ThreeFingerGesture.swipeLeft().forEach { viewModel.handleEvent(it) }
- val state by collectLastValue(viewModel.gestureUiState)
+ val state by collectLastValue(viewModel.tutorialState)
performBackGesture()
assertThat(state).isInstanceOf(Finished::class.java)
@@ -134,15 +134,21 @@ class BackGestureScreenViewModelTest : SysuiTestCase() {
fakeConfigRepository.onAnyConfigurationChange()
}
- private fun Kosmos.assertProgressWhileMovingFingers(deltaX: Float, expected: GestureUiState) {
+ private fun Kosmos.assertProgressWhileMovingFingers(
+ deltaX: Float,
+ expected: TutorialActionState,
+ ) {
assertStateAfterEvents(
events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
expected = expected,
)
}
- private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) {
- val state by collectLastValue(viewModel.gestureUiState)
+ private fun Kosmos.assertStateAfterEvents(
+ events: List<MotionEvent>,
+ expected: TutorialActionState,
+ ) {
+ val state by collectLastValue(viewModel.tutorialState)
events.forEach { viewModel.handleEvent(it) }
assertThat(state).isEqualTo(expected)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt
index 65a995dcd043..7862fd32ca04 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt
@@ -23,16 +23,16 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+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.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture
import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity
@@ -86,8 +86,8 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() {
expected =
InProgress(
progress = 1f,
- progressStartMarker = "drag with gesture",
- progressEndMarker = "release playback realtime",
+ startMarker = "drag with gesture",
+ endMarker = "release playback realtime",
),
)
}
@@ -108,7 +108,7 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() {
@Test
fun gestureRecognitionTakesLatestDistanceThresholdIntoAccount() =
kosmos.runTest {
- val state by collectLastValue(viewModel.gestureUiState)
+ val state by collectLastValue(viewModel.tutorialState)
performHomeGesture()
assertThat(state).isInstanceOf(Finished::class.java)
@@ -121,7 +121,7 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() {
@Test
fun gestureRecognitionTakesLatestVelocityThresholdIntoAccount() =
kosmos.runTest {
- val state by collectLastValue(viewModel.gestureUiState)
+ val state by collectLastValue(viewModel.tutorialState)
performHomeGesture()
assertThat(state).isInstanceOf(Finished::class.java)
@@ -147,8 +147,11 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() {
fakeConfigRepository.onAnyConfigurationChange()
}
- private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) {
- val state by collectLastValue(viewModel.gestureUiState)
+ private fun Kosmos.assertStateAfterEvents(
+ events: List<MotionEvent>,
+ expected: TutorialActionState,
+ ) {
+ val state by collectLastValue(viewModel.tutorialState)
events.forEach { viewModel.handleEvent(it) }
assertThat(state).isEqualTo(expected)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt
index 1bc60b67095e..6180fa98b1cd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt
@@ -23,16 +23,16 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+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.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture
import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity
@@ -89,8 +89,8 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() {
expected =
InProgress(
progress = 1f,
- progressStartMarker = "drag with gesture",
- progressEndMarker = "onPause",
+ startMarker = "drag with gesture",
+ endMarker = "onPause",
),
)
}
@@ -111,7 +111,7 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() {
@Test
fun gestureRecognitionTakesLatestDistanceThresholdIntoAccount() =
kosmos.runTest {
- val state by collectLastValue(viewModel.gestureUiState)
+ val state by collectLastValue(viewModel.tutorialState)
performRecentAppsGesture()
assertThat(state).isInstanceOf(Finished::class.java)
@@ -124,7 +124,7 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() {
@Test
fun gestureRecognitionTakesLatestVelocityThresholdIntoAccount() =
kosmos.runTest {
- val state by collectLastValue(viewModel.gestureUiState)
+ val state by collectLastValue(viewModel.tutorialState)
performRecentAppsGesture()
assertThat(state).isInstanceOf(Finished::class.java)
@@ -150,8 +150,11 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() {
fakeConfigRepository.onAnyConfigurationChange()
}
- private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) {
- val state by collectLastValue(viewModel.gestureUiState)
+ private fun Kosmos.assertStateAfterEvents(
+ events: List<MotionEvent>,
+ expected: TutorialActionState,
+ ) {
+ val state by collectLastValue(viewModel.tutorialState)
events.forEach { viewModel.handleEvent(it) }
assertThat(state).isEqualTo(expected)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt
new file mode 100644
index 000000000000..c113dd9e1eff
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.touchpad.tutorial.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+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.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.asFlow
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TouchpadTutorialScreenViewModelTest : SysuiTestCase() {
+
+ companion object {
+ private const val START_MARKER = "startMarker"
+ private const val END_MARKER = "endMarker"
+ private const val SUCCESS_ANIMATION = 0
+ }
+
+ private val kosmos = testKosmos()
+ private val animationProperties =
+ TutorialAnimationProperties(
+ progressStartMarker = START_MARKER,
+ progressEndMarker = END_MARKER,
+ successAnimation = SUCCESS_ANIMATION,
+ )
+
+ @Before
+ fun before() {
+ kosmos.useUnconfinedTestDispatcher()
+ }
+
+ @Test
+ fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() =
+ kosmos.runTest {
+ val happyPath: Flow<Pair<GestureState, TutorialAnimationProperties>> =
+ listOf(
+ GestureState.NotStarted,
+ GestureState.InProgress(0f),
+ GestureState.InProgress(0.5f),
+ GestureState.InProgress(1f),
+ GestureState.Finished,
+ )
+ .map { it to animationProperties }
+ .asFlow()
+
+ val resultingStates by collectValues(happyPath.mapToTutorialState())
+
+ assertThat(resultingStates)
+ .containsExactly(
+ NotStarted,
+ InProgress(0f, START_MARKER, END_MARKER),
+ InProgress(0.5f, START_MARKER, END_MARKER),
+ InProgress(1f, START_MARKER, END_MARKER),
+ Finished(SUCCESS_ANIMATION),
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() =
+ kosmos.runTest {
+ val errorPath: Flow<Pair<GestureState, TutorialAnimationProperties>> =
+ listOf(
+ GestureState.NotStarted,
+ GestureState.InProgress(0f),
+ GestureState.Error,
+ GestureState.InProgress(0.5f),
+ GestureState.InProgress(1f),
+ GestureState.Finished,
+ )
+ .map { it to animationProperties }
+ .asFlow()
+
+ val resultingStates by collectValues(errorPath.mapToTutorialState())
+
+ assertThat(resultingStates)
+ .containsExactly(
+ NotStarted,
+ InProgress(0f, START_MARKER, END_MARKER),
+ Error,
+ InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)),
+ InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)),
+ Finished(SUCCESS_ANIMATION),
+ )
+ .inOrder()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
index d3071f87f744..51cac6976362 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
@@ -23,66 +23,40 @@ import android.platform.test.annotations.EnableFlags
import android.service.notification.ZenPolicy
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.internal.logging.uiEventLogger
import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
-import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.testKosmos
-import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
-import com.android.systemui.volume.shared.volumePanelLogger
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class AudioStreamSliderViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
private val zenModeRepository = kosmos.fakeZenModeRepository
- private lateinit var mediaStream: AudioStreamSliderViewModel
- private lateinit var alarmsStream: AudioStreamSliderViewModel
- private lateinit var notificationStream: AudioStreamSliderViewModel
- private lateinit var otherStream: AudioStreamSliderViewModel
-
- @Before
- fun setUp() {
- mediaStream = audioStreamSliderViewModel(AudioManager.STREAM_MUSIC)
- alarmsStream = audioStreamSliderViewModel(AudioManager.STREAM_ALARM)
- notificationStream = audioStreamSliderViewModel(AudioManager.STREAM_NOTIFICATION)
- otherStream = audioStreamSliderViewModel(AudioManager.STREAM_VOICE_CALL)
- }
-
- private fun audioStreamSliderViewModel(stream: Int): AudioStreamSliderViewModel {
- return AudioStreamSliderViewModel(
+ private fun Kosmos.audioStreamSliderViewModel(stream: Int): AudioStreamSliderViewModel {
+ return audioStreamSliderViewModelFactory.create(
AudioStreamSliderViewModel.FactoryAudioStreamWrapper(AudioStream(stream)),
- testScope.backgroundScope,
- context,
- kosmos.audioVolumeInteractor,
- kosmos.zenModeInteractor,
- kosmos.uiEventLogger,
- kosmos.volumePanelLogger,
- kosmos.sliderHapticsViewModelFactory,
+ applicationCoroutineScope,
)
}
@Test
@EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
fun slider_media_hasDisabledByModesText() =
- testScope.runTest {
- val mediaSlider by collectLastValue(mediaStream.slider)
+ kosmos.runTest {
+ val mediaSlider by
+ collectLastValue(audioStreamSliderViewModel(AudioManager.STREAM_MUSIC).slider)
zenModeRepository.addMode(
TestModeBuilder()
@@ -112,8 +86,9 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
fun slider_alarms_hasDisabledByModesText() =
- testScope.runTest {
- val alarmsSlider by collectLastValue(alarmsStream.slider)
+ kosmos.runTest {
+ val alarmsSlider by
+ collectLastValue(audioStreamSliderViewModel(AudioManager.STREAM_ALARM).slider)
zenModeRepository.addMode(
TestModeBuilder()
@@ -141,9 +116,10 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
- fun slider_other_hasDisabledByModesText() =
- testScope.runTest {
- val otherSlider by collectLastValue(otherStream.slider)
+ fun slider_other_hasDisabledText() =
+ kosmos.runTest {
+ val otherSlider by
+ collectLastValue(audioStreamSliderViewModel(AudioManager.STREAM_VOICE_CALL).slider)
zenModeRepository.addMode(
TestModeBuilder()
@@ -154,20 +130,17 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() {
)
runCurrent()
- assertThat(otherSlider!!.disabledMessage)
- .isEqualTo("Unavailable because Everything blocked is on")
-
- zenModeRepository.clearModes()
- runCurrent()
-
assertThat(otherSlider!!.disabledMessage).isEqualTo("Unavailable")
}
@Test
@EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
fun slider_notification_hasSpecialDisabledText() =
- testScope.runTest {
- val notificationSlider by collectLastValue(notificationStream.slider)
+ kosmos.runTest {
+ val notificationSlider by
+ collectLastValue(
+ audioStreamSliderViewModel(AudioManager.STREAM_NOTIFICATION).slider
+ )
runCurrent()
assertThat(notificationSlider!!.disabledMessage)
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cd37c22c8bc3..a01ff3d5258f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3798,7 +3798,7 @@
<!-- Title at the top of the keyboard shortcut helper UI when in customize mode. The helper
is a component that shows the user which keyboard shortcuts they can use.
[CHAR LIMIT=NONE] -->
- <string name="shortcut_helper_customize_mode_title">Customize keyboard shortcuts</string>
+ <string name="shortcut_helper_customize_mode_title">Customize shortcuts</string>
<!-- Title at the top of the keyboard shortcut helper remove shortcut dialog.
The helper is a component that shows the user which keyboard shortcuts they can use. Also
allows the user to add/remove custom shortcuts.[CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 7d220b505aa0..6e23a0783c9d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -21,11 +21,9 @@ import android.util.Log;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.PictureInPictureSurfaceTransaction;
-import android.window.TaskSnapshot;
import android.window.WindowAnimationState;
import com.android.internal.os.IResultReceiver;
-import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.wm.shell.recents.IRecentsAnimationController;
public class RecentsAnimationControllerCompat {
@@ -40,18 +38,6 @@ public class RecentsAnimationControllerCompat {
mAnimationController = animationController;
}
- public ThumbnailData screenshotTask(int taskId) {
- try {
- final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
- if (snapshot != null) {
- return ThumbnailData.fromSnapshot(snapshot);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to screenshot task", e);
- }
- return new ThumbnailData();
- }
-
public void setInputConsumerEnabled(boolean enabled) {
try {
mAnimationController.setInputConsumerEnabled(enabled);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index d3c02e6f6449..b159a70066ce 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -29,7 +29,6 @@ import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor;
import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
@@ -93,10 +92,8 @@ public class KeyguardPinViewController
mPasswordEntry.setUserActivityListener(this::onUserInput);
mView.onDevicePostureChanged(mPostureController.getDevicePosture());
mPostureController.addCallback(mPostureCallback);
- if (mFeatureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)) {
- mPasswordEntry.setUsePinShapes(true);
- updateAutoConfirmationState();
- }
+ mPasswordEntry.setUsePinShapes(true);
+ updateAutoConfirmationState();
}
protected void onUserInput() {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index e02e3fbc339b..10f060c13a59 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -22,10 +22,10 @@ import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAK
import android.annotation.MainThread;
import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.Trace;
import android.util.Log;
import android.view.Display;
+import com.android.app.tracing.coroutines.TrackTracer;
import com.android.internal.util.Preconditions;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.dagger.DozeScope;
@@ -314,7 +314,7 @@ public class DozeMachine {
mState = newState;
mDozeLog.traceState(newState);
- Trace.traceCounter(Trace.TRACE_TAG_APP, "doze_machine_state", newState.ordinal());
+ TrackTracer.instantForGroup("keyguard", "doze_machine_state", newState.ordinal());
updatePulseReason(newState, oldState, pulseReason);
performTransitionOnComponents(oldState, newState);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 63ac783ad42b..129a6bb72996 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -35,7 +35,6 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
@@ -57,7 +56,6 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha
NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token
PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token
NotificationMinimalism.token dependsOn NotificationThrottleHun.token
- ModesEmptyShadeFix.token dependsOn FooterViewRefactor.token
ModesEmptyShadeFix.token dependsOn modesUi
// SceneContainer dependencies
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index c039e0188064..2c33c0b4403b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -76,21 +76,10 @@ object Flags {
val LOCKSCREEN_CUSTOM_CLOCKS =
resourceBooleanFlag(R.bool.config_enableLockScreenCustomClocks, "lockscreen_custom_clocks")
- /**
- * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
- * will occur in stages. This is one stage of many to come.
- */
- // TODO(b/255607168): Tracking Bug
- @JvmField val DOZING_MIGRATION_1 = unreleasedFlag("dozing_migration_1")
-
/** Flag to control the revamp of keyguard biometrics progress animation */
// TODO(b/244313043): Tracking bug
@JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp")
- // flag for controlling auto pin confirmation and material u shapes in bouncer
- @JvmField
- val AUTO_PIN_CONFIRMATION = releasedFlag("auto_pin_confirmation", "auto_pin_confirmation")
-
/** Enables code to show contextual loyalty cards in wallet entrypoints */
// TODO(b/294110497): Tracking Bug
@JvmField
@@ -100,10 +89,6 @@ object Flags {
// TODO(b/242908637): Tracking Bug
@JvmField val WALLPAPER_FULLSCREEN_PREVIEW = releasedFlag("wallpaper_fullscreen_preview")
- /** Inflate and bind views upon emitting a blueprint value . */
- // TODO(b/297365780): Tracking Bug
- @JvmField val LAZY_INFLATE_KEYGUARD = releasedFlag("lazy_inflate_keyguard")
-
/** Enables UI updates for AI wallpapers in the wallpaper picker. */
// TODO(b/267722622): Tracking Bug
@JvmField val WALLPAPER_PICKER_UI_FOR_AIWP = releasedFlag("wallpaper_picker_ui_for_aiwp")
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt
index 19a19d551613..c702ba9f401e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt
@@ -25,6 +25,7 @@ import com.android.systemui.CoreStartable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.shortcut.data.repository.CustomInputGesturesRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.CommandQueue
@@ -41,6 +42,7 @@ constructor(
private val stateRepository: ShortcutHelperStateRepository,
private val activityStarter: ActivityStarter,
@Background private val backgroundScope: CoroutineScope,
+ private val customInputGesturesRepository: CustomInputGesturesRepository
) : CoreStartable {
override fun start() {
registerBroadcastReceiver(
@@ -55,6 +57,10 @@ constructor(
action = Intent.ACTION_CLOSE_SYSTEM_DIALOGS,
onReceive = { stateRepository.hide() },
)
+ registerBroadcastReceiver(
+ action = Intent.ACTION_USER_SWITCHED,
+ onReceive = { customInputGesturesRepository.refreshCustomInputGestures() },
+ )
commandQueue.addCallback(
object : CommandQueue.Callbacks {
override fun dismissKeyboardShortcutsMenu() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
index 36cd40052041..e5c638cbdfba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
@@ -25,6 +25,7 @@ import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RES
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
import android.hardware.input.InputSettings
import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult
import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.ERROR_OTHER
@@ -37,6 +38,7 @@ import kotlinx.coroutines.withContext
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
+@SysUISingleton
class CustomInputGesturesRepository
@Inject
constructor(private val userTracker: UserTracker,
@@ -56,7 +58,7 @@ constructor(private val userTracker: UserTracker,
val customInputGestures =
_customInputGesture.onStart { refreshCustomInputGestures() }
- private fun refreshCustomInputGestures() {
+ fun refreshCustomInputGestures() {
setCustomInputGestures(inputGestures = retrieveCustomInputGestures())
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
index d7be5e622276..e255bdea6100 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
@@ -27,14 +27,19 @@ import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
@@ -66,6 +71,11 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to MultiTasking,
KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to MultiTasking,
KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to MultiTasking,
+ KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW to MultiTasking,
+ KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW to MultiTasking,
+ KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW to MultiTasking,
+ KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW to MultiTasking,
+ KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to MultiTasking,
// App Category
KEY_GESTURE_TYPE_LAUNCH_APPLICATION to AppCategories,
@@ -102,15 +112,23 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
R.string.shortcutHelper_category_split_screen,
KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to
R.string.shortcutHelper_category_split_screen,
+ KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW to
+ R.string.shortcutHelper_category_split_screen,
+ KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW to
+ R.string.shortcutHelper_category_split_screen,
+ KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW to
+ R.string.shortcutHelper_category_split_screen,
+ KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW to
+ R.string.shortcutHelper_category_split_screen,
+ KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to R.string.shortcutHelper_category_split_screen,
// App Category
- KEY_GESTURE_TYPE_LAUNCH_APPLICATION to
- R.string.keyboard_shortcut_group_applications,
+ KEY_GESTURE_TYPE_LAUNCH_APPLICATION to R.string.keyboard_shortcut_group_applications,
)
/**
- * App Category shortcut labels are mapped dynamically based on intent
- * see [InputGestureDataAdapter.fetchShortcutLabelByAppLaunchData]
+ * App Category shortcut labels are mapped dynamically based on intent see
+ * [InputGestureDataAdapter.fetchShortcutLabelByAppLaunchData]
*/
val gestureToInternalKeyboardShortcutInfoLabelResIdMap =
mapOf(
@@ -136,6 +154,16 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to R.string.system_multitasking_lhs,
KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to R.string.system_multitasking_rhs,
KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to R.string.system_multitasking_full_screen,
+ KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW to
+ R.string.system_desktop_mode_snap_left_window,
+ KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW to
+ R.string.system_desktop_mode_snap_right_window,
+ KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW to
+ R.string.system_desktop_mode_minimize_window,
+ KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW to
+ R.string.system_desktop_mode_toggle_maximize_window,
+ KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to
+ R.string.system_multitasking_move_to_next_display,
)
val shortcutLabelToKeyGestureTypeMap: Map<String, Int>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index d40fe468b0a5..591383999182 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -538,27 +538,30 @@ public class KeyguardService extends Service {
@Override // Binder interface
public void onFinishedGoingToSleep(
- @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) {
+ @PowerManager.GoToSleepReason int pmSleepReason, boolean
+ powerButtonLaunchGestureTriggered) {
trace("onFinishedGoingToSleep pmSleepReason=" + pmSleepReason
- + " cameraGestureTriggered=" + cameraGestureTriggered);
+ + " powerButtonLaunchTriggered=" + powerButtonLaunchGestureTriggered);
checkPermission();
mKeyguardViewMediator.onFinishedGoingToSleep(
WindowManagerPolicyConstants.translateSleepReasonToOffReason(pmSleepReason),
- cameraGestureTriggered);
- mPowerInteractor.onFinishedGoingToSleep(cameraGestureTriggered);
+ powerButtonLaunchGestureTriggered);
+ mPowerInteractor.onFinishedGoingToSleep(powerButtonLaunchGestureTriggered);
mKeyguardLifecyclesDispatcher.dispatch(
KeyguardLifecyclesDispatcher.FINISHED_GOING_TO_SLEEP);
}
@Override // Binder interface
public void onStartedWakingUp(
- @PowerManager.WakeReason int pmWakeReason, boolean cameraGestureTriggered) {
+ @PowerManager.WakeReason int pmWakeReason,
+ boolean powerButtonLaunchGestureTriggered) {
trace("onStartedWakingUp pmWakeReason=" + pmWakeReason
- + " cameraGestureTriggered=" + cameraGestureTriggered);
+ + " powerButtonLaunchGestureTriggered=" + powerButtonLaunchGestureTriggered);
Trace.beginSection("KeyguardService.mBinder#onStartedWakingUp");
checkPermission();
- mKeyguardViewMediator.onStartedWakingUp(pmWakeReason, cameraGestureTriggered);
- mPowerInteractor.onStartedWakingUp(pmWakeReason, cameraGestureTriggered);
+ mKeyguardViewMediator.onStartedWakingUp(pmWakeReason,
+ powerButtonLaunchGestureTriggered);
+ mPowerInteractor.onStartedWakingUp(pmWakeReason, powerButtonLaunchGestureTriggered);
mKeyguardLifecyclesDispatcher.dispatch(
KeyguardLifecyclesDispatcher.STARTED_WAKING_UP, pmWakeReason);
Trace.endSection();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 63ac5094c400..e65cd9873491 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -109,6 +109,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.app.animation.Interpolators;
+import com.android.app.tracing.coroutines.TrackTracer;
import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
@@ -3983,7 +3984,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
public void setPendingLock(boolean hasPendingLock) {
mPendingLock = hasPendingLock;
- Trace.traceCounter(Trace.TRACE_TAG_APP, "pendingLock", mPendingLock ? 1 : 0);
+ TrackTracer.instantForGroup("keyguard", "pendingLock", mPendingLock ? 1 : 0);
}
private boolean isViewRootReady() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
index 633628f1167e..c3182003227f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
@@ -16,8 +16,7 @@
package com.android.systemui.keyguard;
-import android.os.Trace;
-
+import com.android.app.tracing.coroutines.TrackTracer;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.power.domain.interactor.PowerInteractor;
@@ -80,7 +79,7 @@ public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> impleme
private void setScreenState(int screenState) {
mScreenState = screenState;
- Trace.traceCounter(Trace.TRACE_TAG_APP, "screenState", screenState);
+ TrackTracer.instantForGroup("screen", "screenState", screenState);
}
public interface Observer {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index c0ffda6640b2..c261cfefb2b8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -24,11 +24,11 @@ import android.graphics.Point;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.Trace;
import android.util.DisplayMetrics;
import androidx.annotation.Nullable;
+import com.android.app.tracing.coroutines.TrackTracer;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
@@ -197,7 +197,7 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe
private void setWakefulness(@Wakefulness int wakefulness) {
mWakefulness = wakefulness;
- Trace.traceCounter(Trace.TRACE_TAG_APP, "wakefulness", wakefulness);
+ TrackTracer.instantForGroup("screen", "wakefulness", wakefulness);
}
private void updateLastWakeOriginLocation() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index e5ff2529e335..f295c0ccb3de 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -19,6 +19,7 @@ import android.content.Context
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.os.UserHandle
+import android.view.View
import androidx.annotation.StyleRes
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
@@ -119,6 +120,12 @@ class ScreenRecordPermissionDialogDelegate(
super<BaseMediaProjectionPermissionDialogDelegate>.onCreate(dialog, savedInstanceState)
setDialogTitle(R.string.screenrecord_permission_dialog_title)
dialog.setTitle(R.string.screenrecord_title)
+ setStartButtonOnClickListener { v: View? ->
+ val screenRecordViewBinder: ScreenRecordPermissionViewBinder? =
+ viewBinder as ScreenRecordPermissionViewBinder?
+ screenRecordViewBinder?.startButtonOnClicked()
+ dialog.dismiss()
+ }
setCancelButtonOnClickListener { dialog.dismiss() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
index 9f7e1ade964a..691bdd4a1b27 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
@@ -79,37 +79,38 @@ class ScreenRecordPermissionViewBinder(
override fun bind() {
super.bind()
initRecordOptionsView()
- setStartButtonOnClickListener { _: View? ->
- onStartRecordingClicked?.run()
- if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
- requestScreenCapture(
- captureTarget = null,
- displayId = selectedScreenShareOption.displayId,
- )
- }
- if (selectedScreenShareOption.mode == SINGLE_APP) {
- val intent = Intent(dialog.context, MediaProjectionAppSelectorActivity::class.java)
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ setStartButtonOnClickListener { startButtonOnClicked() }
+ }
- // We can't start activity for result here so we use result receiver to get
- // the selected target to capture
- intent.putExtra(
- MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
- CaptureTargetResultReceiver(),
- )
+ fun startButtonOnClicked() {
+ onStartRecordingClicked?.run()
+ if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
+ requestScreenCapture(
+ captureTarget = null,
+ displayId = selectedScreenShareOption.displayId,
+ )
+ }
+ if (selectedScreenShareOption.mode == SINGLE_APP) {
+ val intent = Intent(dialog.context, MediaProjectionAppSelectorActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- intent.putExtra(
- MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
- hostUserHandle,
- )
- intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID, hostUid)
- intent.putExtra(
- MediaProjectionAppSelectorActivity.EXTRA_SCREEN_SHARE_TYPE,
- MediaProjectionAppSelectorActivity.ScreenShareType.ScreenRecord.name,
- )
- activityStarter.startActivity(intent, /* dismissShade= */ true)
- }
- dialog.dismiss()
+ // We can't start activity for result here so we use result receiver to get
+ // the selected target to capture
+ intent.putExtra(
+ MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
+ CaptureTargetResultReceiver(),
+ )
+
+ intent.putExtra(
+ MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
+ hostUserHandle,
+ )
+ intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID, hostUid)
+ intent.putExtra(
+ MediaProjectionAppSelectorActivity.EXTRA_SCREEN_SHARE_TYPE,
+ MediaProjectionAppSelectorActivity.ScreenShareType.ScreenRecord.name,
+ )
+ activityStarter.startActivity(intent, /* dismissShade= */ true)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e168025b2bf8..c9eb4962ab00 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -162,7 +162,6 @@ import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
@@ -1214,14 +1213,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private boolean hasVisibleNotifications() {
- if (FooterViewRefactor.isEnabled()) {
- return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
- || mMediaDataManager.hasActiveMediaOrRecommendation();
- } else {
- return mNotificationStackScrollLayoutController
- .getVisibleNotificationCount() != 0
- || mMediaDataManager.hasActiveMediaOrRecommendation();
- }
+ return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
+ || mMediaDataManager.hasActiveMediaOrRecommendation();
}
@Override
@@ -2218,9 +2211,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@Override
public void setBouncerShowing(boolean bouncerShowing) {
mBouncerShowing = bouncerShowing;
- if (!FooterViewRefactor.isEnabled()) {
- mNotificationStackScrollLayoutController.updateShowEmptyShadeView();
- }
updateVisibility();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index c88e7b827881..14087a0efcfc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -86,7 +86,6 @@ import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.QsFrameTranslateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -96,8 +95,8 @@ import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
@@ -1022,12 +1021,6 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
}
void updateQsState() {
- if (!FooterViewRefactor.isEnabled()) {
- // Update full screen state; note that this will be true if the QS panel is only
- // partially expanded, and that is fixed with the footer view refactor.
- setQsFullScreen(/* qsFullScreen = */ getExpanded() && !mSplitShadeEnabled);
- }
-
if (mQsStateUpdateListener != null) {
mQsStateUpdateListener.onQsStateUpdated(getExpanded(), mStackScrollerOverscrolling);
}
@@ -1094,10 +1087,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
// Update the light bar
mLightBarController.setQsExpanded(mFullyExpanded);
- if (FooterViewRefactor.isEnabled()) {
- // Update full screen state
- setQsFullScreen(/* qsFullScreen = */ mFullyExpanded && !mSplitShadeEnabled);
- }
+ // Update full screen state
+ setQsFullScreen(/* qsFullScreen = */ mFullyExpanded && !mSplitShadeEnabled);
}
float getLockscreenShadeDragProgress() {
@@ -2268,10 +2259,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
setExpansionHeight(qsHeight);
}
- boolean hasNotifications = FooterViewRefactor.isEnabled()
- ? mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
- : mNotificationStackScrollLayoutController.getVisibleNotificationCount()
- != 0;
+ boolean hasNotifications =
+ mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue();
if (!hasNotifications && !mMediaDataManager.hasActiveMediaOrRecommendation()) {
// No notifications are visible, let's animate to the height of qs instead
if (isQsFragmentCreated()) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index ef62d2da9589..a2edd3ab837d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -15,11 +15,18 @@
*/
package com.android.systemui.shade.data.repository
+import android.annotation.SuppressLint
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
/** Data for the shade, mostly related to expansion of the shade and quick settings. */
interface ShadeRepository {
@@ -36,7 +43,7 @@ interface ShadeRepository {
* Information about the currently running fling animation, or null if no fling animation is
* running.
*/
- val currentFling: StateFlow<FlingInfo?>
+ val currentFling: SharedFlow<FlingInfo?>
/**
* The amount the lockscreen shade has dragged down by the user, [0-1]. 0 means fully collapsed,
@@ -180,7 +187,8 @@ interface ShadeRepository {
/** Business logic for shade interactions */
@SysUISingleton
-class ShadeRepositoryImpl @Inject constructor() : ShadeRepository {
+class ShadeRepositoryImpl @Inject constructor(@Background val backgroundScope: CoroutineScope) :
+ ShadeRepository {
private val _qsExpansion = MutableStateFlow(0f)
@Deprecated("Use ShadeInteractor.qsExpansion instead")
override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
@@ -193,8 +201,13 @@ class ShadeRepositoryImpl @Inject constructor() : ShadeRepository {
override val udfpsTransitionToFullShadeProgress: StateFlow<Float> =
_udfpsTransitionToFullShadeProgress.asStateFlow()
- private val _currentFling: MutableStateFlow<FlingInfo?> = MutableStateFlow(null)
- override val currentFling: StateFlow<FlingInfo?> = _currentFling.asStateFlow()
+ /**
+ * Must be a SharedFlow, since the fling is by definition an event and dropping it has extreme
+ * consequences in some cases (for example, keyguard uses this to decide when to unlock).
+ */
+ @SuppressLint("SharedFlowCreation")
+ override val currentFling: MutableSharedFlow<FlingInfo?> =
+ MutableSharedFlow(replay = 2, onBufferOverflow = BufferOverflow.DROP_OLDEST)
private val _legacyShadeExpansion = MutableStateFlow(0f)
@Deprecated("Use ShadeInteractor.shadeExpansion instead")
@@ -294,7 +307,7 @@ class ShadeRepositoryImpl @Inject constructor() : ShadeRepository {
}
override fun setCurrentFling(info: FlingInfo?) {
- _currentFling.value = info
+ backgroundScope.launch { currentFling.emit(info) }
}
@Deprecated("Should only be called by NPVC and tests")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt
index a747abbc6a6e..1c14d3349027 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt
@@ -28,17 +28,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.node.LayoutModifierNode
-import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.constrain
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.statusbar.chips.ui.compose.modifiers.neverDecreaseWidth
import kotlinx.coroutines.delay
/** Platform-optimized interface for getting current time */
@@ -97,35 +91,3 @@ fun ChronometerText(
modifier = modifier.neverDecreaseWidth(),
)
}
-
-/** A modifier that ensures the width of the content only increases and never decreases. */
-private fun Modifier.neverDecreaseWidth(): Modifier {
- return this.then(neverDecreaseWidthElement)
-}
-
-private data object neverDecreaseWidthElement : ModifierNodeElement<NeverDecreaseWidthNode>() {
- override fun create(): NeverDecreaseWidthNode {
- return NeverDecreaseWidthNode()
- }
-
- override fun update(node: NeverDecreaseWidthNode) {
- error("This should never be called")
- }
-}
-
-private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode {
- private var minWidth = 0
-
- override fun MeasureScope.measure(
- measurable: Measurable,
- constraints: Constraints,
- ): MeasureResult {
- val placeable = measurable.measure(Constraints(minWidth = minWidth).constrain(constraints))
- val width = placeable.width
- val height = placeable.height
-
- minWidth = maxOf(minWidth, width)
-
- return layout(width, height) { placeable.place(0, 0) }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt
new file mode 100644
index 000000000000..505a5fcb18b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.ui.compose.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.constrain
+
+/** A modifier that ensures the width of the content only increases and never decreases. */
+fun Modifier.neverDecreaseWidth(): Modifier {
+ return this.then(neverDecreaseWidthElement)
+}
+
+private data object neverDecreaseWidthElement : ModifierNodeElement<NeverDecreaseWidthNode>() {
+ override fun create(): NeverDecreaseWidthNode {
+ return NeverDecreaseWidthNode()
+ }
+
+ override fun update(node: NeverDecreaseWidthNode) {
+ error("This should never be called")
+ }
+}
+
+private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode {
+ private var minWidth = 0
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints,
+ ): MeasureResult {
+ val placeable = measurable.measure(Constraints(minWidth = minWidth).constrain(constraints))
+ val width = placeable.width
+ val height = placeable.height
+
+ minWidth = maxOf(minWidth, width)
+
+ return layout(width, height) { placeable.place(0, 0) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 254b792f8152..d327fc23fd06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.dagger;
-import static com.android.systemui.Flags.predictiveBackAnimateDialogs;
-
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
@@ -28,7 +26,6 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.CoreStartable;
import com.android.systemui.animation.ActivityTransitionAnimator;
-import com.android.systemui.animation.AnimationFeatureFlags;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
@@ -226,8 +223,7 @@ public interface CentralSurfacesDependenciesModule {
IDreamManager dreamManager,
KeyguardStateController keyguardStateController,
Lazy<AlternateBouncerInteractor> alternateBouncerInteractor,
- InteractionJankMonitor interactionJankMonitor,
- AnimationFeatureFlags animationFeatureFlags) {
+ InteractionJankMonitor interactionJankMonitor) {
DialogTransitionAnimator.Callback callback = new DialogTransitionAnimator.Callback() {
@Override
public boolean isDreaming() {
@@ -249,19 +245,6 @@ public interface CentralSurfacesDependenciesModule {
return alternateBouncerInteractor.get().canShowAlternateBouncerForFingerprint();
}
};
- return new DialogTransitionAnimator(
- mainExecutor, callback, interactionJankMonitor, animationFeatureFlags);
- }
-
- /** */
- @Provides
- @SysUISingleton
- static AnimationFeatureFlags provideAnimationFeatureFlags() {
- return new AnimationFeatureFlags() {
- @Override
- public boolean isPredictiveBackQsDialogAnim() {
- return predictiveBackAnimateDialogs();
- }
- };
+ return new DialogTransitionAnimator(mainExecutor, callback, interactionJankMonitor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 32de65be5b5b..d4d3cdf42fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -27,7 +27,6 @@ import com.android.systemui.statusbar.notification.collection.render.NotifStackC
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
import javax.inject.Inject
@@ -43,7 +42,8 @@ internal constructor(
private val groupExpansionManagerImpl: GroupExpansionManagerImpl,
private val renderListInteractor: RenderNotificationListInteractor,
private val activeNotificationsInteractor: ActiveNotificationsInteractor,
- private val sensitiveNotificationProtectionController: SensitiveNotificationProtectionController,
+ private val sensitiveNotificationProtectionController:
+ SensitiveNotificationProtectionController,
) : Coordinator {
override fun attach(pipeline: NotifPipeline) {
@@ -51,14 +51,11 @@ internal constructor(
groupExpansionManagerImpl.attach(pipeline)
}
+ // TODO: b/293167744 - Remove controller param.
private fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
traceSection("StackCoordinator.onAfterRenderList") {
val notifStats = calculateNotifStats(entries)
- if (FooterViewRefactor.isEnabled) {
- activeNotificationsInteractor.setNotifStats(notifStats)
- } else {
- controller.setNotifStats(notifStats)
- }
+ activeNotificationsInteractor.setNotifStats(notifStats)
renderListInteractor.setRenderedList(entries)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
index fbec6406e9d4..7e2361f24da9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
@@ -26,7 +26,6 @@ import com.android.systemui.shared.notifications.domain.interactor.NotificationS
import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterMessageViewModel
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.util.kotlin.FlowDumperImpl
@@ -35,7 +34,6 @@ import dagger.assisted.AssistedInject
import java.util.Locale
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
@@ -57,9 +55,7 @@ constructor(
dumpManager: DumpManager,
) : FlowDumperImpl(dumpManager) {
val areNotificationsHiddenInShade: Flow<Boolean> by lazy {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- flowOf(false)
- } else if (ModesEmptyShadeFix.isEnabled) {
+ if (ModesEmptyShadeFix.isEnabled) {
zenModeInteractor.areNotificationsHiddenInShade
.dumpWhileCollecting("areNotificationsHiddenInShade")
.flowOn(bgDispatcher)
@@ -70,15 +66,10 @@ constructor(
}
}
- val hasFilteredOutSeenNotifications: StateFlow<Boolean> by lazy {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- MutableStateFlow(false)
- } else {
- seenNotificationsInteractor.hasFilteredOutSeenNotifications.dumpValue(
- "hasFilteredOutSeenNotifications"
- )
- }
- }
+ val hasFilteredOutSeenNotifications: StateFlow<Boolean> =
+ seenNotificationsInteractor.hasFilteredOutSeenNotifications.dumpValue(
+ "hasFilteredOutSeenNotifications"
+ )
val text: Flow<String> by lazy {
if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt
deleted file mode 100644
index 7e6044eb6869..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt
+++ /dev/null
@@ -1,53 +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.statusbar.notification.footer.shared
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the FooterView refactor flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object FooterViewRefactor {
- /** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR
-
- /** A token used for dependency declaration */
- val token: FlagToken
- get() = FlagToken(FLAG_NAME, isEnabled)
-
- /** Is the refactor enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.notificationsFooterViewRefactor()
-
- /**
- * Called to ensure code is only run when the flag is enabled. This protects users from the
- * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
- * build to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun isUnexpectedlyInLegacyMode() =
- RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
- /**
- * Called to ensure code is only run when the flag is disabled. This will throw an exception if
- * the flag is enabled to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index d25889820629..a670f69df601 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -41,7 +41,6 @@ import androidx.annotation.NonNull;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
import com.android.systemui.statusbar.notification.row.FooterViewButton;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -63,16 +62,9 @@ public class FooterView extends StackScrollerDecorView {
private FooterViewButton mSettingsButton;
private FooterViewButton mHistoryButton;
private boolean mShouldBeHidden;
- private boolean mShowHistory;
- // String cache, for performance reasons.
- // Reading them from a Resources object can be quite slow sometimes.
- private String mManageNotificationText;
- private String mManageNotificationHistoryText;
// Footer label
private TextView mSeenNotifsFooterTextView;
- private String mSeenNotifsFilteredText;
- private Drawable mSeenNotifsFilteredIcon;
private @StringRes int mClearAllButtonTextId;
private @StringRes int mClearAllButtonDescriptionId;
@@ -159,8 +151,8 @@ public class FooterView extends StackScrollerDecorView {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
super.dump(pw, args);
DumpUtilsKt.withIncreasedIndent(pw, () -> {
+ // TODO: b/375010573 - update dumps for redesign
pw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility()));
- pw.println("manageButton showHistory: " + mShowHistory);
pw.println("manageButton visibility: "
+ DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
pw.println("dismissButton visibility: "
@@ -170,7 +162,6 @@ public class FooterView extends StackScrollerDecorView {
/** Set the text label for the "Clear all" button. */
public void setClearAllButtonText(@StringRes int textId) {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
if (mClearAllButtonTextId == textId) {
return; // nothing changed
}
@@ -187,9 +178,6 @@ public class FooterView extends StackScrollerDecorView {
/** Set the accessibility content description for the "Clear all" button. */
public void setClearAllButtonDescription(@StringRes int contentDescriptionId) {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- return;
- }
if (mClearAllButtonDescriptionId == contentDescriptionId) {
return; // nothing changed
}
@@ -207,7 +195,6 @@ public class FooterView extends StackScrollerDecorView {
/** Set the text label for the "Manage"/"History" button. */
public void setManageOrHistoryButtonText(@StringRes int textId) {
NotifRedesignFooter.assertInLegacyMode();
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
if (mManageOrHistoryButtonTextId == textId) {
return; // nothing changed
}
@@ -226,9 +213,6 @@ public class FooterView extends StackScrollerDecorView {
/** Set the accessibility content description for the "Clear all" button. */
public void setManageOrHistoryButtonDescription(@StringRes int contentDescriptionId) {
NotifRedesignFooter.assertInLegacyMode();
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- return;
- }
if (mManageOrHistoryButtonDescriptionId == contentDescriptionId) {
return; // nothing changed
}
@@ -247,7 +231,6 @@ public class FooterView extends StackScrollerDecorView {
/** Set the string for a message to be shown instead of the buttons. */
public void setMessageString(@StringRes int messageId) {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
if (mMessageStringId == messageId) {
return; // nothing changed
}
@@ -265,7 +248,6 @@ public class FooterView extends StackScrollerDecorView {
/** Set the icon to be shown before the message (see {@link #setMessageString(int)}). */
public void setMessageIcon(@DrawableRes int iconId) {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
if (mMessageIconId == iconId) {
return; // nothing changed
}
@@ -303,32 +285,17 @@ public class FooterView extends StackScrollerDecorView {
mManageOrHistoryButton = findViewById(R.id.manage_text);
}
mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
- if (!FooterViewRefactor.isEnabled()) {
- updateResources();
- }
updateContent();
updateColors();
}
/** Show a message instead of the footer buttons. */
public void setFooterLabelVisible(boolean isVisible) {
- // In the refactored code, hiding the buttons is handled in the FooterViewModel
- if (FooterViewRefactor.isEnabled()) {
- if (isVisible) {
- mSeenNotifsFooterTextView.setVisibility(View.VISIBLE);
- } else {
- mSeenNotifsFooterTextView.setVisibility(View.GONE);
- }
+ // Note: hiding the buttons is handled in the FooterViewModel
+ if (isVisible) {
+ mSeenNotifsFooterTextView.setVisibility(View.VISIBLE);
} else {
- if (isVisible) {
- mManageOrHistoryButton.setVisibility(View.GONE);
- mClearAllButton.setVisibility(View.GONE);
- mSeenNotifsFooterTextView.setVisibility(View.VISIBLE);
- } else {
- mManageOrHistoryButton.setVisibility(View.VISIBLE);
- mClearAllButton.setVisibility(View.VISIBLE);
- mSeenNotifsFooterTextView.setVisibility(View.GONE);
- }
+ mSeenNotifsFooterTextView.setVisibility(View.GONE);
}
}
@@ -359,10 +326,8 @@ public class FooterView extends StackScrollerDecorView {
/** Set onClickListener for the clear all (end) button. */
public void setClearAllButtonClickListener(OnClickListener listener) {
- if (FooterViewRefactor.isEnabled()) {
- if (mClearAllButtonClickListener == listener) return;
- mClearAllButtonClickListener = listener;
- }
+ if (mClearAllButtonClickListener == listener) return;
+ mClearAllButtonClickListener = listener;
mClearAllButton.setOnClickListener(listener);
}
@@ -379,62 +344,17 @@ public class FooterView extends StackScrollerDecorView {
|| touchY > mContent.getY() + mContent.getHeight();
}
- /** Show "History" instead of "Manage" on the start button. */
- public void showHistory(boolean showHistory) {
- FooterViewRefactor.assertInLegacyMode();
- if (mShowHistory == showHistory) {
- return;
- }
- mShowHistory = showHistory;
- updateContent();
- }
-
private void updateContent() {
- if (FooterViewRefactor.isEnabled()) {
- updateClearAllButtonText();
- updateClearAllButtonDescription();
-
- if (!NotifRedesignFooter.isEnabled()) {
- updateManageOrHistoryButtonText();
- updateManageOrHistoryButtonDescription();
- }
-
- updateMessageString();
- updateMessageIcon();
- } else {
- // NOTE: Prior to the refactor, `updateResources` set the class properties to the right
- // string values. It was always being called together with `updateContent`, which
- // deals with actually associating those string values with the correct views
- // (buttons or text).
- // In the new code, the resource IDs are being set in the view binder (through
- // setMessageString and similar setters). The setters themselves now deal with
- // updating both the resource IDs and the views where appropriate (as in, calling
- // `updateMessageString` when the resource ID changes). This eliminates the need for
- // `updateResources`, which will eventually be removed. There are, however, still
- // situations in which we want to update the views even if the resource IDs didn't
- // change, such as configuration changes.
- if (mShowHistory) {
- mManageOrHistoryButton.setText(mManageNotificationHistoryText);
- mManageOrHistoryButton.setContentDescription(mManageNotificationHistoryText);
- } else {
- mManageOrHistoryButton.setText(mManageNotificationText);
- mManageOrHistoryButton.setContentDescription(mManageNotificationText);
- }
-
- mClearAllButton.setText(R.string.clear_all_notifications_text);
- mClearAllButton.setContentDescription(
- mContext.getString(R.string.accessibility_clear_all));
+ updateClearAllButtonText();
+ updateClearAllButtonDescription();
- mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText);
- mSeenNotifsFooterTextView
- .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null);
+ if (!NotifRedesignFooter.isEnabled()) {
+ updateManageOrHistoryButtonText();
+ updateManageOrHistoryButtonDescription();
}
- }
- /** Whether the start button shows "History" (true) or "Manage" (false). */
- public boolean isHistoryShown() {
- FooterViewRefactor.assertInLegacyMode();
- return mShowHistory;
+ updateMessageString();
+ updateMessageIcon();
}
@Override
@@ -445,9 +365,6 @@ public class FooterView extends StackScrollerDecorView {
}
super.onConfigurationChanged(newConfig);
updateColors();
- if (!FooterViewRefactor.isEnabled()) {
- updateResources();
- }
updateContent();
}
@@ -502,18 +419,6 @@ public class FooterView extends StackScrollerDecorView {
}
}
- private void updateResources() {
- FooterViewRefactor.assertInLegacyMode();
- mManageNotificationText = getContext().getString(R.string.manage_notifications_text);
- mManageNotificationHistoryText = getContext()
- .getString(R.string.manage_notifications_history_text);
- int unlockIconSize = getResources()
- .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size);
- mSeenNotifsFilteredText = getContext().getString(R.string.unlock_to_see_notif_text);
- mSeenNotifsFilteredIcon = getContext().getDrawable(R.drawable.ic_friction_lock_closed);
- mSeenNotifsFilteredIcon.setBounds(0, 0, unlockIconSize, unlockIconSize);
- }
-
@Override
@NonNull
public ExpandableViewState createExpandableViewState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index e724935e3ef4..5696e9f0c5a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -27,7 +27,6 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter.S
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
@@ -144,6 +143,7 @@ class FooterViewModel(
)
}
+// TODO: b/293167744 - remove this, use new viewmodel style
@Module
object FooterViewModelModule {
@Provides
@@ -153,18 +153,13 @@ object FooterViewModelModule {
notificationSettingsInteractor: Provider<NotificationSettingsInteractor>,
seenNotificationsInteractor: Provider<SeenNotificationsInteractor>,
shadeInteractor: Provider<ShadeInteractor>,
- ): Optional<FooterViewModel> {
- return if (FooterViewRefactor.isEnabled) {
- Optional.of(
- FooterViewModel(
- activeNotificationsInteractor.get(),
- notificationSettingsInteractor.get(),
- seenNotificationsInteractor.get(),
- shadeInteractor.get(),
- )
+ ): Optional<FooterViewModel> =
+ Optional.of(
+ FooterViewModel(
+ activeNotificationsInteractor.get(),
+ notificationSettingsInteractor.get(),
+ seenNotificationsInteractor.get(),
+ shadeInteractor.get(),
)
- } else {
- Optional.empty()
- }
- }
+ )
}
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 071d23283c43..76591ac4e453 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
@@ -108,7 +108,6 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
@@ -703,9 +702,6 @@ public class NotificationStackScrollLayout
if (!ModesEmptyShadeFix.isEnabled()) {
inflateEmptyShadeView();
}
- if (!FooterViewRefactor.isEnabled()) {
- inflateFooterView();
- }
}
/**
@@ -741,22 +737,12 @@ public class NotificationStackScrollLayout
}
void reinflateViews() {
- if (!FooterViewRefactor.isEnabled()) {
- inflateFooterView();
- updateFooter();
- }
if (!ModesEmptyShadeFix.isEnabled()) {
inflateEmptyShadeView();
}
mSectionsManager.reinflateViews();
}
- public void setIsRemoteInputActive(boolean isActive) {
- FooterViewRefactor.assertInLegacyMode();
- mIsRemoteInputActive = isActive;
- updateFooter();
- }
-
void sendRemoteInputRowBottomBound(Float bottom) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
if (bottom != null) {
@@ -766,43 +752,6 @@ public class NotificationStackScrollLayout
mScrollViewFields.sendRemoteInputRowBottomBound(bottom);
}
- /** Setter for filtered notifs, to be removed with the FooterViewRefactor flag. */
- public void setHasFilteredOutSeenNotifications(boolean hasFilteredOutSeenNotifications) {
- FooterViewRefactor.assertInLegacyMode();
- mHasFilteredOutSeenNotifications = hasFilteredOutSeenNotifications;
- }
-
- @VisibleForTesting
- public void updateFooter() {
- FooterViewRefactor.assertInLegacyMode();
- if (mFooterView == null || mController == null) {
- return;
- }
- final boolean showHistory = mController.isHistoryEnabled();
- final boolean showDismissView = shouldShowDismissView();
-
- updateFooterView(shouldShowFooterView(showDismissView)/* visible */,
- showDismissView /* showDismissView */,
- showHistory/* showHistory */);
- }
-
- private boolean shouldShowDismissView() {
- FooterViewRefactor.assertInLegacyMode();
- return mController.hasActiveClearableNotifications(ROWS_ALL);
- }
-
- private boolean shouldShowFooterView(boolean showDismissView) {
- FooterViewRefactor.assertInLegacyMode();
- return (showDismissView || mController.getVisibleNotificationCount() > 0)
- && mIsCurrentUserSetup // see: b/193149550
- && !onKeyguard()
- && mUpcomingStatusBarState != StatusBarState.KEYGUARD
- // quick settings don't affect notifications when not in full screen
- && (getQsExpansionFraction() != 1 || !mQsFullScreen)
- && !mScreenOffAnimationController.shouldHideNotificationsFooter()
- && !mIsRemoteInputActive;
- }
-
void updateBgColor() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
@@ -1861,9 +1810,6 @@ public class NotificationStackScrollLayout
*/
private float getAppearEndPosition() {
SceneContainerFlag.assertInLegacyMode();
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- return getAppearEndPositionLegacy();
- }
int appearPosition = mAmbientState.getStackTopMargin();
if (mEmptyShadeView.getVisibility() == GONE) {
@@ -1883,32 +1829,6 @@ public class NotificationStackScrollLayout
return appearPosition + (onKeyguard() ? getTopPadding() : getIntrinsicPadding());
}
- /**
- * The version of {@code getAppearEndPosition} that uses the notif count. The view shouldn't
- * need to know about that, so we want to phase this out with the footer view refactor.
- */
- private float getAppearEndPositionLegacy() {
- FooterViewRefactor.assertInLegacyMode();
-
- int appearPosition = mAmbientState.getStackTopMargin();
- int visibleNotifCount = mController.getVisibleNotificationCount();
- if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) {
- if (isHeadsUpTransition()
- || (mInHeadsUpPinnedMode && !mAmbientState.isDozing())) {
- if (mShelf.getVisibility() != GONE && visibleNotifCount > 1) {
- appearPosition += mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
- }
- appearPosition += getTopHeadsUpPinnedHeight()
- + getPositionInLinearLayout(mAmbientState.getTrackedHeadsUpRow());
- } else if (mShelf.getVisibility() != GONE) {
- appearPosition += mShelf.getIntrinsicHeight();
- }
- } else {
- appearPosition = mEmptyShadeView.getHeight();
- }
- return appearPosition + (onKeyguard() ? getTopPadding() : getIntrinsicPadding());
- }
-
private boolean isHeadsUpTransition() {
return mAmbientState.getTrackedHeadsUpRow() != null;
}
@@ -1928,8 +1848,7 @@ public class NotificationStackScrollLayout
// This can't use expansion fraction as that goes only from 0 to 1. Also when
// appear fraction for HUN is 0, expansion fraction will be already around 0.2-0.3
// and that makes translation jump immediately.
- float appearEndPosition = FooterViewRefactor.isEnabled() ? getAppearEndPosition()
- : getAppearEndPositionLegacy();
+ float appearEndPosition = getAppearEndPosition();
float appearStartPosition = getAppearStartPosition();
float hunAppearFraction = (height - appearStartPosition)
/ (appearEndPosition - appearStartPosition);
@@ -4848,15 +4767,6 @@ public class NotificationStackScrollLayout
}
}
- /**
- * Returns whether or not a History button is shown in the footer. If there is no footer, then
- * this will return false.
- **/
- public boolean isHistoryShown() {
- FooterViewRefactor.assertInLegacyMode();
- return mFooterView != null && mFooterView.isHistoryShown();
- }
-
/** Bind the {@link FooterView} to the NSSL. */
public void setFooterView(@NonNull FooterView footerView) {
int index = -1;
@@ -4866,18 +4776,6 @@ public class NotificationStackScrollLayout
}
mFooterView = footerView;
addView(mFooterView, index);
- if (!FooterViewRefactor.isEnabled()) {
- if (mManageButtonClickListener != null) {
- mFooterView.setManageButtonClickListener(mManageButtonClickListener);
- }
- mFooterView.setClearAllButtonClickListener(v -> {
- if (mFooterClearAllListener != null) {
- mFooterClearAllListener.onClearAll();
- }
- clearNotifications(ROWS_ALL, true /* closeShade */);
- footerView.setClearAllButtonVisible(false /* visible */, true /* animate */);
- });
- }
}
public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
@@ -4890,13 +4788,6 @@ public class NotificationStackScrollLayout
addView(mEmptyShadeView, index);
}
- /** Legacy version, should be removed with the footer refactor flag. */
- public void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) {
- FooterViewRefactor.assertInLegacyMode();
- updateEmptyShadeView(visible, areNotificationsHiddenInShade,
- mHasFilteredOutSeenNotifications);
- }
-
/** Trigger an update for the empty shade resources and visibility. */
public void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade,
boolean hasFilteredOutSeenNotifications) {
@@ -4949,18 +4840,6 @@ public class NotificationStackScrollLayout
return mEmptyShadeView.isVisible();
}
- public void updateFooterView(boolean visible, boolean showDismissView, boolean showHistory) {
- FooterViewRefactor.assertInLegacyMode();
- if (mFooterView == null || mNotificationStackSizeCalculator == null) {
- return;
- }
- boolean animate = mIsExpanded && mAnimationsEnabled;
- mFooterView.setVisible(visible, animate);
- mFooterView.showHistory(showHistory);
- mFooterView.setClearAllButtonVisible(showDismissView, animate);
- mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
- }
-
@VisibleForTesting
public void setClearAllInProgress(boolean clearAllInProgress) {
mClearAllInProgress = clearAllInProgress;
@@ -5244,10 +5123,8 @@ public class NotificationStackScrollLayout
public void setQsFullScreen(boolean qsFullScreen) {
SceneContainerFlag.assertInLegacyMode();
- if (FooterViewRefactor.isEnabled()) {
- if (qsFullScreen == mQsFullScreen) {
- return; // no change
- }
+ if (qsFullScreen == mQsFullScreen) {
+ return; // no change
}
mQsFullScreen = qsFullScreen;
updateAlgorithmLayoutMinHeight();
@@ -5266,8 +5143,6 @@ public class NotificationStackScrollLayout
public void setQsExpansionFraction(float qsExpansionFraction) {
SceneContainerFlag.assertInLegacyMode();
- boolean footerAffected = getQsExpansionFraction() != qsExpansionFraction
- && (getQsExpansionFraction() == 1 || qsExpansionFraction == 1);
mQsExpansionFraction = qsExpansionFraction;
updateUseRoundedRectClipping();
@@ -5276,9 +5151,6 @@ public class NotificationStackScrollLayout
if (getOwnScrollY() > 0) {
setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, getQsExpansionFraction()));
}
- if (!FooterViewRefactor.isEnabled() && footerAffected) {
- updateFooter();
- }
}
@VisibleForTesting
@@ -5456,14 +5328,6 @@ public class NotificationStackScrollLayout
requestChildrenUpdate();
}
- void setUpcomingStatusBarState(int upcomingStatusBarState) {
- FooterViewRefactor.assertInLegacyMode();
- mUpcomingStatusBarState = upcomingStatusBarState;
- if (mUpcomingStatusBarState != mStatusBarState) {
- updateFooter();
- }
- }
-
void onStatePostChange(boolean fromShadeLocked) {
boolean onKeyguard = onKeyguard();
@@ -5472,9 +5336,6 @@ public class NotificationStackScrollLayout
}
setExpandingEnabled(!onKeyguard);
- if (!FooterViewRefactor.isEnabled()) {
- updateFooter();
- }
requestChildrenUpdate();
onUpdateRowStates();
updateVisibility();
@@ -5490,8 +5351,7 @@ public class NotificationStackScrollLayout
if (mEmptyShadeView == null || mEmptyShadeView.getVisibility() == GONE) {
return getMinExpansionHeight();
} else {
- return FooterViewRefactor.isEnabled() ? getAppearEndPosition()
- : getAppearEndPositionLegacy();
+ return getAppearEndPosition();
}
}
@@ -5583,12 +5443,6 @@ public class NotificationStackScrollLayout
for (int i = 0; i < childCount; i++) {
ExpandableView child = getChildAtIndex(i);
child.dump(pw, args);
- if (!FooterViewRefactor.isEnabled()) {
- if (child instanceof FooterView) {
- DumpUtilsKt.withIncreasedIndent(pw,
- () -> dumpFooterViewVisibility(pw));
- }
- }
pw.println();
}
int transientViewCount = getTransientViewCount();
@@ -5615,45 +5469,6 @@ public class NotificationStackScrollLayout
pw.append(" bottomRadius=").println(mBgCornerRadii[4]);
}
- private void dumpFooterViewVisibility(IndentingPrintWriter pw) {
- FooterViewRefactor.assertInLegacyMode();
- final boolean showDismissView = shouldShowDismissView();
-
- pw.println("showFooterView: " + shouldShowFooterView(showDismissView));
- DumpUtilsKt.withIncreasedIndent(
- pw,
- () -> {
- pw.println("showDismissView: " + showDismissView);
- DumpUtilsKt.withIncreasedIndent(
- pw,
- () -> {
- pw.println(
- "hasActiveClearableNotifications: "
- + mController.hasActiveClearableNotifications(
- ROWS_ALL));
- });
- pw.println();
- pw.println("showHistory: " + mController.isHistoryEnabled());
- pw.println();
- pw.println(
- "visibleNotificationCount: "
- + mController.getVisibleNotificationCount());
- pw.println("mIsCurrentUserSetup: " + mIsCurrentUserSetup);
- pw.println("onKeyguard: " + onKeyguard());
- pw.println("mUpcomingStatusBarState: " + mUpcomingStatusBarState);
- if (!SceneContainerFlag.isEnabled()) {
- pw.println("QsExpansionFraction: " + getQsExpansionFraction());
- }
- pw.println("mQsFullScreen: " + mQsFullScreen);
- pw.println(
- "mScreenOffAnimationController"
- + ".shouldHideNotificationsFooter: "
- + mScreenOffAnimationController
- .shouldHideNotificationsFooter());
- pw.println("mIsRemoteInputActive: " + mIsRemoteInputActive);
- });
- }
-
public boolean isFullyHidden() {
return mAmbientState.isFullyHidden();
}
@@ -5764,14 +5579,6 @@ public class NotificationStackScrollLayout
clearNotifications(ROWS_GENTLE, closeShade, hideSilentSection);
}
- /** Legacy version of clearNotifications below. Uses the old data source for notif stats. */
- void clearNotifications(@SelectedRows int selection, boolean closeShade) {
- FooterViewRefactor.assertInLegacyMode();
- final boolean hideSilentSection = !mController.hasNotifications(
- ROWS_GENTLE, false /* clearable */);
- clearNotifications(selection, closeShade, hideSilentSection);
- }
-
/**
* Collects a list of visible rows, and animates them away in a staggered fashion as if they
* were dismissed. Notifications are dismissed in the backend via onClearAllAnimationsEnd.
@@ -5826,25 +5633,6 @@ public class NotificationStackScrollLayout
return canChildBeCleared(row) && matchesSelection(row, selection);
}
- /**
- * Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked.
- */
- public void setManageButtonClickListener(@Nullable OnClickListener listener) {
- FooterViewRefactor.assertInLegacyMode();
- mManageButtonClickListener = listener;
- if (mFooterView != null) {
- mFooterView.setManageButtonClickListener(mManageButtonClickListener);
- }
- }
-
- @VisibleForTesting
- protected void inflateFooterView() {
- FooterViewRefactor.assertInLegacyMode();
- FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_notification_footer, this, false);
- setFooterView(footerView);
- }
-
private void inflateEmptyShadeView() {
ModesEmptyShadeFix.assertInLegacyMode();
@@ -6091,11 +5879,6 @@ public class NotificationStackScrollLayout
mHighPriorityBeforeSpeedBump = highPriorityBeforeSpeedBump;
}
- void setFooterClearAllListener(FooterClearAllListener listener) {
- FooterViewRefactor.assertInLegacyMode();
- mFooterClearAllListener = listener;
- }
-
void setClearAllFinishedWhilePanelExpandedRunnable(Runnable runnable) {
mClearAllFinishedWhilePanelExpandedRunnable = runnable;
}
@@ -6394,17 +6177,6 @@ public class NotificationStackScrollLayout
}
/**
- * Sets whether the current user is set up, which is required to show the footer (b/193149550)
- */
- public void setCurrentUserSetup(boolean isCurrentUserSetup) {
- FooterViewRefactor.assertInLegacyMode();
- if (mIsCurrentUserSetup != isCurrentUserSetup) {
- mIsCurrentUserSetup = isCurrentUserSetup;
- updateFooter();
- }
- }
-
- /**
* Sets a {@link StackStateLogger} which is notified as the {@link StackStateAnimator} updates
* the views.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index a33a9ed2df75..b892bebb3120 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -29,11 +29,8 @@ import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.animation.ObjectAnimator;
import android.content.res.Configuration;
@@ -64,14 +61,10 @@ import com.android.internal.view.OneShotPreDrawListener;
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -92,18 +85,13 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpNotificationViewControllerEmptyImpl;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.EntryWithDismissStats;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -115,14 +103,15 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
+import com.android.systemui.statusbar.notification.collection.render.DefaultNotifStackController;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
-import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
-import com.android.systemui.statusbar.notification.dagger.SilentHeader;
-import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpNotificationViewControllerEmptyImpl;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController;
+import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -137,13 +126,8 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
-import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
-import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.Compile;
import com.android.systemui.util.settings.SecureSettings;
@@ -179,10 +163,8 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private HeadsUpTouchHelper mHeadsUpTouchHelper;
private final NotificationRoundnessManager mNotificationRoundnessManager;
private final TunerService mTunerService;
- private final DeviceProvisionedController mDeviceProvisionedController;
private final DynamicPrivacyController mDynamicPrivacyController;
private final ConfigurationController mConfigurationController;
- private final ZenModeController mZenModeController;
private final MetricsLogger mMetricsLogger;
private final ColorUpdateLogger mColorUpdateLogger;
@@ -193,7 +175,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private final NotifPipeline mNotifPipeline;
private final NotifCollection mNotifCollection;
private final UiEventLogger mUiEventLogger;
- private final NotificationRemoteInputManager mRemoteInputManager;
private final VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
private final ShadeController mShadeController;
private final Provider<WindowRootView> mWindowRootView;
@@ -201,9 +182,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mKeyguardBypassController;
private final PowerInteractor mPowerInteractor;
- private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final NotificationLockscreenUserManager mLockscreenUserManager;
- private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final InteractionJankMonitor mJankMonitor;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@@ -211,8 +190,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private final NotificationStackScrollLogger mLogger;
private final GroupExpansionManager mGroupExpansionManager;
- private final SeenNotificationsInteractor mSeenNotificationsInteractor;
- private final KeyguardTransitionRepository mKeyguardTransitionRepo;
private NotificationStackScrollLayout mView;
private TouchHandler mTouchHandler;
private NotificationSwipeHelper mSwipeHelper;
@@ -220,7 +197,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private Boolean mHistoryEnabled;
private int mBarState;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
- private boolean mIsInTransitionToAod = false;
private final NotificationTargetsHelper mNotificationTargetsHelper;
private final SecureSettings mSecureSettings;
@@ -235,11 +211,9 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private final NotificationListContainerImpl mNotificationListContainer =
new NotificationListContainerImpl();
+ // TODO: b/293167744 - Remove this.
private final NotifStackController mNotifStackController =
- new NotifStackControllerImpl();
-
- @Nullable
- private NotificationActivityStarter mNotificationActivityStarter;
+ new DefaultNotifStackController();
@VisibleForTesting
final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
@@ -248,9 +222,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
public void onViewAttachedToWindow(View v) {
mColorUpdateLogger.logTriggerEvent("NSSLC.onViewAttachedToWindow()");
mConfigurationController.addCallback(mConfigurationListener);
- if (!FooterViewRefactor.isEnabled()) {
- mZenModeController.addCallback(mZenModeControllerCallback);
- }
final int newBarState = mStatusBarStateController.getState();
if (newBarState != mBarState) {
mStateListener.onStateChanged(newBarState);
@@ -264,9 +235,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
public void onViewDetachedFromWindow(View v) {
mColorUpdateLogger.logTriggerEvent("NSSLC.onViewDetachedFromWindow()");
mConfigurationController.removeCallback(mConfigurationListener);
- if (!FooterViewRefactor.isEnabled()) {
- mZenModeController.removeCallback(mZenModeControllerCallback);
- }
mStatusBarStateController.removeCallback(mStateListener);
}
};
@@ -287,28 +255,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
@Nullable
private ObjectAnimator mHideAlphaAnimator = null;
- private final DeviceProvisionedListener mDeviceProvisionedListener =
- new DeviceProvisionedListener() {
- @Override
- public void onDeviceProvisionedChanged() {
- updateCurrentUserIsSetup();
- }
-
- @Override
- public void onUserSwitched() {
- updateCurrentUserIsSetup();
- }
-
- @Override
- public void onUserSetupChanged() {
- updateCurrentUserIsSetup();
- }
-
- private void updateCurrentUserIsSetup() {
- mView.setCurrentUserSetup(mDeviceProvisionedController.isCurrentUserSetup());
- }
- };
-
private final Runnable mSensitiveStateChangedListener = new Runnable() {
@Override
public void run() {
@@ -318,20 +264,10 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
};
- private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> {
- if (!FooterViewRefactor.isEnabled()) {
- // Let's update the footer once the notifications have been updated (in the next frame)
- mView.post(this::updateFooter);
- }
- };
-
@VisibleForTesting
final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onDensityOrFontScaleChanged() {
- if (!FooterViewRefactor.isEnabled()) {
- updateShowEmptyShadeView();
- }
mView.reinflateViews();
}
@@ -351,10 +287,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mView.updateBgColor();
mView.updateDecorViews();
mView.reinflateViews();
- if (!FooterViewRefactor.isEnabled()) {
- updateShowEmptyShadeView();
- updateFooter();
- }
}
@Override
@@ -363,7 +295,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
};
- private NotifStats mNotifStats = NotifStats.getEmpty();
private float mMaxAlphaForKeyguard = 1.0f;
private String mMaxAlphaForKeyguardSource = "constructor";
private float mMaxAlphaForUnhide = 1.0f;
@@ -401,19 +332,9 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
@Override
- public void onUpcomingStateChanged(int newState) {
- if (!FooterViewRefactor.isEnabled()) {
- mView.setUpcomingStatusBarState(newState);
- }
- }
-
- @Override
public void onStatePostChange() {
updateSensitivenessWithAnimation(mStatusBarStateController.goingToFullShade());
mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
- if (!FooterViewRefactor.isEnabled()) {
- updateImportantForAccessibility();
- }
}
};
@@ -422,9 +343,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
public void onUserChanged(int userId) {
updateSensitivenessWithAnimation(false);
mHistoryEnabled = null;
- if (!FooterViewRefactor.isEnabled()) {
- updateFooter();
- }
}
};
@@ -656,7 +574,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
== null) {
mHeadsUpManager.removeNotification(
row.getEntry().getSbn().getKey(),
- /* removeImmediately= */ true ,
+ /* removeImmediately= */ true,
/* reason= */ "onChildSnappedBack"
);
}
@@ -714,14 +632,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
};
- private final ZenModeController.Callback mZenModeControllerCallback =
- new ZenModeController.Callback() {
- @Override
- public void onZenChanged(int zen) {
- updateShowEmptyShadeView();
- }
- };
-
@Inject
public NotificationStackScrollLayoutController(
NotificationStackScrollLayout view,
@@ -734,16 +644,12 @@ public class NotificationStackScrollLayoutController implements Dumpable {
Provider<IStatusBarService> statusBarService,
NotificationRoundnessManager notificationRoundnessManager,
TunerService tunerService,
- DeviceProvisionedController deviceProvisionedController,
DynamicPrivacyController dynamicPrivacyController,
@ShadeDisplayAware ConfigurationController configurationController,
SysuiStatusBarStateController statusBarStateController,
KeyguardMediaController keyguardMediaController,
KeyguardBypassController keyguardBypassController,
PowerInteractor powerInteractor,
- PrimaryBouncerInteractor primaryBouncerInteractor,
- KeyguardTransitionRepository keyguardTransitionRepo,
- ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
MetricsLogger metricsLogger,
ColorUpdateLogger colorUpdateLogger,
@@ -752,14 +658,11 @@ public class NotificationStackScrollLayoutController implements Dumpable {
FalsingManager falsingManager,
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
GroupExpansionManager groupManager,
- @SilentHeader SectionHeaderController silentHeaderController,
NotifPipeline notifPipeline,
NotifCollection notifCollection,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
UiEventLogger uiEventLogger,
- NotificationRemoteInputManager remoteInputManager,
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
- SeenNotificationsInteractor seenNotificationsInteractor,
NotificationListViewBinder viewBinder,
ShadeController shadeController,
Provider<WindowRootView> windowRootView,
@@ -775,7 +678,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
WallpaperInteractor wallpaperInteractor) {
mView = view;
- mKeyguardTransitionRepo = keyguardTransitionRepo;
mViewBinder = viewBinder;
mStackStateLogger = stackLogger;
mLogger = logger;
@@ -795,15 +697,12 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
mNotificationRoundnessManager = notificationRoundnessManager;
mTunerService = tunerService;
- mDeviceProvisionedController = deviceProvisionedController;
mDynamicPrivacyController = dynamicPrivacyController;
mConfigurationController = configurationController;
mStatusBarStateController = statusBarStateController;
mKeyguardMediaController = keyguardMediaController;
mKeyguardBypassController = keyguardBypassController;
mPowerInteractor = powerInteractor;
- mPrimaryBouncerInteractor = primaryBouncerInteractor;
- mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
mColorUpdateLogger = colorUpdateLogger;
@@ -815,13 +714,10 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mJankMonitor = jankMonitor;
mNotificationStackSizeCalculator = notificationStackSizeCalculator;
mGroupExpansionManager = groupManager;
- mSilentHeaderController = silentHeaderController;
mNotifPipeline = notifPipeline;
mNotifCollection = notifCollection;
mUiEventLogger = uiEventLogger;
- mRemoteInputManager = remoteInputManager;
mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
- mSeenNotificationsInteractor = seenNotificationsInteractor;
mShadeController = shadeController;
mWindowRootView = windowRootView;
mNotificationTargetsHelper = notificationTargetsHelper;
@@ -850,18 +746,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mView.setClearAllAnimationListener(this::onAnimationEnd);
mView.setClearAllListener((selection) -> mUiEventLogger.log(
NotificationPanelEvent.fromSelection(selection)));
- if (!FooterViewRefactor.isEnabled()) {
- mView.setFooterClearAllListener(() ->
- mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
- mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
- mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
- @Override
- public void onRemoteInputActive(boolean active) {
- mView.setIsRemoteInputActive(active);
- }
- });
- }
- mView.setClearAllFinishedWhilePanelExpandedRunnable(()-> {
+ mView.setClearAllFinishedWhilePanelExpandedRunnable(() -> {
final Runnable doCollapseRunnable = () ->
mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
mView.postDelayed(doCollapseRunnable, /* delayMillis = */ DELAY_BEFORE_SHADE_CLOSE);
@@ -889,19 +774,11 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled());
mKeyguardBypassController
.registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled);
- if (!FooterViewRefactor.isEnabled()) {
- mView.setManageButtonClickListener(v -> {
- if (mNotificationActivityStarter != null) {
- mNotificationActivityStarter.startHistoryIntent(v, mView.isHistoryShown());
- }
- });
- }
if (!SceneContainerFlag.isEnabled()) {
mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
}
mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
- mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
mLockscreenShadeTransitionController.setStackScroller(this);
@@ -914,9 +791,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
switch (key) {
case Settings.Secure.NOTIFICATION_HISTORY_ENABLED:
mHistoryEnabled = null; // invalidate
- if (!FooterViewRefactor.isEnabled()) {
- updateFooter();
- }
break;
case HIGH_PRIORITY:
mView.setHighPriorityBeforeSpeedBump("1".equals(newValue));
@@ -938,12 +812,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
return kotlin.Unit.INSTANCE;
});
- if (!FooterViewRefactor.isEnabled()) {
- // attach callback, and then call it to update mView immediately
- mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
- mDeviceProvisionedListener.onDeviceProvisionedChanged();
- }
-
if (screenshareNotificationHiding()) {
mSensitiveNotificationProtectionController
.registerSensitiveStateListener(mSensitiveStateChangedListener);
@@ -953,20 +821,12 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
}
mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
- if (!FooterViewRefactor.isEnabled()) {
- mSilentHeaderController.setOnClearSectionClickListener(v -> clearSilentNotifications());
- }
mGroupExpansionManager.registerGroupExpansionChangeListener(
(changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
mViewBinder.bindWhileAttached(mView, this);
- if (!FooterViewRefactor.isEnabled()) {
- collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
- this::onKeyguardTransitionChanged);
- }
-
mView.setWallpaperInteractor(mWallpaperInteractor);
}
@@ -1168,11 +1028,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
return mView != null && mView.isAddOrRemoveAnimationPending();
}
- public int getVisibleNotificationCount() {
- FooterViewRefactor.assertInLegacyMode();
- return mNotifStats.getNumActiveNotifs();
- }
-
public boolean isHistoryEnabled() {
Boolean historyEnabled = mHistoryEnabled;
if (historyEnabled == null) {
@@ -1284,9 +1139,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
public void setQsFullScreen(boolean fullScreen) {
mView.setQsFullScreen(fullScreen);
- if (!FooterViewRefactor.isEnabled()) {
- updateShowEmptyShadeView();
- }
}
public void setScrollingEnabled(boolean enabled) {
@@ -1464,64 +1316,12 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
/**
- * Set the visibility of the view, and propagate it to specific children.
+ * Set the visibility of the view.
*
* @param visible either the view is visible or not.
*/
public void updateVisibility(boolean visible) {
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-
- // Refactor note: the empty shade's visibility doesn't seem to actually depend on the
- // parent visibility (so this update seemingly doesn't do anything). Therefore, this is not
- // modeled in the refactored code.
- if (!FooterViewRefactor.isEnabled() && mView.getVisibility() == View.VISIBLE) {
- // Synchronize EmptyShadeView visibility with the parent container.
- updateShowEmptyShadeView();
- updateImportantForAccessibility();
- }
- }
-
- /**
- * Update whether we should show the empty shade view ("no notifications" in the shade).
- * <p>
- * When in split mode, notifications are always visible regardless of the state of the
- * QuickSettings panel. That being the case, empty view is always shown if the other conditions
- * are true.
- */
- public void updateShowEmptyShadeView() {
- FooterViewRefactor.assertInLegacyMode();
-
- Trace.beginSection("NSSLC.updateShowEmptyShadeView");
-
- final boolean shouldShow = getVisibleNotificationCount() == 0
- && !mView.isQsFullScreen()
- // Hide empty shade view when in transition to AOD.
- // That avoids "No Notifications" to blink when transitioning to AOD.
- // For more details, see: b/228790482
- && !mIsInTransitionToAod
- // Don't show any notification content if the bouncer is showing. See b/267060171.
- && !mPrimaryBouncerInteractor.isBouncerShowing();
-
- mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
-
- Trace.endSection();
- }
-
- /**
- * Update the importantForAccessibility of NotificationStackScrollLayout.
- * <p>
- * We want the NSSL to be unimportant for accessibility when there's no
- * notifications in it while the device is on lock screen, to avoid unlablel NSSL view.
- * Otherwise, we want it to be important for accessibility to enable accessibility
- * auto-scrolling in NSSL.
- */
- public void updateImportantForAccessibility() {
- FooterViewRefactor.assertInLegacyMode();
- if (getVisibleNotificationCount() == 0 && mView.onKeyguard()) {
- mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- } else {
- mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
}
public boolean isShowingEmptyShadeView() {
@@ -1577,34 +1377,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mView.setPulsing(pulsing, animatePulse);
}
- /**
- * Return whether there are any clearable notifications
- */
- public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
- FooterViewRefactor.assertInLegacyMode();
- return hasNotifications(selection, true /* clearable */);
- }
-
- public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
- FooterViewRefactor.assertInLegacyMode();
- boolean hasAlertingMatchingClearable = isClearable
- ? mNotifStats.getHasClearableAlertingNotifs()
- : mNotifStats.getHasNonClearableAlertingNotifs();
- boolean hasSilentMatchingClearable = isClearable
- ? mNotifStats.getHasClearableSilentNotifs()
- : mNotifStats.getHasNonClearableSilentNotifs();
- switch (selection) {
- case ROWS_GENTLE:
- return hasSilentMatchingClearable;
- case ROWS_HIGH_PRIORITY:
- return hasAlertingMatchingClearable;
- case ROWS_ALL:
- return hasSilentMatchingClearable || hasAlertingMatchingClearable;
- default:
- throw new IllegalStateException("Bad selection: " + selection);
- }
- }
-
/** Sets whether the NSSL is displayed over the unoccluded Lockscreen. */
public void setOnLockscreen(boolean isOnLockscreen) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
@@ -1637,9 +1409,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
entry.notifyHeightChanged(true /* needsAnimation */);
- if (!FooterViewRefactor.isEnabled()) {
- updateFooter();
- }
}
public void lockScrollTo(NotificationEntry entry) {
@@ -1662,13 +1431,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
};
}
- public void updateFooter() {
- FooterViewRefactor.assertInLegacyMode();
- Trace.beginSection("NSSLC.updateFooter");
- mView.updateFooter();
- Trace.endSection();
- }
-
public void onUpdateRowStates() {
mView.onUpdateRowStates();
}
@@ -1695,18 +1457,10 @@ public class NotificationStackScrollLayoutController implements Dumpable {
return mView.getTransientViewCount();
}
- public View getTransientView(int i) {
- return mView.getTransientView(i);
- }
-
public NotificationStackScrollLayout getView() {
return mView;
}
- public float calculateGapHeight(ExpandableView previousView, ExpandableView child, int count) {
- return mView.calculateGapHeight(previousView, child, count);
- }
-
NotificationRoundnessManager getNotificationRoundnessManager() {
return mNotificationRoundnessManager;
}
@@ -1772,13 +1526,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
return NotificationSwipeHelper.isTouchInView(event, view);
}
- public void clearSilentNotifications() {
- FooterViewRefactor.assertInLegacyMode();
- // Leave the shade open if there will be other notifs left over to clear
- final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
- mView.clearNotifications(ROWS_GENTLE, closeShade);
- }
-
private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
@SelectedRows int selectedRows) {
if (selectedRows == ROWS_ALL) {
@@ -1880,10 +1627,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mView.animateNextTopPaddingChange();
}
- public void setNotificationActivityStarter(NotificationActivityStarter activityStarter) {
- mNotificationActivityStarter = activityStarter;
- }
-
public NotificationTargetsHelper getNotificationTargetsHelper() {
return mNotificationTargetsHelper;
}
@@ -1898,18 +1641,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
@VisibleForTesting
- void onKeyguardTransitionChanged(TransitionStep transitionStep) {
- FooterViewRefactor.assertInLegacyMode();
- boolean isTransitionToAod = transitionStep.getTo().equals(KeyguardState.AOD)
- && (transitionStep.getFrom().equals(KeyguardState.GONE)
- || transitionStep.getFrom().equals(KeyguardState.OCCLUDED));
- if (mIsInTransitionToAod != isTransitionToAod) {
- mIsInTransitionToAod = isTransitionToAod;
- updateShowEmptyShadeView();
- }
- }
-
- @VisibleForTesting
TouchHandler getTouchHandler() {
return mTouchHandler;
}
@@ -2288,22 +2019,4 @@ public class NotificationStackScrollLayoutController implements Dumpable {
&& !mSwipeHelper.isSwiping();
}
}
-
- private class NotifStackControllerImpl implements NotifStackController {
- @Override
- public void setNotifStats(@NonNull NotifStats notifStats) {
- FooterViewRefactor.assertInLegacyMode();
- mNotifStats = notifStats;
-
- if (!FooterViewRefactor.isEnabled()) {
- mView.setHasFilteredOutSeenNotifications(
- mSeenNotificationsInteractor
- .getHasFilteredOutSeenNotifications().getValue());
-
- updateFooter();
- updateShowEmptyShadeView();
- updateImportantForAccessibility();
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 1653029dc994..06b989aaab57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -35,7 +35,6 @@ import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -463,26 +462,23 @@ public class StackScrollAlgorithm {
if (v == ambientState.getShelf()) {
continue;
}
- if (FooterViewRefactor.isEnabled()) {
- if (v instanceof EmptyShadeView) {
- emptyShadeVisible = true;
- }
- if (v instanceof FooterView footerView) {
- if (emptyShadeVisible || notGoneIndex == 0) {
- // if the empty shade is visible or the footer is the first visible
- // view, we're in a transitory state so let's leave the footer alone.
- if (Flags.notificationsFooterVisibilityFix()
- && !SceneContainerFlag.isEnabled()) {
- // ...except for the hidden state, to prevent it from flashing on
- // the screen (this piece is copied from updateChild, and is not
- // necessary in flexiglass).
- if (footerView.shouldBeHidden()
- || !ambientState.isShadeExpanded()) {
- footerView.getViewState().hidden = true;
- }
+ if (v instanceof EmptyShadeView) {
+ emptyShadeVisible = true;
+ }
+ if (v instanceof FooterView footerView) {
+ if (emptyShadeVisible || notGoneIndex == 0) {
+ // if the empty shade is visible or the footer is the first visible
+ // view, we're in a transitory state so let's leave the footer alone.
+ if (Flags.notificationsFooterVisibilityFix()
+ && !SceneContainerFlag.isEnabled()) {
+ // ...except for the hidden state, to prevent it from flashing on
+ // the screen (this piece is copied from updateChild, and is not
+ // necessary in flexiglass).
+ if (footerView.shouldBeHidden() || !ambientState.isShadeExpanded()) {
+ footerView.getViewState().hidden = true;
}
- continue;
}
+ continue;
}
}
@@ -699,44 +695,28 @@ public class StackScrollAlgorithm {
viewEnd, /* hunMax */ ambientState.getMaxHeadsUpTranslation()
);
if (view instanceof FooterView) {
- if (FooterViewRefactor.isEnabled()) {
- if (SceneContainerFlag.isEnabled()) {
- final float footerEnd =
- stackTop + viewState.getYTranslation() + view.getIntrinsicHeight();
- final boolean noSpaceForFooter = footerEnd > ambientState.getStackCutoff();
- ((FooterView.FooterViewState) viewState).hideContent =
- noSpaceForFooter || (ambientState.isClearAllInProgress()
- && !hasNonClearableNotifs(algorithmState));
- } else {
- // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed
- // already, so we shouldn't need to use ambientState here. However,
- // currently it doesn't get updated quickly enough and can cause the footer to
- // flash when closing the shade. As such, we temporarily also check the
- // ambientState directly.
- if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
- viewState.hidden = true;
- } else {
- final float footerEnd = algorithmState.mCurrentExpandedYPosition
- + view.getIntrinsicHeight();
- final boolean noSpaceForFooter =
- footerEnd > ambientState.getStackEndHeight();
- ((FooterView.FooterViewState) viewState).hideContent =
- noSpaceForFooter || (ambientState.isClearAllInProgress()
- && !hasNonClearableNotifs(algorithmState));
- }
- }
+ if (SceneContainerFlag.isEnabled()) {
+ final float footerEnd =
+ stackTop + viewState.getYTranslation() + view.getIntrinsicHeight();
+ final boolean noSpaceForFooter = footerEnd > ambientState.getStackCutoff();
+ ((FooterView.FooterViewState) viewState).hideContent =
+ noSpaceForFooter || (ambientState.isClearAllInProgress()
+ && !hasNonClearableNotifs(algorithmState));
} else {
- final boolean shadeClosed = !ambientState.isShadeExpanded();
- final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
- if (shadeClosed) {
+ // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed
+ // already, so we shouldn't need to use ambientState here. However,
+ // currently it doesn't get updated quickly enough and can cause the footer to
+ // flash when closing the shade. As such, we temporarily also check the
+ // ambientState directly.
+ if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
viewState.hidden = true;
} else {
final float footerEnd = algorithmState.mCurrentExpandedYPosition
+ view.getIntrinsicHeight();
- final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
+ final boolean noSpaceForFooter =
+ footerEnd > ambientState.getStackEndHeight();
((FooterView.FooterViewState) viewState).hideContent =
- isShelfShowing || noSpaceForFooter
- || (ambientState.isClearAllInProgress()
+ noSpaceForFooter || (ambientState.isClearAllInProgress()
&& !hasNonClearableNotifs(algorithmState));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index b4561686b7b2..1d7e658932ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -40,7 +40,6 @@ import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyS
import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView
import com.android.systemui.statusbar.notification.emptyshade.ui.viewbinder.EmptyShadeViewBinder
import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
@@ -108,25 +107,20 @@ constructor(
launch { bindShelf(shelf) }
bindHideList(viewController, viewModel, hiderTracker)
- if (FooterViewRefactor.isEnabled) {
- val hasNonClearableSilentNotifications: StateFlow<Boolean> =
- viewModel.hasNonClearableSilentNotifications.stateIn(this)
- launch { reinflateAndBindFooter(view, hasNonClearableSilentNotifications) }
- launch {
- if (ModesEmptyShadeFix.isEnabled) {
- reinflateAndBindEmptyShade(view)
- } else {
- bindEmptyShadeLegacy(viewModel.emptyShadeViewFactory.create(), view)
- }
+ val hasNonClearableSilentNotifications: StateFlow<Boolean> =
+ viewModel.hasNonClearableSilentNotifications.stateIn(this)
+ launch { reinflateAndBindFooter(view, hasNonClearableSilentNotifications) }
+ launch {
+ if (ModesEmptyShadeFix.isEnabled) {
+ reinflateAndBindEmptyShade(view)
+ } else {
+ bindEmptyShadeLegacy(viewModel.emptyShadeViewFactory.create(), view)
}
- launch {
- bindSilentHeaderClickListener(view, hasNonClearableSilentNotifications)
- }
- launch {
- viewModel.isImportantForAccessibility.collect { isImportantForAccessibility
- ->
- view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
- }
+ }
+ launch { bindSilentHeaderClickListener(view, hasNonClearableSilentNotifications) }
+ launch {
+ viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
+ view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index ea714608ea66..0b2b84e60f4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -28,7 +28,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
@@ -81,9 +80,6 @@ constructor(
controller.setOverExpansion(0f)
controller.setOverScrollAmount(0)
- if (!FooterViewRefactor.isEnabled) {
- controller.updateFooter()
- }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 38390e7bdb39..fcc671a5bae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotif
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
@@ -75,46 +74,37 @@ constructor(
* we want it to be important for accessibility to enable accessibility auto-scrolling in NSSL.
* See b/242235264 for more details.
*/
- val isImportantForAccessibility: Flow<Boolean> by lazy {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- flowOf(true)
- } else {
- combine(
- activeNotificationsInteractor.areAnyNotificationsPresent,
- notificationStackInteractor.isShowingOnLockscreen,
- ) { hasNotifications, isShowingOnLockscreen ->
- hasNotifications || !isShowingOnLockscreen
- }
- .distinctUntilChanged()
- .dumpWhileCollecting("isImportantForAccessibility")
- .flowOn(bgDispatcher)
- }
- }
+ val isImportantForAccessibility: Flow<Boolean> =
+ combine(
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ notificationStackInteractor.isShowingOnLockscreen,
+ ) { hasNotifications, isShowingOnLockscreen ->
+ hasNotifications || !isShowingOnLockscreen
+ }
+ .distinctUntilChanged()
+ .dumpWhileCollecting("isImportantForAccessibility")
+ .flowOn(bgDispatcher)
val shouldShowEmptyShadeView: Flow<Boolean> by lazy {
ModesEmptyShadeFix.assertInLegacyMode()
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- flowOf(false)
- } else {
- combine(
- activeNotificationsInteractor.areAnyNotificationsPresent,
- shadeInteractor.isQsFullscreen,
- notificationStackInteractor.isShowingOnLockscreen,
- ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen ->
- when {
- hasNotifications -> false
- isQsFullScreen -> false
- // Do not show the empty shade if the lockscreen is visible (including AOD
- // b/228790482 and bouncer b/267060171), except if the shade is opened on
- // top.
- isShowingOnLockscreen -> false
- else -> true
- }
+ combine(
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ shadeInteractor.isQsFullscreen,
+ notificationStackInteractor.isShowingOnLockscreen,
+ ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen ->
+ when {
+ hasNotifications -> false
+ isQsFullScreen -> false
+ // Do not show the empty shade if the lockscreen is visible (including AOD
+ // b/228790482 and bouncer b/267060171), except if the shade is opened on
+ // top.
+ isShowingOnLockscreen -> false
+ else -> true
}
- .distinctUntilChanged()
- .dumpWhileCollecting("shouldShowEmptyShadeView")
- .flowOn(bgDispatcher)
- }
+ }
+ .distinctUntilChanged()
+ .dumpWhileCollecting("shouldShowEmptyShadeView")
+ .flowOn(bgDispatcher)
}
val shouldShowEmptyShadeViewAnimated: Flow<AnimatedValue<Boolean>> by lazy {
@@ -164,18 +154,14 @@ constructor(
*/
val shouldHideFooterView: Flow<Boolean> by lazy {
SceneContainerFlag.assertInLegacyMode()
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- flowOf(false)
- } else {
- // When the shade is closed, the footer is still present in the list, but not visible.
- // This prevents the footer from being shown when a HUN is present, while still allowing
- // the footer to be counted as part of the shade for measurements.
- shadeInteractor.shadeExpansion
- .map { it == 0f }
- .distinctUntilChanged()
- .dumpWhileCollecting("shouldHideFooterView")
- .flowOn(bgDispatcher)
- }
+ // When the shade is closed, the footer is still present in the list, but not visible.
+ // This prevents the footer from being shown when a HUN is present, while still allowing
+ // the footer to be counted as part of the shade for measurements.
+ shadeInteractor.shadeExpansion
+ .map { it == 0f }
+ .distinctUntilChanged()
+ .dumpWhileCollecting("shouldHideFooterView")
+ .flowOn(bgDispatcher)
}
/**
@@ -188,68 +174,64 @@ constructor(
*/
val shouldIncludeFooterView: Flow<AnimatedValue<Boolean>> by lazy {
SceneContainerFlag.assertInLegacyMode()
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- flowOf(AnimatedValue.NotAnimating(false))
- } else {
- combine(
- activeNotificationsInteractor.areAnyNotificationsPresent,
- userSetupInteractor.isUserSetUp,
- notificationStackInteractor.isShowingOnLockscreen,
- shadeInteractor.isQsFullscreen,
- remoteInputInteractor.isRemoteInputActive,
- ) {
- hasNotifications,
- isUserSetUp,
- isShowingOnLockscreen,
- qsFullScreen,
- isRemoteInputActive ->
- when {
- !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
- // Hide the footer until the user setup is complete, to prevent access
- // to settings (b/193149550).
- !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
- // Do not show the footer if the lockscreen is visible (incl. AOD),
- // except if the shade is opened on top. See also b/219680200.
- // Do not animate, as that makes the footer appear briefly when
- // transitioning between the shade and keyguard.
- isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION
- // Do not show the footer if quick settings are fully expanded (except
- // for the foldable split shade view). See b/201427195 && b/222699879.
- qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
- // Hide the footer if remote input is active (i.e. user is replying to a
- // notification). See b/75984847.
- isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
- else -> VisibilityChange.APPEAR_WITH_ANIMATION
- }
+ combine(
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ userSetupInteractor.isUserSetUp,
+ notificationStackInteractor.isShowingOnLockscreen,
+ shadeInteractor.isQsFullscreen,
+ remoteInputInteractor.isRemoteInputActive,
+ ) {
+ hasNotifications,
+ isUserSetUp,
+ isShowingOnLockscreen,
+ qsFullScreen,
+ isRemoteInputActive ->
+ when {
+ !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Hide the footer until the user setup is complete, to prevent access
+ // to settings (b/193149550).
+ !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Do not show the footer if the lockscreen is visible (incl. AOD),
+ // except if the shade is opened on top. See also b/219680200.
+ // Do not animate, as that makes the footer appear briefly when
+ // transitioning between the shade and keyguard.
+ isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION
+ // Do not show the footer if quick settings are fully expanded (except
+ // for the foldable split shade view). See b/201427195 && b/222699879.
+ qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Hide the footer if remote input is active (i.e. user is replying to a
+ // notification). See b/75984847.
+ isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ else -> VisibilityChange.APPEAR_WITH_ANIMATION
}
- .distinctUntilChanged(
- // Equivalent unless visibility changes
- areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
- a.visible == b.visible
- }
- )
- // Should we animate the visibility change?
- .sample(
- // TODO(b/322167853): This check is currently duplicated in FooterViewModel,
- // but instead it should be a field in ShadeAnimationInteractor.
- combine(
- shadeInteractor.isShadeFullyExpanded,
- shadeInteractor.isShadeTouchable,
- ::Pair,
- )
- .onStart { emit(Pair(false, false)) }
- ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
- // Animate if the shade is interactive, but NOT on the lockscreen. Having
- // animations enabled while on the lockscreen makes the footer appear briefly
- // when transitioning between the shade and keyguard.
- val shouldAnimate =
- isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate
- AnimatableEvent(visibilityChange.visible, shouldAnimate)
+ }
+ .distinctUntilChanged(
+ // Equivalent unless visibility changes
+ areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
+ a.visible == b.visible
}
- .toAnimatedValueFlow()
- .dumpWhileCollecting("shouldIncludeFooterView")
- .flowOn(bgDispatcher)
- }
+ )
+ // Should we animate the visibility change?
+ .sample(
+ // TODO(b/322167853): This check is currently duplicated in FooterViewModel,
+ // but instead it should be a field in ShadeAnimationInteractor.
+ combine(
+ shadeInteractor.isShadeFullyExpanded,
+ shadeInteractor.isShadeTouchable,
+ ::Pair,
+ )
+ .onStart { emit(Pair(false, false)) }
+ ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
+ // Animate if the shade is interactive, but NOT on the lockscreen. Having
+ // animations enabled while on the lockscreen makes the footer appear briefly
+ // when transitioning between the shade and keyguard.
+ val shouldAnimate =
+ isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate
+ AnimatableEvent(visibilityChange.visible, shouldAnimate)
+ }
+ .toAnimatedValueFlow()
+ .dumpWhileCollecting("shouldIncludeFooterView")
+ .flowOn(bgDispatcher)
}
// This flow replaces shouldHideFooterView+shouldIncludeFooterView in flexiglass.
@@ -328,25 +310,15 @@ constructor(
APPEAR_WITH_ANIMATION(visible = true, canAnimate = true),
}
- val hasClearableAlertingNotifications: Flow<Boolean> by lazy {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- flowOf(false)
- } else {
- activeNotificationsInteractor.hasClearableAlertingNotifications.dumpWhileCollecting(
- "hasClearableAlertingNotifications"
- )
- }
- }
+ val hasClearableAlertingNotifications: Flow<Boolean> =
+ activeNotificationsInteractor.hasClearableAlertingNotifications.dumpWhileCollecting(
+ "hasClearableAlertingNotifications"
+ )
- val hasNonClearableSilentNotifications: Flow<Boolean> by lazy {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- flowOf(false)
- } else {
- activeNotificationsInteractor.hasNonClearableSilentNotifications.dumpWhileCollecting(
- "hasNonClearableSilentNotifications"
- )
- }
- }
+ val hasNonClearableSilentNotifications: Flow<Boolean> =
+ activeNotificationsInteractor.hasNonClearableSilentNotifications.dumpWhileCollecting(
+ "hasNonClearableSilentNotifications"
+ )
val topHeadsUpRow: Flow<HeadsUpRowKey?> by lazy {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 1474789ea0e3..3d6cd7e49dfe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1487,8 +1487,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mActivityTransitionAnimator.setCallback(mActivityTransitionAnimatorCallback);
mActivityTransitionAnimator.addListener(mActivityTransitionAnimatorListener);
mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
- mStackScrollerController.setNotificationActivityStarter(
- mNotificationActivityStarterLazy.get());
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarterLazy.get());
mShadeController.setNotificationPresenter(mPresenterLazy.get());
mNotificationsController.initialize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 3749b96199f6..8443edd6aa87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone;
import static android.view.WindowInsets.Type.navigationBars;
-import static com.android.systemui.Flags.predictiveBackAnimateBouncer;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -328,7 +327,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private float mQsExpansion;
final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
- private boolean mIsBackAnimationEnabled;
private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
private final ActivityStarter mActivityStarter;
@@ -434,7 +432,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
.map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
mAlternateBouncerInteractor = alternateBouncerInteractor;
mBouncerInteractor = bouncerInteractor;
- mIsBackAnimationEnabled = predictiveBackAnimateBouncer();
mUdfpsOverlayInteractor = udfpsOverlayInteractor;
mActivityStarter = activityStarter;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
@@ -630,7 +627,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private boolean shouldPlayBackAnimation() {
// Suppress back animation when bouncer shouldn't be dismissed on back invocation.
- return !needsFullscreenBouncer() && mIsBackAnimationEnabled;
+ return !needsFullscreenBouncer();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 03324d2a3e6a..c47ed1722bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Flags.predictiveBackAnimateDialogs;
-
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
@@ -285,15 +283,13 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
for (int i = 0; i < mOnCreateRunnables.size(); i++) {
mOnCreateRunnables.get(i).run();
}
- if (predictiveBackAnimateDialogs()) {
- View targetView = getWindow().getDecorView();
- DialogKt.registerAnimationOnBackInvoked(
- /* dialog = */ this,
- /* targetView = */ targetView,
- /* backAnimationSpec= */mDelegate.getBackAnimationSpec(
- () -> targetView.getResources().getDisplayMetrics())
- );
- }
+ View targetView = getWindow().getDecorView();
+ DialogKt.registerAnimationOnBackInvoked(
+ /* dialog = */ this,
+ /* targetView = */ targetView,
+ /* backAnimationSpec= */mDelegate.getBackAnimationSpec(
+ () -> targetView.getResources().getDisplayMetrics())
+ );
}
private void updateWindowSize() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 31cae79c6b94..81d06a8db0b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -32,6 +32,7 @@ import android.os.Trace;
import androidx.annotation.VisibleForTesting;
+import com.android.app.tracing.coroutines.TrackTracer;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -241,7 +242,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController {
private void setKeyguardFadingAway(boolean keyguardFadingAway) {
if (mKeyguardFadingAway != keyguardFadingAway) {
- Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardFadingAway",
+ TrackTracer.instantForGroup("keyguard", "FadingAway",
keyguardFadingAway ? 1 : 0);
mKeyguardFadingAway = keyguardFadingAway;
invokeForEachCallback(Callback::onKeyguardFadingAwayChanged);
@@ -356,7 +357,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController {
@Override
public void notifyKeyguardGoingAway(boolean keyguardGoingAway) {
if (mKeyguardGoingAway != keyguardGoingAway) {
- Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardGoingAway",
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguard##GoingAway",
keyguardGoingAway ? 1 : 0);
mKeyguardGoingAway = keyguardGoingAway;
mKeyguardInteractorLazy.get().setIsKeyguardGoingAway(keyguardGoingAway);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
index 12ed647fdee7..fdc2d8d96f9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.policy.domain.interactor
-import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
import android.content.Context
+import android.media.AudioManager
import android.provider.Settings
import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
@@ -29,6 +29,7 @@ import com.android.settingslib.notification.data.repository.ZenModeRepository
import com.android.settingslib.notification.modes.ZenIcon
import com.android.settingslib.notification.modes.ZenIconLoader
import com.android.settingslib.notification.modes.ZenMode
+import com.android.settingslib.volume.shared.model.AudioStream
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.modes.shared.ModesUi
import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
@@ -67,6 +68,17 @@ constructor(
deviceProvisioningRepository: DeviceProvisioningRepository,
userSetupRepository: UserSetupRepository,
) {
+ /**
+ * List of predicates to determine if the [ZenMode] blocks an audio stream. Typical use case
+ * would be: `zenModeByStreamPredicates[stream](zenMode)`
+ */
+ private val zenModeByStreamPredicates =
+ mapOf<Int, (ZenMode) -> Boolean>(
+ AudioManager.STREAM_MUSIC to { it.policy.priorityCategoryMedia == STATE_DISALLOW },
+ AudioManager.STREAM_ALARM to { it.policy.priorityCategoryAlarms == STATE_DISALLOW },
+ AudioManager.STREAM_SYSTEM to { it.policy.priorityCategorySystem == STATE_DISALLOW },
+ )
+
val isZenAvailable: Flow<Boolean> =
combine(
deviceProvisioningRepository.isDeviceProvisioned,
@@ -125,21 +137,16 @@ constructor(
.flowOn(bgDispatcher)
.distinctUntilChanged()
- val activeModesBlockingEverything: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
- mode.interruptionFilter == INTERRUPTION_FILTER_NONE
- }
-
- val activeModesBlockingMedia: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
- mode.policy.priorityCategoryMedia == STATE_DISALLOW
- }
-
- val activeModesBlockingAlarms: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
- mode.policy.priorityCategoryAlarms == STATE_DISALLOW
- }
+ fun canBeBlockedByZenMode(stream: AudioStream): Boolean =
+ zenModeByStreamPredicates.containsKey(stream.value)
- private fun getFilteredActiveModesFlow(predicate: (ZenMode) -> Boolean): Flow<ActiveZenModes> {
+ fun activeModesBlockingStream(stream: AudioStream): Flow<ActiveZenModes> {
+ val isBlockingStream = zenModeByStreamPredicates[stream.value]
+ require(isBlockingStream != null) {
+ "$stream is unsupported. Use canBeBlockedByZenMode to check if the stream can be affected by the Zen Mode."
+ }
return modes
- .map { modes -> modes.filter { mode -> predicate(mode) } }
+ .map { modes -> modes.filter { isBlockingStream(it) } }
.map { modes -> buildActiveZenModes(modes) }
.flowOn(bgDispatcher)
.distinctUntilChanged()
@@ -194,7 +201,6 @@ constructor(
)
null
}
-
ZEN_DURATION_FOREVER -> null
else -> Duration.ofMinutes(zenDuration.toLong())
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
index ae32b7a6175c..bce55cbdcc4a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
@@ -50,7 +50,7 @@ fun BackGestureTutorialScreen(
)
GestureTutorialScreen(
screenConfig = screenConfig,
- gestureUiStateFlow = viewModel.gestureUiState,
+ tutorialStateFlow = viewModel.tutorialState,
motionEventConsumer = {
easterEggGestureViewModel.accept(it)
viewModel.handleEvent(it)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
index 73c54af595d9..284e23e5a288 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
@@ -18,7 +18,6 @@ package com.android.systemui.touchpad.tutorial.ui.composable
import android.view.MotionEvent
import androidx.activity.compose.BackHandler
-import androidx.annotation.RawRes
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
@@ -27,77 +26,21 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.inputdevice.tutorial.ui.composable.ActionTutorialContent
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.NotStarted
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
import kotlinx.coroutines.flow.Flow
-sealed interface GestureUiState {
- data object NotStarted : GestureUiState
-
- data class Finished(@RawRes val successAnimation: Int) : GestureUiState
-
- data class InProgress(
- val progress: Float = 0f,
- val progressStartMarker: String,
- val progressEndMarker: String,
- ) : GestureUiState
-
- data object Error : GestureUiState
-}
-
-fun GestureState.toGestureUiState(
- progressStartMarker: String,
- progressEndMarker: String,
- successAnimation: Int,
-): GestureUiState {
- return when (this) {
- GestureState.NotStarted -> NotStarted
- is GestureState.InProgress ->
- GestureUiState.InProgress(this.progress, progressStartMarker, progressEndMarker)
- is GestureState.Finished -> GestureUiState.Finished(successAnimation)
- GestureState.Error -> GestureUiState.Error
- }
-}
-
-fun GestureUiState.toTutorialActionState(previousState: TutorialActionState): TutorialActionState {
- return when (this) {
- NotStarted -> TutorialActionState.NotStarted
- is GestureUiState.InProgress -> {
- val inProgress =
- TutorialActionState.InProgress(
- progress = progress,
- startMarker = progressStartMarker,
- endMarker = progressEndMarker,
- )
- if (
- previousState is TutorialActionState.InProgressAfterError ||
- previousState is TutorialActionState.Error
- ) {
- return TutorialActionState.InProgressAfterError(inProgress)
- } else {
- return inProgress
- }
- }
- is Finished -> TutorialActionState.Finished(successAnimation)
- GestureUiState.Error -> TutorialActionState.Error
- }
-}
-
@Composable
fun GestureTutorialScreen(
screenConfig: TutorialScreenConfig,
- gestureUiStateFlow: Flow<GestureUiState>,
+ tutorialStateFlow: Flow<TutorialActionState>,
motionEventConsumer: (MotionEvent) -> Boolean,
easterEggTriggeredFlow: Flow<Boolean>,
onEasterEggFinished: () -> Unit,
@@ -106,25 +49,21 @@ fun GestureTutorialScreen(
) {
BackHandler(onBack = onBack)
val easterEggTriggered by easterEggTriggeredFlow.collectAsStateWithLifecycle(false)
- val gestureState by gestureUiStateFlow.collectAsStateWithLifecycle(NotStarted)
+ val tutorialState by tutorialStateFlow.collectAsStateWithLifecycle(NotStarted)
TouchpadGesturesHandlingBox(
motionEventConsumer,
- gestureState,
+ tutorialState,
easterEggTriggered,
onEasterEggFinished,
) {
- var lastState: TutorialActionState by remember {
- mutableStateOf(TutorialActionState.NotStarted)
- }
- lastState = gestureState.toTutorialActionState(lastState)
- ActionTutorialContent(lastState, onDoneButtonClicked, screenConfig)
+ ActionTutorialContent(tutorialState, onDoneButtonClicked, screenConfig)
}
}
@Composable
private fun TouchpadGesturesHandlingBox(
motionEventConsumer: (MotionEvent) -> Boolean,
- gestureState: GestureUiState,
+ tutorialState: TutorialActionState,
easterEggTriggered: Boolean,
onEasterEggFinished: () -> Unit,
modifier: Modifier = Modifier,
@@ -150,7 +89,7 @@ private fun TouchpadGesturesHandlingBox(
.pointerInteropFilter(
onTouchEvent = { event ->
// FINISHED is the final state so we don't need to process touches anymore
- if (gestureState is Finished) {
+ if (tutorialState is TutorialActionState.Finished) {
false
} else {
motionEventConsumer(event)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
index 4f1f40dc4c05..4acdb6070200 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
@@ -49,7 +49,7 @@ fun HomeGestureTutorialScreen(
)
GestureTutorialScreen(
screenConfig = screenConfig,
- gestureUiStateFlow = viewModel.gestureUiState,
+ tutorialStateFlow = viewModel.tutorialState,
motionEventConsumer = {
easterEggGestureViewModel.accept(it)
viewModel.handleEvent(it)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
index 6c9e26c4b7ea..8dd53a7fb815 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
@@ -50,7 +50,7 @@ fun RecentAppsGestureTutorialScreen(
)
GestureTutorialScreen(
screenConfig = screenConfig,
- gestureUiStateFlow = viewModel.gestureUiState,
+ tutorialStateFlow = viewModel.tutorialState,
motionEventConsumer = {
easterEggGestureViewModel.accept(it)
viewModel.handleEvent(it)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt
index 8e53669a7841..7a3d4d1ba88a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt
@@ -17,12 +17,12 @@
package com.android.systemui.touchpad.tutorial.ui.viewmodel
import android.view.MotionEvent
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
import com.android.systemui.util.kotlin.pairwiseBy
import kotlinx.coroutines.flow.Flow
@@ -30,21 +30,26 @@ import kotlinx.coroutines.flow.Flow
class BackGestureScreenViewModel(val gestureRecognizer: GestureRecognizerAdapter) :
TouchpadTutorialScreenViewModel {
- override val gestureUiState: Flow<GestureUiState> =
- gestureRecognizer.gestureState.pairwiseBy(GestureState.NotStarted) { previous, current ->
- toGestureUiState(current, previous)
- }
+ override val tutorialState: Flow<TutorialActionState> =
+ gestureRecognizer.gestureState
+ .pairwiseBy(NotStarted) { previous, current ->
+ current to toAnimationProperties(current, previous)
+ }
+ .mapToTutorialState()
override fun handleEvent(event: MotionEvent): Boolean {
return gestureRecognizer.handleTouchpadMotionEvent(event)
}
- private fun toGestureUiState(current: GestureState, previous: GestureState): GestureUiState {
+ private fun toAnimationProperties(
+ current: GestureState,
+ previous: GestureState,
+ ): TutorialAnimationProperties {
val (startMarker, endMarker) =
if (current is InProgress && current.direction == GestureDirection.LEFT) {
"gesture to L" to "end progress L"
} else "gesture to R" to "end progress R"
- return current.toGestureUiState(
+ return TutorialAnimationProperties(
progressStartMarker = startMarker,
progressEndMarker = endMarker,
successAnimation = successAnimation(previous),
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt
index 9d6f568fa1b1..c75d44f01e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt
@@ -17,9 +17,8 @@
package com.android.systemui.touchpad.tutorial.ui.viewmodel
import android.view.MotionEvent
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState
import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@@ -27,14 +26,17 @@ import kotlinx.coroutines.flow.map
class HomeGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) :
TouchpadTutorialScreenViewModel {
- override val gestureUiState: Flow<GestureUiState> =
- gestureRecognizer.gestureState.map {
- it.toGestureUiState(
- progressStartMarker = "drag with gesture",
- progressEndMarker = "release playback realtime",
- successAnimation = R.raw.trackpad_home_success,
- )
- }
+ override val tutorialState: Flow<TutorialActionState> =
+ gestureRecognizer.gestureState
+ .map {
+ it to
+ TutorialAnimationProperties(
+ progressStartMarker = "drag with gesture",
+ progressEndMarker = "release playback realtime",
+ successAnimation = R.raw.trackpad_home_success,
+ )
+ }
+ .mapToTutorialState()
override fun handleEvent(event: MotionEvent): Boolean {
return gestureRecognizer.handleTouchpadMotionEvent(event)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt
index 97528583277f..9fab5f3641a4 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt
@@ -17,9 +17,8 @@
package com.android.systemui.touchpad.tutorial.ui.viewmodel
import android.view.MotionEvent
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState
import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@@ -27,14 +26,17 @@ import kotlinx.coroutines.flow.map
class RecentAppsGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) :
TouchpadTutorialScreenViewModel {
- override val gestureUiState: Flow<GestureUiState> =
- gestureRecognizer.gestureState.map {
- it.toGestureUiState(
- progressStartMarker = "drag with gesture",
- progressEndMarker = "onPause",
- successAnimation = R.raw.trackpad_recent_apps_success,
- )
- }
+ override val tutorialState: Flow<TutorialActionState> =
+ gestureRecognizer.gestureState
+ .map {
+ it to
+ TutorialAnimationProperties(
+ progressStartMarker = "drag with gesture",
+ progressEndMarker = "onPause",
+ successAnimation = R.raw.trackpad_recent_apps_success,
+ )
+ }
+ .mapToTutorialState()
override fun handleEvent(event: MotionEvent): Boolean {
return gestureRecognizer.handleTouchpadMotionEvent(event)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt
index 31e953d6643c..3b6e3c76cdeb 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt
@@ -17,11 +17,62 @@
package com.android.systemui.touchpad.tutorial.ui.viewmodel
import android.view.MotionEvent
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
+import androidx.annotation.RawRes
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
interface TouchpadTutorialScreenViewModel {
- val gestureUiState: Flow<GestureUiState>
+ val tutorialState: Flow<TutorialActionState>
fun handleEvent(event: MotionEvent): Boolean
}
+
+data class TutorialAnimationProperties(
+ val progressStartMarker: String,
+ val progressEndMarker: String,
+ @RawRes val successAnimation: Int,
+)
+
+fun Flow<Pair<GestureState, TutorialAnimationProperties>>.mapToTutorialState():
+ Flow<TutorialActionState> {
+ return flow<TutorialActionState> {
+ var lastState: TutorialActionState = TutorialActionState.NotStarted
+ collect { (gestureState, animationProperties) ->
+ val newState = gestureState.toTutorialActionState(animationProperties, lastState)
+ lastState = newState
+ emit(newState)
+ }
+ }
+}
+
+fun GestureState.toTutorialActionState(
+ properties: TutorialAnimationProperties,
+ previousState: TutorialActionState,
+): TutorialActionState {
+ return when (this) {
+ NotStarted -> TutorialActionState.NotStarted
+ is InProgress -> {
+ val inProgress =
+ TutorialActionState.InProgress(
+ progress = progress,
+ startMarker = properties.progressStartMarker,
+ endMarker = properties.progressEndMarker,
+ )
+ if (
+ previousState is TutorialActionState.InProgressAfterError ||
+ previousState is TutorialActionState.Error
+ ) {
+ TutorialActionState.InProgressAfterError(inProgress)
+ } else {
+ inProgress
+ }
+ }
+ is Finished -> TutorialActionState.Finished(properties.successAnimation)
+ GestureState.Error -> TutorialActionState.Error
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
index 65970978b4ec..7d3966b98782 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -17,8 +17,9 @@ package com.android.systemui.unfold
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
-import android.os.Trace
import com.android.app.tracing.TraceStateLogger
+import com.android.app.tracing.coroutines.TrackTracer
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -29,7 +30,6 @@ import com.android.systemui.util.Utils.isDeviceFoldable
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.plus
/**
@@ -45,7 +45,7 @@ constructor(
@Application applicationScope: CoroutineScope,
@Background private val coroutineContext: CoroutineContext,
private val deviceStateRepository: DeviceStateRepository,
- private val deviceStateManager: DeviceStateManager
+ private val deviceStateManager: DeviceStateManager,
) : CoreStartable {
private val isFoldable: Boolean = isDeviceFoldable(context.resources, deviceStateManager)
@@ -61,7 +61,7 @@ constructor(
bgScope.launch {
foldStateRepository.hingeAngle.collect {
- Trace.traceCounter(Trace.TRACE_TAG_APP, "hingeAngle", it.toInt())
+ TrackTracer.instantForGroup("unfold", "hingeAngle", it.toInt())
}
}
bgScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index fa1088426351..3b0c8a6b46f8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -46,7 +46,7 @@ class VolumeDialogCallbacksInteractor
constructor(
private val volumeDialogController: VolumeDialogController,
@VolumeDialogPlugin private val coroutineScope: CoroutineScope,
- @Background private val bgHandler: Handler,
+ @Background private val bgHandler: Handler?,
) {
@SuppressLint("SharedFlowCreation") // event-bus needed
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
index 88af210b6a36..940c79c78d76 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
@@ -19,7 +19,6 @@ package com.android.systemui.volume.dialog.sliders.dagger
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderHapticsViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderTouchesViewBinder
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
import dagger.BindsInstance
import dagger.Subcomponent
@@ -34,8 +33,6 @@ interface VolumeDialogSliderComponent {
fun sliderViewBinder(): VolumeDialogSliderViewBinder
- fun sliderTouchesViewBinder(): VolumeDialogSliderTouchesViewBinder
-
fun sliderHapticsViewBinder(): VolumeDialogSliderHapticsViewBinder
fun overscrollViewBinder(): VolumeDialogOverscrollViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt
deleted file mode 100644
index 4ecac7a81893..000000000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.dialog.sliders.ui
-
-import android.annotation.SuppressLint
-import android.view.View
-import com.android.systemui.res.R
-import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel
-import com.google.android.material.slider.Slider
-import javax.inject.Inject
-
-@VolumeDialogSliderScope
-class VolumeDialogSliderTouchesViewBinder
-@Inject
-constructor(private val viewModel: VolumeDialogSliderInputEventsViewModel) {
-
- @SuppressLint("ClickableViewAccessibility")
- fun bind(view: View) {
- with(view.requireViewById<Slider>(R.id.volume_dialog_slider)) {
- setOnTouchListener { _, event ->
- viewModel.onTouchEvent(event)
- false
- }
- }
- }
-}
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 67ffb0602860..ccd16ac5a331 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
@@ -23,6 +23,7 @@ import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel
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
@@ -35,7 +36,10 @@ import kotlinx.coroutines.flow.onEach
@VolumeDialogSliderScope
class VolumeDialogSliderViewBinder
@Inject
-constructor(private val viewModel: VolumeDialogSliderViewModel) {
+constructor(
+ private val viewModel: VolumeDialogSliderViewModel,
+ private val inputViewModel: VolumeDialogSliderInputEventsViewModel,
+) {
private val sliderValueProperty =
object : FloatPropertyCompat<Slider>("value") {
@@ -51,12 +55,16 @@ constructor(private val viewModel: VolumeDialogSliderViewModel) {
dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
}
+ @SuppressLint("ClickableViewAccessibility")
fun CoroutineScope.bind(view: View) {
var isInitialUpdate = true
val sliderView: Slider = view.requireViewById(R.id.volume_dialog_slider)
val animation = SpringAnimation(sliderView, sliderValueProperty)
animation.spring = springForce
-
+ sliderView.setOnTouchListener { _, event ->
+ inputViewModel.onTouchEvent(event)
+ false
+ }
sliderView.addOnChangeListener { _, value, fromUser ->
viewModel.setStreamVolume(value.roundToInt(), fromUser)
}
@@ -82,7 +90,7 @@ constructor(private val viewModel: VolumeDialogSliderViewModel) {
// coerce the current value to the new value range before animating it. This prevents
// animating from the value that is outside of current [valueFrom, valueTo].
value = value.coerceIn(valueFrom, valueTo)
- setTrackIconActiveStart(model.iconRes)
+ trackIconActiveStart = model.icon
if (isInitialUpdate) {
value = model.value
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index f066b56e7de0..75d427acc05b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -71,7 +71,6 @@ constructor(private val viewModel: VolumeDialogSlidersViewModel) {
viewsToAnimate: Array<View>,
) {
with(component.sliderViewBinder()) { bind(sliderContainer) }
- with(component.sliderTouchesViewBinder()) { bind(sliderContainer) }
with(component.sliderHapticsViewBinder()) { bind(sliderContainer) }
with(component.overscrollViewBinder()) { bind(sliderContainer, viewsToAnimate) }
}
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 5c39b6f9359c..daf4c8275d20 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
@@ -16,13 +16,16 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.drawable.Drawable
import android.media.AudioManager
import androidx.annotation.DrawableRes
-import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -31,11 +34,12 @@ import kotlinx.coroutines.flow.flowOf
class VolumeDialogSliderIconProvider
@Inject
constructor(
- private val notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
+ private val context: Context,
+ private val zenModeInteractor: ZenModeInteractor,
private val audioVolumeInteractor: AudioVolumeInteractor,
) {
- @DrawableRes
+ @SuppressLint("UseCompatLoadingForDrawables")
fun getStreamIcon(
stream: Int,
level: Int,
@@ -43,54 +47,71 @@ constructor(
levelMax: Int,
isMuted: Boolean,
isRoutedToBluetooth: Boolean,
- ): Flow<Int> {
+ ): Flow<Drawable> {
return combine(
- notificationsSoundPolicyInteractor.isZenMuted(AudioStream(stream)),
+ zenModeInteractor.activeModesBlockingStream(AudioStream(stream)),
ringerModeForStream(stream),
- ) { isZenMuted, ringerMode ->
- val isStreamOffline = level == 0 || isMuted
- if (isZenMuted) {
- // TODO(b/372466264) use icon for the corresponding zenmode
- return@combine com.android.internal.R.drawable.ic_qs_dnd
- }
- when (ringerMode?.value) {
- AudioManager.RINGER_MODE_VIBRATE ->
- return@combine R.drawable.ic_volume_ringer_vibrate
- AudioManager.RINGER_MODE_SILENT -> return@combine R.drawable.ic_ring_volume_off
- }
- if (isRoutedToBluetooth) {
- return@combine if (stream == AudioManager.STREAM_VOICE_CALL) {
- R.drawable.ic_volume_bt_sco
- } else {
- if (isStreamOffline) {
- R.drawable.ic_volume_media_bt_mute
- } else {
- R.drawable.ic_volume_media_bt
- }
- }
+ ) { activeModesBlockingStream, ringerMode ->
+ if (activeModesBlockingStream.mainMode?.icon != null) {
+ return@combine activeModesBlockingStream.mainMode.icon.drawable
+ } else {
+ context.getDrawable(
+ getIconRes(
+ stream,
+ level,
+ levelMin,
+ levelMax,
+ isMuted,
+ isRoutedToBluetooth,
+ ringerMode,
+ )
+ )!!
}
+ }
+ }
- return@combine if (isStreamOffline) {
- getMutedIconForStream(stream) ?: getIconForStream(stream)
+ @DrawableRes
+ private fun getIconRes(
+ stream: Int,
+ level: Int,
+ levelMin: Int,
+ levelMax: Int,
+ isMuted: Boolean,
+ isRoutedToBluetooth: Boolean,
+ 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
} else {
- if (level < (levelMax + levelMin) / 2) {
- // This icon is different on TV
- R.drawable.ic_volume_media_low
+ if (isStreamOffline) {
+ R.drawable.ic_volume_media_bt_mute
} else {
- getIconForStream(stream)
+ R.drawable.ic_volume_media_bt
}
}
}
- }
- @DrawableRes
- private fun getMutedIconForStream(stream: Int): Int? {
- return when (stream) {
- AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute
- AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute
- AudioManager.STREAM_ALARM -> R.drawable.ic_volume_alarm_mute
- AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system_mute
- else -> null
+ return if (isStreamOffline) {
+ when (stream) {
+ AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute
+ AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute
+ 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)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
index 5750c049082f..8df9e788905c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
@@ -16,21 +16,21 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
-import androidx.annotation.DrawableRes
+import android.graphics.drawable.Drawable
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
data class VolumeDialogSliderStateModel(
val minValue: Float,
val maxValue: Float,
val value: Float,
- @DrawableRes val iconRes: Int,
+ val icon: Drawable,
)
-fun VolumeDialogStreamModel.toStateModel(@DrawableRes iconRes: Int): VolumeDialogSliderStateModel {
+fun VolumeDialogStreamModel.toStateModel(icon: Drawable): VolumeDialogSliderStateModel {
return VolumeDialogSliderStateModel(
minValue = levelMin.toFloat(),
value = level.toFloat(),
maxValue = levelMax.toFloat(),
- iconRes = iconRes,
+ icon = icon,
)
}
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 6d8457be1014..06d9426fbe04 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
@@ -66,7 +66,8 @@ constructor(
private val model: Flow<VolumeDialogStreamModel> =
interactor.slider
.filter {
- val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0
+ val currentVolumeUpdate = userVolumeUpdates.value ?: return@filter true
+ val lastVolumeUpdateTime = currentVolumeUpdate.timestampMillis
getTimestampMillis() - lastVolumeUpdateTime > VOLUME_UPDATE_GRACE_PERIOD
}
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt
deleted file mode 100644
index e5cf62b91677..000000000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.dialog.ui
-
-import android.content.Context
-import android.content.res.Resources
-import com.android.systemui.dagger.qualifiers.UiBackground
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.onConfigChanged
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
-import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.stateIn
-
-/**
- * Provides cached resources [Flow]s that update when the configuration changes.
- *
- * Consume or use [kotlinx.coroutines.flow.first] to get the value.
- */
-@VolumeDialogScope
-class VolumeDialogResources
-@Inject
-constructor(
- @VolumeDialog private val coroutineScope: CoroutineScope,
- @UiBackground private val uiBackgroundContext: CoroutineContext,
- private val context: Context,
- private val configurationController: ConfigurationController,
-) {
-
- val dialogShowDurationMillis: Flow<Long> = configurationResource {
- getInteger(R.integer.config_dialogShowAnimationDurationMs).toLong()
- }
-
- val dialogHideDurationMillis: Flow<Long> = configurationResource {
- getInteger(R.integer.config_dialogHideAnimationDurationMs).toLong()
- }
-
- private fun <T> configurationResource(get: Resources.() -> T): Flow<T> =
- configurationController.onConfigChanged
- .map { context.resources.get() }
- .onStart { emit(context.resources.get()) }
- .flowOn(uiBackgroundContext)
- .stateIn(coroutineScope, SharingStarted.Eagerly, null)
- .filterNotNull()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index a3166a9978f4..1da2491c68b6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.dialog.ui.binder
import android.app.Dialog
+import android.content.res.Resources
import android.graphics.Rect
import android.graphics.Region
import android.view.View
@@ -25,6 +26,7 @@ import android.view.ViewTreeObserver
import android.view.ViewTreeObserver.InternalInsetsInfo
import androidx.constraintlayout.motion.widget.MotionLayout
import com.android.internal.view.RotationPolicy
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
import com.android.systemui.util.children
import com.android.systemui.volume.SystemUIInterpolators
@@ -33,7 +35,6 @@ import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerVie
import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
-import com.android.systemui.volume.dialog.ui.VolumeDialogResources
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel
@@ -42,7 +43,6 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
@@ -56,7 +56,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
class VolumeDialogViewBinder
@Inject
constructor(
- private val volumeResources: VolumeDialogResources,
+ @Main resources: Resources,
private val viewModel: VolumeDialogViewModel,
private val jankListenerFactory: JankListenerFactory,
private val tracer: VolumeTracer,
@@ -65,6 +65,11 @@ constructor(
private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
) {
+ private val dialogShowAnimationDurationMs =
+ resources.getInteger(R.integer.config_dialogShowAnimationDurationMs).toLong()
+ private val dialogHideAnimationDurationMs =
+ resources.getInteger(R.integer.config_dialogHideAnimationDurationMs).toLong()
+
fun CoroutineScope.bind(dialog: Dialog) {
// Root view of the Volume Dialog.
val root: MotionLayout = dialog.requireViewById(R.id.volume_dialog_root)
@@ -99,12 +104,12 @@ constructor(
is VolumeDialogVisibilityModel.Visible -> {
tracer.traceVisibilityEnd(it)
calculateTranslationX(view)?.let(view::setTranslationX)
- view.animateShow(volumeResources.dialogShowDurationMillis.first())
+ view.animateShow(dialogShowAnimationDurationMs)
}
is VolumeDialogVisibilityModel.Dismissed -> {
tracer.traceVisibilityEnd(it)
view.animateHide(
- duration = volumeResources.dialogHideDurationMillis.first(),
+ duration = dialogHideAnimationDurationMs,
translationX = calculateTranslationX(view),
)
dialog.dismiss()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index b20dffb8ac33..7a6ede4c8b9c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -44,9 +44,9 @@ class VolumeDialogViewModel
@Inject
constructor(
private val context: Context,
- private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+ dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
- private val volumeDialogStateInteractor: VolumeDialogStateInteractor,
+ volumeDialogStateInteractor: VolumeDialogStateInteractor,
devicePostureController: DevicePostureController,
configurationController: ConfigurationController,
) {
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 cec3d1eb86f0..5b8d9b045475 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
@@ -18,9 +18,6 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.content.Context
import android.media.AudioManager
-import android.media.AudioManager.STREAM_ALARM
-import android.media.AudioManager.STREAM_MUSIC
-import android.media.AudioManager.STREAM_NOTIFICATION
import android.util.Log
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.UiEventLogger
@@ -34,8 +31,6 @@ import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.modes.shared.ModesUiIcons
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
-import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
-import com.android.systemui.util.kotlin.combine
import com.android.systemui.volume.panel.shared.VolumePanelLogger
import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import dagger.assisted.Assisted
@@ -43,12 +38,15 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
@@ -101,48 +99,16 @@ constructor(
)
override val slider: StateFlow<SliderState> =
- if (ModesUiIcons.isEnabled) {
- combine(
- audioVolumeInteractor.getAudioStream(audioStream),
- audioVolumeInteractor.canChangeVolume(audioStream),
- audioVolumeInteractor.ringerMode,
- zenModeInteractor.activeModesBlockingEverything,
- zenModeInteractor.activeModesBlockingAlarms,
- zenModeInteractor.activeModesBlockingMedia,
- ) {
- model,
- isEnabled,
- ringerMode,
- modesBlockingEverything,
- modesBlockingAlarms,
- modesBlockingMedia ->
- volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
- model.toState(
- isEnabled,
- ringerMode,
- getStreamDisabledMessage(
- modesBlockingEverything,
- modesBlockingAlarms,
- modesBlockingMedia,
- ),
- )
- }
- .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
- } else {
- combine(
- audioVolumeInteractor.getAudioStream(audioStream),
- audioVolumeInteractor.canChangeVolume(audioStream),
- audioVolumeInteractor.ringerMode,
- ) { model, isEnabled, ringerMode ->
- volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
- model.toState(
- isEnabled,
- ringerMode,
- getStreamDisabledMessageWithoutModes(audioStream),
- )
- }
- .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
- }
+ combine(
+ audioVolumeInteractor.getAudioStream(audioStream),
+ audioVolumeInteractor.canChangeVolume(audioStream),
+ audioVolumeInteractor.ringerMode,
+ streamDisabledMessage(),
+ ) { model, isEnabled, ringerMode, streamDisabledMessage ->
+ volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
+ model.toState(isEnabled, ringerMode, streamDisabledMessage)
+ }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
init {
volumeChanges
@@ -229,40 +195,32 @@ constructor(
)
}
- private fun getStreamDisabledMessage(
- blockingEverything: ActiveZenModes,
- blockingAlarms: ActiveZenModes,
- blockingMedia: ActiveZenModes,
- ): String {
- // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING.
- // In fact, VOICE_CALL should not be affected by interruption filtering at all.
- return if (audioStream.value == STREAM_NOTIFICATION) {
- context.getString(R.string.stream_notification_unavailable)
- } else {
- val blockingModeName =
- when {
- blockingEverything.mainMode != null -> blockingEverything.mainMode.name
- audioStream.value == STREAM_ALARM -> blockingAlarms.mainMode?.name
- audioStream.value == STREAM_MUSIC -> blockingMedia.mainMode?.name
- else -> null
- }
-
- if (blockingModeName != null) {
- context.getString(R.string.stream_unavailable_by_modes, blockingModeName)
+ // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING.
+ // In fact, VOICE_CALL should not be affected by interruption filtering at all.
+ private fun streamDisabledMessage(): Flow<String> {
+ return if (ModesUiIcons.isEnabled) {
+ if (audioStream.value == AudioManager.STREAM_NOTIFICATION) {
+ flowOf(context.getString(R.string.stream_notification_unavailable))
} else {
- // Should not actually be visible, but as a catch-all.
- context.getString(R.string.stream_unavailable_by_unknown)
+ if (zenModeInteractor.canBeBlockedByZenMode(audioStream)) {
+ zenModeInteractor.activeModesBlockingStream(audioStream).map { blockingZenModes
+ ->
+ blockingZenModes.mainMode?.name?.let {
+ context.getString(R.string.stream_unavailable_by_modes, it)
+ } ?: context.getString(R.string.stream_unavailable_by_unknown)
+ }
+ } else {
+ flowOf(context.getString(R.string.stream_unavailable_by_unknown))
+ }
}
- }
- }
-
- private fun getStreamDisabledMessageWithoutModes(audioStream: AudioStream): String {
- // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING.
- // In fact, VOICE_CALL should not be affected by interruption filtering at all.
- return if (audioStream.value == STREAM_NOTIFICATION) {
- context.getString(R.string.stream_notification_unavailable)
} else {
- context.getString(R.string.stream_alarm_unavailable)
+ flowOf(
+ if (audioStream.value == AudioManager.STREAM_NOTIFICATION) {
+ context.getString(R.string.stream_notification_unavailable)
+ } else {
+ context.getString(R.string.stream_alarm_unavailable)
+ }
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 7a3089f33276..7a3089f33276 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 2aa300df4f7c..2aa300df4f7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index 2c37f510a45c..77bac59b9dcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -15,7 +15,6 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -33,15 +32,14 @@ import com.android.systemui.statusbar.notification.collection.render.NotifStackC
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
+import com.android.systemui.util.mockito.withArgCaptor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
@@ -82,9 +80,9 @@ class StackCoordinatorTest : SysuiTestCase() {
sensitiveNotificationProtectionController,
)
coordinator.attach(pipeline)
- val captor = argumentCaptor<OnAfterRenderListListener>()
- verify(pipeline).addOnAfterRenderListListener(captor.capture())
- afterRenderListListener = captor.lastValue
+ afterRenderListListener = withArgCaptor {
+ verify(pipeline).addOnAfterRenderListListener(capture())
+ }
}
@Test
@@ -94,93 +92,9 @@ class StackCoordinatorTest : SysuiTestCase() {
}
@Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
- fun testSetRenderedListOnInteractor_footerFlagOn() {
- afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
- verify(renderListInteractor).setRenderedList(eq(listOf(entry)))
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
fun testSetNotificationStats_clearableAlerting() {
whenever(section.bucket).thenReturn(BUCKET_ALERTING)
afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
- verify(stackController)
- .setNotifStats(
- NotifStats(
- 1,
- hasNonClearableAlertingNotifs = false,
- hasClearableAlertingNotifs = true,
- hasNonClearableSilentNotifs = false,
- hasClearableSilentNotifs = false,
- )
- )
- verifyNoMoreInteractions(activeNotificationsInteractor)
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX)
- fun testSetNotificationStats_isSensitiveStateActive_nonClearableAlerting() {
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
- whenever(section.bucket).thenReturn(BUCKET_ALERTING)
- afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
- verify(stackController)
- .setNotifStats(
- NotifStats(
- 1,
- hasNonClearableAlertingNotifs = true,
- hasClearableAlertingNotifs = false,
- hasNonClearableSilentNotifs = false,
- hasClearableSilentNotifs = false,
- )
- )
- verifyNoMoreInteractions(activeNotificationsInteractor)
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- fun testSetNotificationStats_clearableSilent() {
- whenever(section.bucket).thenReturn(BUCKET_SILENT)
- afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
- verify(stackController)
- .setNotifStats(
- NotifStats(
- 1,
- hasNonClearableAlertingNotifs = false,
- hasClearableAlertingNotifs = false,
- hasNonClearableSilentNotifs = false,
- hasClearableSilentNotifs = true,
- )
- )
- verifyNoMoreInteractions(activeNotificationsInteractor)
- }
-
- @Test
- @DisableFlags(FooterViewRefactor.FLAG_NAME)
- @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX)
- fun testSetNotificationStats_isSensitiveStateActive_nonClearableSilent() {
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
- whenever(section.bucket).thenReturn(BUCKET_SILENT)
- afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
- verify(stackController)
- .setNotifStats(
- NotifStats(
- 1,
- hasNonClearableAlertingNotifs = false,
- hasClearableAlertingNotifs = false,
- hasNonClearableSilentNotifs = true,
- hasClearableSilentNotifs = false,
- )
- )
- verifyNoMoreInteractions(activeNotificationsInteractor)
- }
-
- @Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
- fun testSetNotificationStats_footerFlagOn_clearableAlerting() {
- whenever(section.bucket).thenReturn(BUCKET_ALERTING)
- afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
verify(activeNotificationsInteractor)
.setNotifStats(
NotifStats(
@@ -195,12 +109,8 @@ class StackCoordinatorTest : SysuiTestCase() {
}
@Test
- @EnableFlags(
- FooterViewRefactor.FLAG_NAME,
- FLAG_SCREENSHARE_NOTIFICATION_HIDING,
- FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX,
- )
- fun testSetNotificationStats_footerFlagOn_isSensitiveStateActive_nonClearableAlerting() {
+ @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX)
+ fun testSetNotificationStats_isSensitiveStateActive_nonClearableAlerting() {
whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
whenever(section.bucket).thenReturn(BUCKET_ALERTING)
afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
@@ -218,8 +128,7 @@ class StackCoordinatorTest : SysuiTestCase() {
}
@Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
- fun testSetNotificationStats_footerFlagOn_clearableSilent() {
+ fun testSetNotificationStats_clearableSilent() {
whenever(section.bucket).thenReturn(BUCKET_SILENT)
afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
verify(activeNotificationsInteractor)
@@ -236,12 +145,8 @@ class StackCoordinatorTest : SysuiTestCase() {
}
@Test
- @EnableFlags(
- FooterViewRefactor.FLAG_NAME,
- FLAG_SCREENSHARE_NOTIFICATION_HIDING,
- FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX,
- )
- fun testSetNotificationStats_footerFlagOn_isSensitiveStateActive_nonClearableSilent() {
+ @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX)
+ fun testSetNotificationStats_isSensitiveStateActive_nonClearableSilent() {
whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
whenever(section.bucket).thenReturn(BUCKET_SILENT)
afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
@@ -259,8 +164,7 @@ class StackCoordinatorTest : SysuiTestCase() {
}
@Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
- fun testSetNotificationStats_footerFlagOn_nonClearableRedacted() {
+ fun testSetNotificationStats_nonClearableRedacted() {
entry.setSensitive(true, true)
whenever(section.bucket).thenReturn(BUCKET_ALERTING)
afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
index 25138fd0ff83..57a12df0cfee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
@@ -38,6 +38,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapperTest.Companion.any
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -123,7 +124,8 @@ class IconManagerTest : SysuiTestCase() {
@Test
@EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun testCreateIcons_chipNotifIconFlagEnabled_statusBarChipIconIsNull() {
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun testCreateIcons_chipNotifIconFlagEnabled_cdFlagDisabled_statusBarChipIconIsNotNull() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
entry?.let { iconManager.createIcons(it) }
@@ -133,6 +135,17 @@ class IconManagerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ fun testCreateIcons_chipNotifIconFlagEnabled_cdFlagEnabled_statusBarChipIconIsNull() {
+ val entry =
+ notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
+ entry?.let { iconManager.createIcons(it) }
+ testScope.runCurrent()
+
+ assertThat(entry?.icons?.statusBarChipIcon).isNull()
+ }
+
+ @Test
fun testCreateIcons_importantConversation_shortcutIcon() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
@@ -158,7 +171,7 @@ class IconManagerTest : SysuiTestCase() {
notificationEntry(
hasShortcut = false,
hasMessageSenderIcon = false,
- hasLargeIcon = true
+ hasLargeIcon = true,
)
entry?.channel?.isImportantConversation = true
entry?.let { iconManager.createIcons(it) }
@@ -172,7 +185,7 @@ class IconManagerTest : SysuiTestCase() {
notificationEntry(
hasShortcut = false,
hasMessageSenderIcon = false,
- hasLargeIcon = false
+ hasLargeIcon = false,
)
entry?.channel?.isImportantConversation = true
entry?.let { iconManager.createIcons(it) }
@@ -187,7 +200,7 @@ class IconManagerTest : SysuiTestCase() {
hasShortcut = true,
hasMessageSenderIcon = true,
useMessagingStyle = false,
- hasLargeIcon = true
+ hasLargeIcon = true,
)
entry?.channel?.isImportantConversation = true
entry?.let { iconManager.createIcons(it) }
@@ -205,7 +218,8 @@ class IconManagerTest : SysuiTestCase() {
@Test
@EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun testCreateIcons_sensitiveImportantConversation() {
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun testCreateIcons_cdFlagDisabled_sensitiveImportantConversation() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
entry?.setSensitive(true, true)
@@ -219,8 +233,24 @@ class IconManagerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ fun testCreateIcons_cdFlagEnabled_sensitiveImportantConversation() {
+ val entry =
+ notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
+ entry?.setSensitive(true, true)
+ entry?.channel?.isImportantConversation = true
+ entry?.let { iconManager.createIcons(it) }
+ testScope.runCurrent()
+ assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc)
+ assertThat(entry?.icons?.statusBarChipIcon).isNull()
+ assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc)
+ assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc)
+ }
+
+ @Test
@EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun testUpdateIcons_sensitiveImportantConversation() {
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun testUpdateIcons_cdFlagDisabled_sensitiveImportantConversation() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
entry?.setSensitive(true, true)
@@ -236,6 +266,23 @@ class IconManagerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ fun testUpdateIcons_cdFlagEnabled_sensitiveImportantConversation() {
+ val entry =
+ notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
+ entry?.setSensitive(true, true)
+ entry?.channel?.isImportantConversation = true
+ entry?.let { iconManager.createIcons(it) }
+ // Updating the icons after creation shouldn't break anything
+ entry?.let { iconManager.updateIcons(it) }
+ testScope.runCurrent()
+ assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc)
+ assertThat(entry?.icons?.statusBarChipIcon).isNull()
+ assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc)
+ assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc)
+ }
+
+ @Test
fun testUpdateIcons_sensitivityChange() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
@@ -254,7 +301,7 @@ class IconManagerTest : SysuiTestCase() {
hasShortcut: Boolean,
hasMessageSenderIcon: Boolean,
useMessagingStyle: Boolean = true,
- hasLargeIcon: Boolean
+ hasLargeIcon: Boolean,
): NotificationEntry? {
val n =
Notification.Builder(mContext, "id")
@@ -270,7 +317,7 @@ class IconManagerTest : SysuiTestCase() {
SystemClock.currentThreadTimeMillis(),
Person.Builder()
.setIcon(if (hasMessageSenderIcon) messageIc else null)
- .build()
+ .build(),
)
)
if (useMessagingStyle) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index e1a891662889..3763282cdebc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.stack;
-import static android.view.View.GONE;
import static android.view.WindowInsets.Type.ime;
import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag;
@@ -28,17 +27,14 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
-import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -64,7 +60,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
-import android.widget.TextView;
import androidx.test.filters.SmallTest;
@@ -92,8 +87,6 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
-import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.headsup.AvalancheController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -603,158 +596,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void manageNotifications_visible() {
- FooterView view = mock(FooterView.class);
- mStackScroller.setFooterView(view);
- when(view.willBeGone()).thenReturn(true);
-
- mStackScroller.updateFooterView(true, false, true);
-
- verify(view).setVisible(eq(true), anyBoolean());
- verify(view).setClearAllButtonVisible(eq(false), anyBoolean());
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void clearAll_visible() {
- FooterView view = mock(FooterView.class);
- mStackScroller.setFooterView(view);
- when(view.willBeGone()).thenReturn(true);
-
- mStackScroller.updateFooterView(true, true, true);
-
- verify(view).setVisible(eq(true), anyBoolean());
- verify(view).setClearAllButtonVisible(eq(true), anyBoolean());
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testInflateFooterView() {
- mStackScroller.inflateFooterView();
- ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
- verify(mStackScroller).setFooterView(captor.capture());
-
- assertNotNull(captor.getValue().findViewById(R.id.manage_text));
- assertNotNull(captor.getValue().findViewById(R.id.dismiss_text));
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testUpdateFooter_noNotifications() {
- setBarStateForTest(StatusBarState.SHADE);
- mStackScroller.setCurrentUserSetup(true);
-
- FooterView view = mock(FooterView.class);
- mStackScroller.setFooterView(view);
- mStackScroller.updateFooter();
- verify(mStackScroller, atLeastOnce()).updateFooterView(false, false, true);
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- @DisableSceneContainer
- public void testUpdateFooter_remoteInput() {
- setBarStateForTest(StatusBarState.SHADE);
- mStackScroller.setCurrentUserSetup(true);
-
- mStackScroller.setIsRemoteInputActive(true);
- when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
- .thenReturn(true);
-
- FooterView view = mock(FooterView.class);
- mStackScroller.setFooterView(view);
- mStackScroller.updateFooter();
- verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testUpdateFooter_withoutNotifications() {
- setBarStateForTest(StatusBarState.SHADE);
- mStackScroller.setCurrentUserSetup(true);
-
- when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
- .thenReturn(false);
-
- FooterView view = mock(FooterView.class);
- mStackScroller.setFooterView(view);
- mStackScroller.updateFooter();
- verify(mStackScroller, atLeastOnce()).updateFooterView(false, false, true);
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- @DisableSceneContainer
- public void testUpdateFooter_oneClearableNotification() {
- setBarStateForTest(StatusBarState.SHADE);
- mStackScroller.setCurrentUserSetup(true);
-
- when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
- .thenReturn(true);
-
- FooterView view = mock(FooterView.class);
- mStackScroller.setFooterView(view);
- mStackScroller.updateFooter();
- verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, true);
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- @DisableSceneContainer
- public void testUpdateFooter_withoutHistory() {
- setBarStateForTest(StatusBarState.SHADE);
- mStackScroller.setCurrentUserSetup(true);
-
- when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(false);
- when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
- .thenReturn(true);
-
- FooterView view = mock(FooterView.class);
- mStackScroller.setFooterView(view);
- mStackScroller.updateFooter();
- verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, false);
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void testUpdateFooter_oneClearableNotification_beforeUserSetup() {
- setBarStateForTest(StatusBarState.SHADE);
- mStackScroller.setCurrentUserSetup(false);
-
- when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
- .thenReturn(true);
-
- FooterView view = mock(FooterView.class);
- mStackScroller.setFooterView(view);
- mStackScroller.updateFooter();
- verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- @DisableSceneContainer
- public void testUpdateFooter_oneNonClearableNotification() {
- setBarStateForTest(StatusBarState.SHADE);
- mStackScroller.setCurrentUserSetup(true);
-
- when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
- .thenReturn(false);
- when(mEmptyShadeView.getVisibility()).thenReturn(GONE);
-
- FooterView view = mock(FooterView.class);
- mStackScroller.setFooterView(view);
- mStackScroller.updateFooter();
- verify(mStackScroller, atLeastOnce()).updateFooterView(true, false, true);
- }
-
- @Test
public void testFooterPosition_atEnd() {
// add footer
FooterView view = mock(FooterView.class);
@@ -772,19 +613,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME,
- ModesEmptyShadeFix.FLAG_NAME,
- NotifRedesignFooter.FLAG_NAME})
- public void testReInflatesFooterViews() {
- when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
- clearInvocations(mStackScroller);
- mStackScroller.reinflateViews();
- verify(mStackScroller).setFooterView(any());
- verify(mStackScroller).setEmptyShadeView(any());
- }
-
- @Test
- @EnableFlags(FooterViewRefactor.FLAG_NAME)
@DisableFlags(ModesEmptyShadeFix.FLAG_NAME)
public void testReInflatesEmptyShadeView() {
when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
@@ -1231,31 +1059,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
- public void hasFilteredOutSeenNotifs_updateFooter() {
- mStackScroller.setCurrentUserSetup(true);
-
- // add footer
- mStackScroller.inflateFooterView();
- TextView footerLabel =
- mStackScroller.mFooterView.requireViewById(R.id.unlock_prompt_footer);
-
- mStackScroller.setHasFilteredOutSeenNotifications(true);
- mStackScroller.updateFooter();
-
- assertThat(footerLabel.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, ModesEmptyShadeFix.FLAG_NAME})
- public void hasFilteredOutSeenNotifs_updateEmptyShadeView() {
- mStackScroller.setHasFilteredOutSeenNotifications(true);
- mStackScroller.updateEmptyShadeView(true, false);
-
- verify(mEmptyShadeView).setFooterText(not(eq(0)));
- }
-
- @Test
@DisableSceneContainer
public void testWindowInsetAnimationProgress_updatesBottomInset() {
int imeInset = 100;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
index 59ad38a87146..59ad38a87146 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
index 17093291e8b0..2a1877adc172 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
@@ -24,7 +24,6 @@ fun fakeDialogTransitionAnimator(
@Main mainExecutor: Executor,
isUnlocked: Boolean = true,
isShowingAlternateAuthOnUnlock: Boolean = false,
- isPredictiveBackQsDialogAnim: Boolean = false,
interactionJankMonitor: InteractionJankMonitor,
): DialogTransitionAnimator {
return DialogTransitionAnimator(
@@ -35,10 +34,6 @@ fun fakeDialogTransitionAnimator(
isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
),
interactionJankMonitor = interactionJankMonitor,
- featureFlags =
- object : AnimationFeatureFlags {
- override val isPredictiveBackQsDialogAnim = isPredictiveBackQsDialogAnim
- },
transitionAnimator = fakeTransitionAnimator(mainExecutor),
isForTesting = true,
)
@@ -50,6 +45,8 @@ private class FakeCallback(
private val isShowingAlternateAuthOnUnlock: Boolean = false,
) : DialogTransitionAnimator.Callback {
override fun isDreaming(): Boolean = isDreaming
+
override fun isUnlocked(): Boolean = isUnlocked
+
override fun isShowingAlternateAuthOnUnlock() = isShowingAlternateAuthOnUnlock
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 47991b3b9689..3df3ee983ecf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -154,6 +154,7 @@ val Kosmos.shortcutHelperCoreStartable by
shortcutHelperStateRepository,
activityStarter,
testScope,
+ customInputGesturesRepository
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 4a86fd5e49ff..74deaab67766 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -21,6 +21,8 @@ import com.android.systemui.dagger.SysUISingleton
import dagger.Binds
import dagger.Module
import javax.inject.Inject
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -37,8 +39,8 @@ class FakeShadeRepository @Inject constructor() : ShadeRepository {
override val udfpsTransitionToFullShadeProgress =
_udfpsTransitionToFullShadeProgress.asStateFlow()
- private val _currentFling: MutableStateFlow<FlingInfo?> = MutableStateFlow(null)
- override val currentFling: StateFlow<FlingInfo?> = _currentFling.asStateFlow()
+ override val currentFling: MutableSharedFlow<FlingInfo?> =
+ MutableSharedFlow(replay = 2, onBufferOverflow = BufferOverflow.DROP_OLDEST)
private val _lockscreenShadeExpansion = MutableStateFlow(0f)
override val lockscreenShadeExpansion = _lockscreenShadeExpansion.asStateFlow()
@@ -139,7 +141,7 @@ class FakeShadeRepository @Inject constructor() : ShadeRepository {
}
override fun setCurrentFling(info: FlingInfo?) {
- _currentFling.value = info
+ currentFling.tryEmit(info)
}
override fun setLockscreenShadeExpansion(lockscreenShadeExpansion: Float) {
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index ac545dfb06cc..c4086c5b3835 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -40,6 +40,7 @@ java_defaults {
"junit-params",
"platform-parametric-runner-lib",
+ "platform-compat-test-rules",
// To make sure it won't cause VerifyError (b/324063814)
"platformprotosnano",
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
index 882c91c43ee9..540b0822319c 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
@@ -16,31 +16,52 @@
package com.android.ravenwoodtest.bivalenttest.compat
import android.app.compat.CompatChanges
+import android.compat.testing.PlatformCompatChangeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.ravenwood.RavenwoodEnvironment.CompatIdsForTest
-import org.junit.Assert
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class RavenwoodCompatFrameworkTest {
+
+ @get:Rule
+ val compatRule = PlatformCompatChangeRule()
+
@Test
fun testEnabled() {
- Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_1))
+ assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_1))
}
@Test
fun testDisabled() {
- Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_2))
+ assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_2))
}
@Test
fun testEnabledAfterSForUApps() {
- Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_3))
+ assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_3))
}
@Test
fun testEnabledAfterUForUApps() {
- Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_4))
+ assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_4))
+ }
+
+ @Test
+ @EnableCompatChanges(CompatIdsForTest.TEST_COMPAT_ID_5)
+ fun testEnableCompatChanges() {
+ assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_5))
+ }
+
+ @Test
+ @DisableCompatChanges(CompatIdsForTest.TEST_COMPAT_ID_5)
+ fun testDisableCompatChanges() {
+ assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_5))
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index a69ececd5373..e3d7062ddb4e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -18,6 +18,8 @@ package com.android.server.accessibility;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static com.android.server.accessibility.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
+
import android.accessibilityservice.AccessibilityTrace;
import android.annotation.NonNull;
import android.content.ContentResolver;
@@ -286,8 +288,6 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
public void update() {
- // TODO(b/383901288): update delay time once determined by UX.
- long SHOW_INDICATOR_DELAY_TIME = 150;
long scheduledShowIndicatorTime =
SystemClock.uptimeMillis() + SHOW_INDICATOR_DELAY_TIME;
// If there already is a scheduled show indicator at time before the updated time, just
@@ -432,6 +432,10 @@ public class AutoclickController extends BaseEventStreamTransformation {
*/
public void updateDelay(int delay) {
mDelay = delay;
+
+ if (Flags.enableAutoclickIndicator() && mAutoclickIndicatorView != null) {
+ mAutoclickIndicatorView.setAnimationDuration(delay - SHOW_INDICATOR_DELAY_TIME);
+ }
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
index 54c31e5bca79..816d8e456a9a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
@@ -16,25 +16,43 @@
package com.android.server.accessibility;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.LinearInterpolator;
// A visual indicator for the autoclick feature.
public class AutoclickIndicatorView extends View {
private static final String TAG = AutoclickIndicatorView.class.getSimpleName();
+ // TODO(b/383901288): update delay time once determined by UX.
+ static final int SHOW_INDICATOR_DELAY_TIME = 150;
+
+ static final int MINIMAL_ANIMATION_DURATION = 50;
+
// TODO(b/383901288): allow users to customize the indicator area.
static final float RADIUS = 50;
private final Paint mPaint;
+ private final ValueAnimator mAnimator;
+
+ private final RectF mRingRect;
+
// x and y coordinates of the visual indicator.
private float mX;
private float mY;
+ // Current sweep angle of the animated ring.
+ private float mSweepAngle;
+
+ private int mAnimationDuration = AccessibilityManager.AUTOCLICK_DELAY_DEFAULT;
+
// Status of whether the visual indicator should display or not.
private boolean showIndicator = false;
@@ -46,6 +64,18 @@ public class AutoclickIndicatorView extends View {
mPaint.setARGB(255, 52, 103, 235);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10);
+
+ mAnimator = ValueAnimator.ofFloat(0, 360);
+ mAnimator.setDuration(mAnimationDuration);
+ mAnimator.setInterpolator(new LinearInterpolator());
+ mAnimator.addUpdateListener(
+ animation -> {
+ mSweepAngle = (float) animation.getAnimatedValue();
+ // Redraw the view with the updated angle.
+ invalidate();
+ });
+
+ mRingRect = new RectF();
}
@Override
@@ -53,7 +83,12 @@ public class AutoclickIndicatorView extends View {
super.onDraw(canvas);
if (showIndicator) {
- canvas.drawCircle(mX, mY, RADIUS, mPaint);
+ mRingRect.set(
+ /* left= */ mX - RADIUS,
+ /* top= */ mY - RADIUS,
+ /* right= */ mX + RADIUS,
+ /* bottom= */ mY + RADIUS);
+ canvas.drawArc(mRingRect, /* startAngle= */ -90, mSweepAngle, false, mPaint);
}
}
@@ -75,10 +110,17 @@ public class AutoclickIndicatorView extends View {
public void redrawIndicator() {
showIndicator = true;
invalidate();
+ mAnimator.start();
}
public void clearIndicator() {
showIndicator = false;
+ mAnimator.cancel();
invalidate();
}
+
+ public void setAnimationDuration(int duration) {
+ mAnimationDuration = Math.max(duration, MINIMAL_ANIMATION_DURATION);
+ mAnimator.setDuration(mAnimationDuration);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index f15b8eec3f6b..cd46b38272c2 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -38,7 +38,7 @@ public class TouchState {
// Pointer-related constants
// This constant captures the current implementation detail that
// pointer IDs are between 0 and 31 inclusive (subject to change).
- // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+ // (See MAX_POINTER_ID in frameworks/native/include/input/Input.h)
public static final int MAX_POINTER_COUNT = 32;
// Constant referring to the ids bits of all pointers.
public static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
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 1f3b31692289..aeb2f5e9be84 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -176,7 +176,7 @@ public class VirtualDeviceManagerService extends SystemService {
public VirtualDeviceManagerService(Context context) {
super(context);
mImpl = new VirtualDeviceManagerImpl();
- mNativeImpl = Flags.enableNativeVdm() ? new VirtualDeviceManagerNativeImpl() : null;
+ mNativeImpl = new VirtualDeviceManagerNativeImpl();
mLocalService = new LocalService();
}
@@ -208,9 +208,7 @@ public class VirtualDeviceManagerService extends SystemService {
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
public void onStart() {
publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
- if (Flags.enableNativeVdm()) {
- publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl);
- }
+ publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl);
publishLocalService(VirtualDeviceManagerInternal.class, mLocalService);
ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService(
ActivityTaskManagerInternal.class);
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 778c6864282d..31f6ef9fc062 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1708,7 +1708,7 @@ public class BinaryTransparencyService extends SystemService {
private class PackageUpdatedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (!intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
+ if (!Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
return;
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index dce9760b3971..6459016eec75 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -66,8 +66,7 @@ import com.android.server.wm.WindowManagerInternal;
/**
* The service that listens for gestures detected in sensor firmware and starts the intent
* accordingly.
- * <p>For now, only camera launch gesture is supported, and in the future, more gestures can be
- * added.</p>
+ *
* @hide
*/
public class GestureLauncherService extends SystemService {
@@ -109,10 +108,22 @@ public class GestureLauncherService extends SystemService {
@VisibleForTesting
static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;
- /** Indicates camera should be launched on power double tap. */
+ /** Configuration value indicating double tap power gesture is disabled. */
+ @VisibleForTesting static final int DOUBLE_TAP_POWER_DISABLED_MODE = 0;
+
+ /** Configuration value indicating double tap power gesture should launch camera. */
+ @VisibleForTesting static final int DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE = 1;
+
+ /**
+ * Configuration value indicating double tap power gesture should launch one of many target
+ * actions.
+ */
+ @VisibleForTesting static final int DOUBLE_TAP_POWER_MULTI_TARGET_MODE = 2;
+
+ /** Indicates camera launch is selected as target action for multi target double tap power. */
@VisibleForTesting static final int LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER = 0;
- /** Indicates wallet should be launched on power double tap. */
+ /** Indicates wallet launch is selected as target action for multi target double tap power. */
@VisibleForTesting static final int LAUNCH_WALLET_ON_DOUBLE_TAP_POWER = 1;
/** Number of taps required to launch the double tap shortcut (either camera or wallet). */
@@ -228,6 +239,7 @@ public class GestureLauncherService extends SystemService {
return mId;
}
}
+
public GestureLauncherService(Context context) {
this(context, new MetricsLogger(),
QuickAccessWalletClient.create(context), new UiEventLoggerImpl());
@@ -289,16 +301,15 @@ public class GestureLauncherService extends SystemService {
Settings.Secure.getUriFor(
Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE),
false, mSettingObserver, mUserId);
- } else {
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
- false, mSettingObserver, mUserId);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
- false, mSettingObserver, mUserId);
}
mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
+ false, mSettingObserver, mUserId);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
+ false, mSettingObserver, mUserId);
+ mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED),
false, mSettingObserver, mUserId);
mContext.getContentResolver().registerContentObserver(
@@ -468,23 +479,27 @@ public class GestureLauncherService extends SystemService {
Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
}
-
/** Checks if camera should be launched on double press of the power button. */
public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) {
- boolean res;
-
- if (launchWalletOptionOnPowerDoubleTap()) {
- res = isDoubleTapPowerGestureSettingEnabled(context, userId)
- && getDoubleTapPowerGestureAction(context, userId)
- == LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
- } else {
- // These are legacy settings that will be deprecated once the option to launch both
- // wallet and camera has been created.
- res = isCameraDoubleTapPowerEnabled(context.getResources())
+ if (!launchWalletOptionOnPowerDoubleTap()) {
+ return isCameraDoubleTapPowerEnabled(context.getResources())
&& (Settings.Secure.getIntForUser(context.getContentResolver(),
Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
}
- return res;
+
+ final int doubleTapPowerGestureSettingMode = getDoubleTapPowerGestureMode(
+ context.getResources());
+
+ return switch (doubleTapPowerGestureSettingMode) {
+ case DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE -> Settings.Secure.getIntForUser(
+ context.getContentResolver(),
+ Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0;
+ case DOUBLE_TAP_POWER_MULTI_TARGET_MODE ->
+ isMultiTargetDoubleTapPowerGestureSettingEnabled(context, userId)
+ && getDoubleTapPowerGestureAction(context, userId)
+ == LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
+ default -> false;
+ };
}
/** Checks if wallet should be launched on double tap of the power button. */
@@ -493,7 +508,9 @@ public class GestureLauncherService extends SystemService {
return false;
}
- return isDoubleTapPowerGestureSettingEnabled(context, userId)
+ return getDoubleTapPowerGestureMode(context.getResources())
+ == DOUBLE_TAP_POWER_MULTI_TARGET_MODE
+ && isMultiTargetDoubleTapPowerGestureSettingEnabled(context, userId)
&& getDoubleTapPowerGestureAction(context, userId)
== LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
}
@@ -515,26 +532,40 @@ public class GestureLauncherService extends SystemService {
isDefaultEmergencyGestureEnabled(context.getResources()) ? 1 : 0, userId) != 0;
}
- private static int getDoubleTapPowerGestureAction(Context context, int userId) {
- return Settings.Secure.getIntForUser(
- context.getContentResolver(),
- Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
- LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER,
- userId);
+ /** Gets the double tap power gesture mode. */
+ private static int getDoubleTapPowerGestureMode(Resources resources) {
+ return resources.getInteger(R.integer.config_doubleTapPowerGestureMode);
}
- /** Whether the shortcut to launch app on power double press is enabled. */
- private static boolean isDoubleTapPowerGestureSettingEnabled(Context context, int userId) {
+ /**
+ * Whether the setting for multi target double tap power gesture is enabled.
+ *
+ * <p>Multi target double tap power gesture allows the user to choose one of many target actions
+ * when double tapping the power button.
+ * </p>
+ */
+ private static boolean isMultiTargetDoubleTapPowerGestureSettingEnabled(Context context,
+ int userId) {
return Settings.Secure.getIntForUser(
context.getContentResolver(),
Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
- isDoubleTapConfigEnabled(context.getResources()) ? 1 : 0,
+ getDoubleTapPowerGestureMode(context.getResources())
+ == DOUBLE_TAP_POWER_MULTI_TARGET_MODE ? 1 : 0,
userId)
== 1;
}
- private static boolean isDoubleTapConfigEnabled(Resources resources) {
- return resources.getBoolean(R.bool.config_doubleTapPowerGestureEnabled);
+ /** Gets the selected target action for the multi target double tap power gesture.
+ *
+ * <p>The target actions are defined in {@link Settings.Secure#DOUBLE_TAP_POWER_BUTTON_GESTURE}.
+ * </p>
+ */
+ private static int getDoubleTapPowerGestureAction(Context context, int userId) {
+ return Settings.Secure.getIntForUser(
+ context.getContentResolver(),
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
+ LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER,
+ userId);
}
/**
@@ -595,7 +626,7 @@ public class GestureLauncherService extends SystemService {
|| isCameraLiftTriggerEnabled(resources)
|| isEmergencyGestureEnabled(resources);
if (launchWalletOptionOnPowerDoubleTap()) {
- res |= isDoubleTapConfigEnabled(resources);
+ res |= getDoubleTapPowerGestureMode(resources) != DOUBLE_TAP_POWER_DISABLED_MODE;
} else {
res |= isCameraDoubleTapPowerEnabled(resources);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 41b4cbd9e074..6cca7d16842a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -8950,18 +8950,12 @@ public final class ActiveServices {
if (!mAm.mConstants.mFgsStartRestrictionCheckCallerTargetSdk) {
return true; // In this case, we only check the service's target SDK level.
}
- final int callingUid;
- if (Flags.newFgsRestrictionLogic()) {
- // We always consider SYSTEM_UID to target S+, so just enable the restrictions.
- if (actualCallingUid == Process.SYSTEM_UID) {
- return true;
- }
- callingUid = actualCallingUid;
- } else {
- // Legacy logic used mRecentCallingUid.
- callingUid = r.mRecentCallingUid;
+ // We always consider SYSTEM_UID to target S+, so just enable the restrictions.
+ if (actualCallingUid == Process.SYSTEM_UID) {
+ return true;
}
- if (!CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, callingUid)) {
+ if (!CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID,
+ actualCallingUid)) {
return false; // If the caller targets < S, then we still disable the restrictions.
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3817ba1a28b9..0b7890167c08 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -305,6 +305,10 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
this.stringName = null;
}
+ @VisibleForTesting TempAllowListDuration getAllowlistDurationLocked(IBinder allowlistToken) {
+ return mAllowlistDuration.get(allowlistToken);
+ }
+
void setAllowBgActivityStarts(IBinder token, int flags) {
if (token == null) return;
if ((flags & FLAG_ACTIVITY_SENDER) != 0) {
@@ -323,6 +327,12 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
mAllowBgActivityStartsForActivitySender.remove(token);
mAllowBgActivityStartsForBroadcastSender.remove(token);
mAllowBgActivityStartsForServiceSender.remove(token);
+ if (mAllowlistDuration != null) {
+ mAllowlistDuration.remove(token);
+ if (mAllowlistDuration.isEmpty()) {
+ mAllowlistDuration = null;
+ }
+ }
}
public void registerCancelListenerLocked(IResultReceiver receiver) {
@@ -703,7 +713,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
return res;
}
- private BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
+ @VisibleForTesting BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
IBinder allowlistToken) {
return mAllowBgActivityStartsForActivitySender.contains(allowlistToken)
? BackgroundStartPrivileges.allowBackgroundActivityStarts(allowlistToken)
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 92d33c9eae56..ca34a13c55b1 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -278,24 +278,21 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
* Whether to use the new "while-in-use permission" logic for FGS start
*/
private boolean useNewWiuLogic_forStart() {
- return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
- && CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_START, appInfo.uid);
+ return CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_START, appInfo.uid);
}
/**
* Whether to use the new "while-in-use permission" logic for capabilities
*/
private boolean useNewWiuLogic_forCapabilities() {
- return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
- && CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_CAPABILITIES, appInfo.uid);
+ return CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_CAPABILITIES, appInfo.uid);
}
/**
* Whether to use the new "FGS BG start exemption" logic.
*/
private boolean useNewBfslLogic() {
- return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
- && CompatChanges.isChangeEnabled(USE_NEW_BFSL_LOGIC, appInfo.uid);
+ return CompatChanges.isChangeEnabled(USE_NEW_BFSL_LOGIC, appInfo.uid);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 295e0443371d..8a63f9a24ea3 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -359,7 +359,7 @@ public class AppOpsService extends IAppOpsService.Stub {
private static final Duration RATE_LIMITER_WINDOW = Duration.ofMillis(10);
private final RateLimiter mRateLimiter = new RateLimiter(RATE_LIMITER_WINDOW);
- volatile @NonNull HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this);
+ volatile @NonNull HistoricalRegistry mHistoricalRegistry;
/*
* These are app op restrictions imposed per user from various parties.
@@ -1039,6 +1039,8 @@ public class AppOpsService extends IAppOpsService.Stub {
// will not exist and the nonce will be UNSET.
AppOpsManager.invalidateAppOpModeCache();
AppOpsManager.disableAppOpModeCache();
+
+ mHistoricalRegistry = new HistoricalRegistry(this, context);
}
public void publish() {
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 4d114b4ad4ac..9dd09cef88f9 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -113,7 +113,7 @@ final class AttributedOp {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE,
- DiscreteRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
+ DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
}
/**
@@ -257,7 +257,8 @@ final class AttributedOp {
if (isStarted) {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
- attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP, 1);
+ attributionFlags, attributionChainId,
+ DiscreteOpsRegistry.ACCESS_TYPE_START_OP, 1);
}
}
@@ -344,8 +345,8 @@ final class AttributedOp {
parent.packageName, persistentDeviceId, tag, event.getUidState(),
event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
event.getAttributionFlags(), event.getAttributionChainId(),
- isPausing ? DiscreteRegistry.ACCESS_TYPE_PAUSE_OP
- : DiscreteRegistry.ACCESS_TYPE_FINISH_OP);
+ isPausing ? DiscreteOpsRegistry.ACCESS_TYPE_PAUSE_OP
+ : DiscreteOpsRegistry.ACCESS_TYPE_FINISH_OP);
if (!isPausing) {
mAppOpsService.mInProgressStartOpEventPool.release(event);
@@ -453,7 +454,7 @@ final class AttributedOp {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, event.getUidState(),
event.getFlags(), startTime, event.getAttributionFlags(),
- event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP, 1);
+ event.getAttributionChainId(), DiscreteOpsRegistry.ACCESS_TYPE_RESUME_OP, 1);
if (shouldSendActive) {
mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, tag, event.getVirtualDeviceId(), true,
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
new file mode 100644
index 000000000000..e4c36cc214e8
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -0,0 +1,387 @@
+/*
+ * 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.appop;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.database.DatabaseErrorHandler;
+import android.database.DefaultDatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteRawStatement;
+import android.os.Environment;
+import android.util.IntArray;
+import android.util.Slog;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+class DiscreteOpsDbHelper extends SQLiteOpenHelper {
+ private static final String LOG_TAG = "DiscreteOpsDbHelper";
+ static final String DATABASE_NAME = "app_op_history.db";
+ private static final int DATABASE_VERSION = 1;
+ private static final boolean DEBUG = false;
+
+ DiscreteOpsDbHelper(@NonNull Context context, @NonNull File databaseFile) {
+ super(context, databaseFile.getAbsolutePath(), null, DATABASE_VERSION,
+ new DiscreteOpsDatabaseErrorHandler());
+ setOpenParams(getDatabaseOpenParams());
+ }
+
+ private static SQLiteDatabase.OpenParams getDatabaseOpenParams() {
+ return new SQLiteDatabase.OpenParams.Builder()
+ .addOpenFlags(SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING)
+ .build();
+ }
+
+ @NonNull
+ static File getDatabaseFile() {
+ return new File(new File(Environment.getDataSystemDirectory(), "appops"), DATABASE_NAME);
+ }
+
+ @Override
+ public void onConfigure(SQLiteDatabase db) {
+ db.execSQL("PRAGMA synchronous = NORMAL");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(DiscreteOpsTable.CREATE_TABLE_SQL);
+ db.execSQL(DiscreteOpsTable.CREATE_INDEX_SQL);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ }
+
+ void insertDiscreteOps(@NonNull List<DiscreteOpsSqlRegistry.DiscreteOp> opEvents) {
+ if (opEvents.isEmpty()) {
+ return;
+ }
+
+ SQLiteDatabase db = getWritableDatabase();
+ // TODO (b/383157289) what if database is busy and can't start a transaction? will read
+ // more about it and can be done in a follow up cl.
+ db.beginTransaction();
+ try (SQLiteRawStatement statement = db.createRawStatement(
+ DiscreteOpsTable.INSERT_TABLE_SQL)) {
+ for (DiscreteOpsSqlRegistry.DiscreteOp event : opEvents) {
+ try {
+ statement.bindInt(DiscreteOpsTable.UID_INDEX, event.getUid());
+ bindTextOrNull(statement, DiscreteOpsTable.PACKAGE_NAME_INDEX,
+ event.getPackageName());
+ bindTextOrNull(statement, DiscreteOpsTable.DEVICE_ID_INDEX,
+ event.getDeviceId());
+ statement.bindInt(DiscreteOpsTable.OP_CODE_INDEX, event.getOpCode());
+ bindTextOrNull(statement, DiscreteOpsTable.ATTRIBUTION_TAG_INDEX,
+ event.getAttributionTag());
+ statement.bindLong(DiscreteOpsTable.ACCESS_TIME_INDEX, event.getAccessTime());
+ statement.bindLong(
+ DiscreteOpsTable.ACCESS_DURATION_INDEX, event.getDuration());
+ statement.bindInt(DiscreteOpsTable.UID_STATE_INDEX, event.getUidState());
+ statement.bindInt(DiscreteOpsTable.OP_FLAGS_INDEX, event.getOpFlags());
+ statement.bindInt(DiscreteOpsTable.ATTRIBUTION_FLAGS_INDEX,
+ event.getAttributionFlags());
+ statement.bindLong(DiscreteOpsTable.CHAIN_ID_INDEX, event.getChainId());
+ statement.step();
+ } catch (Exception exception) {
+ Slog.e(LOG_TAG, "Error inserting the discrete op: " + event, exception);
+ } finally {
+ statement.reset();
+ }
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ private void bindTextOrNull(SQLiteRawStatement statement, int index, @Nullable String text) {
+ if (text == null) {
+ statement.bindNull(index);
+ } else {
+ statement.bindText(index, text);
+ }
+ }
+
+ /**
+ * This will be used as an offset for inserting new chain id in discrete ops table.
+ */
+ long getLargestAttributionChainId() {
+ long chainId = 0;
+ try {
+ SQLiteDatabase db = getReadableDatabase();
+ db.beginTransactionReadOnly();
+ try (SQLiteRawStatement statement =
+ db.createRawStatement(DiscreteOpsTable.SELECT_MAX_ATTRIBUTION_CHAIN_ID)) {
+ if (statement.step()) {
+ chainId = statement.getColumnLong(0);
+ if (chainId < 0) {
+ chainId = 0;
+ }
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ } catch (SQLiteException exception) {
+ Slog.e(LOG_TAG, "Error reading attribution chain id", exception);
+ }
+ return chainId;
+ }
+
+ void execSQL(@NonNull String sql) {
+ execSQL(sql, null);
+ }
+
+ void execSQL(@NonNull String sql, Object[] bindArgs) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "DB execSQL, sql: " + sql);
+ }
+ SQLiteDatabase db = getWritableDatabase();
+ if (bindArgs == null) {
+ db.execSQL(sql);
+ } else {
+ db.execSQL(sql, bindArgs);
+ }
+ }
+
+ /**
+ * Returns a list of {@link DiscreteOpsSqlRegistry.DiscreteOp} based on the given filters.
+ */
+ List<DiscreteOpsSqlRegistry.DiscreteOp> getDiscreteOps(
+ @AppOpsManager.HistoricalOpsRequestFilter int requestFilters,
+ int uidFilter, @Nullable String packageNameFilter,
+ @Nullable String attributionTagFilter, IntArray opCodesFilter, int opFlagsFilter,
+ long beginTime, long endTime, int limit, String orderByColumn) {
+ List<SQLCondition> conditions = prepareConditions(beginTime, endTime, requestFilters,
+ uidFilter, packageNameFilter,
+ attributionTagFilter, opCodesFilter, opFlagsFilter);
+ String sql = buildSql(conditions, orderByColumn, limit);
+
+ SQLiteDatabase db = getReadableDatabase();
+ List<DiscreteOpsSqlRegistry.DiscreteOp> results = new ArrayList<>();
+ db.beginTransactionReadOnly();
+ try (SQLiteRawStatement statement = db.createRawStatement(sql)) {
+ int size = conditions.size();
+ for (int i = 0; i < size; i++) {
+ SQLCondition condition = conditions.get(i);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, condition + ", binding value = " + condition.mFilterValue);
+ }
+ switch (condition.mColumnFilter) {
+ case PACKAGE_NAME, ATTR_TAG -> statement.bindText(i + 1,
+ condition.mFilterValue.toString());
+ case UID, OP_CODE_EQUAL, OP_FLAGS -> statement.bindInt(i + 1,
+ Integer.parseInt(condition.mFilterValue.toString()));
+ case BEGIN_TIME, END_TIME -> statement.bindLong(i + 1,
+ Long.parseLong(condition.mFilterValue.toString()));
+ case OP_CODE_IN -> Slog.d(LOG_TAG, "No binding for In operator");
+ default -> Slog.w(LOG_TAG, "unknown sql condition " + condition);
+ }
+ }
+
+ while (statement.step()) {
+ int uid = statement.getColumnInt(0);
+ String packageName = statement.getColumnText(1);
+ String deviceId = statement.getColumnText(2);
+ int opCode = statement.getColumnInt(3);
+ String attributionTag = statement.getColumnText(4);
+ long accessTime = statement.getColumnLong(5);
+ long duration = statement.getColumnLong(6);
+ int uidState = statement.getColumnInt(7);
+ int opFlags = statement.getColumnInt(8);
+ int attributionFlags = statement.getColumnInt(9);
+ long chainId = statement.getColumnLong(10);
+ DiscreteOpsSqlRegistry.DiscreteOp event = new DiscreteOpsSqlRegistry.DiscreteOp(uid,
+ packageName, attributionTag, deviceId, opCode,
+ opFlags, attributionFlags, uidState, chainId, accessTime, duration);
+ results.add(event);
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ return results;
+ }
+
+ private String buildSql(List<SQLCondition> conditions, String orderByColumn, int limit) {
+ StringBuilder sql = new StringBuilder(DiscreteOpsTable.SELECT_TABLE_DATA);
+ if (!conditions.isEmpty()) {
+ sql.append(" WHERE ");
+ int size = conditions.size();
+ for (int i = 0; i < size; i++) {
+ sql.append(conditions.get(i).toString());
+ if (i < size - 1) {
+ sql.append(" AND ");
+ }
+ }
+ }
+
+ if (orderByColumn != null) {
+ sql.append(" ORDER BY ").append(orderByColumn);
+ }
+ if (limit > 0) {
+ sql.append(" LIMIT ").append(limit);
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Sql query " + sql);
+ }
+ return sql.toString();
+ }
+
+ /**
+ * Creates where conditions for package, uid, attribution tag and app op codes,
+ * app op codes condition does not support argument binding.
+ */
+ private List<SQLCondition> prepareConditions(long beginTime, long endTime, int requestFilters,
+ int uid, @Nullable String packageName, @Nullable String attributionTag,
+ IntArray opCodes, int opFlags) {
+ final List<SQLCondition> conditions = new ArrayList<>();
+
+ if (beginTime != -1) {
+ conditions.add(new SQLCondition(ColumnFilter.BEGIN_TIME, beginTime));
+ }
+ if (endTime != -1) {
+ conditions.add(new SQLCondition(ColumnFilter.END_TIME, endTime));
+ }
+ if (opFlags != 0) {
+ conditions.add(new SQLCondition(ColumnFilter.OP_FLAGS, opFlags));
+ }
+
+ if (requestFilters != 0) {
+ if ((requestFilters & AppOpsManager.FILTER_BY_PACKAGE_NAME) != 0) {
+ conditions.add(new SQLCondition(ColumnFilter.PACKAGE_NAME, packageName));
+ }
+ if ((requestFilters & AppOpsManager.FILTER_BY_UID) != 0) {
+ conditions.add(new SQLCondition(ColumnFilter.UID, uid));
+
+ }
+ if ((requestFilters & AppOpsManager.FILTER_BY_ATTRIBUTION_TAG) != 0) {
+ conditions.add(new SQLCondition(ColumnFilter.ATTR_TAG, attributionTag));
+ }
+ // filter op codes
+ if (opCodes != null && opCodes.size() == 1) {
+ conditions.add(new SQLCondition(ColumnFilter.OP_CODE_EQUAL, opCodes.get(0)));
+ } else if (opCodes != null && opCodes.size() > 1) {
+ StringBuilder b = new StringBuilder();
+ int size = opCodes.size();
+ for (int i = 0; i < size; i++) {
+ b.append(opCodes.get(i));
+ if (i < size - 1) {
+ b.append(", ");
+ }
+ }
+ conditions.add(new SQLCondition(ColumnFilter.OP_CODE_IN, b.toString()));
+ }
+ }
+ return conditions;
+ }
+
+ /**
+ * This class prepares a where clause condition for discrete ops table column.
+ */
+ static final class SQLCondition {
+ private final ColumnFilter mColumnFilter;
+ private final Object mFilterValue;
+
+ SQLCondition(ColumnFilter columnFilter, Object filterValue) {
+ mColumnFilter = columnFilter;
+ mFilterValue = filterValue;
+ }
+
+ @Override
+ public String toString() {
+ if (mColumnFilter == ColumnFilter.OP_CODE_IN) {
+ return mColumnFilter + " ( " + mFilterValue + " )";
+ }
+ return mColumnFilter.toString();
+ }
+ }
+
+ /**
+ * This enum describes the where clause conditions for different columns in discrete ops
+ * table.
+ */
+ private enum ColumnFilter {
+ PACKAGE_NAME(DiscreteOpsTable.Columns.PACKAGE_NAME + " = ? "),
+ UID(DiscreteOpsTable.Columns.UID + " = ? "),
+ ATTR_TAG(DiscreteOpsTable.Columns.ATTRIBUTION_TAG + " = ? "),
+ END_TIME(DiscreteOpsTable.Columns.ACCESS_TIME + " < ? "),
+ OP_CODE_EQUAL(DiscreteOpsTable.Columns.OP_CODE + " = ? "),
+ BEGIN_TIME(DiscreteOpsTable.Columns.ACCESS_TIME + " + "
+ + DiscreteOpsTable.Columns.ACCESS_DURATION + " > ? "),
+ OP_FLAGS("(" + DiscreteOpsTable.Columns.OP_FLAGS + " & ? ) != 0"),
+ OP_CODE_IN(DiscreteOpsTable.Columns.OP_CODE + " IN ");
+
+ final String mCondition;
+
+ ColumnFilter(String condition) {
+ mCondition = condition;
+ }
+
+ @Override
+ public String toString() {
+ return mCondition;
+ }
+ }
+
+ static final class DiscreteOpsDatabaseErrorHandler implements DatabaseErrorHandler {
+ private final DefaultDatabaseErrorHandler mDefaultDatabaseErrorHandler =
+ new DefaultDatabaseErrorHandler();
+
+ @Override
+ public void onCorruption(SQLiteDatabase dbObj) {
+ Slog.e(LOG_TAG, "discrete ops database got corrupted.");
+ mDefaultDatabaseErrorHandler.onCorruption(dbObj);
+ }
+ }
+
+ // USED for testing only
+ List<DiscreteOpsSqlRegistry.DiscreteOp> getAllDiscreteOps(@NonNull String sql) {
+ SQLiteDatabase db = getReadableDatabase();
+ List<DiscreteOpsSqlRegistry.DiscreteOp> results = new ArrayList<>();
+ db.beginTransactionReadOnly();
+ try (SQLiteRawStatement statement = db.createRawStatement(sql)) {
+ while (statement.step()) {
+ int uid = statement.getColumnInt(0);
+ String packageName = statement.getColumnText(1);
+ String deviceId = statement.getColumnText(2);
+ int opCode = statement.getColumnInt(3);
+ String attributionTag = statement.getColumnText(4);
+ long accessTime = statement.getColumnLong(5);
+ long duration = statement.getColumnLong(6);
+ int uidState = statement.getColumnInt(7);
+ int opFlags = statement.getColumnInt(8);
+ int attributionFlags = statement.getColumnInt(9);
+ long chainId = statement.getColumnLong(10);
+ DiscreteOpsSqlRegistry.DiscreteOp event = new DiscreteOpsSqlRegistry.DiscreteOp(uid,
+ packageName, attributionTag, deviceId, opCode,
+ opFlags, attributionFlags, uidState, chainId, accessTime, duration);
+ results.add(event);
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ return results;
+ }
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java
new file mode 100644
index 000000000000..c38ee55b4f42
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java
@@ -0,0 +1,106 @@
+/*
+ * 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.appop;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for migrating discrete ops from xml to sqlite
+ */
+public class DiscreteOpsMigrationHelper {
+ /**
+ * migrate discrete ops from xml to sqlite.
+ */
+ static void migrateDiscreteOpsToSqlite(DiscreteOpsXmlRegistry xmlRegistry,
+ DiscreteOpsSqlRegistry sqlRegistry) {
+ DiscreteOpsXmlRegistry.DiscreteOps xmlOps = xmlRegistry.getAllDiscreteOps();
+ List<DiscreteOpsSqlRegistry.DiscreteOp> discreteOps = getSqlDiscreteOps(xmlOps);
+ sqlRegistry.migrateXmlData(discreteOps, xmlOps.mChainIdOffset);
+ xmlRegistry.deleteDiscreteOpsDir();
+ }
+
+ /**
+ * rollback discrete ops from sqlite to xml.
+ */
+ static void migrateDiscreteOpsToXml(DiscreteOpsSqlRegistry sqlRegistry,
+ DiscreteOpsXmlRegistry xmlRegistry) {
+ List<DiscreteOpsSqlRegistry.DiscreteOp> sqlOps = sqlRegistry.getAllDiscreteOps();
+ DiscreteOpsXmlRegistry.DiscreteOps xmlOps = getXmlDiscreteOps(sqlOps);
+ xmlRegistry.migrateSqliteData(xmlOps);
+ sqlRegistry.deleteDatabase();
+ }
+
+ /**
+ * Convert sqlite flat rows to hierarchical data.
+ */
+ private static DiscreteOpsXmlRegistry.DiscreteOps getXmlDiscreteOps(
+ List<DiscreteOpsSqlRegistry.DiscreteOp> discreteOps) {
+ DiscreteOpsXmlRegistry.DiscreteOps xmlOps =
+ new DiscreteOpsXmlRegistry.DiscreteOps(0);
+ if (discreteOps.isEmpty()) {
+ return xmlOps;
+ }
+
+ for (DiscreteOpsSqlRegistry.DiscreteOp discreteOp : discreteOps) {
+ xmlOps.addDiscreteAccess(discreteOp.getOpCode(), discreteOp.getUid(),
+ discreteOp.getPackageName(), discreteOp.getDeviceId(),
+ discreteOp.getAttributionTag(), discreteOp.getOpFlags(),
+ discreteOp.getUidState(),
+ discreteOp.getAccessTime(), discreteOp.getDuration(),
+ discreteOp.getAttributionFlags(), (int) discreteOp.getChainId());
+ }
+ return xmlOps;
+ }
+
+ /**
+ * Convert xml (hierarchical) data to flat row based data.
+ */
+ private static List<DiscreteOpsSqlRegistry.DiscreteOp> getSqlDiscreteOps(
+ DiscreteOpsXmlRegistry.DiscreteOps discreteOps) {
+ List<DiscreteOpsSqlRegistry.DiscreteOp> opEvents = new ArrayList<>();
+
+ if (discreteOps.isEmpty()) {
+ return opEvents;
+ }
+
+ discreteOps.mUids.forEach((uid, discreteUidOps) -> {
+ discreteUidOps.mPackages.forEach((packageName, packageOps) -> {
+ packageOps.mPackageOps.forEach((opcode, ops) -> {
+ ops.mDeviceAttributedOps.forEach((deviceId, deviceOps) -> {
+ deviceOps.mAttributedOps.forEach((tag, attributedOps) -> {
+ for (DiscreteOpsXmlRegistry.DiscreteOpEvent attributedOp :
+ attributedOps) {
+ DiscreteOpsSqlRegistry.DiscreteOp
+ opModel = new DiscreteOpsSqlRegistry.DiscreteOp(uid,
+ packageName, tag,
+ deviceId, opcode, attributedOp.mOpFlag,
+ attributedOp.mAttributionFlags,
+ attributedOp.mUidState, attributedOp.mAttributionChainId,
+ attributedOp.mNoteTime,
+ attributedOp.mNoteDuration);
+ opEvents.add(opModel);
+ }
+ });
+ });
+ });
+ });
+ });
+
+ return opEvents;
+ }
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
new file mode 100644
index 000000000000..88b3f6dce4c2
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
@@ -0,0 +1,298 @@
+/*
+ * 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.appop;
+
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_COARSE_LOCATION;
+import static android.app.AppOpsManager.OP_EMERGENCY_LOCATION;
+import static android.app.AppOpsManager.OP_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_FLAG_SELF;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
+import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
+import static android.app.AppOpsManager.OP_PROCESS_OUTGOING_CALLS;
+import static android.app.AppOpsManager.OP_READ_ICC_SMS;
+import static android.app.AppOpsManager.OP_READ_SMS;
+import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
+import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
+import static android.app.AppOpsManager.OP_SEND_SMS;
+import static android.app.AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+import static android.app.AppOpsManager.OP_WRITE_ICC_SMS;
+import static android.app.AppOpsManager.OP_WRITE_SMS;
+
+import static java.lang.Long.min;
+import static java.lang.Math.max;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.permission.flags.Flags;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * This class provides interface for xml and sqlite implementation. Implementation manages
+ * information about recent accesses to ops for permission usage timeline.
+ * <p>
+ * The discrete history is kept for limited time (initial default is 24 hours, set in
+ * {@link DiscreteOpsRegistry#sDiscreteHistoryCutoff} and discarded after that.
+ * <p>
+ * Discrete history is quantized to reduce resources footprint. By default, quantization is set to
+ * one minute in {@link DiscreteOpsRegistry#sDiscreteHistoryQuantization}. All access times are
+ * aligned to the closest quantized time. All durations (except -1, meaning no duration) are
+ * rounded up to the closest quantized interval.
+ * <p>
+ * When data is queried through API, events are deduplicated and for every time quant there can
+ * be only one {@link AppOpsManager.AttributedOpEntry}. Each entry contains information about
+ * different accesses which happened in specified time quant - across dimensions of
+ * {@link AppOpsManager.UidState} and {@link AppOpsManager.OpFlags}. For each dimension
+ * it is only possible to know if at least one access happened in the time quant.
+ * <p>
+ * INITIALIZATION: We can initialize persistence only after the system is ready
+ * as we need to check the optional configuration override from the settings
+ * database which is not initialized at the time the app ops service is created. This class
+ * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All
+ * outside calls are going through {@link HistoricalRegistry}.
+ *
+ */
+abstract class DiscreteOpsRegistry {
+ private static final String TAG = DiscreteOpsRegistry.class.getSimpleName();
+
+ static final boolean DEBUG_LOG = false;
+ static final String PROPERTY_DISCRETE_HISTORY_CUTOFF = "discrete_history_cutoff_millis";
+ static final String PROPERTY_DISCRETE_HISTORY_QUANTIZATION =
+ "discrete_history_quantization_millis";
+ static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
+ static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
+ static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
+ + "," + OP_EMERGENCY_LOCATION + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + ","
+ + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + ","
+ + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
+ + "," + OP_RESERVED_FOR_TESTING;
+ static final int[] sDiscreteOpsToLog =
+ new int[]{OP_FINE_LOCATION, OP_COARSE_LOCATION, OP_EMERGENCY_LOCATION, OP_CAMERA,
+ OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, OP_PHONE_CALL_CAMERA,
+ OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, OP_READ_SMS,
+ OP_WRITE_SMS, OP_SEND_SMS, OP_READ_ICC_SMS, OP_WRITE_ICC_SMS,
+ OP_SMS_FINANCIAL_TRANSACTIONS, OP_SYSTEM_ALERT_WINDOW, OP_MONITOR_LOCATION,
+ OP_MONITOR_HIGH_POWER_LOCATION, OP_PROCESS_OUTGOING_CALLS,
+ };
+
+ static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis();
+ static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
+ // The duration for which the data is kept, default is 7 days and max 30 days enforced.
+ static long sDiscreteHistoryCutoff;
+
+ static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION = Duration.ofMinutes(1).toMillis();
+ // discrete ops are rounded up to quantization time, meaning we record one op per time bucket
+ // in case of duplicate op events.
+ static long sDiscreteHistoryQuantization;
+
+ static int[] sDiscreteOps;
+ static int sDiscreteFlags;
+
+ static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
+ | OP_FLAG_TRUSTED_PROXY;
+
+ boolean mDebugMode = false;
+
+ static final int ACCESS_TYPE_NOTE_OP =
+ FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__NOTE_OP;
+ static final int ACCESS_TYPE_START_OP =
+ FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__START_OP;
+ static final int ACCESS_TYPE_FINISH_OP =
+ FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__FINISH_OP;
+ static final int ACCESS_TYPE_PAUSE_OP =
+ FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__PAUSE_OP;
+ static final int ACCESS_TYPE_RESUME_OP =
+ FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__RESUME_OP;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ACCESS_TYPE_"}, value = {
+ ACCESS_TYPE_NOTE_OP,
+ ACCESS_TYPE_START_OP,
+ ACCESS_TYPE_FINISH_OP,
+ ACCESS_TYPE_PAUSE_OP,
+ ACCESS_TYPE_RESUME_OP
+ })
+ @interface AccessType {}
+
+ void systemReady() {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
+ AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties p) -> {
+ setDiscreteHistoryParameters(p);
+ });
+ setDiscreteHistoryParameters(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_PRIVACY));
+ }
+
+ abstract void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId,
+ int op, @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
+ @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
+ @DiscreteOpsRegistry.AccessType int accessType);
+
+ /**
+ * A periodic callback from {@link AppOpsService} to flush the in memory events to disk.
+ * The shutdown callback is also plugged into it.
+ * <p>
+ * This method flushes in memory records to disk, and also clears old records from disk.
+ */
+ abstract void writeAndClearOldAccessHistory();
+
+ /** Remove all discrete op events. */
+ abstract void clearHistory();
+
+ /** Remove all discrete op events for given UID and package. */
+ abstract void clearHistory(int uid, String packageName);
+
+ /**
+ * Offset access time by given timestamp, new access time would be accessTime - offsetMillis.
+ */
+ abstract void offsetHistory(long offset);
+
+ abstract void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
+ long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
+ @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ Set<String> attributionExemptPkgs);
+
+ abstract void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter,
+ @Nullable String attributionTagFilter,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
+ @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+ int nDiscreteOps);
+
+ void setDebugMode(boolean debugMode) {
+ this.mDebugMode = debugMode;
+ }
+
+ static long discretizeTimeStamp(long timeStamp) {
+ return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+
+ }
+
+ static long discretizeDuration(long duration) {
+ return duration == -1 ? -1 : (duration + sDiscreteHistoryQuantization - 1)
+ / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+ }
+
+ static boolean isDiscreteOp(int op, @AppOpsManager.OpFlags int flags) {
+ if (!ArrayUtils.contains(sDiscreteOps, op)) {
+ return false;
+ }
+ if ((flags & (sDiscreteFlags)) == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ // could this be impl detail of discrete registry, just one test is using the method
+ // abstract DiscreteRegistry.DiscreteOps getAllDiscreteOps();
+
+ private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
+ if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_CUTOFF)) {
+ sDiscreteHistoryCutoff = p.getLong(PROPERTY_DISCRETE_HISTORY_CUTOFF,
+ DEFAULT_DISCRETE_HISTORY_CUTOFF);
+ if (!Build.IS_DEBUGGABLE && !mDebugMode) {
+ sDiscreteHistoryCutoff = min(MAXIMUM_DISCRETE_HISTORY_CUTOFF,
+ sDiscreteHistoryCutoff);
+ }
+ } else {
+ sDiscreteHistoryCutoff = DEFAULT_DISCRETE_HISTORY_CUTOFF;
+ }
+ if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_QUANTIZATION)) {
+ sDiscreteHistoryQuantization = p.getLong(PROPERTY_DISCRETE_HISTORY_QUANTIZATION,
+ DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
+ if (!Build.IS_DEBUGGABLE && !mDebugMode) {
+ sDiscreteHistoryQuantization = max(DEFAULT_DISCRETE_HISTORY_QUANTIZATION,
+ sDiscreteHistoryQuantization);
+ }
+ } else {
+ sDiscreteHistoryQuantization = DEFAULT_DISCRETE_HISTORY_QUANTIZATION;
+ }
+ sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS) ? sDiscreteFlags =
+ p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
+ sDiscreteOps = p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST) ? parseOpsList(
+ p.getString(PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS)) : parseOpsList(
+ DEFAULT_DISCRETE_OPS);
+ }
+
+ private static int[] parseOpsList(String opsList) {
+ String[] strArr;
+ if (opsList.isEmpty()) {
+ strArr = new String[0];
+ } else {
+ strArr = opsList.split(",");
+ }
+ int nOps = strArr.length;
+ int[] result = new int[nOps];
+ try {
+ for (int i = 0; i < nOps; i++) {
+ result[i] = Integer.parseInt(strArr[i]);
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Failed to parse Discrete ops list: " + e.getMessage());
+ return parseOpsList(DEFAULT_DISCRETE_OPS);
+ }
+ return result;
+ }
+
+ /**
+ * Whether app op access tacking is enabled and a metric event should be logged.
+ */
+ static boolean shouldLogAccess(int op) {
+ return Flags.appopAccessTrackingLoggingEnabled()
+ && ArrayUtils.contains(sDiscreteOpsToLog, op);
+ }
+
+ String getAttributionTag(String attributionTag, String packageName) {
+ if (attributionTag == null || packageName == null) {
+ return attributionTag;
+ }
+ int firstChar = 0;
+ if (attributionTag.startsWith(packageName)) {
+ firstChar = packageName.length();
+ if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar)
+ == '.') {
+ firstChar++;
+ }
+ }
+ return attributionTag.substring(firstChar);
+ }
+
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
new file mode 100644
index 000000000000..4b3981cd4bc0
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
@@ -0,0 +1,689 @@
+/*
+ * 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.appop;
+
+import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED;
+import static android.app.AppOpsManager.flagsToString;
+import static android.app.AppOpsManager.getUidStateName;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.LongSparseArray;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.ServiceThread;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class handles sqlite persistence layer for discrete ops.
+ */
+public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
+ private static final String TAG = "DiscreteOpsSqlRegistry";
+
+ private final Context mContext;
+ private final DiscreteOpsDbHelper mDiscreteOpsDbHelper;
+ private final SqliteWriteHandler mSqliteWriteHandler;
+ private final DiscreteOpCache mDiscreteOpCache = new DiscreteOpCache(512);
+ private static final long THREE_HOURS = Duration.ofHours(3).toMillis();
+ private static final int WRITE_CACHE_EVICTED_OP_EVENTS = 1;
+ private static final int DELETE_OLD_OP_EVENTS = 2;
+ // Attribution chain id is used to identify an attribution source chain, This is
+ // set for startOp only. PermissionManagerService resets this ID on device restart, so
+ // we use previously persisted chain id as offset, and add it to chain id received from
+ // permission manager service.
+ private long mChainIdOffset;
+ private final File mDatabaseFile;
+
+ DiscreteOpsSqlRegistry(Context context) {
+ this(context, DiscreteOpsDbHelper.getDatabaseFile());
+ }
+
+ DiscreteOpsSqlRegistry(Context context, File databaseFile) {
+ ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true);
+ thread.start();
+ mContext = context;
+ mDatabaseFile = databaseFile;
+ mSqliteWriteHandler = new SqliteWriteHandler(thread.getLooper());
+ mDiscreteOpsDbHelper = new DiscreteOpsDbHelper(context, databaseFile);
+ mChainIdOffset = mDiscreteOpsDbHelper.getLargestAttributionChainId();
+ }
+
+ @Override
+ void recordDiscreteAccess(int uid, String packageName,
+ @NonNull String deviceId, int op,
+ @Nullable String attributionTag, int flags, int uidState,
+ long accessTime, long accessDuration, int attributionFlags, int attributionChainId,
+ int accessType) {
+ if (shouldLogAccess(op)) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType,
+ uidState, flags, attributionFlags,
+ getAttributionTag(attributionTag, packageName),
+ attributionChainId);
+ }
+
+ if (!isDiscreteOp(op, flags)) {
+ return;
+ }
+
+ long offsetChainId = attributionChainId;
+ if (attributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+ offsetChainId = attributionChainId + mChainIdOffset;
+ // PermissionManagerService chain id reached the max value,
+ // reset offset, it's going to be very rare.
+ if (attributionChainId == Integer.MAX_VALUE) {
+ mChainIdOffset = offsetChainId;
+ }
+ }
+ DiscreteOp discreteOpEvent = new DiscreteOp(uid, packageName, attributionTag, deviceId, op,
+ flags, attributionFlags, uidState, offsetChainId, accessTime, accessDuration);
+ mDiscreteOpCache.add(discreteOpEvent);
+ }
+
+ @Override
+ void writeAndClearOldAccessHistory() {
+ // Let the sql impl also follow the same disk write frequencies as xml,
+ // controlled by AppOpsService.
+ mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.getAllEventsAndClear());
+ if (!mSqliteWriteHandler.hasMessages(DELETE_OLD_OP_EVENTS)) {
+ if (mSqliteWriteHandler.sendEmptyMessageDelayed(DELETE_OLD_OP_EVENTS, THREE_HOURS)) {
+ Slog.w(TAG, "DELETE_OLD_OP_EVENTS is not queued");
+ }
+ }
+ }
+
+ @Override
+ void clearHistory() {
+ mDiscreteOpCache.clear();
+ mDiscreteOpsDbHelper.execSQL(DiscreteOpsTable.DELETE_TABLE_DATA);
+ }
+
+ @Override
+ void clearHistory(int uid, String packageName) {
+ mDiscreteOpCache.clear(uid, packageName);
+ mDiscreteOpsDbHelper.execSQL(DiscreteOpsTable.DELETE_DATA_FOR_UID_PACKAGE,
+ new Object[]{uid, packageName});
+ }
+
+ @Override
+ void offsetHistory(long offset) {
+ mDiscreteOpCache.offsetTimestamp(offset);
+ mDiscreteOpsDbHelper.execSQL(DiscreteOpsTable.OFFSET_ACCESS_TIME,
+ new Object[]{offset});
+ }
+
+ private IntArray getAppOpCodes(@AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String[] opNamesFilter) {
+ if ((filter & AppOpsManager.FILTER_BY_OP_NAMES) != 0) {
+ IntArray opCodes = new IntArray(opNamesFilter.length);
+ for (int i = 0; i < opNamesFilter.length; i++) {
+ int op;
+ try {
+ op = AppOpsManager.strOpToOp(opNamesFilter[i]);
+ } catch (IllegalArgumentException ex) {
+ Slog.w(TAG, "Appop `" + opNamesFilter[i] + "` is not recognized.");
+ continue;
+ }
+ opCodes.add(op);
+ }
+ return opCodes;
+ }
+ return null;
+ }
+
+ @Override
+ void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
+ long beginTimeMillis, long endTimeMillis, int filter, int uidFilter,
+ @Nullable String packageNameFilter,
+ @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, int opFlagsFilter,
+ Set<String> attributionExemptPkgs) {
+ // flush the cache into database before read.
+ writeAndClearOldAccessHistory();
+ boolean assembleChains = attributionExemptPkgs != null;
+ IntArray opCodes = getAppOpCodes(filter, opNamesFilter);
+ List<DiscreteOp> discreteOps = mDiscreteOpsDbHelper.getDiscreteOps(filter, uidFilter,
+ packageNameFilter, attributionTagFilter, opCodes, opFlagsFilter, beginTimeMillis,
+ endTimeMillis, -1, null);
+
+ LongSparseArray<AttributionChain> attributionChains = null;
+ if (assembleChains) {
+ attributionChains = createAttributionChains(discreteOps, attributionExemptPkgs);
+ }
+
+ int nEvents = discreteOps.size();
+ for (int j = 0; j < nEvents; j++) {
+ DiscreteOp event = discreteOps.get(j);
+ AppOpsManager.OpEventProxyInfo proxy = null;
+ if (assembleChains && event.mChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+ AttributionChain chain = attributionChains.get(event.mChainId);
+ if (chain != null && chain.isComplete()
+ && chain.isStart(event)
+ && chain.mLastVisibleEvent != null) {
+ DiscreteOp proxyEvent = chain.mLastVisibleEvent;
+ proxy = new AppOpsManager.OpEventProxyInfo(proxyEvent.mUid,
+ proxyEvent.mPackageName, proxyEvent.mAttributionTag);
+ }
+ }
+ result.addDiscreteAccess(event.mOpCode, event.mUid, event.mPackageName,
+ event.mAttributionTag, event.mUidState, event.mOpFlags,
+ event.mDiscretizedAccessTime, event.mDiscretizedDuration, proxy);
+ }
+ }
+
+ @Override
+ void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter,
+ @Nullable String attributionTagFilter,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
+ @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+ int nDiscreteOps) {
+ writeAndClearOldAccessHistory();
+ IntArray opCodes = new IntArray();
+ if (dumpOp != AppOpsManager.OP_NONE) {
+ opCodes.add(dumpOp);
+ }
+ List<DiscreteOp> discreteOps = mDiscreteOpsDbHelper.getDiscreteOps(filter, uidFilter,
+ packageNameFilter, attributionTagFilter, opCodes, 0, -1,
+ -1, nDiscreteOps, DiscreteOpsTable.Columns.ACCESS_TIME);
+
+ pw.print(prefix);
+ pw.print("Largest chain id: ");
+ pw.print(mDiscreteOpsDbHelper.getLargestAttributionChainId());
+ pw.println();
+ pw.println("UID|PACKAGE_NAME|DEVICE_ID|OP_NAME|ATTRIBUTION_TAG|UID_STATE|OP_FLAGS|"
+ + "ATTR_FLAGS|CHAIN_ID|ACCESS_TIME|DURATION");
+ int discreteOpsCount = discreteOps.size();
+ for (int i = 0; i < discreteOpsCount; i++) {
+ DiscreteOp event = discreteOps.get(i);
+ date.setTime(event.mAccessTime);
+ pw.println(event.mUid + "|" + event.mPackageName + "|" + event.mDeviceId + "|"
+ + AppOpsManager.opToName(event.mOpCode) + "|" + event.mAttributionTag + "|"
+ + getUidStateName(event.mUidState) + "|"
+ + flagsToString(event.mOpFlags) + "|" + event.mAttributionFlags + "|"
+ + event.mChainId + "|"
+ + sdf.format(date) + "|" + event.mDuration);
+ }
+ pw.println();
+ }
+
+ void migrateXmlData(List<DiscreteOp> opEvents, int chainIdOffset) {
+ mChainIdOffset = chainIdOffset;
+ mDiscreteOpsDbHelper.insertDiscreteOps(opEvents);
+ }
+
+ LongSparseArray<AttributionChain> createAttributionChains(
+ List<DiscreteOp> discreteOps, Set<String> attributionExemptPkgs) {
+ LongSparseArray<AttributionChain> chains = new LongSparseArray<>();
+ final int count = discreteOps.size();
+
+ for (int i = 0; i < count; i++) {
+ DiscreteOp opEvent = discreteOps.get(i);
+ if (opEvent.mChainId == ATTRIBUTION_CHAIN_ID_NONE
+ || (opEvent.mAttributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) {
+ continue;
+ }
+ AttributionChain chain = chains.get(opEvent.mChainId);
+ if (chain == null) {
+ chain = new AttributionChain(attributionExemptPkgs);
+ chains.put(opEvent.mChainId, chain);
+ }
+ chain.addEvent(opEvent);
+ }
+ return chains;
+ }
+
+ static class AttributionChain {
+ List<DiscreteOp> mChain = new ArrayList<>();
+ Set<String> mExemptPkgs;
+ DiscreteOp mStartEvent = null;
+ DiscreteOp mLastVisibleEvent = null;
+
+ AttributionChain(Set<String> exemptPkgs) {
+ mExemptPkgs = exemptPkgs;
+ }
+
+ boolean isComplete() {
+ return !mChain.isEmpty() && getStart() != null && isEnd(mChain.get(mChain.size() - 1));
+ }
+
+ DiscreteOp getStart() {
+ return mChain.isEmpty() || !isStart(mChain.get(0)) ? null : mChain.get(0);
+ }
+
+ private boolean isEnd(DiscreteOp event) {
+ return event != null
+ && (event.mAttributionFlags & ATTRIBUTION_FLAG_ACCESSOR) != 0;
+ }
+
+ private boolean isStart(DiscreteOp event) {
+ return event != null
+ && (event.mAttributionFlags & ATTRIBUTION_FLAG_RECEIVER) != 0;
+ }
+
+ DiscreteOp getLastVisible() {
+ // Search all nodes but the first one, which is the start node
+ for (int i = mChain.size() - 1; i > 0; i--) {
+ DiscreteOp event = mChain.get(i);
+ if (!mExemptPkgs.contains(event.mPackageName)) {
+ return event;
+ }
+ }
+ return null;
+ }
+
+ void addEvent(DiscreteOp opEvent) {
+ // check if we have a matching event except duration.
+ DiscreteOp matchingItem = null;
+ for (int i = 0; i < mChain.size(); i++) {
+ DiscreteOp item = mChain.get(i);
+ if (item.equalsExceptDuration(opEvent)) {
+ matchingItem = item;
+ break;
+ }
+ }
+
+ if (matchingItem != null) {
+ // exact match or existing event has longer duration
+ if (matchingItem.mDuration == opEvent.mDuration
+ || matchingItem.mDuration > opEvent.mDuration) {
+ return;
+ }
+ mChain.remove(matchingItem);
+ }
+
+ if (mChain.isEmpty() || isEnd(opEvent)) {
+ mChain.add(opEvent);
+ } else if (isStart(opEvent)) {
+ mChain.add(0, opEvent);
+ } else {
+ for (int i = 0; i < mChain.size(); i++) {
+ DiscreteOp currEvent = mChain.get(i);
+ if ((!isStart(currEvent)
+ && currEvent.mAccessTime > opEvent.mAccessTime)
+ || (i == mChain.size() - 1 && isEnd(currEvent))) {
+ mChain.add(i, opEvent);
+ break;
+ } else if (i == mChain.size() - 1) {
+ mChain.add(opEvent);
+ break;
+ }
+ }
+ }
+ mStartEvent = isComplete() ? getStart() : null;
+ mLastVisibleEvent = isComplete() ? getLastVisible() : null;
+ }
+ }
+
+ /**
+ * Handler to write asynchronously to sqlite database.
+ */
+ class SqliteWriteHandler extends Handler {
+ SqliteWriteHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case WRITE_CACHE_EVICTED_OP_EVENTS:
+ List<DiscreteOp> opEvents = (List<DiscreteOp>) msg.obj;
+ mDiscreteOpsDbHelper.insertDiscreteOps(opEvents);
+ break;
+ case DELETE_OLD_OP_EVENTS:
+ long cutOffTimeStamp = System.currentTimeMillis() - sDiscreteHistoryCutoff;
+ mDiscreteOpsDbHelper.execSQL(
+ DiscreteOpsTable.DELETE_TABLE_DATA_BEFORE_ACCESS_TIME,
+ new Object[]{cutOffTimeStamp});
+ break;
+ default:
+ throw new IllegalStateException("Unexpected value: " + msg.what);
+ }
+ }
+ }
+
+ /**
+ * A write cache for discrete ops. The noteOp, start/finishOp discrete op events are written to
+ * the cache first.
+ * <p>
+ * These events are persisted into sqlite database
+ * 1) Periodic interval, controlled by {@link AppOpsService}
+ * 2) When total events in the cache exceeds cache limit.
+ * 3) During read call we flush the whole cache to sqlite.
+ * 4) During shutdown.
+ */
+ class DiscreteOpCache {
+ private final int mCapacity;
+ private final ArraySet<DiscreteOp> mCache;
+
+ DiscreteOpCache(int capacity) {
+ mCapacity = capacity;
+ mCache = new ArraySet<>();
+ }
+
+ public void add(DiscreteOp opEvent) {
+ synchronized (this) {
+ if (mCache.contains(opEvent)) {
+ return;
+ }
+ mCache.add(opEvent);
+ if (mCache.size() >= mCapacity) {
+ if (DEBUG_LOG) {
+ Slog.i(TAG, "Current discrete ops cache size: " + mCache.size());
+ }
+ List<DiscreteOp> evictedEvents = evict();
+ if (DEBUG_LOG) {
+ Slog.i(TAG, "Evicted discrete ops size: " + evictedEvents.size());
+ }
+ // if nothing to evict, just write the whole cache to disk
+ if (evictedEvents.isEmpty()) {
+ Slog.w(TAG, "No discrete ops event is evicted, write cache to db.");
+ evictedEvents.addAll(mCache);
+ mCache.clear();
+ }
+ mSqliteWriteHandler.obtainMessage(WRITE_CACHE_EVICTED_OP_EVENTS, evictedEvents);
+ }
+ }
+ }
+
+ /**
+ * Evict entries older than {@link DiscreteOpsRegistry#sDiscreteHistoryQuantization}.
+ */
+ private List<DiscreteOp> evict() {
+ synchronized (this) {
+ List<DiscreteOp> evictedEvents = new ArrayList<>();
+ Set<DiscreteOp> snapshot = new ArraySet<>(mCache);
+ long evictionTimestamp = System.currentTimeMillis() - sDiscreteHistoryQuantization;
+ evictionTimestamp = discretizeTimeStamp(evictionTimestamp);
+ for (DiscreteOp opEvent : snapshot) {
+ if (opEvent.mDiscretizedAccessTime <= evictionTimestamp) {
+ evictedEvents.add(opEvent);
+ mCache.remove(opEvent);
+ }
+ }
+ return evictedEvents;
+ }
+ }
+
+ /**
+ * Remove all the entries from cache.
+ *
+ * @return return all removed entries.
+ */
+ public List<DiscreteOp> getAllEventsAndClear() {
+ synchronized (this) {
+ List<DiscreteOp> cachedOps = new ArrayList<>(mCache.size());
+ if (mCache.isEmpty()) {
+ return cachedOps;
+ }
+ cachedOps.addAll(mCache);
+ mCache.clear();
+ return cachedOps;
+ }
+ }
+
+ /**
+ * Remove all entries from the cache.
+ */
+ public void clear() {
+ synchronized (this) {
+ mCache.clear();
+ }
+ }
+
+ /**
+ * Offset access time by given offset milliseconds.
+ */
+ public void offsetTimestamp(long offsetMillis) {
+ synchronized (this) {
+ List<DiscreteOp> cachedOps = new ArrayList<>(mCache);
+ mCache.clear();
+ for (DiscreteOp discreteOp : cachedOps) {
+ add(new DiscreteOp(discreteOp.getUid(), discreteOp.mPackageName,
+ discreteOp.getAttributionTag(), discreteOp.getDeviceId(),
+ discreteOp.mOpCode, discreteOp.mOpFlags,
+ discreteOp.getAttributionFlags(), discreteOp.getUidState(),
+ discreteOp.getChainId(), discreteOp.mAccessTime - offsetMillis,
+ discreteOp.getDuration())
+ );
+ }
+ }
+ }
+
+ /** Remove cached events for given UID and package. */
+ public void clear(int uid, String packageName) {
+ synchronized (this) {
+ Set<DiscreteOp> snapshot = new ArraySet<>(mCache);
+ for (DiscreteOp currentEvent : snapshot) {
+ if (Objects.equals(packageName, currentEvent.mPackageName)
+ && uid == currentEvent.getUid()) {
+ mCache.remove(currentEvent);
+ }
+ }
+ }
+ }
+ }
+
+ /** Immutable discrete op object. */
+ static class DiscreteOp {
+ private final int mUid;
+ private final String mPackageName;
+ private final String mAttributionTag;
+ private final String mDeviceId;
+ private final int mOpCode;
+ private final int mOpFlags;
+ private final int mAttributionFlags;
+ private final int mUidState;
+ private final long mChainId;
+ private final long mAccessTime;
+ private final long mDuration;
+ // store discretized timestamp to avoid repeated calculations.
+ private final long mDiscretizedAccessTime;
+ private final long mDiscretizedDuration;
+
+ DiscreteOp(int uid, String packageName, String attributionTag, String deviceId,
+ int opCode,
+ int mOpFlags, int mAttributionFlags, int uidState, long chainId, long accessTime,
+ long duration) {
+ this.mUid = uid;
+ this.mPackageName = packageName.intern();
+ this.mAttributionTag = attributionTag;
+ this.mDeviceId = deviceId;
+ this.mOpCode = opCode;
+ this.mOpFlags = mOpFlags;
+ this.mAttributionFlags = mAttributionFlags;
+ this.mUidState = uidState;
+ this.mChainId = chainId;
+ this.mAccessTime = accessTime;
+ this.mDiscretizedAccessTime = discretizeTimeStamp(accessTime);
+ this.mDuration = duration;
+ this.mDiscretizedDuration = discretizeDuration(duration);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DiscreteOp that)) return false;
+
+ if (mUid != that.mUid) return false;
+ if (mOpCode != that.mOpCode) return false;
+ if (mOpFlags != that.mOpFlags) return false;
+ if (mAttributionFlags != that.mAttributionFlags) return false;
+ if (mUidState != that.mUidState) return false;
+ if (mChainId != that.mChainId) return false;
+ if (!Objects.equals(mPackageName, that.mPackageName)) {
+ return false;
+ }
+ if (!Objects.equals(mAttributionTag, that.mAttributionTag)) {
+ return false;
+ }
+ if (!Objects.equals(mDeviceId, that.mDeviceId)) {
+ return false;
+ }
+ if (mDiscretizedAccessTime != that.mDiscretizedAccessTime) {
+ return false;
+ }
+ return mDiscretizedDuration == that.mDiscretizedDuration;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mUid;
+ result = 31 * result + (mPackageName != null ? mPackageName.hashCode() : 0);
+ result = 31 * result + (mAttributionTag != null ? mAttributionTag.hashCode() : 0);
+ result = 31 * result + (mDeviceId != null ? mDeviceId.hashCode() : 0);
+ result = 31 * result + mOpCode;
+ result = 31 * result + mOpFlags;
+ result = 31 * result + mAttributionFlags;
+ result = 31 * result + mUidState;
+ result = 31 * result + Objects.hash(mChainId);
+ result = 31 * result + Objects.hash(mDiscretizedAccessTime);
+ result = 31 * result + Objects.hash(mDiscretizedDuration);
+ return result;
+ }
+
+ public boolean equalsExceptDuration(DiscreteOp that) {
+ if (mUid != that.mUid) return false;
+ if (mOpCode != that.mOpCode) return false;
+ if (mOpFlags != that.mOpFlags) return false;
+ if (mAttributionFlags != that.mAttributionFlags) return false;
+ if (mUidState != that.mUidState) return false;
+ if (mChainId != that.mChainId) return false;
+ if (!Objects.equals(mPackageName, that.mPackageName)) {
+ return false;
+ }
+ if (!Objects.equals(mAttributionTag, that.mAttributionTag)) {
+ return false;
+ }
+ if (!Objects.equals(mDeviceId, that.mDeviceId)) {
+ return false;
+ }
+ return mAccessTime == that.mAccessTime;
+ }
+
+ @Override
+ public String toString() {
+ return "DiscreteOp{"
+ + "uid=" + mUid
+ + ", packageName='" + mPackageName + '\''
+ + ", attributionTag='" + mAttributionTag + '\''
+ + ", deviceId='" + mDeviceId + '\''
+ + ", opCode=" + AppOpsManager.opToName(mOpCode)
+ + ", opFlag=" + flagsToString(mOpFlags)
+ + ", attributionFlag=" + mAttributionFlags
+ + ", uidState=" + getUidStateName(mUidState)
+ + ", chainId=" + mChainId
+ + ", accessTime=" + mAccessTime
+ + ", duration=" + mDuration + '}';
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public String getAttributionTag() {
+ return mAttributionTag;
+ }
+
+ public String getDeviceId() {
+ return mDeviceId;
+ }
+
+ public int getOpCode() {
+ return mOpCode;
+ }
+
+ @AppOpsManager.OpFlags
+ public int getOpFlags() {
+ return mOpFlags;
+ }
+
+
+ @AppOpsManager.AttributionFlags
+ public int getAttributionFlags() {
+ return mAttributionFlags;
+ }
+
+ @AppOpsManager.UidState
+ public int getUidState() {
+ return mUidState;
+ }
+
+ public long getChainId() {
+ return mChainId;
+ }
+
+ public long getAccessTime() {
+ return mAccessTime;
+ }
+
+ public long getDuration() {
+ return mDuration;
+ }
+ }
+
+ // API for tests only, can be removed or changed.
+ void recordDiscreteAccess(DiscreteOp discreteOpEvent) {
+ mDiscreteOpCache.add(discreteOpEvent);
+ }
+
+ // API for tests only, can be removed or changed.
+ List<DiscreteOp> getCachedDiscreteOps() {
+ return new ArrayList<>(mDiscreteOpCache.mCache);
+ }
+
+ // API for tests only, can be removed or changed.
+ List<DiscreteOp> getAllDiscreteOps() {
+ List<DiscreteOp> ops = new ArrayList<>(mDiscreteOpCache.mCache);
+ ops.addAll(mDiscreteOpsDbHelper.getAllDiscreteOps(DiscreteOpsTable.SELECT_TABLE_DATA));
+ return ops;
+ }
+
+ // API for testing and migration
+ long getLargestAttributionChainId() {
+ return mDiscreteOpsDbHelper.getLargestAttributionChainId();
+ }
+
+ // API for testing and migration
+ void deleteDatabase() {
+ mDiscreteOpsDbHelper.close();
+ mContext.deleteDatabase(mDatabaseFile.getName());
+ }
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsTable.java b/services/core/java/com/android/server/appop/DiscreteOpsTable.java
new file mode 100644
index 000000000000..9cb19aa30a15
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsTable.java
@@ -0,0 +1,128 @@
+/*
+ * 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.appop;
+
+
+/**
+ * SQLite table for storing app op accesses.
+ */
+final class DiscreteOpsTable {
+ private static final String TABLE_NAME = "app_op_accesses";
+ private static final String INDEX_APP_OP = "app_op_access_index";
+
+ static final class Columns {
+ /** Auto increment primary key. */
+ static final String ID = "id";
+ /** UID of the package accessing private data. */
+ static final String UID = "uid";
+ /** Package accessing private data. */
+ static final String PACKAGE_NAME = "package_name";
+ /** The device from which the private data is accessed. */
+ static final String DEVICE_ID = "device_id";
+ /** Op code representing private data i.e. location, mic etc. */
+ static final String OP_CODE = "op_code";
+ /** Attribution tag provided when accessing the private data. */
+ static final String ATTRIBUTION_TAG = "attribution_tag";
+ /** Timestamp when private data is accessed, number of milliseconds that have passed
+ * since Unix epoch */
+ static final String ACCESS_TIME = "access_time";
+ /** For how long the private data is accessed. */
+ static final String ACCESS_DURATION = "access_duration";
+ /** App process state, whether the app is in foreground, background or cached etc. */
+ static final String UID_STATE = "uid_state";
+ /** App op flags */
+ static final String OP_FLAGS = "op_flags";
+ /** Attribution flags */
+ static final String ATTRIBUTION_FLAGS = "attribution_flags";
+ /** Chain id */
+ static final String CHAIN_ID = "chain_id";
+ }
+
+ static final int UID_INDEX = 1;
+ static final int PACKAGE_NAME_INDEX = 2;
+ static final int DEVICE_ID_INDEX = 3;
+ static final int OP_CODE_INDEX = 4;
+ static final int ATTRIBUTION_TAG_INDEX = 5;
+ static final int ACCESS_TIME_INDEX = 6;
+ static final int ACCESS_DURATION_INDEX = 7;
+ static final int UID_STATE_INDEX = 8;
+ static final int OP_FLAGS_INDEX = 9;
+ static final int ATTRIBUTION_FLAGS_INDEX = 10;
+ static final int CHAIN_ID_INDEX = 11;
+
+ static final String CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS "
+ + TABLE_NAME + "("
+ + Columns.ID + " INTEGER PRIMARY KEY,"
+ + Columns.UID + " INTEGER,"
+ + Columns.PACKAGE_NAME + " TEXT,"
+ + Columns.DEVICE_ID + " TEXT NOT NULL,"
+ + Columns.OP_CODE + " INTEGER,"
+ + Columns.ATTRIBUTION_TAG + " TEXT,"
+ + Columns.ACCESS_TIME + " INTEGER,"
+ + Columns.ACCESS_DURATION + " INTEGER,"
+ + Columns.UID_STATE + " INTEGER,"
+ + Columns.OP_FLAGS + " INTEGER,"
+ + Columns.ATTRIBUTION_FLAGS + " INTEGER,"
+ + Columns.CHAIN_ID + " INTEGER"
+ + ")";
+
+ static final String INSERT_TABLE_SQL = "INSERT INTO " + TABLE_NAME + "("
+ + Columns.UID + ", "
+ + Columns.PACKAGE_NAME + ", "
+ + Columns.DEVICE_ID + ", "
+ + Columns.OP_CODE + ", "
+ + Columns.ATTRIBUTION_TAG + ", "
+ + Columns.ACCESS_TIME + ", "
+ + Columns.ACCESS_DURATION + ", "
+ + Columns.UID_STATE + ", "
+ + Columns.OP_FLAGS + ", "
+ + Columns.ATTRIBUTION_FLAGS + ", "
+ + Columns.CHAIN_ID + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+ static final String SELECT_MAX_ATTRIBUTION_CHAIN_ID = "SELECT MAX(" + Columns.CHAIN_ID + ")"
+ + " FROM " + TABLE_NAME;
+
+ static final String SELECT_TABLE_DATA = "SELECT DISTINCT "
+ + Columns.UID + ","
+ + Columns.PACKAGE_NAME + ","
+ + Columns.DEVICE_ID + ","
+ + Columns.OP_CODE + ","
+ + Columns.ATTRIBUTION_TAG + ","
+ + Columns.ACCESS_TIME + ","
+ + Columns.ACCESS_DURATION + ","
+ + Columns.UID_STATE + ","
+ + Columns.OP_FLAGS + ","
+ + Columns.ATTRIBUTION_FLAGS + ","
+ + Columns.CHAIN_ID
+ + " FROM " + TABLE_NAME;
+
+ static final String DELETE_TABLE_DATA = "DELETE FROM " + TABLE_NAME;
+
+ static final String DELETE_TABLE_DATA_BEFORE_ACCESS_TIME = "DELETE FROM " + TABLE_NAME
+ + " WHERE " + Columns.ACCESS_TIME + " < ?";
+
+ static final String DELETE_DATA_FOR_UID_PACKAGE = "DELETE FROM " + DiscreteOpsTable.TABLE_NAME
+ + " WHERE " + Columns.UID + " = ? AND " + Columns.PACKAGE_NAME + " = ?";
+
+ static final String OFFSET_ACCESS_TIME = "UPDATE " + DiscreteOpsTable.TABLE_NAME
+ + " SET " + Columns.ACCESS_TIME + " = ACCESS_TIME - ?";
+
+ // Index on access time, uid and op code
+ static final String CREATE_INDEX_SQL = "CREATE INDEX IF NOT EXISTS "
+ + INDEX_APP_OP + " ON " + TABLE_NAME
+ + " (" + Columns.ACCESS_TIME + ", " + Columns.UID + ", " + Columns.OP_CODE + ")";
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java b/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
new file mode 100644
index 000000000000..1523cca86607
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
@@ -0,0 +1,220 @@
+/*
+ * 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.appop;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A testing class, which supports both xml and sqlite persistence for discrete ops, the class
+ * logs warning if there is a mismatch in the behavior.
+ */
+class DiscreteOpsTestingShim extends DiscreteOpsRegistry {
+ private static final String LOG_TAG = "DiscreteOpsTestingShim";
+ private final DiscreteOpsRegistry mXmlRegistry;
+ private final DiscreteOpsRegistry mSqlRegistry;
+
+ DiscreteOpsTestingShim(DiscreteOpsRegistry xmlRegistry,
+ DiscreteOpsRegistry sqlRegistry) {
+ mXmlRegistry = xmlRegistry;
+ mSqlRegistry = sqlRegistry;
+ }
+
+ @Override
+ void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op,
+ @Nullable String attributionTag, int flags, int uidState, long accessTime,
+ long accessDuration, int attributionFlags, int attributionChainId, int accessType) {
+ long start = SystemClock.uptimeMillis();
+ mXmlRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags,
+ uidState, accessTime, accessDuration, attributionFlags, attributionChainId,
+ accessType);
+ long start2 = SystemClock.uptimeMillis();
+ mSqlRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags,
+ uidState, accessTime, accessDuration, attributionFlags, attributionChainId,
+ accessType);
+ long end = SystemClock.uptimeMillis();
+ long xmlTimeTaken = start2 - start;
+ long sqlTimeTaken = end - start2;
+ Log.i(LOG_TAG,
+ "recordDiscreteAccess: XML time taken : " + xmlTimeTaken + ", SQL time taken : "
+ + sqlTimeTaken + ", diff (sql - xml): " + (sqlTimeTaken - xmlTimeTaken));
+ }
+
+
+ @Override
+ void writeAndClearOldAccessHistory() {
+ mXmlRegistry.writeAndClearOldAccessHistory();
+ mSqlRegistry.writeAndClearOldAccessHistory();
+ }
+
+ @Override
+ void clearHistory() {
+ mXmlRegistry.clearHistory();
+ mSqlRegistry.clearHistory();
+ }
+
+ @Override
+ void clearHistory(int uid, String packageName) {
+ mXmlRegistry.clearHistory(uid, packageName);
+ mSqlRegistry.clearHistory(uid, packageName);
+ }
+
+ @Override
+ void offsetHistory(long offset) {
+ mXmlRegistry.offsetHistory(offset);
+ mSqlRegistry.offsetHistory(offset);
+ }
+
+ @Override
+ void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
+ long beginTimeMillis, long endTimeMillis, int filter, int uidFilter,
+ @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, int flagsFilter,
+ Set<String> attributionExemptPkgs) {
+ AppOpsManager.HistoricalOps result2 =
+ new AppOpsManager.HistoricalOps(beginTimeMillis, endTimeMillis);
+
+ long start = System.currentTimeMillis();
+ mXmlRegistry.addFilteredDiscreteOpsToHistoricalOps(result2, beginTimeMillis, endTimeMillis,
+ filter, uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter,
+ flagsFilter, attributionExemptPkgs);
+ long start2 = System.currentTimeMillis();
+ mSqlRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis, endTimeMillis,
+ filter, uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter,
+ flagsFilter, attributionExemptPkgs);
+ long end = System.currentTimeMillis();
+ long xmlTimeTaken = start2 - start;
+ long sqlTimeTaken = end - start2;
+ try {
+ assertHistoricalOpsAreEquals(result, result2);
+ } catch (Exception ex) {
+ Slog.e(LOG_TAG, "different output when reading discrete ops", ex);
+ }
+ Log.i(LOG_TAG, "Read: XML time taken : " + xmlTimeTaken + ", SQL time taken : "
+ + sqlTimeTaken + ", diff (sql - xml): " + (sqlTimeTaken - xmlTimeTaken));
+ }
+
+ void assertHistoricalOpsAreEquals(AppOpsManager.HistoricalOps sqlResult,
+ AppOpsManager.HistoricalOps xmlResult) {
+ assertEquals(sqlResult.getUidCount(), xmlResult.getUidCount());
+ int uidCount = sqlResult.getUidCount();
+
+ for (int i = 0; i < uidCount; i++) {
+ AppOpsManager.HistoricalUidOps sqlUidOps = sqlResult.getUidOpsAt(i);
+ AppOpsManager.HistoricalUidOps xmlUidOps = xmlResult.getUidOpsAt(i);
+ Slog.i(LOG_TAG, "sql uid: " + sqlUidOps.getUid() + ", xml uid: " + xmlUidOps.getUid());
+ assertEquals(sqlUidOps.getUid(), xmlUidOps.getUid());
+ assertEquals(sqlUidOps.getPackageCount(), xmlUidOps.getPackageCount());
+
+ int packageCount = sqlUidOps.getPackageCount();
+ for (int p = 0; p < packageCount; p++) {
+ AppOpsManager.HistoricalPackageOps sqlPackageOps = sqlUidOps.getPackageOpsAt(p);
+ AppOpsManager.HistoricalPackageOps xmlPackageOps = xmlUidOps.getPackageOpsAt(p);
+ Slog.i(LOG_TAG, "sql package: " + sqlPackageOps.getPackageName() + ", xml package: "
+ + xmlPackageOps.getPackageName());
+ assertEquals(sqlPackageOps.getPackageName(), xmlPackageOps.getPackageName());
+ assertEquals(sqlPackageOps.getAttributedOpsCount(),
+ xmlPackageOps.getAttributedOpsCount());
+
+ int attrCount = sqlPackageOps.getAttributedOpsCount();
+ for (int a = 0; a < attrCount; a++) {
+ AppOpsManager.AttributedHistoricalOps sqlAttrOps =
+ sqlPackageOps.getAttributedOpsAt(a);
+ AppOpsManager.AttributedHistoricalOps xmlAttrOps =
+ xmlPackageOps.getAttributedOpsAt(a);
+ Slog.i(LOG_TAG, "sql tag: " + sqlAttrOps.getTag() + ", xml tag: "
+ + xmlAttrOps.getTag());
+ assertEquals(sqlAttrOps.getTag(), xmlAttrOps.getTag());
+ assertEquals(sqlAttrOps.getOpCount(), xmlAttrOps.getOpCount());
+
+ int opCount = sqlAttrOps.getOpCount();
+ for (int o = 0; o < opCount; o++) {
+ AppOpsManager.HistoricalOp sqlHistoricalOp = sqlAttrOps.getOpAt(o);
+ AppOpsManager.HistoricalOp xmlHistoricalOp = xmlAttrOps.getOpAt(o);
+ Slog.i(LOG_TAG, "sql op: " + sqlHistoricalOp.getOpName() + ", xml op: "
+ + xmlHistoricalOp.getOpName());
+ assertEquals(sqlHistoricalOp.getOpName(), xmlHistoricalOp.getOpName());
+ assertEquals(sqlHistoricalOp.getDiscreteAccessCount(),
+ xmlHistoricalOp.getDiscreteAccessCount());
+
+ int accessCount = sqlHistoricalOp.getDiscreteAccessCount();
+ for (int x = 0; x < accessCount; x++) {
+ AppOpsManager.AttributedOpEntry sqlOpEntry =
+ sqlHistoricalOp.getDiscreteAccessAt(x);
+ AppOpsManager.AttributedOpEntry xmlOpEntry =
+ xmlHistoricalOp.getDiscreteAccessAt(x);
+ Slog.i(LOG_TAG, "sql keys: " + sqlOpEntry.collectKeys() + ", xml keys: "
+ + xmlOpEntry.collectKeys());
+ assertEquals(sqlOpEntry.collectKeys(), xmlOpEntry.collectKeys());
+ assertEquals(sqlOpEntry.isRunning(), xmlOpEntry.isRunning());
+ ArraySet<Long> keys = sqlOpEntry.collectKeys();
+ final int keyCount = keys.size();
+ for (int k = 0; k < keyCount; k++) {
+ final long key = keys.valueAt(k);
+ final int flags = extractFlagsFromKey(key);
+ assertEquals(sqlOpEntry.getLastDuration(flags),
+ xmlOpEntry.getLastDuration(flags));
+ assertEquals(sqlOpEntry.getLastProxyInfo(flags),
+ xmlOpEntry.getLastProxyInfo(flags));
+ assertEquals(sqlOpEntry.getLastAccessTime(flags),
+ xmlOpEntry.getLastAccessTime(flags));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // code duplicated for assertions
+ private static final int FLAGS_MASK = 0xFFFFFFFF;
+
+ public static int extractFlagsFromKey(@AppOpsManager.DataBucketKey long key) {
+ return (int) (key & FLAGS_MASK);
+ }
+
+ private void assertEquals(Object actual, Object expected) {
+ if (!Objects.equals(actual, expected)) {
+ throw new IllegalStateException("Actual (" + actual + ") is not equal to expected ("
+ + expected + ")");
+ }
+ }
+
+ @Override
+ void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter,
+ @Nullable String attributionTagFilter, int filter, int dumpOp,
+ @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+ int nDiscreteOps) {
+ mXmlRegistry.dump(pw, uidFilter, packageNameFilter, attributionTagFilter, filter, dumpOp,
+ sdf, date, prefix, nDiscreteOps);
+ pw.println("--------------------------------------------------------");
+ pw.println("--------------------------------------------------------");
+ mSqlRegistry.dump(pw, uidFilter, packageNameFilter, attributionTagFilter, filter, dumpOp,
+ sdf, date, prefix, nDiscreteOps);
+ }
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
index 7f161f618618..a6e3fc7cc66a 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
@@ -24,48 +24,20 @@ import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
-import static android.app.AppOpsManager.OP_CAMERA;
-import static android.app.AppOpsManager.OP_COARSE_LOCATION;
-import static android.app.AppOpsManager.OP_EMERGENCY_LOCATION;
-import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
-import static android.app.AppOpsManager.OP_FLAG_SELF;
-import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
-import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.app.AppOpsManager.OP_NONE;
-import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
-import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
-import static android.app.AppOpsManager.OP_PROCESS_OUTGOING_CALLS;
-import static android.app.AppOpsManager.OP_READ_ICC_SMS;
-import static android.app.AppOpsManager.OP_READ_SMS;
-import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
-import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
-import static android.app.AppOpsManager.OP_SEND_SMS;
-import static android.app.AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS;
-import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.app.AppOpsManager.OP_WRITE_ICC_SMS;
-import static android.app.AppOpsManager.OP_WRITE_SMS;
import static android.app.AppOpsManager.flagsToString;
import static android.app.AppOpsManager.getUidStateName;
import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
-import static java.lang.Long.min;
import static java.lang.Math.max;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
-import android.os.AsyncTask;
-import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.permission.flags.Flags;
-import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
@@ -84,10 +56,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
-import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
@@ -99,100 +68,30 @@ import java.util.Objects;
import java.util.Set;
/**
- * This class manages information about recent accesses to ops for permission usage timeline.
- *
- * The discrete history is kept for limited time (initial default is 24 hours, set in
- * {@link DiscreteRegistry#sDiscreteHistoryCutoff) and discarded after that.
- *
- * Discrete history is quantized to reduce resources footprint. By default quantization is set to
- * one minute in {@link DiscreteRegistry#sDiscreteHistoryQuantization}. All access times are aligned
- * to the closest quantized time. All durations (except -1, meaning no duration) are rounded up to
- * the closest quantized interval.
- *
- * When data is queried through API, events are deduplicated and for every time quant there can
- * be only one {@link AppOpsManager.AttributedOpEntry}. Each entry contains information about
- * different accesses which happened in specified time quant - across dimensions of
- * {@link AppOpsManager.UidState} and {@link AppOpsManager.OpFlags}. For each dimension
- * it is only possible to know if at least one access happened in the time quant.
+ * Xml persistence implementation for discrete ops.
*
+ * <p>
* Every time state is saved (default is 30 minutes), memory state is dumped to a
* new file and memory state is cleared. Files older than time limit are deleted
* during the process.
- *
+ * <p>
* When request comes in, files are read and requested information is collected
* and delivered. Information is cached in memory until the next state save (up to 30 minutes), to
* avoid reading disk if more API calls come in a quick succession.
- *
+ * <p>
* THREADING AND LOCKING:
- * For in-memory transactions this class relies on {@link DiscreteRegistry#mInMemoryLock}. It is
- * assumed that the same lock is used for in-memory transactions in {@link AppOpsService},
- * {@link HistoricalRegistry}, and {@link DiscreteRegistry}.
- * {@link DiscreteRegistry#recordDiscreteAccess(int, String, int, String, int, int, long, long)}
- * must only be called while holding this lock.
- * {@link DiscreteRegistry#mOnDiskLock} is used when disk transactions are performed.
- * It is very important to release {@link DiscreteRegistry#mInMemoryLock} as soon as possible, as
- * no AppOps related transactions across the system can be performed while it is held.
+ * For in-memory transactions this class relies on {@link DiscreteOpsXmlRegistry#mInMemoryLock}.
+ * It is assumed that the same lock is used for in-memory transactions in {@link AppOpsService},
+ * {@link HistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
+ * {@link DiscreteOpsRegistry#recordDiscreteAccess} must only be called while holding this lock.
+ * {@link DiscreteOpsXmlRegistry#mOnDiskLock} is used when disk transactions are performed.
+ * It is very important to release {@link DiscreteOpsXmlRegistry#mInMemoryLock} as soon as
+ * possible, as no AppOps related transactions across the system can be performed while it is held.
*
- * INITIALIZATION: We can initialize persistence only after the system is ready
- * as we need to check the optional configuration override from the settings
- * database which is not initialized at the time the app ops service is created. This class
- * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All
- * outside calls are going through {@link HistoricalRegistry}, where
- * {@link HistoricalRegistry#isPersistenceInitializedMLocked()} check is done.
*/
-
-final class DiscreteRegistry {
+class DiscreteOpsXmlRegistry extends DiscreteOpsRegistry {
static final String DISCRETE_HISTORY_FILE_SUFFIX = "tl";
- private static final String TAG = DiscreteRegistry.class.getSimpleName();
-
- private static final String PROPERTY_DISCRETE_HISTORY_CUTOFF = "discrete_history_cutoff_millis";
- private static final String PROPERTY_DISCRETE_HISTORY_QUANTIZATION =
- "discrete_history_quantization_millis";
- private static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
- private static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
- private static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
- + "," + OP_EMERGENCY_LOCATION + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + ","
- + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + ","
- + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
- + "," + OP_RESERVED_FOR_TESTING;
- private static final int[] sDiscreteOpsToLog =
- new int[]{OP_FINE_LOCATION, OP_COARSE_LOCATION, OP_EMERGENCY_LOCATION, OP_CAMERA,
- OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, OP_PHONE_CALL_CAMERA,
- OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, OP_READ_SMS,
- OP_WRITE_SMS, OP_SEND_SMS, OP_READ_ICC_SMS, OP_WRITE_ICC_SMS,
- OP_SMS_FINANCIAL_TRANSACTIONS, OP_SYSTEM_ALERT_WINDOW, OP_MONITOR_LOCATION,
- OP_MONITOR_HIGH_POWER_LOCATION, OP_PROCESS_OUTGOING_CALLS,
- };
- private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis();
- private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
- private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION =
- Duration.ofMinutes(1).toMillis();
-
- static final int ACCESS_TYPE_NOTE_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__NOTE_OP;
- static final int ACCESS_TYPE_START_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__START_OP;
- static final int ACCESS_TYPE_FINISH_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__FINISH_OP;
- static final int ACCESS_TYPE_PAUSE_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__PAUSE_OP;
- static final int ACCESS_TYPE_RESUME_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__RESUME_OP;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"ACCESS_TYPE_"}, value = {
- ACCESS_TYPE_NOTE_OP,
- ACCESS_TYPE_START_OP,
- ACCESS_TYPE_FINISH_OP,
- ACCESS_TYPE_PAUSE_OP,
- ACCESS_TYPE_RESUME_OP
- })
- public @interface AccessType {}
-
- private static long sDiscreteHistoryCutoff;
- private static long sDiscreteHistoryQuantization;
- private static int[] sDiscreteOps;
- private static int sDiscreteFlags;
+ private static final String TAG = DiscreteOpsXmlRegistry.class.getSimpleName();
private static final String TAG_HISTORY = "h";
private static final String ATTR_VERSION = "v";
@@ -221,9 +120,6 @@ final class DiscreteRegistry {
private static final String ATTR_ATTRIBUTION_FLAGS = "af";
private static final String ATTR_CHAIN_ID = "ci";
- private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
- | OP_FLAG_TRUSTED_PROXY;
-
// Lock for read/write access to on disk state
private final Object mOnDiskLock = new Object();
@@ -239,14 +135,12 @@ final class DiscreteRegistry {
@GuardedBy("mOnDiskLock")
private DiscreteOps mCachedOps = null;
- private boolean mDebugMode = false;
-
- DiscreteRegistry(Object inMemoryLock) {
- this(inMemoryLock, new File(new File(Environment.getDataSystemDirectory(), "appops"),
- "discrete"));
+ DiscreteOpsXmlRegistry(Object inMemoryLock) {
+ this(inMemoryLock, getDiscreteOpsDir());
}
- DiscreteRegistry(Object inMemoryLock, File discreteAccessDir) {
+ // constructor for tests.
+ DiscreteOpsXmlRegistry(Object inMemoryLock, File discreteAccessDir) {
mInMemoryLock = inMemoryLock;
synchronized (mOnDiskLock) {
mDiscreteAccessDir = discreteAccessDir;
@@ -258,40 +152,8 @@ final class DiscreteRegistry {
}
}
- void systemReady() {
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
- AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties p) -> {
- setDiscreteHistoryParameters(p);
- });
- setDiscreteHistoryParameters(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_PRIVACY));
- }
-
- private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
- if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_CUTOFF)) {
- sDiscreteHistoryCutoff = p.getLong(PROPERTY_DISCRETE_HISTORY_CUTOFF,
- DEFAULT_DISCRETE_HISTORY_CUTOFF);
- if (!Build.IS_DEBUGGABLE && !mDebugMode) {
- sDiscreteHistoryCutoff = min(MAXIMUM_DISCRETE_HISTORY_CUTOFF,
- sDiscreteHistoryCutoff);
- }
- } else {
- sDiscreteHistoryCutoff = DEFAULT_DISCRETE_HISTORY_CUTOFF;
- }
- if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_QUANTIZATION)) {
- sDiscreteHistoryQuantization = p.getLong(PROPERTY_DISCRETE_HISTORY_QUANTIZATION,
- DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
- if (!Build.IS_DEBUGGABLE && !mDebugMode) {
- sDiscreteHistoryQuantization = max(DEFAULT_DISCRETE_HISTORY_QUANTIZATION,
- sDiscreteHistoryQuantization);
- }
- } else {
- sDiscreteHistoryQuantization = DEFAULT_DISCRETE_HISTORY_QUANTIZATION;
- }
- sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS) ? sDiscreteFlags =
- p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
- sDiscreteOps = p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST) ? parseOpsList(
- p.getString(PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS)) : parseOpsList(
- DEFAULT_DISCRETE_OPS);
+ static File getDiscreteOpsDir() {
+ return new File(new File(Environment.getDataSystemDirectory(), "appops"), "discrete");
}
void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op,
@@ -300,17 +162,9 @@ final class DiscreteRegistry {
@AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
@AccessType int accessType) {
if (shouldLogAccess(op)) {
- int firstChar = 0;
- if (attributionTag != null && attributionTag.startsWith(packageName)) {
- firstChar = packageName.length();
- if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar)
- == '.') {
- firstChar++;
- }
- }
FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType,
uidState, flags, attributionFlags,
- attributionTag == null ? null : attributionTag.substring(firstChar),
+ getAttributionTag(attributionTag, packageName),
attributionChainId);
}
@@ -331,7 +185,7 @@ final class DiscreteRegistry {
}
}
- void writeAndClearAccessHistory() {
+ void writeAndClearOldAccessHistory() {
synchronized (mOnDiskLock) {
if (mDiscreteAccessDir == null) {
Slog.d(TAG, "State not saved - persistence not initialized.");
@@ -350,6 +204,22 @@ final class DiscreteRegistry {
}
}
+ void migrateSqliteData(DiscreteOps sqliteOps) {
+ synchronized (mOnDiskLock) {
+ if (mDiscreteAccessDir == null) {
+ Slog.d(TAG, "State not saved - persistence not initialized.");
+ return;
+ }
+ synchronized (mInMemoryLock) {
+ mDiscreteOps.mLargestChainId = sqliteOps.mLargestChainId;
+ mDiscreteOps.mChainIdOffset = sqliteOps.mChainIdOffset;
+ }
+ if (!sqliteOps.isEmpty()) {
+ persistDiscreteOpsLocked(sqliteOps);
+ }
+ }
+ }
+
void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@@ -369,7 +239,7 @@ final class DiscreteRegistry {
discreteOps.applyToHistoricalOps(result, attributionChains);
}
- private int readLargestChainIdFromDiskLocked() {
+ int readLargestChainIdFromDiskLocked() {
final File[] files = mDiscreteAccessDir.listFiles();
if (files != null && files.length > 0) {
File latestFile = null;
@@ -497,6 +367,13 @@ final class DiscreteRegistry {
}
}
+ void deleteDiscreteOpsDir() {
+ synchronized (mOnDiskLock) {
+ mCachedOps = null;
+ FileUtils.deleteContentsAndDir(mDiscreteAccessDir);
+ }
+ }
+
void clearHistory(int uid, String packageName) {
synchronized (mOnDiskLock) {
DiscreteOps discreteOps;
@@ -1506,26 +1383,6 @@ final class DiscreteRegistry {
}
}
- private static int[] parseOpsList(String opsList) {
- String[] strArr;
- if (opsList.isEmpty()) {
- strArr = new String[0];
- } else {
- strArr = opsList.split(",");
- }
- int nOps = strArr.length;
- int[] result = new int[nOps];
- try {
- for (int i = 0; i < nOps; i++) {
- result[i] = Integer.parseInt(strArr[i]);
- }
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Failed to parse Discrete ops list: " + e.getMessage());
- return parseOpsList(DEFAULT_DISCRETE_OPS);
- }
- return result;
- }
-
private static List<DiscreteOpEvent> stableListMerge(List<DiscreteOpEvent> a,
List<DiscreteOpEvent> b) {
int nA = a.size();
@@ -1570,34 +1427,4 @@ final class DiscreteRegistry {
}
return result;
}
-
- private static boolean isDiscreteOp(int op, @AppOpsManager.OpFlags int flags) {
- if (!ArrayUtils.contains(sDiscreteOps, op)) {
- return false;
- }
- if ((flags & (sDiscreteFlags)) == 0) {
- return false;
- }
- return true;
- }
-
- private static boolean shouldLogAccess(int op) {
- return Flags.appopAccessTrackingLoggingEnabled()
- && ArrayUtils.contains(sDiscreteOpsToLog, op);
- }
-
- private static long discretizeTimeStamp(long timeStamp) {
- return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
-
- }
-
- private static long discretizeDuration(long duration) {
- return duration == -1 ? -1 : (duration + sDiscreteHistoryQuantization - 1)
- / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
- }
-
- void setDebugMode(boolean debugMode) {
- this.mDebugMode = debugMode;
- }
}
-
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 5e67f26ba1f6..ba391d0a9995 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -35,6 +35,7 @@ import android.app.AppOpsManager.OpFlags;
import android.app.AppOpsManager.OpHistoryFlags;
import android.app.AppOpsManager.UidState;
import android.content.ContentResolver;
+import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Build;
@@ -45,6 +46,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.UserHandle;
+import android.permission.flags.Flags;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.LongSparseArray;
@@ -135,7 +137,7 @@ final class HistoricalRegistry {
private static final String PARAMETER_DELIMITER = ",";
private static final String PARAMETER_ASSIGNMENT = "=";
- private volatile @NonNull DiscreteRegistry mDiscreteRegistry;
+ private volatile @NonNull DiscreteOpsRegistry mDiscreteRegistry;
@GuardedBy("mLock")
private @NonNull LinkedList<HistoricalOps> mPendingWrites = new LinkedList<>();
@@ -196,13 +198,30 @@ final class HistoricalRegistry {
@GuardedBy("mOnDiskLock")
private Persistence mPersistence;
- HistoricalRegistry(@NonNull Object lock) {
+ private final Context mContext;
+
+ HistoricalRegistry(@NonNull Object lock, Context context) {
mInMemoryLock = lock;
- mDiscreteRegistry = new DiscreteRegistry(lock);
+ mContext = context;
+ if (Flags.enableSqliteAppopsAccesses()) {
+ mDiscreteRegistry = new DiscreteOpsSqlRegistry(context);
+ if (DiscreteOpsXmlRegistry.getDiscreteOpsDir().exists()) {
+ DiscreteOpsSqlRegistry sqlRegistry = (DiscreteOpsSqlRegistry) mDiscreteRegistry;
+ DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(context);
+ DiscreteOpsMigrationHelper.migrateDiscreteOpsToSqlite(xmlRegistry, sqlRegistry);
+ }
+ } else {
+ mDiscreteRegistry = new DiscreteOpsXmlRegistry(context);
+ if (DiscreteOpsDbHelper.getDatabaseFile().exists()) { // roll-back sqlite
+ DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(context);
+ DiscreteOpsXmlRegistry xmlRegistry = (DiscreteOpsXmlRegistry) mDiscreteRegistry;
+ DiscreteOpsMigrationHelper.migrateDiscreteOpsToXml(sqlRegistry, xmlRegistry);
+ }
+ }
}
HistoricalRegistry(@NonNull HistoricalRegistry other) {
- this(other.mInMemoryLock);
+ this(other.mInMemoryLock, other.mContext);
mMode = other.mMode;
mBaseSnapshotInterval = other.mBaseSnapshotInterval;
mIntervalCompressionMultiplier = other.mIntervalCompressionMultiplier;
@@ -475,7 +494,7 @@ final class HistoricalRegistry {
@NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
@OpFlags int flags, long accessTime,
@AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
- @DiscreteRegistry.AccessType int accessType, int accessCount) {
+ @DiscreteOpsRegistry.AccessType int accessType, int accessCount) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -512,7 +531,7 @@ final class HistoricalRegistry {
@NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
@OpFlags int flags, long eventStartTime, long increment,
@AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
- @DiscreteRegistry.AccessType int accessType) {
+ @DiscreteOpsRegistry.AccessType int accessType) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -648,7 +667,7 @@ final class HistoricalRegistry {
}
void writeAndClearDiscreteHistory() {
- mDiscreteRegistry.writeAndClearAccessHistory();
+ mDiscreteRegistry.writeAndClearOldAccessHistory();
}
void clearAllHistory() {
@@ -743,7 +762,7 @@ final class HistoricalRegistry {
}
persistPendingHistory(pendingWrites);
}
- mDiscreteRegistry.writeAndClearAccessHistory();
+ mDiscreteRegistry.writeAndClearOldAccessHistory();
}
private void persistPendingHistory(@NonNull List<HistoricalOps> pendingWrites) {
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index e89f43bd7196..20c33275b8f1 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -876,7 +876,28 @@ final class CompatConfig {
}
@Nullable
+ @android.ravenwood.annotation.RavenwoodReplace(
+ blockedBy = PackageManager.class,
+ reason = "PackageManager.getApplicationInfo() isn't supported yet")
private Long getVersionCodeOrNull(String packageName) {
+ return getVersionCodeOrNullImpl(packageName);
+ }
+
+ @SuppressWarnings("unused")
+ @Nullable
+ private Long getVersionCodeOrNull$ravenwood(String packageName) {
+ try {
+ // It's possible that the context is mocked, try the real method first
+ return getVersionCodeOrNullImpl(packageName);
+ } catch (Throwable e) {
+ // For now, Ravenwood doesn't support the concept of "app updates", so let's
+ // just use a fixed version code for all packages.
+ return 1L;
+ }
+ }
+
+ @Nullable
+ private Long getVersionCodeOrNullImpl(String packageName) {
try {
ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(
packageName, MATCH_ANY_USER);
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 5d9db65fe2b2..d89db8d5581b 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -312,6 +312,13 @@ public final class DeviceStateManagerService extends SystemService {
mProcessObserver);
}
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ mDeviceStatePolicy.getDeviceStateProvider().onSystemReady();
+ }
+ }
+
@VisibleForTesting
Handler getHandler() {
return mHandler;
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 8d07609cef30..8a8ebc2ffc21 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -91,6 +91,11 @@ public interface DeviceStateProvider extends Dumpable {
@interface SupportedStatesUpdatedReason {}
/**
+ * Called when the system boot phase advances to PHASE_SYSTEM_SERVICES_READY.
+ */
+ default void onSystemReady() {};
+
+ /**
* Registers a listener for changes in provider state.
* <p>
* It is <b>required</b> that
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index c384b5434bce..9349ea54e4ee 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -1030,10 +1030,20 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
}
@ServiceThreadOnly
+ void addAndStartAction(final HdmiCecFeatureAction action, final boolean remove) {
+ assertRunOnServiceThread();
+ if (hasAction(action.getClass()) && remove) {
+ // If the action is currently running, remove it and restart it.
+ Slog.i(TAG, action.getClass().getName() + " is in progress. Restarting.");
+ removeAction(action.getClass());
+ }
+ addAndStartAction(action);
+ }
+
+ @ServiceThreadOnly
void startNewAvbAudioStatusAction(int targetAddress) {
assertRunOnServiceThread();
- removeAction(AbsoluteVolumeAudioStatusAction.class);
- addAndStartAction(new AbsoluteVolumeAudioStatusAction(this, targetAddress));
+ addAndStartAction(new AbsoluteVolumeAudioStatusAction(this, targetAddress), true);
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 1e90ab279d32..510e4f5e1868 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -317,11 +317,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
|| ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
&& lastSystemAudioControlStatus && isSystemAudioControlFeatureEnabled())) {
- if (hasAction(SystemAudioInitiationActionFromAvr.class)) {
- Slog.i(TAG, "SystemAudioInitiationActionFromAvr is in progress. Restarting.");
- removeAction(SystemAudioInitiationActionFromAvr.class);
- }
- addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
+ addAndStartAction(new SystemAudioInitiationActionFromAvr(this), true);
}
}
@@ -457,6 +453,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
HdmiLogger.debug("AVR device is not directly connected with TV");
return Constants.ABORT_NOT_IN_CORRECT_MODE;
} else {
+ // Action has been removed if it existed, do not attempt to remove again before start.
addAndStartAction(new ArcInitiationActionFromAvr(this));
return Constants.HANDLED;
}
@@ -477,11 +474,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
&& !getActions(ArcTerminationActionFromAvr.class).get(0).mCallbacks.isEmpty()) {
IHdmiControlCallback callback =
getActions(ArcTerminationActionFromAvr.class).get(0).mCallbacks.get(0);
- removeAction(ArcTerminationActionFromAvr.class);
- addAndStartAction(new ArcTerminationActionFromAvr(this, callback));
+ addAndStartAction(new ArcTerminationActionFromAvr(this, callback), true);
} else {
- removeAction(ArcTerminationActionFromAvr.class);
- addAndStartAction(new ArcTerminationActionFromAvr(this));
+ addAndStartAction(new ArcTerminationActionFromAvr(this), true);
}
return Constants.HANDLED;
}
@@ -1036,11 +1031,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
void onSystemAudioControlFeatureSupportChanged(boolean enabled) {
setSystemAudioControlFeatureEnabled(enabled);
if (enabled) {
- if (hasAction(SystemAudioInitiationActionFromAvr.class)) {
- Slog.i(TAG, "SystemAudioInitiationActionFromAvr is in progress. Restarting.");
- removeAction(SystemAudioInitiationActionFromAvr.class);
- }
- addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
+ addAndStartAction(new SystemAudioInitiationActionFromAvr(this), true);
}
}
@@ -1221,8 +1212,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
removeAction(ArcTerminationActionFromAvr.class);
if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)
&& isDirectConnectToTv() && !isArcEnabled()) {
- removeAction(ArcInitiationActionFromAvr.class);
- addAndStartAction(new ArcInitiationActionFromAvr(this));
+ addAndStartAction(new ArcInitiationActionFromAvr(this), true);
}
}
@@ -1367,10 +1357,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
if (mService.isDeviceDiscoveryHandledByPlayback()) {
return;
}
- if (hasAction(DeviceDiscoveryAction.class)) {
- Slog.i(TAG, "Device Discovery Action is in progress. Restarting.");
- removeAction(DeviceDiscoveryAction.class);
- }
DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
new DeviceDiscoveryCallback() {
@Override
@@ -1380,7 +1366,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
}
});
- addAndStartAction(action);
+ addAndStartAction(action, true);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 0b667fc10880..86abbc403e50 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -21,7 +21,6 @@ import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.hardware.display.DeviceProductInfo;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -32,7 +31,6 @@ import android.os.PowerManager;
import android.os.SystemProperties;
import android.sysprop.HdmiProperties;
import android.util.Slog;
-import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LocalePicker;
@@ -151,10 +149,6 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
private void launchDeviceDiscovery() {
assertRunOnServiceThread();
clearDeviceInfoList();
- if (hasAction(DeviceDiscoveryAction.class)) {
- Slog.i(TAG, "Device Discovery Action is in progress. Restarting.");
- removeAction(DeviceDiscoveryAction.class);
- }
DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
new DeviceDiscoveryAction.DeviceDiscoveryCallback() {
@Override
@@ -163,25 +157,21 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
mService.getHdmiCecNetwork().addCecDevice(info);
}
- // Since we removed all devices when it starts and device discovery action
- // does not poll local devices, we should put device info of local device
- // manually here.
+ // Since we removed all devices when it starts and device discovery
+ // action does not poll local devices, we should put device info of
+ // local device manually here.
for (HdmiCecLocalDevice device : mService.getAllCecLocalDevices()) {
mService.getHdmiCecNetwork().addCecDevice(device.getDeviceInfo());
}
- List<HotplugDetectionAction> hotplugActions =
- getActions(HotplugDetectionAction.class);
- if (hotplugActions.isEmpty()) {
+ if (!hasAction(HotplugDetectionAction.class)) {
addAndStartAction(
- new HotplugDetectionAction(HdmiCecLocalDevicePlayback.this));
+ new HotplugDetectionAction(
+ HdmiCecLocalDevicePlayback.this));
}
if (mService.isHdmiControlEnhancedBehaviorFlagEnabled()) {
- List<PowerStatusMonitorActionFromPlayback>
- powerStatusMonitorActionsFromPlayback =
- getActions(PowerStatusMonitorActionFromPlayback.class);
- if (powerStatusMonitorActionsFromPlayback.isEmpty()) {
+ if (!hasAction(PowerStatusMonitorActionFromPlayback.class)) {
addAndStartAction(
new PowerStatusMonitorActionFromPlayback(
HdmiCecLocalDevicePlayback.this));
@@ -189,7 +179,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
}
}
});
- addAndStartAction(action);
+ addAndStartAction(action, true);
}
@Override
@@ -235,8 +225,16 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
return;
}
- removeAction(DeviceSelectActionFromPlayback.class);
- addAndStartAction(new DeviceSelectActionFromPlayback(this, targetDevice, callback));
+ List<DeviceSelectActionFromPlayback> actions = getActions(
+ DeviceSelectActionFromPlayback.class);
+ if (!actions.isEmpty()) {
+ DeviceSelectActionFromPlayback action = actions.get(0);
+ if (action.getTargetAddress() == targetDevice.getLogicalAddress()) {
+ return;
+ }
+ }
+ addAndStartAction(new DeviceSelectActionFromPlayback(this, targetDevice, callback),
+ true);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 424102cbdd89..3d6d34bf9911 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -220,10 +220,6 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
List<HdmiCecMessage> bufferedActiveSource = mDelayedMessageBuffer
.getBufferedMessagesWithOpcode(Constants.MESSAGE_ACTIVE_SOURCE);
if (bufferedActiveSource.isEmpty()) {
- if (hasAction(RequestActiveSourceAction.class)) {
- Slog.i(TAG, "RequestActiveSourceAction is in progress. Restarting.");
- removeAction(RequestActiveSourceAction.class);
- }
addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
@Override
public void onComplete(int result) {
@@ -231,7 +227,7 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
launchRoutingControl(routingForBootup);
}
}
- }));
+ }), true);
} else {
addCecDeviceForBufferedActiveSource(bufferedActiveSource.get(0));
}
@@ -328,8 +324,15 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
return;
}
- removeAction(DeviceSelectActionFromTv.class);
- addAndStartAction(new DeviceSelectActionFromTv(this, targetDevice, callback));
+ List<DeviceSelectActionFromTv> actions = getActions(DeviceSelectActionFromTv.class);
+ if (!actions.isEmpty()) {
+ DeviceSelectActionFromTv action = actions.get(0);
+ if (action.getTargetAddress() == targetDevice.getLogicalAddress()) {
+ return;
+ }
+ }
+ addAndStartAction(new DeviceSelectActionFromTv(this, targetDevice, callback),
+ true);
}
@ServiceThreadOnly
@@ -475,9 +478,8 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
HdmiCecMessageBuilder.buildRoutingChange(
getDeviceInfo().getLogicalAddress(), oldPath, newPath);
mService.sendCecCommand(routingChange);
- removeAction(RoutingControlAction.class);
addAndStartAction(
- new RoutingControlAction(this, newPath, callback));
+ new RoutingControlAction(this, newPath, callback), true);
}
@ServiceThreadOnly
@@ -801,16 +803,12 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mSelectRequestBuffer.process();
resetSelectRequestBuffer();
- List<HotplugDetectionAction> hotplugActions
- = getActions(HotplugDetectionAction.class);
- if (hotplugActions.isEmpty()) {
+ if (!hasAction(HotplugDetectionAction.class)) {
addAndStartAction(
new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
}
- List<PowerStatusMonitorAction> powerStatusActions
- = getActions(PowerStatusMonitorAction.class);
- if (powerStatusActions.isEmpty()) {
+ if (!hasAction(PowerStatusMonitorAction.class)) {
addAndStartAction(
new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 35ef18b144e7..bd8b67b9d626 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -17,7 +17,6 @@
package com.android.server.hdmi;
import static android.media.tv.flags.Flags.hdmiControlEnhancedBehavior;
-
import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_DISABLED;
@@ -107,7 +106,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.KeyEvent;
-import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -1218,9 +1216,6 @@ public class HdmiControlService extends SystemService {
audioSystem.terminateSystemAudioMode();
}
if (isArcEnabled) {
- if (audioSystem.hasAction(ArcTerminationActionFromAvr.class)) {
- audioSystem.removeAction(ArcTerminationActionFromAvr.class);
- }
audioSystem.addAndStartAction(new ArcTerminationActionFromAvr(audioSystem,
new IHdmiControlCallback.Stub() {
@Override
@@ -1228,7 +1223,7 @@ public class HdmiControlService extends SystemService {
mAddressAllocated = false;
initializeCecLocalDevices(INITIATED_BY_SOUNDBAR_MODE);
}
- }));
+ }), true);
}
}
if (!isArcEnabled) {
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
index 9a3cde156300..d05ded5367d0 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
@@ -68,6 +68,8 @@ 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();
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index febf24edc294..e25ea4b43827 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -74,6 +74,8 @@ class InputSettingsObserver extends ContentObserver {
Map.entry(Settings.System.getUriFor(
Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED),
(reason) -> updateMouseAccelerationEnabled()),
+ Map.entry(Settings.System.getUriFor(Settings.System.MOUSE_SCROLLING_SPEED),
+ (reason) -> updateMouseScrollingSpeed()),
Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED),
(reason) -> updateTouchpadPointerSpeed()),
Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING),
@@ -199,6 +201,11 @@ class InputSettingsObserver extends ContentObserver {
InputSettings.isMousePointerAccelerationEnabled(mContext));
}
+ private void updateMouseScrollingSpeed() {
+ mNative.setMouseScrollingSpeed(
+ constrainPointerSpeedValue(InputSettings.getMouseScrollingSpeed(mContext)));
+ }
+
private void updateTouchpadPointerSpeed() {
mNative.setTouchpadPointerSpeed(
constrainPointerSpeedValue(InputSettings.getTouchpadPointerSpeed(mContext)));
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 7dbde64a6412..d426e8292f14 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -136,6 +136,8 @@ interface NativeInputManagerService {
void setMouseScrollingAccelerationEnabled(boolean enabled);
+ void setMouseScrollingSpeed(int speed);
+
void setMouseSwapPrimaryButtonEnabled(boolean enabled);
void setMouseAccelerationEnabled(boolean enabled);
@@ -428,6 +430,9 @@ interface NativeInputManagerService {
public native void setMouseScrollingAccelerationEnabled(boolean enabled);
@Override
+ public native void setMouseScrollingSpeed(int speed);
+
+ @Override
public native void setMouseSwapPrimaryButtonEnabled(boolean enabled);
@Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 7698a87f80c9..740c4f195852 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -36,6 +36,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
/**
* A class that manages registration/unregistration of clients and manages messages to/from clients.
@@ -312,15 +313,9 @@ import java.util.concurrent.ConcurrentHashMap;
@Override
public void onCloseEndpointSession(int sessionId, byte reason) {
- boolean callbackInvoked = false;
- for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
- if (broker.hasSessionId(sessionId)) {
- broker.onCloseEndpointSession(sessionId, reason);
- callbackInvoked = true;
- break;
- }
- }
-
+ boolean callbackInvoked =
+ invokeCallbackForMatchingSession(
+ sessionId, (broker) -> broker.onCloseEndpointSession(sessionId, reason));
if (!callbackInvoked) {
Log.w(TAG, "onCloseEndpointSession: unknown session ID " + sessionId);
}
@@ -328,15 +323,9 @@ import java.util.concurrent.ConcurrentHashMap;
@Override
public void onEndpointSessionOpenComplete(int sessionId) {
- boolean callbackInvoked = false;
- for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
- if (broker.hasSessionId(sessionId)) {
- broker.onEndpointSessionOpenComplete(sessionId);
- callbackInvoked = true;
- break;
- }
- }
-
+ boolean callbackInvoked =
+ invokeCallbackForMatchingSession(
+ sessionId, (broker) -> broker.onEndpointSessionOpenComplete(sessionId));
if (!callbackInvoked) {
Log.w(TAG, "onEndpointSessionOpenComplete: unknown session ID " + sessionId);
}
@@ -344,15 +333,9 @@ import java.util.concurrent.ConcurrentHashMap;
@Override
public void onMessageReceived(int sessionId, HubMessage message) {
- boolean callbackInvoked = false;
- for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
- if (broker.hasSessionId(sessionId)) {
- broker.onMessageReceived(sessionId, message);
- callbackInvoked = true;
- break;
- }
- }
-
+ boolean callbackInvoked =
+ invokeCallbackForMatchingSession(
+ sessionId, (broker) -> broker.onMessageReceived(sessionId, message));
if (!callbackInvoked) {
Log.w(TAG, "onMessageReceived: unknown session ID " + sessionId);
}
@@ -360,18 +343,36 @@ import java.util.concurrent.ConcurrentHashMap;
@Override
public void onMessageDeliveryStatusReceived(int sessionId, int sequenceNumber, byte errorCode) {
- boolean callbackInvoked = false;
+ boolean callbackInvoked =
+ invokeCallbackForMatchingSession(
+ sessionId,
+ (broker) ->
+ broker.onMessageDeliveryStatusReceived(
+ sessionId, sequenceNumber, errorCode));
+ if (!callbackInvoked) {
+ Log.w(TAG, "onMessageDeliveryStatusReceived: unknown session ID " + sessionId);
+ }
+ }
+
+ /**
+ * Invokes a callback for a session with matching ID.
+ *
+ * @param callback The callback to execute
+ * @return true if a callback was executed
+ */
+ private boolean invokeCallbackForMatchingSession(
+ int sessionId, Consumer<ContextHubEndpointBroker> callback) {
for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
if (broker.hasSessionId(sessionId)) {
- broker.onMessageDeliveryStatusReceived(sessionId, sequenceNumber, errorCode);
- callbackInvoked = true;
- break;
+ try {
+ callback.accept(broker);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Exception while invoking callback", e);
+ }
+ return true;
}
}
-
- if (!callbackInvoked) {
- Log.w(TAG, "onMessageDeliveryStatusReceived: unknown session ID " + sessionId);
- }
+ return false;
}
/** Unregister the hub (called during init() failure). Silence errors. */
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 34bb4155c943..3babc0ae065c 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -18,6 +18,7 @@ package com.android.server.media.quality;
import android.content.ContentValues;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.media.quality.AmbientBacklightSettings;
@@ -35,8 +36,13 @@ import android.media.quality.SoundProfileHandle;
import android.os.Binder;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
import com.android.server.SystemService;
@@ -45,9 +51,11 @@ import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -64,10 +72,13 @@ public class MediaQualityService extends SystemService {
private final MediaQualityDbHelper mMediaQualityDbHelper;
private final BiMap<Long, String> mPictureProfileTempIdMap;
private final BiMap<Long, String> mSoundProfileTempIdMap;
+ private final PackageManager mPackageManager;
+ private final SparseArray<UserState> mUserStates = new SparseArray<>();
public MediaQualityService(Context context) {
super(context);
mContext = context;
+ mPackageManager = mContext.getPackageManager();
mPictureProfileTempIdMap = new BiMap<>();
mSoundProfileTempIdMap = new BiMap<>();
mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
@@ -85,12 +96,20 @@ public class MediaQualityService extends SystemService {
@Override
public PictureProfile createPictureProfile(PictureProfile pp, UserHandle user) {
+ if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
+ && !incomingPackageEqualsCallingUidPackage(pp.getPackageName()))
+ && !hasGlobalPictureQualityServicePermission()) {
+ notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
+
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = getContentValues(null,
pp.getProfileType(),
pp.getName(),
- pp.getPackageName(),
+ pp.getPackageName() == null || pp.getPackageName().isEmpty()
+ ? getPackageOfCallingUid() : pp.getPackageName(),
pp.getInputId(),
pp.getParameters());
@@ -104,9 +123,13 @@ public class MediaQualityService extends SystemService {
@Override
public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
- Long intId = mPictureProfileTempIdMap.getKey(id);
+ Long dbId = mPictureProfileTempIdMap.getKey(id);
+ if (!hasPermissionToUpdatePictureProfile(dbId, pp)) {
+ notifyError(id, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
- ContentValues values = getContentValues(intId,
+ ContentValues values = getContentValues(dbId,
pp.getProfileType(),
pp.getName(),
pp.getPackageName(),
@@ -118,27 +141,51 @@ public class MediaQualityService extends SystemService {
null, values);
}
+ private boolean hasPermissionToUpdatePictureProfile(Long dbId, PictureProfile toUpdate) {
+ PictureProfile fromDb = getPictureProfile(dbId);
+ return fromDb.getProfileType() == toUpdate.getProfileType()
+ && fromDb.getPackageName().equals(toUpdate.getPackageName())
+ && fromDb.getName().equals(toUpdate.getName())
+ && fromDb.getName().equals(getPackageOfCallingUid());
+ }
+
@Override
public void removePictureProfile(String id, UserHandle user) {
- Long intId = mPictureProfileTempIdMap.getKey(id);
- if (intId != null) {
+ Long dbId = mPictureProfileTempIdMap.getKey(id);
+
+ if (!hasPermissionToRemovePictureProfile(dbId)) {
+ notifyError(id, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
+
+ if (dbId != null) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
String selection = BaseParameters.PARAMETER_ID + " = ?";
- String[] selectionArgs = {Long.toString(intId)};
- db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
+ String[] selectionArgs = {Long.toString(dbId)};
+ int result = db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
selectionArgs);
- mPictureProfileTempIdMap.remove(intId);
+ if (result == 0) {
+ notifyError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
+ mPictureProfileTempIdMap.remove(dbId);
}
}
+ private boolean hasPermissionToRemovePictureProfile(Long dbId) {
+ PictureProfile fromDb = getPictureProfile(dbId);
+ return fromDb.getName().equalsIgnoreCase(getPackageOfCallingUid());
+ }
+
@Override
public PictureProfile getPictureProfile(int type, String name, Bundle options,
UserHandle user) {
boolean includeParams =
options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
- + BaseParameters.PARAMETER_NAME + " = ?";
- String[] selectionArguments = {Integer.toString(type), name};
+ + BaseParameters.PARAMETER_NAME + " = ? AND "
+ + BaseParameters.PARAMETER_PACKAGE + " = ?";
+ String[] selectionArguments = {Integer.toString(type), name, getPackageOfCallingUid()};
try (
Cursor cursor = getCursorAfterQuerying(
@@ -156,13 +203,42 @@ public class MediaQualityService extends SystemService {
return null;
}
cursor.moveToFirst();
- return getPictureProfileWithTempIdFromCursor(cursor);
+ return convertCursorToPictureProfileWithTempId(cursor);
+ }
+ }
+
+ private PictureProfile getPictureProfile(Long dbId) {
+ String selection = BaseParameters.PARAMETER_ID + " = ?";
+ String[] selectionArguments = {Long.toString(dbId)};
+
+ try (
+ Cursor cursor = getCursorAfterQuerying(
+ mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+ getMediaProfileColumns(false), selection, selectionArguments)
+ ) {
+ int count = cursor.getCount();
+ if (count == 0) {
+ return null;
+ }
+ if (count > 1) {
+ Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%d"
+ + " in %s. Should only ever be 0 or 1.", count, dbId,
+ mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
+ return null;
+ }
+ cursor.moveToFirst();
+ return convertCursorToPictureProfileWithTempId(cursor);
}
}
@Override
public List<PictureProfile> getPictureProfilesByPackage(
String packageName, Bundle options, UserHandle user) {
+ if (!hasGlobalPictureQualityServicePermission()) {
+ notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
+
boolean includeParams =
options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
@@ -172,23 +248,31 @@ public class MediaQualityService extends SystemService {
}
@Override
- public List<PictureProfile> getAvailablePictureProfiles(Bundle options, UserHandle user) {
- String[] packageNames = mContext.getPackageManager().getPackagesForUid(
- Binder.getCallingUid());
- if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
- return getPictureProfilesByPackage(packageNames[0], options, user);
+ public List<PictureProfile> getAvailablePictureProfiles(
+ Bundle options, UserHandle user) {
+ String packageName = getPackageOfCallingUid();
+ if (packageName != null) {
+ return getPictureProfilesByPackage(packageName, options, user);
}
return new ArrayList<>();
}
@Override
public boolean setDefaultPictureProfile(String profileId, UserHandle user) {
+ if (!hasGlobalPictureQualityServicePermission()) {
+ notifyError(profileId, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
// TODO: pass the profile ID to MediaQuality HAL when ready.
return false;
}
@Override
public List<String> getPictureProfilePackageNames(UserHandle user) {
+ if (!hasGlobalPictureQualityServicePermission()) {
+ notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
String [] column = {BaseParameters.PARAMETER_PACKAGE};
List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
null, null);
@@ -210,12 +294,19 @@ public class MediaQualityService extends SystemService {
@Override
public SoundProfile createSoundProfile(SoundProfile sp, UserHandle user) {
+ if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty()
+ && !incomingPackageEqualsCallingUidPackage(sp.getPackageName()))
+ && !hasGlobalPictureQualityServicePermission()) {
+ //TODO: error handling
+ return null;
+ }
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = getContentValues(null,
sp.getProfileType(),
sp.getName(),
- sp.getPackageName(),
+ sp.getPackageName() == null || sp.getPackageName().isEmpty()
+ ? getPackageOfCallingUid() : sp.getPackageName(),
sp.getInputId(),
sp.getParameters());
@@ -229,9 +320,14 @@ public class MediaQualityService extends SystemService {
@Override
public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
- Long intId = mSoundProfileTempIdMap.getKey(id);
+ Long dbId = mSoundProfileTempIdMap.getKey(id);
+
+ if (!hasPermissionToUpdateSoundProfile(dbId, sp)) {
+ //TODO: error handling
+ return;
+ }
- ContentValues values = getContentValues(intId,
+ ContentValues values = getContentValues(dbId,
sp.getProfileType(),
sp.getName(),
sp.getPackageName(),
@@ -242,27 +338,49 @@ public class MediaQualityService extends SystemService {
db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
}
+ private boolean hasPermissionToUpdateSoundProfile(Long dbId, SoundProfile sp) {
+ SoundProfile fromDb = getSoundProfile(dbId);
+ return fromDb.getProfileType() == sp.getProfileType()
+ && fromDb.getPackageName().equals(sp.getPackageName())
+ && fromDb.getName().equals(sp.getName())
+ && fromDb.getName().equals(getPackageOfCallingUid());
+ }
+
@Override
public void removeSoundProfile(String id, UserHandle user) {
Long intId = mSoundProfileTempIdMap.getKey(id);
+ if (!hasPermissionToRemoveSoundProfile(intId)) {
+ //TODO: error handling
+ return;
+ }
+
if (intId != null) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
String selection = BaseParameters.PARAMETER_ID + " = ?";
String[] selectionArgs = {Long.toString(intId)};
- db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
+ int result = db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
selectionArgs);
+ if (result == 0) {
+ //TODO: error handling
+ }
mSoundProfileTempIdMap.remove(intId);
}
}
+ private boolean hasPermissionToRemoveSoundProfile(Long dbId) {
+ SoundProfile fromDb = getSoundProfile(dbId);
+ return fromDb.getName().equalsIgnoreCase(getPackageOfCallingUid());
+ }
+
@Override
public SoundProfile getSoundProfile(int type, String id, Bundle options,
UserHandle user) {
boolean includeParams =
options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
- + BaseParameters.PARAMETER_ID + " = ?";
- String[] selectionArguments = {String.valueOf(type), id};
+ + BaseParameters.PARAMETER_ID + " = ? AND "
+ + BaseParameters.PARAMETER_PACKAGE + " = ?";
+ String[] selectionArguments = {String.valueOf(type), id, getPackageOfCallingUid()};
try (
Cursor cursor = getCursorAfterQuerying(
@@ -280,13 +398,42 @@ public class MediaQualityService extends SystemService {
return null;
}
cursor.moveToFirst();
- return getSoundProfileWithTempIdFromCursor(cursor);
+ return convertCursorToSoundProfileWithTempId(cursor);
+ }
+ }
+
+ private SoundProfile getSoundProfile(Long dbId) {
+ String selection = BaseParameters.PARAMETER_ID + " = ?";
+ String[] selectionArguments = {Long.toString(dbId)};
+
+ try (
+ Cursor cursor = getCursorAfterQuerying(
+ mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
+ getMediaProfileColumns(false), selection, selectionArguments)
+ ) {
+ int count = cursor.getCount();
+ if (count == 0) {
+ return null;
+ }
+ if (count > 1) {
+ Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%s "
+ + "in %s. Should only ever be 0 or 1.", count, dbId,
+ mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME));
+ return null;
+ }
+ cursor.moveToFirst();
+ return convertCursorToSoundProfileWithTempId(cursor);
}
}
@Override
public List<SoundProfile> getSoundProfilesByPackage(
String packageName, Bundle options, UserHandle user) {
+ if (!hasGlobalSoundQualityServicePermission()) {
+ //TODO: error handling
+ return new ArrayList<>();
+ }
+
boolean includeParams =
options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
@@ -296,24 +443,30 @@ public class MediaQualityService extends SystemService {
}
@Override
- public List<SoundProfile> getAvailableSoundProfiles(
- Bundle options, UserHandle user) {
- String[] packageNames = mContext.getPackageManager().getPackagesForUid(
- Binder.getCallingUid());
- if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
- return getSoundProfilesByPackage(packageNames[0], options, user);
+ public List<SoundProfile> getAvailableSoundProfiles(Bundle options, UserHandle user) {
+ String packageName = getPackageOfCallingUid();
+ if (packageName != null) {
+ return getSoundProfilesByPackage(packageName, options, user);
}
return new ArrayList<>();
}
@Override
public boolean setDefaultSoundProfile(String profileId, UserHandle user) {
+ if (!hasGlobalSoundQualityServicePermission()) {
+ //TODO: error handling
+ return false;
+ }
// TODO: pass the profile ID to MediaQuality HAL when ready.
return false;
}
@Override
public List<String> getSoundProfilePackageNames(UserHandle user) {
+ if (!hasGlobalSoundQualityServicePermission()) {
+ //TODO: error handling
+ return new ArrayList<>();
+ }
String [] column = {BaseParameters.PARAMETER_NAME};
List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
null, null);
@@ -323,6 +476,37 @@ public class MediaQualityService extends SystemService {
.collect(Collectors.toList());
}
+ private String getPackageOfCallingUid() {
+ String[] packageNames = mPackageManager.getPackagesForUid(
+ Binder.getCallingUid());
+ if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
+ return packageNames[0];
+ }
+ return null;
+ }
+
+ private boolean incomingPackageEqualsCallingUidPackage(String incomingPackage) {
+ return incomingPackage.equalsIgnoreCase(getPackageOfCallingUid());
+ }
+
+ private boolean hasGlobalPictureQualityServicePermission() {
+ return mPackageManager.checkPermission(android.Manifest.permission
+ .MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE,
+ mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean hasGlobalSoundQualityServicePermission() {
+ return mPackageManager.checkPermission(android.Manifest.permission
+ .MANAGE_GLOBAL_SOUND_QUALITY_SERVICE,
+ mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean hasReadColorZonesPermission() {
+ return mPackageManager.checkPermission(android.Manifest.permission
+ .READ_COLOR_ZONES,
+ mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED;
+ }
+
private void populateTempIdMap(BiMap<Long, String> map, Long id) {
if (id != null && map.getValue(id) == null) {
String uuid;
@@ -430,7 +614,7 @@ public class MediaQualityService extends SystemService {
return columns.toArray(new String[0]);
}
- private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) {
+ private PictureProfile convertCursorToPictureProfileWithTempId(Cursor cursor) {
return new PictureProfile(
getTempId(mPictureProfileTempIdMap, cursor),
getType(cursor),
@@ -442,7 +626,7 @@ public class MediaQualityService extends SystemService {
);
}
- private SoundProfile getSoundProfileWithTempIdFromCursor(Cursor cursor) {
+ private SoundProfile convertCursorToSoundProfileWithTempId(Cursor cursor) {
return new SoundProfile(
getTempId(mSoundProfileTempIdMap, cursor),
getType(cursor),
@@ -502,7 +686,7 @@ public class MediaQualityService extends SystemService {
) {
List<PictureProfile> pictureProfiles = new ArrayList<>();
while (cursor.moveToNext()) {
- pictureProfiles.add(getPictureProfileWithTempIdFromCursor(cursor));
+ pictureProfiles.add(convertCursorToPictureProfileWithTempId(cursor));
}
return pictureProfiles;
}
@@ -517,30 +701,64 @@ public class MediaQualityService extends SystemService {
) {
List<SoundProfile> soundProfiles = new ArrayList<>();
while (cursor.moveToNext()) {
- soundProfiles.add(getSoundProfileWithTempIdFromCursor(cursor));
+ soundProfiles.add(convertCursorToSoundProfileWithTempId(cursor));
}
return soundProfiles;
}
}
+ private void notifyError(String profileId, int errorCode, int uid, int pid) {
+ UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
+ int n = userState.mCallbacks.beginBroadcast();
+
+ for (int i = 0; i < n; ++i) {
+ try {
+ IPictureProfileCallback callback = userState.mCallbacks.getBroadcastItem(i);
+ Pair<Integer, Integer> pidUid = userState.mCallbackPidUidMap.get(callback);
+
+ if (pidUid.first == pid && pidUid.second == uid) {
+ userState.mCallbacks.getBroadcastItem(i).onError(profileId, errorCode);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "failed to report added input to callback", e);
+ }
+ }
+ userState.mCallbacks.finishBroadcast();
+ }
+
@Override
public void registerPictureProfileCallback(final IPictureProfileCallback callback) {
+ int callingPid = Binder.getCallingPid();
+ int callingUid = Binder.getCallingUid();
+
+ UserState userState = getOrCreateUserStateLocked(Binder.getCallingUid());
+ userState.mCallbackPidUidMap.put(callback, Pair.create(callingPid, callingUid));
}
+
@Override
public void registerSoundProfileCallback(final ISoundProfileCallback callback) {
}
@Override
public void registerAmbientBacklightCallback(IAmbientBacklightCallback callback) {
+ if (!hasReadColorZonesPermission()) {
+ //TODO: error handling
+ }
}
@Override
public void setAmbientBacklightSettings(
AmbientBacklightSettings settings, UserHandle user) {
+ if (!hasReadColorZonesPermission()) {
+ //TODO: error handling
+ }
}
@Override
public void setAmbientBacklightEnabled(boolean enabled, UserHandle user) {
+ if (!hasReadColorZonesPermission()) {
+ //TODO: error handling
+ }
}
@Override
@@ -551,20 +769,34 @@ public class MediaQualityService extends SystemService {
@Override
public List<String> getPictureProfileAllowList(UserHandle user) {
+ if (!hasGlobalPictureQualityServicePermission()) {
+ //TODO: error handling
+ return new ArrayList<>();
+ }
return new ArrayList<>();
}
@Override
public void setPictureProfileAllowList(List<String> packages, UserHandle user) {
+ if (!hasGlobalPictureQualityServicePermission()) {
+ //TODO: error handling
+ }
}
@Override
public List<String> getSoundProfileAllowList(UserHandle user) {
+ if (!hasGlobalSoundQualityServicePermission()) {
+ //TODO: error handling
+ return new ArrayList<>();
+ }
return new ArrayList<>();
}
@Override
public void setSoundProfileAllowList(List<String> packages, UserHandle user) {
+ if (!hasGlobalSoundQualityServicePermission()) {
+ //TODO: error handling
+ }
}
@Override
@@ -574,6 +806,9 @@ public class MediaQualityService extends SystemService {
@Override
public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) {
+ if (!hasGlobalPictureQualityServicePermission()) {
+ //TODO: error handling
+ }
}
@Override
@@ -583,6 +818,9 @@ public class MediaQualityService extends SystemService {
@Override
public void setSuperResolutionEnabled(boolean enabled, UserHandle user) {
+ if (!hasGlobalPictureQualityServicePermission()) {
+ //TODO: error handling
+ }
}
@Override
@@ -592,6 +830,9 @@ public class MediaQualityService extends SystemService {
@Override
public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) {
+ if (!hasGlobalSoundQualityServicePermission()) {
+ //TODO: error handling
+ }
}
@Override
@@ -604,4 +845,38 @@ public class MediaQualityService extends SystemService {
return false;
}
}
+
+ private class MediaQualityManagerCallbackList extends
+ RemoteCallbackList<IPictureProfileCallback> {
+ @Override
+ public void onCallbackDied(IPictureProfileCallback callback) {
+ //todo
+ }
+ }
+
+ private final class UserState {
+ // A list of callbacks.
+ private final MediaQualityManagerCallbackList mCallbacks =
+ new MediaQualityManagerCallbackList();
+
+ private final Map<IPictureProfileCallback, Pair<Integer, Integer>> mCallbackPidUidMap =
+ new HashMap<>();
+
+ private UserState(Context context, int userId) {
+
+ }
+ }
+
+ private UserState getOrCreateUserStateLocked(int userId) {
+ UserState userState = getUserStateLocked(userId);
+ if (userState == null) {
+ userState = new UserState(mContext, userId);
+ mUserStates.put(userId, userState);
+ }
+ return userState;
+ }
+
+ private UserState getUserStateLocked(int userId) {
+ return mUserStates.get(userId);
+ }
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 0b40d64e3a09..3f2c2228e453 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -325,7 +325,7 @@ public class ConditionProviders extends ManagedServices {
for (int i = 0; i < N; i++) {
final Condition c = conditions[i];
if (mCallback != null) {
- mCallback.onConditionChanged(c.id, c);
+ mCallback.onConditionChanged(c.id, c, info.uid);
}
}
}
@@ -515,7 +515,7 @@ public class ConditionProviders extends ManagedServices {
public interface Callback {
void onServiceAdded(ComponentName component);
- void onConditionChanged(Uri id, Condition condition);
+ void onConditionChanged(Uri id, Condition condition, int callerUid);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f50e8aa7eb7b..9567c818fa18 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5903,8 +5903,9 @@ public class NotificationManagerService extends SystemService {
// TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined.
@Override
public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
- enforcePolicyAccess(Binder.getCallingUid(), "getZenRules");
- return mZenModeHelper.getZenRules(getCallingZenUser());
+ int callingUid = Binder.getCallingUid();
+ enforcePolicyAccess(callingUid, "getZenRules");
+ return mZenModeHelper.getZenRules(getCallingZenUser(), callingUid);
}
@Override
@@ -5912,15 +5913,17 @@ public class NotificationManagerService extends SystemService {
if (!android.app.Flags.modesApi()) {
throw new IllegalStateException("getAutomaticZenRules called with flag off!");
}
- enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
- return mZenModeHelper.getAutomaticZenRules(getCallingZenUser());
+ int callingUid = Binder.getCallingUid();
+ enforcePolicyAccess(callingUid, "getAutomaticZenRules");
+ return mZenModeHelper.getAutomaticZenRules(getCallingZenUser(), callingUid);
}
@Override
public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
Objects.requireNonNull(id, "Id is null");
- enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
- return mZenModeHelper.getAutomaticZenRule(getCallingZenUser(), id);
+ int callingUid = Binder.getCallingUid();
+ enforcePolicyAccess(callingUid, "getAutomaticZenRule");
+ return mZenModeHelper.getAutomaticZenRule(getCallingZenUser(), id, callingUid);
}
@Override
@@ -6065,8 +6068,9 @@ public class NotificationManagerService extends SystemService {
@Condition.State
public int getAutomaticZenRuleState(@NonNull String id) {
Objects.requireNonNull(id, "id is null");
- enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRuleState");
- return mZenModeHelper.getAutomaticZenRuleState(getCallingZenUser(), id);
+ int callingUid = Binder.getCallingUid();
+ enforcePolicyAccess(callingUid, "getAutomaticZenRuleState");
+ return mZenModeHelper.getAutomaticZenRuleState(getCallingZenUser(), id, callingUid);
}
@Override
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 52d0c41614d5..d44baeb58a28 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -113,15 +113,18 @@ public class ZenModeConditions implements ConditionProviders.Callback {
}
@Override
- public void onConditionChanged(Uri id, Condition condition) {
+ public void onConditionChanged(Uri id, Condition condition, int callingUid) {
if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
ZenModeConfig config = mHelper.getConfig();
if (config == null) return;
- final int callingUid = Binder.getCallingUid();
+ if (!Flags.fixCallingUidFromCps()) {
+ // Old behavior: overwrite with known-bad callingUid (always system_server).
+ callingUid = Binder.getCallingUid();
+ }
// This change is known to be for UserHandle.CURRENT because ConditionProviders for
// background users are not bound.
- mHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, condition,
+ mHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, id, condition,
callingUid == Process.SYSTEM_UID ? ZenModeConfig.ORIGIN_SYSTEM
: ZenModeConfig.ORIGIN_APP,
callingUid);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index b571d62c0cba..0a63f3fb36d0 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -413,13 +413,13 @@ public class ZenModeHelper {
}
// TODO: b/310620812 - Make private (or inline) when MODES_API is inlined.
- public List<ZenRule> getZenRules(UserHandle user) {
+ public List<ZenRule> getZenRules(UserHandle user, int callingUid) {
List<ZenRule> rules = new ArrayList<>();
synchronized (mConfigLock) {
ZenModeConfig config = getConfigLocked(user);
if (config == null) return rules;
for (ZenRule rule : config.automaticRules.values()) {
- if (canManageAutomaticZenRule(rule)) {
+ if (canManageAutomaticZenRule(rule, callingUid)) {
rules.add(rule);
}
}
@@ -432,8 +432,8 @@ public class ZenModeHelper {
* (which means the owned rules for a regular app, and every rule for system callers) together
* with their ids.
*/
- Map<String, AutomaticZenRule> getAutomaticZenRules(UserHandle user) {
- List<ZenRule> ruleList = getZenRules(user);
+ Map<String, AutomaticZenRule> getAutomaticZenRules(UserHandle user, int callingUid) {
+ List<ZenRule> ruleList = getZenRules(user, callingUid);
HashMap<String, AutomaticZenRule> rules = new HashMap<>(ruleList.size());
for (ZenRule rule : ruleList) {
rules.put(rule.id, zenRuleToAutomaticZenRule(rule));
@@ -441,7 +441,7 @@ public class ZenModeHelper {
return rules;
}
- public AutomaticZenRule getAutomaticZenRule(UserHandle user, String id) {
+ public AutomaticZenRule getAutomaticZenRule(UserHandle user, String id, int callingUid) {
ZenRule rule;
synchronized (mConfigLock) {
ZenModeConfig config = getConfigLocked(user);
@@ -449,7 +449,7 @@ public class ZenModeHelper {
rule = config.automaticRules.get(id);
}
if (rule == null) return null;
- if (canManageAutomaticZenRule(rule)) {
+ if (canManageAutomaticZenRule(rule, callingUid)) {
return zenRuleToAutomaticZenRule(rule);
}
return null;
@@ -591,7 +591,7 @@ public class ZenModeHelper {
+ " reason=" + reason);
}
ZenModeConfig.ZenRule oldRule = config.automaticRules.get(ruleId);
- if (oldRule == null || !canManageAutomaticZenRule(oldRule)) {
+ if (oldRule == null || !canManageAutomaticZenRule(oldRule, callingUid)) {
throw new SecurityException(
"Cannot update rules not owned by your condition provider");
}
@@ -859,7 +859,7 @@ public class ZenModeHelper {
newConfig = config.copy();
ZenRule ruleToRemove = newConfig.automaticRules.get(id);
if (ruleToRemove == null) return false;
- if (canManageAutomaticZenRule(ruleToRemove)) {
+ if (canManageAutomaticZenRule(ruleToRemove, callingUid)) {
newConfig.automaticRules.remove(id);
maybePreserveRemovedRule(newConfig, ruleToRemove, origin);
if (ruleToRemove.getPkg() != null
@@ -893,7 +893,8 @@ public class ZenModeHelper {
newConfig = config.copy();
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
- if (Objects.equals(rule.getPkg(), packageName) && canManageAutomaticZenRule(rule)) {
+ if (Objects.equals(rule.getPkg(), packageName)
+ && canManageAutomaticZenRule(rule, callingUid)) {
newConfig.automaticRules.removeAt(i);
maybePreserveRemovedRule(newConfig, rule, origin);
}
@@ -938,14 +939,14 @@ public class ZenModeHelper {
}
@Condition.State
- int getAutomaticZenRuleState(UserHandle user, String id) {
+ int getAutomaticZenRuleState(UserHandle user, String id, int callingUid) {
synchronized (mConfigLock) {
ZenModeConfig config = getConfigLocked(user);
if (config == null) {
return Condition.STATE_UNKNOWN;
}
ZenRule rule = config.automaticRules.get(id);
- if (rule == null || !canManageAutomaticZenRule(rule)) {
+ if (rule == null || !canManageAutomaticZenRule(rule, callingUid)) {
return Condition.STATE_UNKNOWN;
}
if (Flags.modesApi() && Flags.modesUi()) {
@@ -968,7 +969,7 @@ public class ZenModeHelper {
newConfig = config.copy();
ZenRule rule = newConfig.automaticRules.get(id);
if (Flags.modesApi()) {
- if (rule != null && canManageAutomaticZenRule(rule)) {
+ if (rule != null && canManageAutomaticZenRule(rule, callingUid)) {
setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
condition, origin, callingUid);
}
@@ -980,8 +981,8 @@ public class ZenModeHelper {
}
}
- void setAutomaticZenRuleState(UserHandle user, Uri ruleDefinition, Condition condition,
- @ConfigOrigin int origin, int callingUid) {
+ void setAutomaticZenRuleStateFromConditionProvider(UserHandle user, Uri ruleDefinition,
+ Condition condition, @ConfigOrigin int origin, int callingUid) {
checkSetRuleStateOrigin("setAutomaticZenRuleState(Uri ruleDefinition)", origin);
ZenModeConfig newConfig;
synchronized (mConfigLock) {
@@ -992,7 +993,7 @@ public class ZenModeHelper {
List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleDefinition, condition);
if (Flags.modesApi()) {
for (int i = matchingRules.size() - 1; i >= 0; i--) {
- if (!canManageAutomaticZenRule(matchingRules.get(i))) {
+ if (!canManageAutomaticZenRule(matchingRules.get(i), callingUid)) {
matchingRules.remove(i);
}
}
@@ -1125,15 +1126,21 @@ public class ZenModeHelper {
return count;
}
- public boolean canManageAutomaticZenRule(ZenRule rule) {
- final int callingUid = Binder.getCallingUid();
+ public boolean canManageAutomaticZenRule(ZenRule rule, int callingUid) {
+ if (!com.android.server.notification.Flags.fixCallingUidFromCps()) {
+ // Old behavior: ignore supplied callingUid and instead obtain it here. Will be
+ // incorrect if not currently handling a Binder call.
+ callingUid = Binder.getCallingUid();
+ }
+
if (callingUid == 0 || callingUid == Process.SYSTEM_UID) {
+ // Checked specifically, because checkCallingPermission() will fail.
return true;
} else if (mContext.checkCallingPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED) {
return true;
} else {
- String[] packages = mPm.getPackagesForUid(Binder.getCallingUid());
+ String[] packages = mPm.getPackagesForUid(callingUid);
if (packages != null) {
final int packageCount = packages.length;
for (int i = 0; i < packageCount; i++) {
@@ -2902,8 +2909,8 @@ public class ZenModeHelper {
}
/**
- * Checks that the {@code origin} supplied to {@link #setAutomaticZenRuleState} overloads makes
- * sense.
+ * Checks that the {@code origin} supplied to {@link #setAutomaticZenRuleState} or
+ * {@link #setAutomaticZenRuleStateFromConditionProvider} makes sense.
*/
private static void checkSetRuleStateOrigin(String method, @ConfigOrigin int origin) {
if (!Flags.modesApi()) {
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index f15c23e110a4..2b4d71e85dc0 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -196,4 +196,14 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-} \ No newline at end of file
+}
+
+flag {
+ name: "fix_calling_uid_from_cps"
+ namespace: "systemui"
+ description: "Correctly checks zen rule ownership when a CPS notifies with a Condition"
+ bug: "379722187"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 1b22154c10f6..d33c860343c5 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -28,6 +28,7 @@ import android.os.IBinder;
import android.os.IIdmap2;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemService;
import android.text.TextUtils;
@@ -40,7 +41,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* To prevent idmap2d from continuously running, the idmap daemon will terminate after 10 seconds
@@ -66,7 +66,7 @@ class IdmapDaemon {
private static IdmapDaemon sInstance;
private volatile IIdmap2 mService;
- private final AtomicInteger mOpenedCount = new AtomicInteger();
+ private int mOpenedCount = 0;
private final Object mIdmapToken = new Object();
/**
@@ -74,15 +74,20 @@ class IdmapDaemon {
* finalized, the idmap service will be stopped after a period of time unless another connection
* to the service is open.
**/
- private class Connection implements AutoCloseable {
+ private final class Connection implements AutoCloseable {
@Nullable
private final IIdmap2 mIdmap2;
private boolean mOpened = true;
- private Connection(IIdmap2 idmap2) {
+ private Connection() {
+ mIdmap2 = null;
+ mOpened = false;
+ }
+
+ private Connection(@NonNull IIdmap2 idmap2) {
+ mIdmap2 = idmap2;
synchronized (mIdmapToken) {
- mOpenedCount.incrementAndGet();
- mIdmap2 = idmap2;
+ ++mOpenedCount;
}
}
@@ -94,20 +99,22 @@ class IdmapDaemon {
}
mOpened = false;
- if (mOpenedCount.decrementAndGet() != 0) {
+ if (--mOpenedCount != 0) {
// Only post the callback to stop the service if the service does not have an
// open connection.
return;
}
+ final var service = mService;
FgThread.getHandler().postDelayed(() -> {
synchronized (mIdmapToken) {
- // Only stop the service if the service does not have an open connection.
- if (mService == null || mOpenedCount.get() != 0) {
+ // Only stop the service if it's the one we were scheduled for and
+ // it does not have an open connection.
+ if (mService != service || mOpenedCount != 0) {
return;
}
- stopIdmapService();
+ stopIdmapServiceLocked();
mService = null;
}
}, mIdmapToken, SERVICE_TIMEOUT_MS);
@@ -175,6 +182,8 @@ class IdmapDaemon {
}
boolean idmapExists(String overlayPath, int userId) {
+ // The only way to verify an idmap is to read its state on disk.
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try (Connection c = connect()) {
final IIdmap2 idmap2 = c.getIdmap2();
if (idmap2 == null) {
@@ -187,6 +196,8 @@ class IdmapDaemon {
} catch (Exception e) {
Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath, e);
return false;
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
}
@@ -242,14 +253,16 @@ class IdmapDaemon {
} catch (Exception e) {
Slog.wtf(TAG, "failed to get all fabricated overlays", e);
} finally {
- try {
- if (c.getIdmap2() != null && iteratorId != -1) {
- c.getIdmap2().releaseFabricatedOverlayIterator(iteratorId);
+ if (c != null) {
+ try {
+ if (c.getIdmap2() != null && iteratorId != -1) {
+ c.getIdmap2().releaseFabricatedOverlayIterator(iteratorId);
+ }
+ } catch (RemoteException e) {
+ // ignore
}
- } catch (RemoteException e) {
- // ignore
+ c.close();
}
- c.close();
}
return allInfos;
}
@@ -271,9 +284,11 @@ class IdmapDaemon {
}
@Nullable
- private IBinder getIdmapService() throws TimeoutException, RemoteException {
+ private IBinder getIdmapServiceLocked() throws TimeoutException, RemoteException {
try {
- SystemService.start(IDMAP_DAEMON);
+ if (!SystemService.isRunning(IDMAP_DAEMON)) {
+ SystemService.start(IDMAP_DAEMON);
+ }
} catch (RuntimeException e) {
Slog.wtf(TAG, "Failed to enable idmap2 daemon", e);
if (e.getMessage().contains("failed to set system property")) {
@@ -306,9 +321,11 @@ class IdmapDaemon {
walltimeMillis - endWalltimeMillis + SERVICE_CONNECT_WALLTIME_TIMEOUT_MS));
}
- private static void stopIdmapService() {
+ private static void stopIdmapServiceLocked() {
try {
- SystemService.stop(IDMAP_DAEMON);
+ if (SystemService.isRunning(IDMAP_DAEMON)) {
+ SystemService.stop(IDMAP_DAEMON);
+ }
} catch (RuntimeException e) {
// If the idmap daemon cannot be disabled for some reason, it is okay
// since we already finished invoking idmap.
@@ -326,9 +343,9 @@ class IdmapDaemon {
return new Connection(mService);
}
- IBinder binder = getIdmapService();
+ IBinder binder = getIdmapServiceLocked();
if (binder == null) {
- return new Connection(null);
+ return new Connection();
}
mService = IIdmap2.Stub.asInterface(binder);
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index cc5c88b77293..d806770e5c91 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -19,7 +19,6 @@ package com.android.server.om;
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.om.OverlayableInfo;
-import android.content.res.Flags;
import android.net.Uri;
import android.os.Process;
import android.text.TextUtils;
@@ -163,15 +162,11 @@ public class OverlayActorEnforcer {
return ActorState.UNABLE_TO_GET_TARGET_OVERLAYABLE;
}
- // Framework doesn't have <overlayable> declaration by design, and we still want to be able
- // to enable its overlays from the packages with the permission.
- if (targetOverlayable == null
- && !(Flags.rroControlForAndroidNoOverlayable() && targetPackageName.equals(
- "android"))) {
+ if (targetOverlayable == null) {
return ActorState.MISSING_OVERLAYABLE;
}
- final String actor = targetOverlayable == null ? null : targetOverlayable.actor;
+ String actor = targetOverlayable.actor;
if (TextUtils.isEmpty(actor)) {
// If there's no actor defined, fallback to the legacy permission check
try {
diff --git a/services/core/java/com/android/server/om/OverlayReferenceMapper.java b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
index fdceabe74dd8..18de9952ed19 100644
--- a/services/core/java/com/android/server/om/OverlayReferenceMapper.java
+++ b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
@@ -26,15 +26,13 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.pkg.AndroidPackage;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -121,20 +119,16 @@ public class OverlayReferenceMapper {
return actorPair.first;
}
- @NonNull
+ @Nullable
@Override
- public Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg) {
+ public Pair<String, String> getTargetToOverlayables(@NonNull AndroidPackage pkg) {
String target = pkg.getOverlayTarget();
if (TextUtils.isEmpty(target)) {
- return Collections.emptyMap();
+ return null;
}
String overlayable = pkg.getOverlayTargetOverlayableName();
- Map<String, Set<String>> targetToOverlayables = new HashMap<>();
- Set<String> overlayables = new HashSet<>();
- overlayables.add(overlayable);
- targetToOverlayables.put(target, overlayables);
- return targetToOverlayables;
+ return Pair.create(target, overlayable);
}
};
}
@@ -174,7 +168,7 @@ public class OverlayReferenceMapper {
}
// TODO(b/135203078): Replace with isOverlay boolean flag check; fix test mocks
- if (!mProvider.getTargetToOverlayables(pkg).isEmpty()) {
+ if (mProvider.getTargetToOverlayables(pkg) != null) {
addOverlay(pkg, otherPkgs, changed);
}
@@ -245,20 +239,17 @@ public class OverlayReferenceMapper {
String target = targetPkg.getPackageName();
removeTarget(target, changedPackages);
- Map<String, String> overlayablesToActors = targetPkg.getOverlayables();
- for (String overlayable : overlayablesToActors.keySet()) {
- String actor = overlayablesToActors.get(overlayable);
+ final Map<String, String> overlayablesToActors = targetPkg.getOverlayables();
+ for (final var entry : overlayablesToActors.entrySet()) {
+ final String overlayable = entry.getKey();
+ final String actor = entry.getValue();
addTargetToMap(actor, target, changedPackages);
for (AndroidPackage overlayPkg : otherPkgs.values()) {
- Map<String, Set<String>> targetToOverlayables =
+ var targetToOverlayables =
mProvider.getTargetToOverlayables(overlayPkg);
- Set<String> overlayables = targetToOverlayables.get(target);
- if (CollectionUtils.isEmpty(overlayables)) {
- continue;
- }
-
- if (overlayables.contains(overlayable)) {
+ if (targetToOverlayables != null && targetToOverlayables.first.equals(target)
+ && Objects.equals(targetToOverlayables.second, overlayable)) {
String overlay = overlayPkg.getPackageName();
addOverlayToMap(actor, target, overlay, changedPackages);
}
@@ -310,25 +301,22 @@ public class OverlayReferenceMapper {
String overlay = overlayPkg.getPackageName();
removeOverlay(overlay, changedPackages);
- Map<String, Set<String>> targetToOverlayables =
+ Pair<String, String> targetToOverlayables =
mProvider.getTargetToOverlayables(overlayPkg);
- for (Map.Entry<String, Set<String>> entry : targetToOverlayables.entrySet()) {
- String target = entry.getKey();
- Set<String> overlayables = entry.getValue();
+ if (targetToOverlayables != null) {
+ String target = targetToOverlayables.first;
AndroidPackage targetPkg = otherPkgs.get(target);
if (targetPkg == null) {
- continue;
+ return;
}
-
String targetPkgName = targetPkg.getPackageName();
Map<String, String> overlayableToActor = targetPkg.getOverlayables();
- for (String overlayable : overlayables) {
- String actor = overlayableToActor.get(overlayable);
- if (TextUtils.isEmpty(actor)) {
- continue;
- }
- addOverlayToMap(actor, targetPkgName, overlay, changedPackages);
+ String overlayable = targetToOverlayables.second;
+ String actor = overlayableToActor.get(overlayable);
+ if (TextUtils.isEmpty(actor)) {
+ return;
}
+ addOverlayToMap(actor, targetPkgName, overlay, changedPackages);
}
}
}
@@ -430,11 +418,11 @@ public class OverlayReferenceMapper {
String getActorPkg(@NonNull String actor);
/**
- * Mock response of multiple overlay tags.
+ * Mock response of overlay tags.
*
* TODO(b/119899133): Replace with actual implementation; fix OverlayReferenceMapperTests
*/
- @NonNull
- Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg);
+ @Nullable
+ Pair<String, String> getTargetToOverlayables(@NonNull AndroidPackage pkg);
}
}
diff --git a/services/core/java/com/android/server/pm/ResilientAtomicFile.java b/services/core/java/com/android/server/pm/ResilientAtomicFile.java
index 3aefc5a64926..473ed6136e9a 100644
--- a/services/core/java/com/android/server/pm/ResilientAtomicFile.java
+++ b/services/core/java/com/android/server/pm/ResilientAtomicFile.java
@@ -23,6 +23,7 @@ import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.security.FileIntegrity;
import libcore.io.IoUtils;
@@ -121,6 +122,11 @@ final class ResilientAtomicFile implements Closeable {
}
public void finishWrite(FileOutputStream str) throws IOException {
+ finishWrite(str, true /* doFsVerity */);
+ }
+
+ @VisibleForTesting
+ public void finishWrite(FileOutputStream str, final boolean doFsVerity) throws IOException {
if (mMainOutStream != str) {
throw new IllegalStateException("Invalid incoming stream.");
}
@@ -145,13 +151,15 @@ final class ResilientAtomicFile implements Closeable {
finalizeOutStream(reserveOutStream);
}
- // Protect both main and reserve using fs-verity.
- try (ParcelFileDescriptor mainPfd = ParcelFileDescriptor.dup(mainInStream.getFD());
- ParcelFileDescriptor copyPfd = ParcelFileDescriptor.dup(reserveInStream.getFD())) {
- FileIntegrity.setUpFsVerity(mainPfd);
- FileIntegrity.setUpFsVerity(copyPfd);
- } catch (IOException e) {
- Slog.e(LOG_TAG, "Failed to verity-protect " + mDebugName, e);
+ if (doFsVerity) {
+ // Protect both main and reserve using fs-verity.
+ try (ParcelFileDescriptor mainPfd = ParcelFileDescriptor.dup(mainInStream.getFD());
+ ParcelFileDescriptor copyPfd = ParcelFileDescriptor.dup(reserveInStream.getFD())) {
+ FileIntegrity.setUpFsVerity(mainPfd);
+ FileIntegrity.setUpFsVerity(copyPfd);
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Failed to verity-protect " + mDebugName, e);
+ }
}
} catch (IOException e) {
Slog.e(LOG_TAG, "Failed to write reserve copy " + mDebugName + ": " + mReserveCopy, e);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 44789e4c4de2..027da4986ce6 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -179,7 +179,7 @@ abstract class ShortcutPackageItem {
itemOut.endDocument();
os.flush();
- file.finishWrite(os);
+ mShortcutUser.mService.injectFinishWrite(file, os);
} catch (XmlPullParserException | IOException e) {
Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
file.failWrite(os);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 2785da5cbdbd..373c1ed3c386 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1008,7 +1008,7 @@ public class ShortcutService extends IShortcutService.Stub {
out.endDocument();
// Close.
- file.finishWrite(outs);
+ injectFinishWrite(file, outs);
} catch (IOException e) {
Slog.w(TAG, "Failed to write to file " + file.getBaseFile(), e);
file.failWrite(outs);
@@ -1096,7 +1096,7 @@ public class ShortcutService extends IShortcutService.Stub {
saveUserInternalLocked(userId, os, /* forBackup= */ false);
}
- file.finishWrite(os);
+ injectFinishWrite(file, os);
// Remove all dangling bitmap files.
cleanupDanglingBitmapDirectoriesLocked(userId);
@@ -5067,6 +5067,12 @@ public class ShortcutService extends IShortcutService.Stub {
return Build.FINGERPRINT;
}
+ // Injection point.
+ void injectFinishWrite(@NonNull final ResilientAtomicFile file,
+ @NonNull final FileOutputStream os) throws IOException {
+ file.finishWrite(os);
+ }
+
final void wtf(String message) {
wtf(message, /* exception= */ null);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 672eb4caf798..9d840d0c0d35 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1681,8 +1681,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// handle overflow
if (attributionChainId < 0) {
- attributionChainId = 0;
sAttributionChainIds.set(0);
+ attributionChainId = sAttributionChainIds.incrementAndGet();
}
return attributionChainId;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5ab59657d4ce..516213b32354 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -562,8 +562,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
volatile boolean mPowerKeyHandled;
volatile boolean mBackKeyHandled;
volatile boolean mEndCallKeyHandled;
- volatile boolean mCameraGestureTriggered;
- volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
+ volatile boolean mPowerButtonLaunchGestureTriggered;
+ volatile boolean mPowerButtonLaunchGestureTriggeredDuringGoingToSleep;
/**
* {@code true} if the device is entering a low-power state; {@code false otherwise}.
@@ -5893,7 +5893,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mGestureLauncherService == null) {
return false;
}
- mCameraGestureTriggered = false;
+ mPowerButtonLaunchGestureTriggered = false;
final MutableBoolean outLaunched = new MutableBoolean(false);
final boolean intercept =
mGestureLauncherService.interceptPowerKeyDown(event, interactive, outLaunched);
@@ -5903,9 +5903,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// detector from processing the power key later on.
return intercept;
}
- mCameraGestureTriggered = true;
+ mPowerButtonLaunchGestureTriggered = true;
if (mRequestedOrSleepingDefaultDisplay) {
- mCameraGestureTriggeredDuringGoingToSleep = true;
+ mPowerButtonLaunchGestureTriggeredDuringGoingToSleep = true;
// Wake device up early to prevent display doing redundant turning off/on stuff.
mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture();
}
@@ -6282,13 +6282,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onFinishedGoingToSleep(pmSleepReason,
- mCameraGestureTriggeredDuringGoingToSleep);
+ mPowerButtonLaunchGestureTriggeredDuringGoingToSleep);
}
if (mDisplayFoldController != null) {
mDisplayFoldController.finishedGoingToSleep();
}
- mCameraGestureTriggeredDuringGoingToSleep = false;
- mCameraGestureTriggered = false;
+ mPowerButtonLaunchGestureTriggeredDuringGoingToSleep = false;
+ mPowerButtonLaunchGestureTriggered = false;
}
// Called on the PowerManager's Notifier thread.
@@ -6319,10 +6319,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mDefaultDisplayRotation.updateOrientationListener();
if (mKeyguardDelegate != null) {
- mKeyguardDelegate.onStartedWakingUp(pmWakeReason, mCameraGestureTriggered);
+ mKeyguardDelegate.onStartedWakingUp(pmWakeReason, mPowerButtonLaunchGestureTriggered);
}
- mCameraGestureTriggered = false;
+ mPowerButtonLaunchGestureTriggered = false;
}
// Called on the PowerManager's Notifier thread.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index da8b01ac86fb..587447b8af26 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -198,7 +198,7 @@ public class KeyguardServiceDelegate {
if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE
|| mKeyguardState.interactiveState == INTERACTIVE_STATE_WAKING) {
mKeyguardService.onStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN,
- false /* cameraGestureTriggered */);
+ false /* powerButtonLaunchGestureTriggered */);
}
if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
mKeyguardService.onFinishedWakingUp();
@@ -319,10 +319,10 @@ public class KeyguardServiceDelegate {
}
public void onStartedWakingUp(
- @PowerManager.WakeReason int pmWakeReason, boolean cameraGestureTriggered) {
+ @PowerManager.WakeReason int pmWakeReason, boolean powerButtonLaunchGestureTriggered) {
if (mKeyguardService != null) {
if (DEBUG) Log.v(TAG, "onStartedWakingUp()");
- mKeyguardService.onStartedWakingUp(pmWakeReason, cameraGestureTriggered);
+ mKeyguardService.onStartedWakingUp(pmWakeReason, powerButtonLaunchGestureTriggered);
}
mKeyguardState.interactiveState = INTERACTIVE_STATE_WAKING;
}
@@ -383,9 +383,11 @@ public class KeyguardServiceDelegate {
}
public void onFinishedGoingToSleep(
- @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) {
+ @PowerManager.GoToSleepReason int pmSleepReason,
+ boolean powerButtonLaunchGestureTriggered) {
if (mKeyguardService != null) {
- mKeyguardService.onFinishedGoingToSleep(pmSleepReason, cameraGestureTriggered);
+ mKeyguardService.onFinishedGoingToSleep(pmSleepReason,
+ powerButtonLaunchGestureTriggered);
}
mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP;
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index cd789eaed1b3..f2342e0d5688 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -113,9 +113,10 @@ public class KeyguardServiceWrapper implements IKeyguardService {
@Override
public void onFinishedGoingToSleep(
- @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) {
+ @PowerManager.GoToSleepReason int pmSleepReason,
+ boolean powerButtonLaunchGestureTriggered) {
try {
- mService.onFinishedGoingToSleep(pmSleepReason, cameraGestureTriggered);
+ mService.onFinishedGoingToSleep(pmSleepReason, powerButtonLaunchGestureTriggered);
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
@@ -123,9 +124,9 @@ public class KeyguardServiceWrapper implements IKeyguardService {
@Override
public void onStartedWakingUp(
- @PowerManager.WakeReason int pmWakeReason, boolean cameraGestureTriggered) {
+ @PowerManager.WakeReason int pmWakeReason, boolean powerButtonLaunchGestureTriggered) {
try {
- mService.onStartedWakingUp(pmWakeReason, cameraGestureTriggered);
+ mService.onStartedWakingUp(pmWakeReason, powerButtonLaunchGestureTriggered);
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index caaf5a2b16d0..9206cce12cd6 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -2205,6 +2205,11 @@ public class BatteryStatsImpl extends BatteryStats {
getWakelockDurationRetriever() {
return mWakelockDurationRetriever;
}
+
+ @Override
+ public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+ return BatteryStatsImpl.this.networkStatsDelta(stats, oldStats);
+ }
}
private final PowerStatsCollectorInjector mPowerStatsCollectorInjector =
@@ -12392,83 +12397,13 @@ public class BatteryStatsImpl extends BatteryStats {
return networkStatsManager.getWifiUidStats();
}
- static class NetworkStatsDelta {
- int mUid;
- int mSet;
- long mRxBytes;
- long mRxPackets;
- long mTxBytes;
- long mTxPackets;
-
- public int getUid() {
- return mUid;
- }
-
-
- public int getSet() {
- return mSet;
- }
-
- public long getRxBytes() {
- return mRxBytes;
- }
-
- public long getRxPackets() {
- return mRxPackets;
- }
-
- public long getTxBytes() {
- return mTxBytes;
- }
-
- public long getTxPackets() {
- return mTxPackets;
- }
-
- @Override
- public String toString() {
- return "NetworkStatsDelta{mUid=" + mUid + ", mSet=" + mSet + ", mRxBytes=" + mRxBytes
- + ", mRxPackets=" + mRxPackets + ", mTxBytes=" + mTxBytes + ", mTxPackets="
- + mTxPackets + '}';
- }
- }
-
- static List<NetworkStatsDelta> computeDelta(NetworkStats currentStats,
- NetworkStats lastStats) {
- List<NetworkStatsDelta> deltaList = new ArrayList<>();
- for (NetworkStats.Entry entry : currentStats) {
- NetworkStatsDelta delta = new NetworkStatsDelta();
- delta.mUid = entry.getUid();
- delta.mSet = entry.getSet();
- NetworkStats.Entry lastEntry = null;
- if (lastStats != null) {
- for (NetworkStats.Entry e : lastStats) {
- if (e.getUid() == entry.getUid() && e.getSet() == entry.getSet()
- && e.getTag() == entry.getTag()
- && e.getMetered() == entry.getMetered()
- && e.getRoaming() == entry.getRoaming()
- && e.getDefaultNetwork() == entry.getDefaultNetwork()
- /*&& Objects.equals(e.getIface(), entry.getIface())*/) {
- lastEntry = e;
- break;
- }
- }
- }
- if (lastEntry != null) {
- delta.mRxBytes = Math.max(0, entry.getRxBytes() - lastEntry.getRxBytes());
- delta.mRxPackets = Math.max(0, entry.getRxPackets() - lastEntry.getRxPackets());
- delta.mTxBytes = Math.max(0, entry.getTxBytes() - lastEntry.getTxBytes());
- delta.mTxPackets = Math.max(0, entry.getTxPackets() - lastEntry.getTxPackets());
- } else {
- delta.mRxBytes = entry.getRxBytes();
- delta.mRxPackets = entry.getRxPackets();
- delta.mTxBytes = entry.getTxBytes();
- delta.mTxPackets = entry.getTxPackets();
- }
- deltaList.add(delta);
+ @VisibleForTesting
+ protected NetworkStats networkStatsDelta(@NonNull NetworkStats stats,
+ @Nullable NetworkStats oldStats) {
+ if (oldStats == null) {
+ return stats;
}
-
- return deltaList;
+ return stats.subtract(oldStats);
}
/**
@@ -12486,12 +12421,12 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ NetworkStats delta;
// Grab a separate lock to acquire the network stats, which may do I/O.
- List<NetworkStatsDelta> delta;
synchronized (mWifiNetworkLock) {
final NetworkStats latestStats = readWifiNetworkStatsLocked(networkStatsManager);
if (latestStats != null) {
- delta = computeDelta(latestStats, mLastWifiNetworkStats);
+ delta = networkStatsDelta(latestStats, mLastWifiNetworkStats);
mLastWifiNetworkStats = latestStats;
} else {
delta = null;
@@ -12501,15 +12436,15 @@ public class BatteryStatsImpl extends BatteryStats {
}
private void onWifiPowerStatsRetrieved(WifiActivityEnergyInfo wifiActivityEnergyInfo,
- List<NetworkStatsDelta> networkStatsDeltas, long elapsedRealtimeMs, long uptimeMs) {
+ NetworkStats networkStatsDelta, long elapsedRealtimeMs, long uptimeMs) {
// Do not populate consumed energy, because energy attribution is done by
// WifiPowerStatsProcessor.
- updateWifiBatteryStats(wifiActivityEnergyInfo, networkStatsDeltas, POWER_DATA_UNAVAILABLE,
+ updateWifiBatteryStats(wifiActivityEnergyInfo, networkStatsDelta, POWER_DATA_UNAVAILABLE,
elapsedRealtimeMs, uptimeMs);
}
private void updateWifiBatteryStats(WifiActivityEnergyInfo info,
- List<NetworkStatsDelta> delta, long consumedChargeUC, long elapsedRealtimeMs,
+ NetworkStats delta, long consumedChargeUC, long elapsedRealtimeMs,
long uptimeMs) {
synchronized (this) {
if (!mOnBatteryInternal || mIgnoreNextExternalStats) {
@@ -12535,7 +12470,7 @@ public class BatteryStatsImpl extends BatteryStats {
long totalTxPackets = 0;
long totalRxPackets = 0;
if (delta != null) {
- for (NetworkStatsDelta entry : delta) {
+ for (NetworkStats.Entry entry : delta) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Wifi uid " + entry.getUid()
+ ": delta rx=" + entry.getRxBytes()
@@ -12879,11 +12814,11 @@ public class BatteryStatsImpl extends BatteryStats {
mLastModemActivityInfo = activityInfo;
// Grab a separate lock to acquire the network stats, which may do I/O.
- List<NetworkStatsDelta> delta = null;
+ NetworkStats delta = null;
synchronized (mModemNetworkLock) {
final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager);
if (latestStats != null) {
- delta = computeDelta(latestStats, mLastModemNetworkStats);
+ delta = networkStatsDelta(latestStats, mLastModemNetworkStats);
mLastModemNetworkStats = latestStats;
}
}
@@ -12892,15 +12827,15 @@ public class BatteryStatsImpl extends BatteryStats {
}
private void onMobileRadioPowerStatsRetrieved(ModemActivityInfo modemActivityInfo,
- List<NetworkStatsDelta> networkStatsDeltas, long elapsedRealtimeMs, long uptimeMs) {
+ NetworkStats networkStatsDelta, long elapsedRealtimeMs, long uptimeMs) {
// Do not populate consumed energy, because energy attribution is done by
// MobileRadioPowerStatsProcessor.
- updateCellularBatteryStats(modemActivityInfo, networkStatsDeltas, POWER_DATA_UNAVAILABLE,
+ updateCellularBatteryStats(modemActivityInfo, networkStatsDelta, POWER_DATA_UNAVAILABLE,
elapsedRealtimeMs, uptimeMs);
}
private void updateCellularBatteryStats(@Nullable ModemActivityInfo deltaInfo,
- @Nullable List<NetworkStatsDelta> delta, long consumedChargeUC, long elapsedRealtimeMs,
+ @Nullable NetworkStats delta, long consumedChargeUC, long elapsedRealtimeMs,
long uptimeMs) {
// Add modem tx power to history.
addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs);
@@ -13003,7 +12938,7 @@ public class BatteryStatsImpl extends BatteryStats {
long totalRxPackets = 0;
long totalTxPackets = 0;
if (delta != null) {
- for (NetworkStatsDelta entry : delta) {
+ for (NetworkStats.Entry entry : delta) {
if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
continue;
}
@@ -13044,7 +12979,7 @@ public class BatteryStatsImpl extends BatteryStats {
// Now distribute proportional blame to the apps that did networking.
long totalPackets = totalRxPackets + totalTxPackets;
if (totalPackets > 0) {
- for (NetworkStatsDelta entry : delta) {
+ for (NetworkStats.Entry entry : delta) {
if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
continue;
}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
index cbd6fab2a9f7..f971e2e882c3 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
@@ -38,7 +38,6 @@ import com.android.internal.os.PowerStats;
import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.LongSupplier;
@@ -71,7 +70,7 @@ public class MobileRadioPowerStatsCollector extends PowerStatsCollector {
interface Observer {
void onMobileRadioPowerStatsRetrieved(
@Nullable ModemActivityInfo modemActivityDelta,
- @Nullable List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas,
+ @Nullable NetworkStats networkStatsDeltas,
long elapsedRealtimeMs, long uptimeMs);
}
@@ -86,6 +85,8 @@ public class MobileRadioPowerStatsCollector extends PowerStatsCollector {
TelephonyManager getTelephonyManager();
LongSupplier getCallDurationSupplier();
LongSupplier getPhoneSignalScanDurationSupplier();
+
+ NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats);
}
private final Injector mInjector;
@@ -190,7 +191,7 @@ public class MobileRadioPowerStatsCollector extends PowerStatsCollector {
mPowerStats.uidStats.clear();
ModemActivityInfo modemActivityDelta = collectModemActivityInfo();
- List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas = collectNetworkStats();
+ NetworkStats networkStatsDeltas = collectNetworkStats();
mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
@@ -288,17 +289,15 @@ public class MobileRadioPowerStatsCollector extends PowerStatsCollector {
return deltaInfo;
}
- private List<BatteryStatsImpl.NetworkStatsDelta> collectNetworkStats() {
+ private NetworkStats collectNetworkStats() {
NetworkStats networkStats = mNetworkStatsSupplier.get();
if (networkStats == null) {
return null;
}
- List<BatteryStatsImpl.NetworkStatsDelta> delta =
- BatteryStatsImpl.computeDelta(networkStats, mLastNetworkStats);
+ NetworkStats delta = mInjector.networkStatsDelta(networkStats, mLastNetworkStats);
mLastNetworkStats = networkStats;
- for (int i = delta.size() - 1; i >= 0; i--) {
- BatteryStatsImpl.NetworkStatsDelta uidDelta = delta.get(i);
+ for (NetworkStats.Entry uidDelta : delta) {
long rxBytes = uidDelta.getRxBytes();
long txBytes = uidDelta.getTxBytes();
long rxPackets = uidDelta.getRxPackets();
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
index 1fdeac9816d0..5440bcf1d124 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
@@ -31,7 +31,6 @@ import com.android.internal.os.PowerStats;
import com.android.server.power.stats.format.WifiPowerStatsLayout;
import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@@ -43,7 +42,7 @@ public class WifiPowerStatsCollector extends PowerStatsCollector {
interface Observer {
void onWifiPowerStatsRetrieved(WifiActivityEnergyInfo info,
- List<BatteryStatsImpl.NetworkStatsDelta> delta, long elapsedRealtimeMs,
+ NetworkStats delta, long elapsedRealtimeMs,
long uptimeMs);
}
@@ -66,6 +65,8 @@ public class WifiPowerStatsCollector extends PowerStatsCollector {
Supplier<NetworkStats> getWifiNetworkStatsSupplier();
WifiManager getWifiManager();
WifiStatsRetriever getWifiStatsRetriever();
+
+ NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats);
}
private final Injector mInjector;
@@ -161,7 +162,7 @@ public class WifiPowerStatsCollector extends PowerStatsCollector {
} else {
collectWifiActivityStats();
}
- List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas = collectNetworkStats();
+ NetworkStats networkStatsDeltas = collectNetworkStats();
collectWifiScanTime();
mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
@@ -227,17 +228,15 @@ public class WifiPowerStatsCollector extends PowerStatsCollector {
mPowerStats.durationMs = duration;
}
- private List<BatteryStatsImpl.NetworkStatsDelta> collectNetworkStats() {
+ private NetworkStats collectNetworkStats() {
NetworkStats networkStats = mNetworkStatsSupplier.get();
if (networkStats == null) {
return null;
}
- List<BatteryStatsImpl.NetworkStatsDelta> delta =
- BatteryStatsImpl.computeDelta(networkStats, mLastNetworkStats);
+ NetworkStats delta = mInjector.networkStatsDelta(networkStats, mLastNetworkStats);
mLastNetworkStats = networkStats;
- for (int i = delta.size() - 1; i >= 0; i--) {
- BatteryStatsImpl.NetworkStatsDelta uidDelta = delta.get(i);
+ for (NetworkStats.Entry uidDelta : delta) {
long rxBytes = uidDelta.getRxBytes();
long txBytes = uidDelta.getTxBytes();
long rxPackets = uidDelta.getRxPackets();
diff --git a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
index a75d110e3cd1..17739712d65a 100644
--- a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
+++ b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
@@ -88,6 +88,5 @@ public class ResourcesManagerShellCommand extends ShellCommand {
out.println(" Print this help text.");
out.println(" dump <PROCESS>");
out.println(" Dump the Resources objects in use as well as the history of Resources");
-
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 093df8c5075c..29f1f93a844f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3209,7 +3209,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
true /* forActivity */)) {
return false;
}
- if (mAppCompatController.mAllowRestrictedResizability.getAsBoolean()) {
+ if (mAppCompatController.getResizeOverrides().allowRestrictedResizability()) {
return false;
}
// If the user preference respects aspect ratio, then it becomes non-resizable.
@@ -3240,8 +3240,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The caller will check both application and activity level property.
return true;
}
- return !AppCompatController.allowRestrictedResizability(wms.mContext.getPackageManager(),
- appInfo.packageName);
+ return !AppCompatResizeOverrides.allowRestrictedResizability(
+ wms.mContext.getPackageManager(), appInfo.packageName);
}
boolean isResizeable() {
@@ -8435,8 +8435,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
@ActivityInfo.SizeChangesSupportMode
private int supportsSizeChanges() {
- if (mAppCompatController.getAppCompatResizeOverrides()
- .shouldOverrideForceNonResizeApp()) {
+ final AppCompatResizeOverrides resizeOverrides = mAppCompatController.getResizeOverrides();
+ if (resizeOverrides.shouldOverrideForceNonResizeApp()) {
return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
}
@@ -8444,8 +8444,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return SIZE_CHANGES_SUPPORTED_METADATA;
}
- if (mAppCompatController.getAppCompatResizeOverrides()
- .shouldOverrideForceResizeApp()) {
+ if (resizeOverrides.shouldOverrideForceResizeApp()) {
return SIZE_CHANGES_SUPPORTED_OVERRIDE;
}
@@ -10221,7 +10220,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAppCompatController.getAppCompatOrientationOverrides()
.shouldIgnoreOrientationRequestLoop());
proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP,
- mAppCompatController.getAppCompatResizeOverrides().shouldOverrideForceResizeApp());
+ mAppCompatController.getResizeOverrides().shouldOverrideForceResizeApp());
proto.write(SHOULD_ENABLE_USER_ASPECT_RATIO_SETTINGS,
mAppCompatController.getAppCompatAspectRatioOverrides()
.shouldEnableUserAspectRatioSettings());
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 4433d64f0d00..0967078deac3 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -15,23 +15,17 @@
*/
package com.android.server.wm;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY;
-
import android.annotation.NonNull;
import android.content.pm.PackageManager;
import com.android.server.wm.utils.OptPropFactory;
import java.io.PrintWriter;
-import java.util.function.BooleanSupplier;
/**
* Allows the interaction with all the app compat policies and configurations
*/
class AppCompatController {
-
- @NonNull
- private final ActivityRecord mActivityRecord;
@NonNull
private final TransparentPolicy mTransparentPolicy;
@NonNull
@@ -50,56 +44,28 @@ class AppCompatController {
private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
@NonNull
private final AppCompatSizeCompatModePolicy mAppCompatSizeCompatModePolicy;
- @NonNull
- final BooleanSupplier mAllowRestrictedResizability;
AppCompatController(@NonNull WindowManagerService wmService,
@NonNull ActivityRecord activityRecord) {
- mActivityRecord = activityRecord;
final PackageManager packageManager = wmService.mContext.getPackageManager();
final OptPropFactory optPropBuilder = new OptPropFactory(packageManager,
activityRecord.packageName);
mAppCompatDeviceStateQuery = new AppCompatDeviceStateQuery(activityRecord);
mTransparentPolicy = new TransparentPolicy(activityRecord,
wmService.mAppCompatConfiguration);
- mAppCompatOverrides = new AppCompatOverrides(activityRecord,
+ mAppCompatOverrides = new AppCompatOverrides(activityRecord, packageManager,
wmService.mAppCompatConfiguration, optPropBuilder, mAppCompatDeviceStateQuery);
mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, mAppCompatOverrides);
mAppCompatAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
mTransparentPolicy, mAppCompatOverrides);
- mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(mActivityRecord,
+ mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(activityRecord,
wmService.mAppCompatConfiguration);
- mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord,
+ mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(activityRecord,
wmService.mAppCompatConfiguration);
mDesktopAppCompatAspectRatioPolicy = new DesktopAppCompatAspectRatioPolicy(activityRecord,
mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration);
- mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(mActivityRecord,
+ mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(activityRecord,
mAppCompatOverrides);
- mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> {
- // Application level.
- if (allowRestrictedResizability(packageManager, mActivityRecord.packageName)) {
- return true;
- }
- // Activity level.
- try {
- return packageManager.getPropertyAsUser(
- PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY,
- mActivityRecord.mActivityComponent.getPackageName(),
- mActivityRecord.mActivityComponent.getClassName(),
- mActivityRecord.mUserId).getBoolean();
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
- });
- }
-
- static boolean allowRestrictedResizability(PackageManager pm, String packageName) {
- try {
- return pm.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, packageName)
- .getBoolean();
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
}
@NonNull
@@ -138,8 +104,8 @@ class AppCompatController {
}
@NonNull
- AppCompatResizeOverrides getAppCompatResizeOverrides() {
- return mAppCompatOverrides.getAppCompatResizeOverrides();
+ AppCompatResizeOverrides getResizeOverrides() {
+ return mAppCompatOverrides.getResizeOverrides();
}
@NonNull
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index 2f03105846bd..58b37becc373 100644
--- a/services/core/java/com/android/server/wm/AppCompatOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.annotation.NonNull;
+import android.content.pm.PackageManager;
import com.android.server.wm.utils.OptPropFactory;
@@ -34,13 +35,14 @@ public class AppCompatOverrides {
@NonNull
private final AppCompatFocusOverrides mAppCompatFocusOverrides;
@NonNull
- private final AppCompatResizeOverrides mAppCompatResizeOverrides;
+ private final AppCompatResizeOverrides mResizeOverrides;
@NonNull
private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
@NonNull
private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
AppCompatOverrides(@NonNull ActivityRecord activityRecord,
+ @NonNull PackageManager packageManager,
@NonNull AppCompatConfiguration appCompatConfiguration,
@NonNull OptPropFactory optPropBuilder,
@NonNull AppCompatDeviceStateQuery appCompatDeviceStateQuery) {
@@ -55,7 +57,8 @@ public class AppCompatOverrides {
mAppCompatReachabilityOverrides);
mAppCompatFocusOverrides = new AppCompatFocusOverrides(activityRecord,
appCompatConfiguration, optPropBuilder);
- mAppCompatResizeOverrides = new AppCompatResizeOverrides(activityRecord, optPropBuilder);
+ mResizeOverrides = new AppCompatResizeOverrides(activityRecord, packageManager,
+ optPropBuilder);
mAppCompatLetterboxOverrides = new AppCompatLetterboxOverrides(activityRecord,
appCompatConfiguration);
}
@@ -81,8 +84,8 @@ public class AppCompatOverrides {
}
@NonNull
- AppCompatResizeOverrides getAppCompatResizeOverrides() {
- return mAppCompatResizeOverrides;
+ AppCompatResizeOverrides getResizeOverrides() {
+ return mResizeOverrides;
}
@NonNull
diff --git a/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java b/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java
index 60c18254eca7..fa53153dd143 100644
--- a/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java
@@ -19,13 +19,17 @@ package com.android.server.wm;
import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY;
import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
import android.annotation.NonNull;
+import android.content.pm.PackageManager;
import com.android.server.wm.utils.OptPropFactory;
+import java.util.function.BooleanSupplier;
+
/**
* Encapsulate app compat logic about resizability.
*/
@@ -37,11 +41,40 @@ class AppCompatResizeOverrides {
@NonNull
private final OptPropFactory.OptProp mAllowForceResizeOverrideOptProp;
+ @NonNull
+ private final BooleanSupplier mAllowRestrictedResizability;
+
AppCompatResizeOverrides(@NonNull ActivityRecord activityRecord,
+ @NonNull PackageManager packageManager,
@NonNull OptPropFactory optPropBuilder) {
mActivityRecord = activityRecord;
mAllowForceResizeOverrideOptProp = optPropBuilder.create(
PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+ mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> {
+ // Application level.
+ if (allowRestrictedResizability(packageManager, mActivityRecord.packageName)) {
+ return true;
+ }
+ // Activity level.
+ try {
+ return packageManager.getPropertyAsUser(
+ PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY,
+ mActivityRecord.mActivityComponent.getPackageName(),
+ mActivityRecord.mActivityComponent.getClassName(),
+ mActivityRecord.mUserId).getBoolean();
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ });
+ }
+
+ static boolean allowRestrictedResizability(PackageManager pm, String packageName) {
+ try {
+ return pm.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, packageName)
+ .getBoolean();
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
}
/**
@@ -75,4 +108,9 @@ class AppCompatResizeOverrides {
return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
isChangeEnabled(mActivityRecord, FORCE_NON_RESIZE_APP));
}
+
+ /** @see android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY */
+ boolean allowRestrictedResizability() {
+ return mAllowRestrictedResizability.getAsBoolean();
+ }
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 98ed6f76b2f9..54ae80cfe98a 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -103,6 +103,8 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
// again, so that the control with leash can be eventually dispatched
if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending
&& mControlTarget != null) {
+ ProtoLog.d(WM_DEBUG_IME,
+ "onPostLayout: IME control ready to be dispatched, ws=%s", ws);
mGivenInsetsReady = true;
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
@@ -118,6 +120,8 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
mStatsToken = null;
} else if (wasServerVisible && !isServerVisible()) {
+ ProtoLog.d(WM_DEBUG_IME, "onPostLayout: setImeShowing(false) was: %s, ws=%s",
+ isImeShowing(), ws);
setImeShowing(false);
}
}
@@ -621,6 +625,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
// request (cancelling the initial show) or hide request (aborting the initial show).
logIsScheduledAndReadyToShowIme(!visible /* aborted */);
}
+ ProtoLog.d(WM_DEBUG_IME, "receiveImeStatsToken: visible=%s", visible);
if (visible) {
ImeTracker.forLogging().onCancelled(
mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 65cf4ee733dd..379e312e58c0 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -346,6 +346,7 @@ public:
void setMousePointerAccelerationEnabled(ui::LogicalDisplayId displayId, bool enabled);
void setMouseReverseVerticalScrollingEnabled(bool enabled);
void setMouseScrollingAccelerationEnabled(bool enabled);
+ void setMouseScrollingSpeed(int32_t speed);
void setMouseSwapPrimaryButtonEnabled(bool enabled);
void setMouseAccelerationEnabled(bool enabled);
void setTouchpadPointerSpeed(int32_t speed);
@@ -500,6 +501,9 @@ private:
// True if mouse scrolling acceleration is enabled.
bool mouseScrollingAccelerationEnabled{true};
+ // The mouse scrolling speed, as a number from -7 (slowest) to 7 (fastest).
+ int32_t mouseScrollingSpeed{0};
+
// True if mouse vertical scrolling is reversed.
bool mouseReverseVerticalScrollingEnabled{false};
@@ -843,6 +847,9 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon
mLocked.mouseScrollingAccelerationEnabled
? android::os::IInputConstants::DEFAULT_MOUSE_WHEEL_ACCELERATION
: 1;
+ outConfig->wheelVelocityControlParameters.scale = mLocked.mouseScrollingAccelerationEnabled
+ ? 1
+ : exp2f(mLocked.mouseScrollingSpeed * POINTER_SPEED_EXPONENT);
outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled;
outConfig->pointerCaptureRequest = mLocked.pointerCaptureRequest;
@@ -1451,6 +1458,21 @@ void NativeInputManager::setMouseScrollingAccelerationEnabled(bool enabled) {
InputReaderConfiguration::Change::POINTER_SPEED);
}
+void NativeInputManager::setMouseScrollingSpeed(int32_t speed) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ if (mLocked.mouseScrollingSpeed == speed) {
+ return;
+ }
+
+ mLocked.mouseScrollingSpeed = speed;
+ } // release lock
+
+ mInputManager->getReader().requestRefreshConfiguration(
+ InputReaderConfiguration::Change::POINTER_SPEED);
+}
+
void NativeInputManager::setMouseSwapPrimaryButtonEnabled(bool enabled) {
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -3243,6 +3265,11 @@ static void nativeSetMouseScrollingAccelerationEnabled(JNIEnv* env, jobject nati
im->setMouseScrollingAccelerationEnabled(enabled);
}
+static void nativeSetMouseScrollingSpeed(JNIEnv* env, jobject nativeImplObj, jint speed) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+ im->setMouseScrollingSpeed(speed);
+}
+
static void nativeSetMouseReverseVerticalScrollingEnabled(JNIEnv* env, jobject nativeImplObj,
bool enabled) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -3319,6 +3346,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
(void*)nativeSetMouseReverseVerticalScrollingEnabled},
{"setMouseScrollingAccelerationEnabled", "(Z)V",
(void*)nativeSetMouseScrollingAccelerationEnabled},
+ {"setMouseScrollingSpeed", "(I)V", (void*)nativeSetMouseScrollingSpeed},
{"setMouseSwapPrimaryButtonEnabled", "(Z)V", (void*)nativeSetMouseSwapPrimaryButtonEnabled},
{"setMouseAccelerationEnabled", "(Z)V", (void*)nativeSetMouseAccelerationEnabled},
{"setTouchpadPointerSpeed", "(I)V", (void*)nativeSetTouchpadPointerSpeed},
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9ab9a8f44ed9..c5d42ad9f081 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1787,9 +1787,11 @@ public final class SystemServer implements Dumpable {
SignedConfigService.registerUpdateReceiver(mSystemContext);
t.traceEnd();
- t.traceBegin("AppIntegrityService");
- mSystemServiceManager.startService(AppIntegrityManagerService.class);
- t.traceEnd();
+ if (!android.server.Flags.removeAppIntegrityManagerService()) {
+ t.traceBegin("AppIntegrityService");
+ mSystemServiceManager.startService(AppIntegrityManagerService.class);
+ t.traceEnd();
+ }
t.traceBegin("StartLogcatManager");
mSystemServiceManager.startService(LogcatManagerService.class);
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 0d222fb4409e..4d021ec2c0d3 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -51,4 +51,11 @@ flag {
description: "Remove GameManagerService from Wear"
bug: "340929737"
is_fixed_read_only: true
+}
+
+flag {
+ name: "remove_app_integrity_manager_service"
+ namespace: "package_manager_service"
+ description: "Remove AppIntegrityManagerService"
+ bug: "364200023"
} \ No newline at end of file
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 6c4158e60ebb..8e0eb6b14432 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -63,7 +63,6 @@ android_robolectric_test {
instrumentation_for: "FrameworksServicesLib",
- upstream: true,
strict_mode: false,
}
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 3ace3fb11506..95b38e56f276 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -66,7 +66,6 @@ android_robolectric_test {
instrumentation_for: "BackupFrameworksServicesLib",
- upstream: true,
strict_mode: false,
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index e6ff5068368f..da58aa1f6c66 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -86,6 +86,7 @@ android_ravenwood_test {
"src/com/android/server/inputmethod/**/ClientControllerTest.java",
],
auto_gen_config: true,
+ team: "trendy_team_ravenwood",
}
android_test {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
index 7277fd79fdd5..66aaa562b873 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
@@ -45,6 +45,7 @@ import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -78,10 +79,7 @@ import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
@Presubmit
@RunWith(JUnit4.class)
@@ -885,18 +883,15 @@ public class AppsFilterImplTest {
return null;
}
- @NonNull
+ @Nullable
@Override
- public Map<String, Set<String>> getTargetToOverlayables(
+ public Pair<String, String> getTargetToOverlayables(
@NonNull AndroidPackage pkg) {
if (overlay.getPackageName().equals(pkg.getPackageName())) {
- Map<String, Set<String>> map = new ArrayMap<>();
- Set<String> set = new ArraySet<>();
- set.add(overlay.getOverlayTargetOverlayableName());
- map.put(overlay.getOverlayTarget(), set);
- return map;
+ return Pair.create(overlay.getOverlayTarget(),
+ overlay.getOverlayTargetOverlayableName());
}
- return Collections.emptyMap();
+ return null;
}
},
mMockHandler);
@@ -977,18 +972,15 @@ public class AppsFilterImplTest {
return null;
}
- @NonNull
+ @Nullable
@Override
- public Map<String, Set<String>> getTargetToOverlayables(
+ public Pair<String, String> getTargetToOverlayables(
@NonNull AndroidPackage pkg) {
if (overlay.getPackageName().equals(pkg.getPackageName())) {
- Map<String, Set<String>> map = new ArrayMap<>();
- Set<String> set = new ArraySet<>();
- set.add(overlay.getOverlayTargetOverlayableName());
- map.put(overlay.getOverlayTarget(), set);
- return map;
+ return Pair.create(overlay.getOverlayTarget(),
+ overlay.getOverlayTargetOverlayableName());
}
- return Collections.emptyMap();
+ return null;
}
},
mMockHandler);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 9e96800ca2e9..4a09802fc822 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -258,7 +258,6 @@ public class MockingOomAdjusterTests {
mService.mOomAdjuster = mService.mProcessStateController.getOomAdjuster();
mService.mOomAdjuster.mAdjSeq = 10000;
mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
- mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC);
mUiTierSize = mService.mConstants.TIERED_CACHED_ADJ_UI_TIER_SIZE;
mFirstNonUiCachedAdj = sFirstUiCachedAdj + mUiTierSize;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index 89b48bad2358..27eada013642 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Process.INVALID_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -27,9 +29,11 @@ import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_CANC
import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_FORCE_STOPPED;
import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_SUPERSEDED;
import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_USER_STOPPED;
+import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.cancelReasonToString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -39,9 +43,11 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
+import android.app.BackgroundStartPrivileges;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.IPackageManager;
+import android.os.Binder;
import android.os.Looper;
import android.os.UserHandle;
@@ -179,6 +185,34 @@ public class PendingIntentControllerTest {
}
}
+ @Test
+ public void testClearAllowBgActivityStartsClearsToken() {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ Binder token = new Binder();
+ pir.setAllowBgActivityStarts(token, FLAG_ACTIVITY_SENDER);
+ assertEquals(BackgroundStartPrivileges.allowBackgroundActivityStarts(token),
+ pir.getBackgroundStartPrivilegesForActivitySender(token));
+ pir.clearAllowBgActivityStarts(token);
+ assertEquals(BackgroundStartPrivileges.NONE,
+ pir.getBackgroundStartPrivilegesForActivitySender(token));
+ }
+
+ @Test
+ public void testClearAllowBgActivityStartsClearsDuration() {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ Binder token = new Binder();
+ pir.setAllowlistDurationLocked(token, 1000,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, REASON_NOTIFICATION_SERVICE,
+ "NotificationManagerService");
+ PendingIntentRecord.TempAllowListDuration allowlistDurationLocked =
+ pir.getAllowlistDurationLocked(token);
+ assertEquals(1000, allowlistDurationLocked.duration);
+ pir.clearAllowBgActivityStarts(token);
+ PendingIntentRecord.TempAllowListDuration allowlistDurationLockedAfterClear =
+ pir.getAllowlistDurationLocked(token);
+ assertNull(allowlistDurationLockedAfterClear);
+ }
+
private void assertCancelReason(int expectedReason, int actualReason) {
final String errMsg = "Expected: " + cancelReasonToString(expectedReason)
+ "; Actual: " + cancelReasonToString(actualReason);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
index 00b911bc1ae7..cd3683ba0ca8 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
@@ -158,6 +158,11 @@ public class MobileRadioPowerStatsCollectorTest {
public LongSupplier getPhoneSignalScanDurationSupplier() {
return mScanDurationSupplier;
}
+
+ @Override
+ public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+ return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+ }
};
@Before
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 4b6fcc39dcef..8a081f8e16cc 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -283,6 +283,11 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
protected void updateBatteryPropertiesLocked() {
}
+ @Override
+ protected NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+ return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+ }
+
public static class DummyExternalStatsSync implements ExternalStatsSync {
public int flags = 0;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/NetworkStatsTestUtils.java b/services/tests/powerstatstests/src/com/android/server/power/stats/NetworkStatsTestUtils.java
new file mode 100644
index 000000000000..21be6546b011
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/NetworkStatsTestUtils.java
@@ -0,0 +1,86 @@
+/*
+ * 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.power.stats;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkStats;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NetworkStatsTestUtils {
+ /**
+ * Equivalent to NetworkStats.subtract, reimplementing the method for Ravenwood tests.
+ */
+ @NonNull
+ public static NetworkStats networkStatsDelta(@NonNull NetworkStats currentStats,
+ @Nullable NetworkStats lastStats) {
+ if (!RavenwoodRule.isOnRavenwood()) {
+ if (lastStats == null) {
+ return currentStats;
+ }
+ return currentStats.subtract(lastStats);
+ }
+
+ List<NetworkStats.Entry> entries = new ArrayList<>();
+ for (NetworkStats.Entry entry : currentStats) {
+ NetworkStats.Entry lastEntry = null;
+ int uid = entry.getUid();
+ if (lastStats != null) {
+ for (NetworkStats.Entry e : lastStats) {
+ if (e.getUid() == uid && e.getSet() == entry.getSet()
+ && e.getTag() == entry.getTag()
+ && e.getMetered() == entry.getMetered()
+ && e.getRoaming() == entry.getRoaming()
+ && e.getDefaultNetwork() == entry.getDefaultNetwork()
+ /*&& Objects.equals(e.getIface(), entry.getIface())*/) {
+ lastEntry = e;
+ break;
+ }
+ }
+ }
+ long rxBytes, rxPackets, txBytes, txPackets;
+ if (lastEntry != null) {
+ rxBytes = Math.max(0, entry.getRxBytes() - lastEntry.getRxBytes());
+ rxPackets = Math.max(0, entry.getRxPackets() - lastEntry.getRxPackets());
+ txBytes = Math.max(0, entry.getTxBytes() - lastEntry.getTxBytes());
+ txPackets = Math.max(0, entry.getTxPackets() - lastEntry.getTxPackets());
+ } else {
+ rxBytes = entry.getRxBytes();
+ rxPackets = entry.getRxPackets();
+ txBytes = entry.getTxBytes();
+ txPackets = entry.getTxPackets();
+ }
+
+ NetworkStats.Entry uidEntry = mock(NetworkStats.Entry.class);
+ when(uidEntry.getUid()).thenReturn(uid);
+ when(uidEntry.getRxBytes()).thenReturn(rxBytes);
+ when(uidEntry.getRxPackets()).thenReturn(rxPackets);
+ when(uidEntry.getTxBytes()).thenReturn(txBytes);
+ when(uidEntry.getTxPackets()).thenReturn(txPackets);
+
+ entries.add(uidEntry);
+ }
+ NetworkStats delta = mock(NetworkStats.class);
+ when(delta.iterator()).thenAnswer(inv -> entries.iterator());
+ return delta;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
index 8b5e6ee9cf89..a26b2c955380 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
@@ -168,6 +168,11 @@ public class WifiPowerStatsCollectorTest {
public WifiManager getWifiManager() {
return mWifiManager;
}
+
+ @Override
+ public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+ return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+ }
};
@Before
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
index 4ed44a0563e3..6acd36885b1e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
@@ -56,6 +56,7 @@ import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
import com.android.server.power.stats.BatteryUsageStatsRule;
import com.android.server.power.stats.MobileRadioPowerStatsCollector;
+import com.android.server.power.stats.NetworkStatsTestUtils;
import com.android.server.power.stats.PowerStatsCollector;
import com.android.server.power.stats.PowerStatsUidResolver;
import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
@@ -152,6 +153,11 @@ public class MobileRadioPowerStatsProcessorTest {
public LongSupplier getPhoneSignalScanDurationSupplier() {
return mScanDurationSupplier;
}
+
+ @Override
+ public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+ return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+ }
};
@Before
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
index 535f2da603b8..a20274fb5ded 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
@@ -43,6 +43,7 @@ import android.telephony.TelephonyManager;
import com.android.internal.os.Clock;
import com.android.server.power.stats.BatteryUsageStatsRule;
import com.android.server.power.stats.MobileRadioPowerStatsCollector;
+import com.android.server.power.stats.NetworkStatsTestUtils;
import com.android.server.power.stats.PowerStatsCollector;
import com.android.server.power.stats.PowerStatsUidResolver;
import com.android.server.power.stats.format.PowerStatsLayout;
@@ -135,6 +136,11 @@ public class PhoneCallPowerStatsProcessorTest {
public LongSupplier getPhoneSignalScanDurationSupplier() {
return mScanDurationSupplier;
}
+
+ @Override
+ public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+ return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+ }
};
@Before
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
index 1e097692f55e..bd92a84fc815 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
@@ -56,6 +56,7 @@ import com.android.internal.os.Clock;
import com.android.internal.os.PowerProfile;
import com.android.server.power.stats.BatteryUsageStatsRule;
import com.android.server.power.stats.MockBatteryStatsImpl;
+import com.android.server.power.stats.NetworkStatsTestUtils;
import com.android.server.power.stats.PowerStatsCollector;
import com.android.server.power.stats.PowerStatsUidResolver;
import com.android.server.power.stats.WifiPowerStatsCollector;
@@ -178,6 +179,11 @@ public class WifiPowerStatsProcessorTest {
public WifiStatsRetriever getWifiStatsRetriever() {
return mWifiStatsRetriever;
}
+
+ @Override
+ public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+ return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+ }
};
@Before
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 82efae45e1a4..92c6db5b7b96 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -21,6 +21,9 @@ import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_VIA_SYS
import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
import static android.service.quickaccesswallet.Flags.launchWalletViaSysuiCallbacks;
+import static com.android.server.GestureLauncherService.DOUBLE_TAP_POWER_DISABLED_MODE;
+import static com.android.server.GestureLauncherService.DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE;
+import static com.android.server.GestureLauncherService.DOUBLE_TAP_POWER_MULTI_TARGET_MODE;
import static com.android.server.GestureLauncherService.LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
import static com.android.server.GestureLauncherService.LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
import static com.android.server.GestureLauncherService.POWER_DOUBLE_TAP_MAX_TIME_MS;
@@ -163,7 +166,7 @@ public class GestureLauncherServiceTest {
new GestureLauncherService(
mContext, mMetricsLogger, mQuickAccessWalletClient, mUiEventLogger);
- withDoubleTapPowerGestureEnableSettingValue(true);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
}
@@ -215,68 +218,117 @@ public class GestureLauncherServiceTest {
}
@Test
- public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingDisabled() {
- if (launchWalletOptionOnPowerDoubleTap()) {
- withDoubleTapPowerEnabledConfigValue(false);
- withDoubleTapPowerGestureEnableSettingValue(false);
- withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
- } else {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
- }
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_flagEnabled_configFalseSettingDisabled() {
+ withDoubleTapPowerModeConfigValue(
+ DOUBLE_TAP_POWER_DISABLED_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(false);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@Test
- public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingEnabled() {
- if (launchWalletOptionOnPowerDoubleTap()) {
- withDoubleTapPowerEnabledConfigValue(false);
- withDoubleTapPowerGestureEnableSettingValue(true);
- withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
- assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
- mContext, FAKE_USER_ID));
- } else {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(0);
- assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
- mContext, FAKE_USER_ID));
- }
+ @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_flagDisabled_configFalseSettingDisabled() {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+
+ assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
}
@Test
- public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingDisabled() {
- if (launchWalletOptionOnPowerDoubleTap()) {
- withDoubleTapPowerEnabledConfigValue(true);
- withDoubleTapPowerGestureEnableSettingValue(false);
- withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
- } else {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(1);
- }
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_flagEnabled_configFalseSettingEnabled() {
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_DISABLED_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@Test
- public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingEnabled() {
- if (launchWalletOptionOnPowerDoubleTap()) {
- withDoubleTapPowerEnabledConfigValue(true);
- withDoubleTapPowerGestureEnableSettingValue(true);
- withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
- } else {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- }
+ @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_flagDisabled_configFalseSettingEnabled() {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+
+ assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_flagEnabled_configTrueSettingDisabled() {
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(false);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
+ assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_flagDisabled_configTrueSettingDisabled() {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+
+ assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_flagEnabled_configTrueSettingEnabled() {
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
+ assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_flagDisabled_configTrueSettingEnabled() {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+
assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@Test
@RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_launchCameraMode_settingEnabled() {
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+
+ assertTrue(
+ mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_launchCameraMode_settingDisabled() {
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+
+ assertFalse(
+ mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
public void testIsCameraDoubleTapPowerSettingEnabled_actionWallet() {
- withDoubleTapPowerEnabledConfigValue(true);
- withDoubleTapPowerGestureEnableSettingValue(true);
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
assertFalse(
@@ -287,8 +339,8 @@ public class GestureLauncherServiceTest {
@Test
@RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
public void testIsWalletDoubleTapPowerSettingEnabled() {
- withDoubleTapPowerEnabledConfigValue(true);
- withDoubleTapPowerGestureEnableSettingValue(true);
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
assertTrue(
@@ -299,11 +351,11 @@ public class GestureLauncherServiceTest {
@Test
@RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
public void testIsWalletDoubleTapPowerSettingEnabled_configDisabled() {
- withDoubleTapPowerEnabledConfigValue(false);
- withDoubleTapPowerGestureEnableSettingValue(true);
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_DISABLED_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
- assertTrue(
+ assertFalse(
mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@@ -311,8 +363,8 @@ public class GestureLauncherServiceTest {
@Test
@RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
public void testIsWalletDoubleTapPowerSettingEnabled_settingDisabled() {
- withDoubleTapPowerEnabledConfigValue(true);
- withDoubleTapPowerGestureEnableSettingValue(false);
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(false);
withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
assertFalse(
@@ -323,8 +375,8 @@ public class GestureLauncherServiceTest {
@Test
@RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
public void testIsWalletDoubleTapPowerSettingEnabled_actionCamera() {
- withDoubleTapPowerEnabledConfigValue(true);
- withDoubleTapPowerGestureEnableSettingValue(true);
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
assertFalse(
@@ -449,13 +501,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() {
- if (launchWalletOptionOnPowerDoubleTap()) {
- withDoubleTapPowerGestureEnableSettingValue(false);
- } else {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
- }
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ disableDoubleTapPowerGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -498,13 +544,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffInteractive() {
- if (launchWalletOptionOnPowerDoubleTap()) {
- withDoubleTapPowerGestureEnableSettingValue(false);
- } else {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
- }
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ disableDoubleTapPowerGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -549,9 +589,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOffInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ disableDoubleTapPowerGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1031,9 +1069,7 @@ public class GestureLauncherServiceTest {
public void
testInterceptPowerKeyDown_triggerEmergency_cameraGestureEnabled_doubleTap_cooldownTriggered() {
// Enable camera double tap gesture
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
// Enable power button cooldown
withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
@@ -1220,10 +1256,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_longpress() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
- withUserSetupCompleteValue(true);
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1400,13 +1433,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffNotInteractive() {
- if (launchWalletOptionOnPowerDoubleTap()) {
- withDoubleTapPowerGestureEnableSettingValue(false);
- } else {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
- }
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ disableDoubleTapPowerGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1449,9 +1476,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffNotInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ disableDoubleTapPowerGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1495,9 +1520,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOffNotInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ disableDoubleTapPowerGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1630,9 +1653,7 @@ public class GestureLauncherServiceTest {
@Test
public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOnNotInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1823,12 +1844,13 @@ public class GestureLauncherServiceTest {
.thenReturn(enableConfigValue);
}
- private void withDoubleTapPowerEnabledConfigValue(boolean enable) {
- when(mResources.getBoolean(com.android.internal.R.bool.config_doubleTapPowerGestureEnabled))
- .thenReturn(enable);
+ private void withDoubleTapPowerModeConfigValue(
+ int modeConfigValue) {
+ when(mResources.getInteger(com.android.internal.R.integer.config_doubleTapPowerGestureMode))
+ .thenReturn(modeConfigValue);
}
- private void withDoubleTapPowerGestureEnableSettingValue(boolean enable) {
+ private void withMultiTargetDoubleTapPowerGestureEnableSettingValue(boolean enable) {
Settings.Secure.putIntForUser(
mContentResolver,
Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
@@ -1910,8 +1932,8 @@ public class GestureLauncherServiceTest {
private void enableWalletGesture() {
withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
- withDoubleTapPowerGestureEnableSettingValue(true);
- withDoubleTapPowerEnabledConfigValue(true);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
mGestureLauncherService.updateWalletDoubleTapPowerEnabled();
withUserSetupCompleteValue(true);
@@ -1926,8 +1948,9 @@ public class GestureLauncherServiceTest {
private void enableCameraGesture() {
if (launchWalletOptionOnPowerDoubleTap()) {
- withDoubleTapPowerEnabledConfigValue(true);
- withDoubleTapPowerGestureEnableSettingValue(true);
+ withDoubleTapPowerModeConfigValue(
+ DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
} else {
withCameraDoubleTapPowerEnableConfigValue(true);
@@ -1937,6 +1960,18 @@ public class GestureLauncherServiceTest {
withUserSetupCompleteValue(true);
}
+ private void disableDoubleTapPowerGesture() {
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_DISABLED_MODE);
+ withMultiTargetDoubleTapPowerGestureEnableSettingValue(false);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
+ mGestureLauncherService.updateWalletDoubleTapPowerEnabled();
+ withUserSetupCompleteValue(true);
+ }
+
private void sendPowerKeyDownToGestureLauncherServiceAndAssertValues(
long eventTime, boolean expectedIntercept, boolean expectedOutLaunchedValue) {
KeyEvent keyEvent =
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
new file mode 100644
index 000000000000..84713079c9d3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
@@ -0,0 +1,309 @@
+/*
+ * 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.appop;
+
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Process;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.LongSparseArray;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.appop.DiscreteOpsSqlRegistry.DiscreteOp;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class DiscreteAppOpSqlPersistenceTest {
+ private static final String DATABASE_NAME = "test_app_ops.db";
+ private DiscreteOpsSqlRegistry mDiscreteRegistry;
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ @Before
+ public void setUp() {
+ mDiscreteRegistry = new DiscreteOpsSqlRegistry(mContext,
+ mContext.getDatabasePath(DATABASE_NAME));
+ mDiscreteRegistry.systemReady();
+ }
+
+ @After
+ public void cleanUp() {
+ mContext.deleteDatabase(DATABASE_NAME);
+ }
+
+ @Test
+ public void discreteOpEventIsRecorded() {
+ DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent);
+ List<DiscreteOp> discreteOps = mDiscreteRegistry.getCachedDiscreteOps();
+ assertThat(discreteOps.size()).isEqualTo(1);
+ assertThat(discreteOps).contains(opEvent);
+ }
+
+ @Test
+ public void discreteOpEventIsPersistedToDisk() {
+ DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent);
+ flushDiscreteOpsToDatabase();
+ assertThat(mDiscreteRegistry.getCachedDiscreteOps()).isEmpty();
+ List<DiscreteOp> discreteOps = mDiscreteRegistry.getAllDiscreteOps();
+ assertThat(discreteOps.size()).isEqualTo(1);
+ assertThat(discreteOps).contains(opEvent);
+ }
+
+ @Test
+ public void discreteOpEventInSameMinuteIsNotRecorded() {
+ long oneMinuteMillis = Duration.ofMinutes(1).toMillis();
+ // round timestamp at minute level and add 5 seconds
+ long accessTime = System.currentTimeMillis() / oneMinuteMillis * oneMinuteMillis + 5000;
+ DiscreteOp opEvent = new DiscreteOpBuilder(mContext).setAccessTime(accessTime).build();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent);
+ // create duplicate event in same minute, with added 30 seconds
+ DiscreteOp opEvent2 =
+ new DiscreteOpBuilder(mContext).setAccessTime(accessTime + 30000).build();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent2);
+ List<DiscreteOp> discreteOps = mDiscreteRegistry.getAllDiscreteOps();
+
+ assertThat(discreteOps.size()).isEqualTo(1);
+ assertThat(discreteOps).contains(opEvent);
+ }
+
+ @Test
+ public void multipleDiscreteOpEventAreRecorded() {
+ DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+ DiscreteOp opEvent2 = new DiscreteOpBuilder(mContext).setPackageName(
+ "test.package").build();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent);
+ mDiscreteRegistry.recordDiscreteAccess(opEvent2);
+
+ List<DiscreteOp> discreteOps = mDiscreteRegistry.getAllDiscreteOps();
+ assertThat(discreteOps).contains(opEvent);
+ assertThat(discreteOps).contains(opEvent2);
+ assertThat(discreteOps.size()).isEqualTo(2);
+ }
+
+ @Test
+ public void clearDiscreteOps() {
+ DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent);
+ flushDiscreteOpsToDatabase();
+ DiscreteOp opEvent2 = new DiscreteOpBuilder(mContext).setUid(12345).setPackageName(
+ "abc").build();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent2);
+ mDiscreteRegistry.clearHistory();
+ assertThat(mDiscreteRegistry.getAllDiscreteOps()).isEmpty();
+ }
+
+ @Test
+ public void clearDiscreteOpsForPackage() {
+ DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent);
+ flushDiscreteOpsToDatabase();
+ mDiscreteRegistry.recordDiscreteAccess(new DiscreteOpBuilder(mContext).build());
+ mDiscreteRegistry.clearHistory(Process.myUid(), mContext.getPackageName());
+
+ assertThat(mDiscreteRegistry.getAllDiscreteOps()).isEmpty();
+ }
+
+ @Test
+ public void offsetDiscreteOps() {
+ DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+ long event2AccessTime = System.currentTimeMillis() - 300000;
+ DiscreteOp opEvent2 = new DiscreteOpBuilder(mContext).setAccessTime(
+ event2AccessTime).build();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent);
+ flushDiscreteOpsToDatabase();
+ mDiscreteRegistry.recordDiscreteAccess(opEvent2);
+ long offset = Duration.ofMinutes(2).toMillis();
+
+ mDiscreteRegistry.offsetHistory(offset);
+
+ // adjust input for assertion
+ DiscreteOp e1 = new DiscreteOpBuilder(opEvent)
+ .setAccessTime(opEvent.getAccessTime() - offset).build();
+ DiscreteOp e2 = new DiscreteOpBuilder(opEvent2)
+ .setAccessTime(event2AccessTime - offset).build();
+
+ List<DiscreteOp> results = mDiscreteRegistry.getAllDiscreteOps();
+ assertThat(results.size()).isEqualTo(2);
+ assertThat(results).contains(e1);
+ assertThat(results).contains(e2);
+ }
+
+ @Test
+ public void completeAttributionChain() {
+ long chainId = 100;
+ DiscreteOp event1 = new DiscreteOpBuilder(mContext)
+ .setChainId(chainId)
+ .setAttributionFlags(ATTRIBUTION_FLAG_RECEIVER | ATTRIBUTION_FLAG_TRUSTED)
+ .build();
+ DiscreteOp event2 = new DiscreteOpBuilder(mContext)
+ .setChainId(chainId)
+ .setAttributionFlags(ATTRIBUTION_FLAG_ACCESSOR | ATTRIBUTION_FLAG_TRUSTED)
+ .build();
+ List<DiscreteOp> events = new ArrayList<>();
+ events.add(event1);
+ events.add(event2);
+
+ LongSparseArray<DiscreteOpsSqlRegistry.AttributionChain> chains =
+ mDiscreteRegistry.createAttributionChains(events, new ArraySet<>());
+
+ assertThat(chains.size()).isGreaterThan(0);
+ DiscreteOpsSqlRegistry.AttributionChain chain = chains.get(chainId);
+ assertThat(chain).isNotNull();
+ assertThat(chain.isComplete()).isTrue();
+ assertThat(chain.getStart()).isEqualTo(event1);
+ assertThat(chain.getLastVisible()).isEqualTo(event2);
+ }
+
+ @Test
+ public void addToHistoricalOps() {
+ long beginTimeMillis = System.currentTimeMillis();
+ DiscreteOp event1 = new DiscreteOpBuilder(mContext)
+ .build();
+ DiscreteOp event2 = new DiscreteOpBuilder(mContext)
+ .setUid(123457)
+ .build();
+ mDiscreteRegistry.recordDiscreteAccess(event1);
+ flushDiscreteOpsToDatabase();
+ mDiscreteRegistry.recordDiscreteAccess(event2);
+
+ long endTimeMillis = System.currentTimeMillis() + 500;
+ AppOpsManager.HistoricalOps results = new AppOpsManager.HistoricalOps(beginTimeMillis,
+ endTimeMillis);
+
+ mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(results, beginTimeMillis,
+ endTimeMillis, 0, 0, null, null, null, 0, new ArraySet<>());
+ Log.i("Manjeet", "TEST read " + results);
+ assertWithMessage("results shouldn't be empty").that(results.isEmpty()).isFalse();
+ }
+
+ @Test
+ public void dump() {
+ DiscreteOp event1 = new DiscreteOpBuilder(mContext)
+ .setAccessTime(1732221340628L)
+ .setUid(12345)
+ .build();
+ DiscreteOp event2 = new DiscreteOpBuilder(mContext)
+ .setAccessTime(1732227340628L)
+ .setUid(123457)
+ .build();
+ mDiscreteRegistry.recordDiscreteAccess(event1);
+ flushDiscreteOpsToDatabase();
+ mDiscreteRegistry.recordDiscreteAccess(event2);
+ }
+
+ /** This clears in-memory cache and push records into the database. */
+ private void flushDiscreteOpsToDatabase() {
+ mDiscreteRegistry.writeAndClearOldAccessHistory();
+ }
+
+ /**
+ * Creates default op event for CAMERA app op with current time as access time
+ * and 1 minute duration
+ */
+ private static class DiscreteOpBuilder {
+ private int mUid;
+ private String mPackageName;
+ private String mAttributionTag;
+ private String mDeviceId;
+ private int mOpCode;
+ private int mOpFlags;
+ private int mAttributionFlags;
+ private int mUidState;
+ private long mChainId;
+ private long mAccessTime;
+ private long mDuration;
+
+ DiscreteOpBuilder(Context context) {
+ mUid = Process.myUid();
+ mPackageName = context.getPackageName();
+ mAttributionTag = null;
+ mDeviceId = String.valueOf(context.getDeviceId());
+ mOpCode = AppOpsManager.OP_CAMERA;
+ mOpFlags = AppOpsManager.OP_FLAG_SELF;
+ mAttributionFlags = ATTRIBUTION_FLAG_ACCESSOR;
+ mUidState = UID_STATE_FOREGROUND;
+ mChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+ mAccessTime = System.currentTimeMillis();
+ mDuration = Duration.ofMinutes(1).toMillis();
+ }
+
+ DiscreteOpBuilder(DiscreteOp discreteOp) {
+ this.mUid = discreteOp.getUid();
+ this.mPackageName = discreteOp.getPackageName();
+ this.mAttributionTag = discreteOp.getAttributionTag();
+ this.mDeviceId = discreteOp.getDeviceId();
+ this.mOpCode = discreteOp.getOpCode();
+ this.mOpFlags = discreteOp.getOpFlags();
+ this.mAttributionFlags = discreteOp.getAttributionFlags();
+ this.mUidState = discreteOp.getUidState();
+ this.mChainId = discreteOp.getChainId();
+ this.mAccessTime = discreteOp.getAccessTime();
+ this.mDuration = discreteOp.getDuration();
+ }
+
+ public DiscreteOpBuilder setUid(int uid) {
+ this.mUid = uid;
+ return this;
+ }
+
+ public DiscreteOpBuilder setPackageName(String packageName) {
+ this.mPackageName = packageName;
+ return this;
+ }
+
+ public DiscreteOpBuilder setAttributionFlags(int attributionFlags) {
+ this.mAttributionFlags = attributionFlags;
+ return this;
+ }
+
+ public DiscreteOpBuilder setChainId(long chainId) {
+ this.mChainId = chainId;
+ return this;
+ }
+
+ public DiscreteOpBuilder setAccessTime(long accessTime) {
+ this.mAccessTime = accessTime;
+ return this;
+ }
+
+ public DiscreteOp build() {
+ return new DiscreteOp(mUid, mPackageName, mAttributionTag, mDeviceId, mOpCode, mOpFlags,
+ mAttributionFlags, mUidState, mChainId, mAccessTime, mDuration);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
index 2ff0c6288ece..ae973be17904 100644
--- a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
@@ -47,9 +47,12 @@ import org.junit.runner.RunWith;
import java.io.File;
import java.util.List;
+/**
+ * Test xml persistence implementation for discrete ops.
+ */
@RunWith(AndroidJUnit4.class)
-public class DiscreteAppOpPersistenceTest {
- private DiscreteRegistry mDiscreteRegistry;
+public class DiscreteAppOpXmlPersistenceTest {
+ private DiscreteOpsXmlRegistry mDiscreteRegistry;
private final Object mLock = new Object();
private File mMockDataDirectory;
private final Context mContext =
@@ -61,13 +64,13 @@ public class DiscreteAppOpPersistenceTest {
@Before
public void setUp() {
mMockDataDirectory = mContext.getDir("mock_data", Context.MODE_PRIVATE);
- mDiscreteRegistry = new DiscreteRegistry(mLock, mMockDataDirectory);
+ mDiscreteRegistry = new DiscreteOpsXmlRegistry(mLock, mMockDataDirectory);
mDiscreteRegistry.systemReady();
}
@After
public void cleanUp() {
- mDiscreteRegistry.writeAndClearAccessHistory();
+ mDiscreteRegistry.writeAndClearOldAccessHistory();
FileUtils.deleteContents(mMockDataDirectory);
}
@@ -87,14 +90,14 @@ public class DiscreteAppOpPersistenceTest {
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
uidState, accessTime, duration, attributionFlags, attributionChainId,
- DiscreteRegistry.ACCESS_TYPE_FINISH_OP);
+ DiscreteOpsXmlRegistry.ACCESS_TYPE_FINISH_OP);
// Verify in-memory object is correct
fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
duration, uidState, opFlags, attributionFlags, attributionChainId);
// Write to disk and clear the in-memory object
- mDiscreteRegistry.writeAndClearAccessHistory();
+ mDiscreteRegistry.writeAndClearOldAccessHistory();
// Verify the storage file is created and then verify its content is correct
File[] files = FileUtils.listFilesOrEmpty(mMockDataDirectory);
@@ -119,12 +122,12 @@ public class DiscreteAppOpPersistenceTest {
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
uidState, accessTime, duration, attributionFlags, attributionChainId,
- DiscreteRegistry.ACCESS_TYPE_START_OP);
+ DiscreteOpsXmlRegistry.ACCESS_TYPE_START_OP);
fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
duration, uidState, opFlags, attributionFlags, attributionChainId);
- mDiscreteRegistry.writeAndClearAccessHistory();
+ mDiscreteRegistry.writeAndClearOldAccessHistory();
File[] files = FileUtils.listFilesOrEmpty(mMockDataDirectory);
assertThat(files.length).isEqualTo(1);
@@ -136,30 +139,31 @@ public class DiscreteAppOpPersistenceTest {
int expectedOp, String expectedDeviceId, String expectedAttrTag,
long expectedAccessTime, long expectedAccessDuration, int expectedUidState,
int expectedOpFlags, int expectedAttrFlags, int expectedAttrChainId) {
- DiscreteRegistry.DiscreteOps discreteOps = mDiscreteRegistry.getAllDiscreteOps();
+ DiscreteOpsXmlRegistry.DiscreteOps discreteOps = mDiscreteRegistry.getAllDiscreteOps();
assertThat(discreteOps.isEmpty()).isFalse();
assertThat(discreteOps.mUids.size()).isEqualTo(1);
- DiscreteRegistry.DiscreteUidOps discreteUidOps = discreteOps.mUids.get(expectedUid);
+ DiscreteOpsXmlRegistry.DiscreteUidOps discreteUidOps = discreteOps.mUids.get(expectedUid);
assertThat(discreteUidOps.mPackages.size()).isEqualTo(1);
- DiscreteRegistry.DiscretePackageOps discretePackageOps =
+ DiscreteOpsXmlRegistry.DiscretePackageOps discretePackageOps =
discreteUidOps.mPackages.get(expectedPackageName);
assertThat(discretePackageOps.mPackageOps.size()).isEqualTo(1);
- DiscreteRegistry.DiscreteOp discreteOp = discretePackageOps.mPackageOps.get(expectedOp);
+ DiscreteOpsXmlRegistry.DiscreteOp discreteOp =
+ discretePackageOps.mPackageOps.get(expectedOp);
assertThat(discreteOp.mDeviceAttributedOps.size()).isEqualTo(1);
- DiscreteRegistry.DiscreteDeviceOp discreteDeviceOp =
+ DiscreteOpsXmlRegistry.DiscreteDeviceOp discreteDeviceOp =
discreteOp.mDeviceAttributedOps.get(expectedDeviceId);
assertThat(discreteDeviceOp.mAttributedOps.size()).isEqualTo(1);
- List<DiscreteRegistry.DiscreteOpEvent> discreteOpEvents =
+ List<DiscreteOpsXmlRegistry.DiscreteOpEvent> discreteOpEvents =
discreteDeviceOp.mAttributedOps.get(expectedAttrTag);
assertThat(discreteOpEvents.size()).isEqualTo(1);
- DiscreteRegistry.DiscreteOpEvent discreteOpEvent = discreteOpEvents.get(0);
+ DiscreteOpsXmlRegistry.DiscreteOpEvent discreteOpEvent = discreteOpEvents.get(0);
assertThat(discreteOpEvent.mNoteTime).isEqualTo(expectedAccessTime);
assertThat(discreteOpEvent.mNoteDuration).isEqualTo(expectedAccessDuration);
assertThat(discreteOpEvent.mUidState).isEqualTo(expectedUidState);
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
new file mode 100644
index 000000000000..21cc3bac3938
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.appop;
+
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.AppOpsManager;
+import android.companion.virtual.VirtualDeviceManager;
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.Process;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.time.Duration;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class DiscreteOpsMigrationAndRollbackTest {
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private static final String DATABASE_NAME = "test_app_ops.db";
+ private static final int RECORD_COUNT = 500;
+ private final File mMockDataDirectory = mContext.getDir("mock_data", Context.MODE_PRIVATE);
+ final Object mLock = new Object();
+
+ @After
+ @Before
+ public void clean() {
+ mContext.deleteDatabase(DATABASE_NAME);
+ FileUtils.deleteContents(mMockDataDirectory);
+ }
+
+ @Test
+ public void migrateFromXmlToSqlite() {
+ // write records to xml registry
+ DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(mLock, mMockDataDirectory);
+ xmlRegistry.systemReady();
+ for (int i = 1; i <= RECORD_COUNT; i++) {
+ DiscreteOpsSqlRegistry.DiscreteOp opEvent =
+ new DiscreteOpBuilder(mContext)
+ .setChainId(i)
+ .setUid(10000 + i) // make all records unique
+ .build();
+ xmlRegistry.recordDiscreteAccess(opEvent.getUid(), opEvent.getPackageName(),
+ opEvent.getDeviceId(), opEvent.getOpCode(), opEvent.getAttributionTag(),
+ opEvent.getOpFlags(), opEvent.getUidState(), opEvent.getAccessTime(),
+ opEvent.getDuration(), opEvent.getAttributionFlags(),
+ (int) opEvent.getChainId(), DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP);
+ }
+ xmlRegistry.writeAndClearOldAccessHistory();
+ assertThat(xmlRegistry.readLargestChainIdFromDiskLocked()).isEqualTo(RECORD_COUNT);
+ assertThat(xmlRegistry.getAllDiscreteOps().mUids.size()).isEqualTo(RECORD_COUNT);
+
+ // migration to sql registry
+ DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(mContext,
+ mContext.getDatabasePath(DATABASE_NAME));
+ sqlRegistry.systemReady();
+ DiscreteOpsMigrationHelper.migrateDiscreteOpsToSqlite(xmlRegistry, sqlRegistry);
+ List<DiscreteOpsSqlRegistry.DiscreteOp> sqlOps = sqlRegistry.getAllDiscreteOps();
+
+ assertThat(xmlRegistry.getAllDiscreteOps().mUids).isEmpty();
+ assertThat(sqlOps.size()).isEqualTo(RECORD_COUNT);
+ assertThat(sqlRegistry.getLargestAttributionChainId()).isEqualTo(RECORD_COUNT);
+ }
+
+ @Test
+ public void migrateFromSqliteToXml() {
+ // write to sql registry
+ DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(mContext,
+ mContext.getDatabasePath(DATABASE_NAME));
+ sqlRegistry.systemReady();
+ for (int i = 1; i <= RECORD_COUNT; i++) {
+ DiscreteOpsSqlRegistry.DiscreteOp opEvent =
+ new DiscreteOpBuilder(mContext)
+ .setChainId(i)
+ .setUid(RECORD_COUNT + i) // make all records unique
+ .build();
+ sqlRegistry.recordDiscreteAccess(opEvent.getUid(), opEvent.getPackageName(),
+ opEvent.getDeviceId(), opEvent.getOpCode(), opEvent.getAttributionTag(),
+ opEvent.getOpFlags(), opEvent.getUidState(), opEvent.getAccessTime(),
+ opEvent.getDuration(), opEvent.getAttributionFlags(),
+ (int) opEvent.getChainId(), DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP);
+ }
+ sqlRegistry.writeAndClearOldAccessHistory();
+ assertThat(sqlRegistry.getAllDiscreteOps().size()).isEqualTo(RECORD_COUNT);
+ assertThat(sqlRegistry.getLargestAttributionChainId()).isEqualTo(RECORD_COUNT);
+
+ // migration to xml registry
+ DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(mLock, mMockDataDirectory);
+ xmlRegistry.systemReady();
+ DiscreteOpsMigrationHelper.migrateDiscreteOpsToXml(sqlRegistry, xmlRegistry);
+ DiscreteOpsXmlRegistry.DiscreteOps xmlOps = xmlRegistry.getAllDiscreteOps();
+
+ assertThat(sqlRegistry.getAllDiscreteOps()).isEmpty();
+ assertThat(xmlOps.mLargestChainId).isEqualTo(RECORD_COUNT);
+ assertThat(xmlOps.mUids.size()).isEqualTo(RECORD_COUNT);
+ }
+
+ private static class DiscreteOpBuilder {
+ private int mUid;
+ private String mPackageName;
+ private String mAttributionTag;
+ private String mDeviceId;
+ private int mOpCode;
+ private int mOpFlags;
+ private int mAttributionFlags;
+ private int mUidState;
+ private int mChainId;
+ private long mAccessTime;
+ private long mDuration;
+
+ DiscreteOpBuilder(Context context) {
+ mUid = Process.myUid();
+ mPackageName = context.getPackageName();
+ mAttributionTag = null;
+ mDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+ mOpCode = AppOpsManager.OP_CAMERA;
+ mOpFlags = AppOpsManager.OP_FLAG_SELF;
+ mAttributionFlags = ATTRIBUTION_FLAG_ACCESSOR;
+ mUidState = UID_STATE_FOREGROUND;
+ mChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+ mAccessTime = System.currentTimeMillis();
+ mDuration = Duration.ofMinutes(1).toMillis();
+ }
+
+ public DiscreteOpBuilder setUid(int uid) {
+ this.mUid = uid;
+ return this;
+ }
+
+ public DiscreteOpBuilder setChainId(int chainId) {
+ this.mChainId = chainId;
+ return this;
+ }
+
+ public DiscreteOpsSqlRegistry.DiscreteOp build() {
+ return new DiscreteOpsSqlRegistry.DiscreteOp(mUid, mPackageName, mAttributionTag,
+ mDeviceId,
+ mOpCode, mOpFlags, mAttributionFlags, mUidState, mChainId, mAccessTime,
+ mDuration);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 02441648a589..4f551119b42e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -887,6 +887,21 @@ public class HdmiCecLocalDeviceAudioSystemTest {
systemAudioModeRequest_fromAudioSystem);
}
+ @Test
+ public void addAndStartAction_remove() throws Exception {
+ // utilize callback test to test if addAndStartAction(action, remove)
+ TestCallback callback = new TestCallback();
+
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+ new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem, callback),
+ true);
+
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(
+ ArcTerminationActionFromAvr.class).size()).isEqualTo(1);
+ }
+
private static class TestCallback extends IHdmiControlCallback.Stub {
private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
index 1352adef783f..ad6e467efeef 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
@@ -76,12 +76,10 @@ class OverlayReferenceMapperTests {
val overlay1 = mockOverlay(1)
mapper = mapper(
overlayToTargetToOverlayables = mapOf(
- overlay0.packageName to mapOf(
- target.packageName to target.overlayables.keys
- ),
- overlay1.packageName to mapOf(
- target.packageName to target.overlayables.keys
- )
+ overlay0.packageName to android.util.Pair(target.packageName,
+ target.overlayables.keys.first()),
+ overlay1.packageName to android.util.Pair(target.packageName,
+ target.overlayables.keys.first())
)
)
val existing = mapper.addInOrder(overlay0, overlay1) {
@@ -134,42 +132,38 @@ class OverlayReferenceMapperTests {
}
@Test
- fun overlayWithMultipleTargets() {
- val target0 = mockTarget(0)
- val target1 = mockTarget(1)
+ fun overlayWithoutTarget() {
val overlay = mockOverlay()
- mapper = mapper(
- overlayToTargetToOverlayables = mapOf(
- overlay.packageName to mapOf(
- target0.packageName to target0.overlayables.keys,
- target1.packageName to target1.overlayables.keys
- )
- )
- )
- mapper.addInOrder(target0, target1, overlay) {
- assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
- }
- assertMapping(ACTOR_PACKAGE_NAME to setOf(target0, target1, overlay))
- mapper.remove(target0) {
- assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ mapper.addInOrder(overlay) {
+ assertThat(it).isEmpty()
}
- assertMapping(ACTOR_PACKAGE_NAME to setOf(target1, overlay))
- mapper.remove(target1) {
- assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ // An overlay can only have visibility exposed through its target
+ assertEmpty()
+ mapper.remove(overlay) {
+ assertThat(it).isEmpty()
}
assertEmpty()
}
@Test
- fun overlayWithoutTarget() {
+ fun targetWithNullOverlayable() {
+ val target = mockTarget()
val overlay = mockOverlay()
- mapper.addInOrder(overlay) {
+ mapper = mapper(
+ overlayToTargetToOverlayables = mapOf(
+ overlay.packageName to android.util.Pair(target.packageName, null)
+ )
+ )
+ val existing = mapper.addInOrder(overlay) {
assertThat(it).isEmpty()
}
- // An overlay can only have visibility exposed through its target
assertEmpty()
- mapper.remove(overlay) {
- assertThat(it).isEmpty()
+ mapper.addInOrder(target, existing = existing) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
+ assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
+ mapper.remove(target) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
}
assertEmpty()
}
@@ -219,17 +213,15 @@ class OverlayReferenceMapperTests {
namedActors: Map<String, Map<String, String>> = Uri.parse(ACTOR_NAME).run {
mapOf(authority!! to mapOf(pathSegments.first() to ACTOR_PACKAGE_NAME))
},
- overlayToTargetToOverlayables: Map<String, Map<String, Set<String>>> = mapOf(
- mockOverlay().packageName to mapOf(
- mockTarget().run { packageName to overlayables.keys }
- )
- )
+ overlayToTargetToOverlayables: Map<String, android.util.Pair<String, String>> = mapOf(
+ mockOverlay().packageName to mockTarget().run { android.util.Pair(packageName!!,
+ overlayables.keys.first()) })
) = OverlayReferenceMapper(deferRebuild, object : OverlayReferenceMapper.Provider {
override fun getActorPkg(actor: String) =
OverlayActorEnforcer.getPackageNameForActor(actor, namedActors).first
override fun getTargetToOverlayables(pkg: AndroidPackage) =
- overlayToTargetToOverlayables[pkg.packageName] ?: emptyMap()
+ overlayToTargetToOverlayables[pkg.packageName]
})
private fun mockTarget(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 4e030d499c25..3ef360a752f6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -112,6 +112,7 @@ import org.mockito.stubbing.Answer;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -556,6 +557,12 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
}
@Override
+ void injectFinishWrite(@NonNull ResilientAtomicFile file,
+ @NonNull FileOutputStream os) throws IOException {
+ file.finishWrite(os, false /* doFsVerity */);
+ }
+
+ @Override
void wtf(String message, Throwable th) {
// During tests, WTF is fatal.
fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th));
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 c01283a236c4..0d86d4c3fa28 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -159,7 +159,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Test for the first launch path, no settings file available.
*/
- public void FirstInitialize() {
+ public void testFirstInitialize() {
assertResetTimes(START_TIME, START_TIME + INTERVAL);
}
@@ -167,7 +167,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* Test for {@link ShortcutService#getLastResetTimeLocked()} and
* {@link ShortcutService#getNextResetTimeLocked()}.
*/
- public void UpdateAndGetNextResetTimeLocked() {
+ public void testUpdateAndGetNextResetTimeLocked() {
assertResetTimes(START_TIME, START_TIME + INTERVAL);
// Advance clock.
@@ -196,7 +196,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Test for the restoration from saved file.
*/
- public void InitializeFromSavedFile() {
+ public void testInitializeFromSavedFile() {
mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
@@ -220,7 +220,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Add various broken cases.
}
- public void LoadConfig() {
+ public void testLoadConfig() {
mService.updateConfigurationLocked(
ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
+ ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
@@ -261,22 +261,22 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// === Test for app side APIs ===
/** Test for {@link android.content.pm.ShortcutManager#getMaxShortcutCountForActivity()} */
- public void GetMaxDynamicShortcutCount() {
+ public void testGetMaxDynamicShortcutCount() {
assertEquals(MAX_SHORTCUTS, mManager.getMaxShortcutCountForActivity());
}
/** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
- public void GetRemainingCallCount() {
+ public void testGetRemainingCallCount() {
assertEquals(MAX_UPDATES_PER_INTERVAL, mManager.getRemainingCallCount());
}
- public void GetIconMaxDimensions() {
+ public void testGetIconMaxDimensions() {
assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxWidth());
assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxHeight());
}
/** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
- public void GetRateLimitResetTime() {
+ public void testGetRateLimitResetTime() {
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
@@ -284,7 +284,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
}
- public void SetDynamicShortcuts() {
+ public void testSetDynamicShortcuts() {
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
@@ -354,7 +354,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void AddDynamicShortcuts() {
+ public void testAddDynamicShortcuts() {
setCaller(CALLING_PACKAGE_1, USER_10);
final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -402,7 +402,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void PushDynamicShortcut() {
+ public void testPushDynamicShortcut() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
+ ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
@@ -544,7 +544,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_10));
}
- public void PushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
+ public void testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
throws InterruptedException {
mService.updateConfigurationLocked(
ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=500");
@@ -576,6 +576,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
Mockito.reset(mMockUsageStatsManagerInternal);
for (int i = 2; i <= 10; i++) {
final ShortcutInfo si = makeShortcut("s" + i);
+ setCaller(CALLING_PACKAGE_2, USER_10);
mManager.pushDynamicShortcut(si);
}
verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
@@ -595,7 +596,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
eq(CALLING_PACKAGE_2), any(), eq(USER_10));
}
- public void UnlimitedCalls() {
+ public void testUnlimitedCalls() {
setCaller(CALLING_PACKAGE_1, USER_10);
final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -626,7 +627,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(3, mManager.getRemainingCallCount());
}
- public void PublishWithNoActivity() {
+ public void testPublishWithNoActivity() {
// If activity is not explicitly set, use the default one.
mRunningUsers.put(USER_11, true);
@@ -732,7 +733,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void PublishWithNoActivity_noMainActivityInPackage() {
+ public void testPublishWithNoActivity_noMainActivityInPackage() {
mRunningUsers.put(USER_11, true);
runWithCaller(CALLING_PACKAGE_2, USER_11, () -> {
@@ -751,7 +752,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void DeleteDynamicShortcuts() {
+ public void testDeleteDynamicShortcuts() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -792,7 +793,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(2, mManager.getRemainingCallCount());
}
- public void DeleteAllDynamicShortcuts() {
+ public void testDeleteAllDynamicShortcuts() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -821,7 +822,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(1, mManager.getRemainingCallCount());
}
- public void Icons() throws IOException {
+ public void testIcons() throws IOException {
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
final Icon res512x512 = Icon.createWithResource(getTestContext(), R.drawable.black_512x512);
@@ -1035,7 +1036,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
*/
}
- public void CleanupDanglingBitmaps() throws Exception {
+ public void testCleanupDanglingBitmaps() throws Exception {
assertBitmapDirectories(USER_10, EMPTY_STRINGS);
assertBitmapDirectories(USER_11, EMPTY_STRINGS);
@@ -1204,7 +1205,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
maxSize));
}
- public void ShrinkBitmap() {
+ public void 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);
@@ -1227,7 +1228,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
return out.getFile();
}
- public void OpenIconFileForWrite() throws IOException {
+ public void testOpenIconFileForWrite() throws IOException {
mInjectedCurrentTimeMillis = 1000;
final File p10_1_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
@@ -1301,7 +1302,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertFalse(p11_1_3.getName().contains("_"));
}
- public void UpdateShortcuts() {
+ public void testUpdateShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"),
@@ -1432,7 +1433,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void UpdateShortcuts_icons() {
+ public void testUpdateShortcuts_icons() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1")
@@ -1526,7 +1527,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ShortcutManagerGetShortcuts_shortcutTypes() {
+ public void testShortcutManagerGetShortcuts_shortcutTypes() {
// Create 3 manifest and 3 dynamic shortcuts
addManifestShortcutResource(
@@ -1617,7 +1618,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s1", "s2");
}
- public void CachedShortcuts() {
+ public void testCachedShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1701,7 +1702,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
"s2");
}
- public void CachedShortcuts_accessShortcutsPermission() {
+ public void testCachedShortcuts_accessShortcutsPermission() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1743,7 +1744,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3");
}
- public void CachedShortcuts_canPassShortcutLimit() {
+ public void testCachedShortcuts_canPassShortcutLimit() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4");
@@ -1781,7 +1782,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// === Test for launcher side APIs ===
- public void GetShortcuts() {
+ public void testGetShortcuts() {
// Set up shortcuts.
@@ -1998,7 +1999,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
"s1", "s3");
}
- public void GetShortcuts_shortcutKinds() throws Exception {
+ public void testGetShortcuts_shortcutKinds() throws Exception {
// Create 3 manifest and 3 dynamic shortcuts
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -2109,7 +2110,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void GetShortcuts_resolveStrings() throws Exception {
+ public void testGetShortcuts_resolveStrings() throws Exception {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
.setId("id")
@@ -2157,7 +2158,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void GetShortcuts_personsFlag() {
+ public void testGetShortcuts_personsFlag() {
ShortcutInfo s = new ShortcutInfo.Builder(mClientContext, "id")
.setShortLabel("label")
.setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
@@ -2205,7 +2206,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
// TODO resource
- public void GetShortcutInfo() {
+ public void testGetShortcutInfo() {
// Create shortcuts.
setCaller(CALLING_PACKAGE_1);
final ShortcutInfo s1_1 = makeShortcut(
@@ -2280,7 +2281,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals("ABC", findById(list, "s1").getTitle());
}
- public void PinShortcutAndGetPinnedShortcuts() {
+ public void testPinShortcutAndGetPinnedShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2361,7 +2362,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* This is similar to the above test, except it used "disable" instead of "remove". It also
* does "enable".
*/
- public void DisableAndEnableShortcuts() {
+ public void testDisableAndEnableShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2486,7 +2487,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void DisableShortcuts_thenRepublish() {
+ public void testDisableShortcuts_thenRepublish() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
@@ -2556,7 +2557,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void PinShortcutAndGetPinnedShortcuts_multi() {
+ public void testPinShortcutAndGetPinnedShortcuts_multi() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
@@ -2832,7 +2833,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void PinShortcutAndGetPinnedShortcuts_assistant() {
+ public void testPinShortcutAndGetPinnedShortcuts_assistant() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
@@ -2888,7 +2889,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void PinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
+ public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
@@ -3477,7 +3478,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void StartShortcut() {
+ public void testStartShortcut() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final ShortcutInfo s1_1 = makeShortcut(
@@ -3612,7 +3613,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check extra, etc
}
- public void LauncherCallback() throws Throwable {
+ public void testLauncherCallback() throws Throwable {
// Disable throttling for this test.
mService.updateConfigurationLocked(
ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999,"
@@ -3778,7 +3779,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.isEmpty();
}
- public void LauncherCallback_crossProfile() throws Throwable {
+ public void testLauncherCallback_crossProfile() throws Throwable {
prepareCrossProfileDataSet();
final Handler h = new Handler(Looper.getMainLooper());
@@ -3901,7 +3902,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// === Test for persisting ===
- public void SaveAndLoadUser_empty() {
+ public void testSaveAndLoadUser_empty() {
assertTrue(mManager.setDynamicShortcuts(list()));
Log.i(TAG, "Saved state");
@@ -3918,7 +3919,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Try save and load, also stop/start the user.
*/
- public void SaveAndLoadUser() {
+ public void testSaveAndLoadUser() {
// First, create some shortcuts and save.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -4059,7 +4060,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check all other fields
}
- public void LoadCorruptedShortcuts() throws Exception {
+ public void testLoadCorruptedShortcuts() throws Exception {
initService();
addPackage("com.android.chrome", 0, 0);
@@ -4073,7 +4074,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false));
}
- public void SaveCorruptAndLoadUser() throws Exception {
+ public void 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);
@@ -4229,7 +4230,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check all other fields
}
- public void CleanupPackage() {
+ public void testCleanupPackage() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s0_1"))));
@@ -4506,7 +4507,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mService.saveDirtyInfo();
}
- public void CleanupPackage_republishManifests() {
+ public void testCleanupPackage_republishManifests() {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
@@ -4574,7 +4575,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void HandleGonePackage_crossProfile() {
+ public void testHandleGonePackage_crossProfile() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
@@ -4846,7 +4847,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(expected, spi.canRestoreTo(mService, pi, true));
}
- public void CanRestoreTo() {
+ public void testCanRestoreTo() {
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 10, "sig1", "sig2");
addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 10, "sig1");
@@ -4909,7 +4910,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkCanRestoreTo(DISABLED_REASON_BACKUP_NOT_SUPPORTED, spi3, true, 10, true, "sig1");
}
- public void HandlePackageDelete() {
+ public void testHandlePackageDelete() {
checkHandlePackageDeleteInner((userId, packageName) -> {
uninstallPackage(userId, packageName);
mService.mPackageMonitor.onReceive(getTestContext(),
@@ -4917,7 +4918,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void HandlePackageDisable() {
+ public void testHandlePackageDisable() {
checkHandlePackageDeleteInner((userId, packageName) -> {
disablePackage(userId, packageName);
mService.mPackageMonitor.onReceive(getTestContext(),
@@ -5049,7 +5050,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
/** Almost ame as testHandlePackageDelete, except it doesn't uninstall packages. */
- public void HandlePackageClearData() {
+ public void testHandlePackageClearData() {
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_32x32));
setCaller(CALLING_PACKAGE_1, USER_10);
@@ -5125,7 +5126,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_11));
}
- public void HandlePackageClearData_manifestRepublished() {
+ public void testHandlePackageClearData_manifestRepublished() {
mRunningUsers.put(USER_11, true);
@@ -5167,7 +5168,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void HandlePackageUpdate() throws Throwable {
+ public void testHandlePackageUpdate() throws Throwable {
// Set up shortcuts and launchers.
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -5341,7 +5342,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Test the case where an updated app has resource IDs changed.
*/
- public void HandlePackageUpdate_resIdChanged() throws Exception {
+ public void testHandlePackageUpdate_resIdChanged() throws Exception {
final Icon icon1 = Icon.createWithResource(getTestContext(), /* res ID */ 1000);
final Icon icon2 = Icon.createWithResource(getTestContext(), /* res ID */ 1001);
@@ -5416,7 +5417,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void HandlePackageUpdate_systemAppUpdate() {
+ public void testHandlePackageUpdate_systemAppUpdate() {
// Package1 is a system app. Package 2 is not a system app, so it's not scanned
// in this test at all.
@@ -5522,7 +5523,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mService.getUserShortcutsLocked(USER_10).getLastAppScanOsFingerprint());
}
- public void HandlePackageChanged() {
+ public void testHandlePackageChanged() {
final ComponentName ACTIVITY1 = new ComponentName(CALLING_PACKAGE_1, "act1");
final ComponentName ACTIVITY2 = new ComponentName(CALLING_PACKAGE_1, "act2");
@@ -5652,7 +5653,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void HandlePackageUpdate_activityNoLongerMain() throws Throwable {
+ public void testHandlePackageUpdate_activityNoLongerMain() throws Throwable {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithActivity("s1a",
@@ -5738,7 +5739,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* - Unpinned dynamic shortcuts
* - Bitmaps
*/
- public void BackupAndRestore() {
+ public void testBackupAndRestore() {
assertFileNotExists("user-0/shortcut_dump/restore-0-start.txt");
assertFileNotExists("user-0/shortcut_dump/restore-1-payload.xml");
@@ -5759,7 +5760,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_success(/*firstRestore=*/ true);
}
- public void BackupAndRestore_backupRestoreTwice() {
+ public void testBackupAndRestore_backupRestoreTwice() {
prepareForBackupTest();
checkBackupAndRestore_success(/*firstRestore=*/ true);
@@ -5775,7 +5776,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_success(/*firstRestore=*/ false);
}
- public void BackupAndRestore_restoreToNewVersion() {
+ public void testBackupAndRestore_restoreToNewVersion() {
prepareForBackupTest();
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2);
@@ -5784,7 +5785,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_success(/*firstRestore=*/ true);
}
- public void BackupAndRestore_restoreToSuperSetSignatures() {
+ public void testBackupAndRestore_restoreToSuperSetSignatures() {
prepareForBackupTest();
// Change package signatures.
@@ -5981,7 +5982,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void BackupAndRestore_publisherWrongSignature() {
+ public void testBackupAndRestore_publisherWrongSignature() {
prepareForBackupTest();
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature
@@ -5989,7 +5990,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH);
}
- public void BackupAndRestore_publisherNoLongerBackupTarget() {
+ public void testBackupAndRestore_publisherNoLongerBackupTarget() {
prepareForBackupTest();
updatePackageInfo(CALLING_PACKAGE_1,
@@ -6118,7 +6119,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void BackupAndRestore_launcherLowerVersion() {
+ public void testBackupAndRestore_launcherLowerVersion() {
prepareForBackupTest();
addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version
@@ -6127,7 +6128,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_success(/*firstRestore=*/ true);
}
- public void BackupAndRestore_launcherWrongSignature() {
+ public void testBackupAndRestore_launcherWrongSignature() {
prepareForBackupTest();
addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature
@@ -6135,7 +6136,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_launcherNotRestored(true);
}
- public void BackupAndRestore_launcherNoLongerBackupTarget() {
+ public void testBackupAndRestore_launcherNoLongerBackupTarget() {
prepareForBackupTest();
updatePackageInfo(LAUNCHER_1,
@@ -6240,7 +6241,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void BackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
+ public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
prepareForBackupTest();
updatePackageInfo(CALLING_PACKAGE_1,
@@ -6338,7 +6339,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void BackupAndRestore_disabled() {
+ public void testBackupAndRestore_disabled() {
prepareCrossProfileDataSet();
// Before doing backup & restore, disable s1.
@@ -6403,7 +6404,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
- public void BackupAndRestore_manifestRePublished() {
+ public void testBackupAndRestore_manifestRePublished() {
// Publish two manifest shortcuts.
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -6494,7 +6495,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* logcat.
* - if it has allowBackup=false, we don't touch any of the existing shortcuts.
*/
- public void BackupAndRestore_appAlreadyInstalledWhenRestored() {
+ public void testBackupAndRestore_appAlreadyInstalledWhenRestored() {
// Pre-backup. Same as testBackupAndRestore_manifestRePublished().
// Publish two manifest shortcuts.
@@ -6619,7 +6620,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Test for restoring the pre-P backup format.
*/
- public void BackupAndRestore_api27format() throws Exception {
+ public void testBackupAndRestore_api27format() throws Exception {
final byte[] payload = readTestAsset("shortcut/shortcut_api27_backup.xml").getBytes();
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "22222");
@@ -6657,7 +6658,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
- public void SaveAndLoad_crossProfile() {
+ public void testSaveAndLoad_crossProfile() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("Before save & load");
@@ -6860,7 +6861,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.getPackageUserId());
}
- public void OnApplicationActive_permission() {
+ public void testOnApplicationActive_permission() {
assertExpectException(SecurityException.class, "Missing permission", () ->
mManager.onApplicationActive(CALLING_PACKAGE_1, USER_10));
@@ -6869,7 +6870,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mManager.onApplicationActive(CALLING_PACKAGE_1, USER_10);
}
- public void GetShareTargets_permission() {
+ public void testGetShareTargets_permission() {
addPackage(CHOOSER_ACTIVITY_PACKAGE, CHOOSER_ACTIVITY_UID, 10, "sig1");
mInjectedChooserActivity =
ComponentName.createRelative(CHOOSER_ACTIVITY_PACKAGE, ".ChooserActivity");
@@ -6888,7 +6889,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void HasShareTargets_permission() {
+ public void testHasShareTargets_permission() {
assertExpectException(SecurityException.class, "Missing permission", () ->
mManager.hasShareTargets(CALLING_PACKAGE_1));
@@ -6897,7 +6898,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mManager.hasShareTargets(CALLING_PACKAGE_1);
}
- public void isSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException {
+ public void testisSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException {
setCaller(LAUNCHER_1, USER_10);
IntentFilter filter_any = new IntentFilter();
@@ -6912,18 +6913,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mManager.hasShareTargets(CALLING_PACKAGE_1);
}
- public void Dumpsys_crossProfile() {
+ public void testDumpsys_crossProfile() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("test1", /* force= */ true);
}
- public void Dumpsys_withIcons() throws IOException {
- Icons();
+ public void testDumpsys_withIcons() throws IOException {
+ testIcons();
// Dump after having some icons.
dumpsysOnLogcat("test1", /* force= */ true);
}
- public void ManifestShortcut_publishOnUnlockUser() {
+ public void testManifestShortcut_publishOnUnlockUser() {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1);
@@ -7137,7 +7138,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertNull(mService.getPackageShortcutForTest(LAUNCHER_1, USER_10));
}
- public void ManifestShortcut_publishOnBroadcast() {
+ public void testManifestShortcut_publishOnBroadcast() {
// First, no packages are installed.
uninstallPackage(USER_10, CALLING_PACKAGE_1);
uninstallPackage(USER_10, CALLING_PACKAGE_2);
@@ -7393,7 +7394,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ManifestShortcuts_missingMandatoryFields() {
+ public void testManifestShortcuts_missingMandatoryFields() {
// Start with no apps installed.
uninstallPackage(USER_10, CALLING_PACKAGE_1);
uninstallPackage(USER_10, CALLING_PACKAGE_2);
@@ -7462,7 +7463,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ManifestShortcuts_intentDefinitions() {
+ public void testManifestShortcuts_intentDefinitions() {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_error_4);
@@ -7604,7 +7605,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ManifestShortcuts_checkAllFields() {
+ public void testManifestShortcuts_checkAllFields() {
mService.handleUnlockUser(USER_10);
// Package 1 updated, which has one valid manifest shortcut and one invalid.
@@ -7709,7 +7710,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ManifestShortcuts_localeChange() throws InterruptedException {
+ public void testManifestShortcuts_localeChange() throws InterruptedException {
mService.handleUnlockUser(USER_10);
// Package 1 updated, which has one valid manifest shortcut and one invalid.
@@ -7813,7 +7814,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ManifestShortcuts_updateAndDisabled_notPinned() {
+ public void testManifestShortcuts_updateAndDisabled_notPinned() {
mService.handleUnlockUser(USER_10);
// First, just publish a manifest shortcut.
@@ -7853,7 +7854,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ManifestShortcuts_updateAndDisabled_pinned() {
+ public void testManifestShortcuts_updateAndDisabled_pinned() {
mService.handleUnlockUser(USER_10);
// First, just publish a manifest shortcut.
@@ -7909,7 +7910,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ManifestShortcuts_duplicateInSingleActivity() {
+ public void testManifestShortcuts_duplicateInSingleActivity() {
mService.handleUnlockUser(USER_10);
// The XML has two shortcuts with the same ID.
@@ -7934,7 +7935,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ManifestShortcuts_duplicateInTwoActivities() {
+ public void testManifestShortcuts_duplicateInTwoActivities() {
mService.handleUnlockUser(USER_10);
// ShortcutActivity has shortcut ms1
@@ -7986,7 +7987,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Manifest shortcuts cannot override shortcuts that were published via the APIs.
*/
- public void ManifestShortcuts_cannotOverrideNonManifest() {
+ public void testManifestShortcuts_cannotOverrideNonManifest() {
mService.handleUnlockUser(USER_10);
// Create a non-pinned dynamic shortcut and a non-dynamic pinned shortcut.
@@ -8059,7 +8060,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Make sure the APIs won't work on manifest shortcuts.
*/
- public void ManifestShortcuts_immutable() {
+ public void testManifestShortcuts_immutable() {
mService.handleUnlockUser(USER_10);
// Create a non-pinned manifest shortcut, a pinned shortcut that was originally
@@ -8152,7 +8153,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
/**
* Make sure the APIs won't work on manifest shortcuts.
*/
- public void ManifestShortcuts_tooMany() {
+ public void testManifestShortcuts_tooMany() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8171,7 +8172,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void MaxShortcutCount_set() {
+ public void testMaxShortcutCount_set() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8252,7 +8253,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void MaxShortcutCount_add() {
+ public void testMaxShortcutCount_add() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8379,7 +8380,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void MaxShortcutCount_update() {
+ public void testMaxShortcutCount_update() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8470,7 +8471,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ShortcutsPushedOutByManifest() {
+ public void testShortcutsPushedOutByManifest() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8578,7 +8579,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void ReturnedByServer() {
+ public void testReturnedByServer() {
// Package 1 updated, with manifest shortcuts.
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -8624,7 +8625,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void IsForegroundDefaultLauncher_true() {
+ public void testIsForegroundDefaultLauncher_true() {
// random uid in the USER_10 range.
final int uid = 1000024;
@@ -8635,7 +8636,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
- public void IsForegroundDefaultLauncher_defaultButNotForeground() {
+ public void testIsForegroundDefaultLauncher_defaultButNotForeground() {
// random uid in the USER_10 range.
final int uid = 1000024;
@@ -8645,7 +8646,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertFalse(mInternal.isForegroundDefaultLauncher("default", uid));
}
- public void IsForegroundDefaultLauncher_foregroundButNotDefault() {
+ public void testIsForegroundDefaultLauncher_foregroundButNotDefault() {
// random uid in the USER_10 range.
final int uid = 1000024;
@@ -8655,7 +8656,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertFalse(mInternal.isForegroundDefaultLauncher("another", uid));
}
- public void ParseShareTargetsFromManifest() {
+ public void testParseShareTargetsFromManifest() {
// These values must exactly match the content of shortcuts_share_targets.xml resource
List<ShareTargetInfo> expectedValues = new ArrayList<>();
expectedValues.add(new ShareTargetInfo(
@@ -8707,7 +8708,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
}
- public void ShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
+ public void testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
List<ShareTargetInfo> expectedValues = new ArrayList<>();
expectedValues.add(new ShareTargetInfo(
new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
@@ -8773,7 +8774,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
}
- public void IsSharingShortcut() throws IntentFilter.MalformedMimeTypeException {
+ public void testIsSharingShortcut() throws IntentFilter.MalformedMimeTypeException {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_share_targets);
@@ -8823,7 +8824,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
filter_any));
}
- public void IsSharingShortcut_PinnedAndCachedOnlyShortcuts()
+ public void testIsSharingShortcut_PinnedAndCachedOnlyShortcuts()
throws IntentFilter.MalformedMimeTypeException {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -8880,7 +8881,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
filter_any));
}
- public void AddingShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+ public void testAddingShortcuts_ExcludesHiddenFromLauncherShortcuts() {
final ShortcutInfo s1 = makeShortcutExcludedFromLauncher("s1");
final ShortcutInfo s2 = makeShortcutExcludedFromLauncher("s2");
final ShortcutInfo s3 = makeShortcutExcludedFromLauncher("s3");
@@ -8901,7 +8902,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void UpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+ public void testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
final ShortcutInfo s1 = makeShortcut("s1");
final ShortcutInfo s2 = makeShortcut("s2");
final ShortcutInfo s3 = makeShortcut("s3");
@@ -8914,7 +8915,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void PinHiddenShortcuts_ThrowsException() {
+ public void testPinHiddenShortcuts_ThrowsException() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertThrown(IllegalArgumentException.class, () -> {
mManager.requestPinShortcut(makeShortcutExcludedFromLauncher("s1"), null);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
index af7f703e9c31..b33233107766 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -101,9 +101,12 @@ public class ConditionProvidersTest extends UiServiceTestCase {
mProviders.notifyConditions("package", msi, conditionsToNotify);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]));
- verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]));
- verify(mCallback).onConditionChanged(eq(Uri.parse("c")), eq(conditionsToNotify[2]));
+ verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]),
+ eq(100));
+ verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]),
+ eq(100));
+ verify(mCallback).onConditionChanged(eq(Uri.parse("c")), eq(conditionsToNotify[2]),
+ eq(100));
verifyNoMoreInteractions(mCallback);
}
@@ -121,8 +124,10 @@ public class ConditionProvidersTest extends UiServiceTestCase {
mProviders.notifyConditions("package", msi, conditionsToNotify);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]));
- verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]));
+ verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]),
+ eq(100));
+ verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]),
+ eq(100));
verifyNoMoreInteractions(mCallback);
}
@@ -141,8 +146,10 @@ public class ConditionProvidersTest extends UiServiceTestCase {
mProviders.notifyConditions("package", msi, conditionsToNotify);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]));
- verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[3]));
+ verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]),
+ eq(100));
+ verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[3]),
+ eq(100));
verifyNoMoreInteractions(mCallback);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 301165f8151d..7885c9b902e2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -11213,7 +11213,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Representative used to verify getCallingZenUser().
mBinderService.getAutomaticZenRules();
- verify(zenModeHelper).getAutomaticZenRules(eq(UserHandle.CURRENT));
+ verify(zenModeHelper).getAutomaticZenRules(eq(UserHandle.CURRENT), anyInt());
}
@Test
@@ -11225,7 +11225,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Representative used to verify getCallingZenUser().
mBinderService.getAutomaticZenRules();
- verify(zenModeHelper).getAutomaticZenRules(eq(Binder.getCallingUserHandle()));
+ verify(zenModeHelper).getAutomaticZenRules(eq(Binder.getCallingUserHandle()), anyInt());
}
/** Prepares for a zen-related test that uses a mocked {@link ZenModeHelper}. */
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 1884bbd39bb9..6ef078b6da8a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -291,7 +291,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.allCombinationsOf(FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING);
+ return FlagsParameterization.allCombinationsOf(FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING,
+ com.android.server.notification.Flags.FLAG_FIX_CALLING_UID_FROM_CPS);
}
public ZenModeHelperTest(FlagsParameterization flags) {
@@ -2617,7 +2618,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- public void testSetAutomaticZenRuleState_nullPkg() {
+ public void testSetAutomaticZenRuleStateFromConditionProvider_nullPkg() {
AutomaticZenRule zenRule = new AutomaticZenRule("name",
null,
new ComponentName(mContext.getPackageName(), "ScheduleConditionProvider"),
@@ -2627,10 +2628,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, null, zenRule,
ORIGIN_APP, "test", CUSTOM_PKG_UID);
- mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, zenRule.getConditionId(),
- new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- ORIGIN_APP,
- CUSTOM_PKG_UID);
+ mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT,
+ zenRule.getConditionId(), new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ ORIGIN_APP, CUSTOM_PKG_UID);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertEquals(STATE_TRUE, ruleInConfig.condition.state);
@@ -2726,8 +2726,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ORIGIN_SYSTEM, "test", SYSTEM_UID);
Condition condition = new Condition(sharedUri, "", STATE_TRUE);
- mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, sharedUri, condition,
- ORIGIN_SYSTEM, SYSTEM_UID);
+ mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, sharedUri,
+ condition, ORIGIN_SYSTEM, SYSTEM_UID);
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
@@ -2741,8 +2741,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
condition = new Condition(sharedUri, "", STATE_FALSE);
- mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, sharedUri, condition,
- ORIGIN_SYSTEM, SYSTEM_UID);
+ mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, sharedUri,
+ condition, ORIGIN_SYSTEM, SYSTEM_UID);
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
@@ -2780,9 +2780,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.setOwner(OWNER)
.setDeviceEffects(zde)
.build(),
- ORIGIN_APP, "reasons", 0);
+ ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
- AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
assertThat(savedRule.getDeviceEffects()).isEqualTo(
new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
@@ -2814,9 +2815,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.setOwner(OWNER)
.setDeviceEffects(zde)
.build(),
- ORIGIN_SYSTEM, "reasons", 0);
+ ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
- AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
}
@@ -2845,7 +2847,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ORIGIN_USER_IN_SYSTEMUI,
"reasons", 0);
- AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
}
@@ -2863,7 +2866,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.setOwner(OWNER)
.setDeviceEffects(original)
.build(),
- ORIGIN_SYSTEM, "reasons", 0);
+ ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
ZenDeviceEffects updateFromApp = new ZenDeviceEffects.Builder()
.setShouldUseNightMode(true) // Good
@@ -2875,9 +2878,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.setOwner(OWNER)
.setDeviceEffects(updateFromApp)
.build(),
- ORIGIN_APP, "reasons", 0);
+ ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
- AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
assertThat(savedRule.getDeviceEffects()).isEqualTo(
new ZenDeviceEffects.Builder()
.setShouldUseNightMode(true) // From update.
@@ -2898,7 +2902,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.setOwner(OWNER)
.setDeviceEffects(original)
.build(),
- ORIGIN_SYSTEM, "reasons", 0);
+ ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
ZenDeviceEffects updateFromSystem = new ZenDeviceEffects.Builder()
.setShouldUseNightMode(true) // Good
@@ -2908,9 +2912,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new AutomaticZenRule.Builder("Rule", CONDITION_ID)
.setDeviceEffects(updateFromSystem)
.build(),
- ORIGIN_SYSTEM, "reasons", 0);
+ ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
- AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromSystem);
}
@@ -2926,7 +2931,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.setOwner(OWNER)
.setDeviceEffects(original)
.build(),
- ORIGIN_SYSTEM, "reasons", 0);
+ ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
ZenDeviceEffects updateFromUser = new ZenDeviceEffects.Builder()
.setShouldUseNightMode(true)
@@ -2939,9 +2944,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new AutomaticZenRule.Builder("Rule", CONDITION_ID)
.setDeviceEffects(updateFromUser)
.build(),
- ORIGIN_USER_IN_SYSTEMUI, "reasons", 0);
+ ORIGIN_USER_IN_SYSTEMUI, "reasons", SYSTEM_UID);
- AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser);
}
@@ -2959,15 +2965,16 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.allowCalls(ZenPolicy.PEOPLE_TYPE_NONE) // default is stars
.build())
.build(),
- ORIGIN_APP, "reasons", 0);
+ ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
new AutomaticZenRule.Builder("Rule", CONDITION_ID)
// no zen policy
.build(),
- ORIGIN_APP, "reasons", 0);
+ ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
- AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
.isEqualTo(STATE_DISALLOW);
}
@@ -2988,7 +2995,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.allowReminders(true)
.build())
.build(),
- ORIGIN_SYSTEM, "reasons", 0);
+ ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
new AutomaticZenRule.Builder("Rule", CONDITION_ID)
@@ -2996,9 +3003,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS)
.build())
.build(),
- ORIGIN_APP, "reasons", 0);
+ ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
- AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
.isEqualTo(STATE_ALLOW); // from update
assertThat(savedRule.getZenPolicy().getPriorityCallSenders())
@@ -4441,7 +4449,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
rule.triggerDescription = TRIGGER_DESC;
mZenModeHelper.mConfig.automaticRules.put(rule.id, rule);
- AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, rule.id);
+ AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, rule.id,
+ SYSTEM_UID);
assertEquals(NAME, actual.getName());
assertEquals(OWNER, actual.getOwner());
@@ -4508,16 +4517,17 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.build();
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
// Checks the name can be changed by the app because the user has not modified it.
AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule)
.setName("NewName")
.build();
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
- "reason", SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ "reason", CUSTOM_PKG_UID);
+ rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
assertThat(rule.getName()).isEqualTo("NewName");
// The user modifies some other field in the rule, which makes the rule as a whole not
@@ -4534,8 +4544,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.setName("NewAppName")
.build();
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
- "reason", SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ "reason", CUSTOM_PKG_UID);
+ rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
assertThat(rule.getName()).isEqualTo("NewAppName");
// The user modifies the name.
@@ -4544,7 +4554,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate,
ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
assertThat(rule.getName()).isEqualTo("UserProvidedName");
// The app is no longer able to modify the name.
@@ -4552,8 +4562,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.setName("NewAppName")
.build();
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
- "reason", SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ "reason", CUSTOM_PKG_UID);
+ rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
assertThat(rule.getName()).isEqualTo("UserProvidedName");
}
@@ -4568,8 +4578,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
// Adds the rule using the app, to avoid having any user modified bits set.
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
// Modifies the filter, icon, zen policy, and device effects
ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
@@ -4589,7 +4600,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Update the rule with the AZR from origin user.
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate,
ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
// UPDATE_ORIGIN_USER should change the bitmask and change the values.
assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
@@ -4625,8 +4636,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
// Adds the rule using the app, to avoid having any user modified bits set.
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
// Modifies the icon, zen policy and device effects
ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
@@ -4646,7 +4658,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Update the rule with the AZR from origin systemUI.
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_SYSTEM,
"reason", SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
// UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask.
assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID);
@@ -4675,8 +4687,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
// Adds the rule using the app, to avoid having any user modified bits set.
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
ZenPolicy policy = new ZenPolicy.Builder()
.allowReminders(true)
@@ -4693,7 +4706,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Since the rule is not already user modified, UPDATE_ORIGIN_APP can modify the rule.
// The bitmask is not modified.
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
- "reason", SYSTEM_UID);
+ "reason", CUSTOM_PKG_UID);
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
assertThat(storedRule.userModifiedFields).isEqualTo(0);
@@ -4717,9 +4730,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Zen rule update coming from the app again. This cannot fully update the rule, because
// the rule is already considered user modified.
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleIdUser, azrUpdate, ORIGIN_APP,
- "reason", SYSTEM_UID);
+ "reason", CUSTOM_PKG_UID);
AutomaticZenRule ruleUser = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
- ruleIdUser);
+ ruleIdUser, CUSTOM_PKG_UID);
// The app can only change the value if the rule is not already user modified,
// so the rule is not changed, and neither is the bitmask.
@@ -4749,8 +4762,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build())
.build();
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
// The values are modified but the bitmask is not.
assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
@@ -4771,7 +4785,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
// Adds the rule using the app, to avoid having any user modified bits set.
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+ mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
// Sets Device Effects to null
@@ -4781,8 +4795,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Zen rule update coming from app, but since the rule isn't already
// user modified, it can be updated.
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_APP, "reason",
- SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ CUSTOM_PKG_UID);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
// When AZR's ZenDeviceEffects is null, the updated rule's device effects are kept.
assertThat(rule.getDeviceEffects()).isEqualTo(zde);
@@ -4797,8 +4812,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
// Adds the rule using the app, to avoid having any user modified bits set.
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
// Set zen policy to null
@@ -4808,8 +4822,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Zen rule update coming from app, but since the rule isn't already
// user modified, it can be updated.
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_APP, "reason",
- SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ CUSTOM_PKG_UID);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
// When AZR's ZenPolicy is null, we expect the updated rule's policy to be unchanged
// (equivalent to the provided policy, with additional fields filled in with defaults).
@@ -4829,8 +4844,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
// Adds the rule using the app, to avoid having any user modified bits set.
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
// Create a fully populated ZenPolicy.
ZenPolicy policy = new ZenPolicy.Builder()
@@ -4860,7 +4874,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Default config defined in getDefaultConfigParser() is used as the original rule.
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr,
ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
// New ZenPolicy differs from the default config
assertThat(rule.getZenPolicy()).isNotNull();
@@ -4890,8 +4905,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
// Adds the rule using the app, to avoid having any user modified bits set.
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
@@ -4903,7 +4919,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Applies the update to the rule.
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr,
ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
// New ZenDeviceEffects is used; all fields considered set, since previously were null.
assertThat(rule.getDeviceEffects()).isNotNull();
@@ -5286,7 +5302,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, update,
ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
- AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
assertThat(result).isNotNull();
assertThat(result.getOwner().getClassName()).isEqualTo("brand.new.cps");
}
@@ -5306,7 +5323,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, update,
ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
- AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID);
assertThat(result).isNotNull();
assertThat(result.getOwner().getClassName()).isEqualTo("old.third.party.cps");
}
@@ -5518,8 +5536,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
- .isEqualTo(1000);
+ assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID).getCreationTime()).isEqualTo(1000);
// User customizes it.
AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
@@ -5546,7 +5564,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// - ZenPolicy is the one that the user had set.
// - rule still has the user-modified fields.
AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
- newRuleId);
+ newRuleId, CUSTOM_PKG_UID);
assertThat(finalRule.getCreationTime()).isEqualTo(1000); // And not 3000.
assertThat(newRuleId).isEqualTo(ruleId);
assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
@@ -5575,8 +5593,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
- .isEqualTo(1000);
+ assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID).getCreationTime()).isEqualTo(1000);
// App deletes it.
mTestClock.advanceByMillis(1000);
@@ -5592,7 +5610,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Verify that the rule was recreated. This means id and creation time are new.
AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
- newRuleId);
+ newRuleId, CUSTOM_PKG_UID);
assertThat(finalRule.getCreationTime()).isEqualTo(3000);
assertThat(newRuleId).isNotEqualTo(ruleId);
}
@@ -5609,8 +5627,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
- .isEqualTo(1000);
+ assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID)
+ .getCreationTime()).isEqualTo(1000);
// User customizes it.
mTestClock.advanceByMillis(1000);
@@ -5637,7 +5655,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Verify that the rule was recreated. This means id and creation time are new, and the rule
// matches the latest data supplied to addAZR.
AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
- newRuleId);
+ newRuleId, CUSTOM_PKG_UID);
assertThat(finalRule.getCreationTime()).isEqualTo(4000);
assertThat(newRuleId).isNotEqualTo(ruleId);
assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
@@ -5660,8 +5678,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
- .isEqualTo(1000);
+ assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ CUSTOM_PKG_UID).getCreationTime()).isEqualTo(1000);
// User customizes it.
mTestClock.advanceByMillis(1000);
@@ -5686,7 +5704,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Verify that the rule was recreated. This means id and creation time are new.
AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
- newRuleId);
+ newRuleId, CUSTOM_PKG_UID);
assertThat(finalRule.getCreationTime()).isEqualTo(4000);
assertThat(newRuleId).isNotEqualTo(ruleId);
}
@@ -5728,7 +5746,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Verify that the rule was NOT restored:
assertThat(newRuleId).isNotEqualTo(ruleId);
AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
- newRuleId);
+ newRuleId, CUSTOM_PKG_UID);
assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
assertThat(finalRule.getOwner()).isEqualTo(new ComponentName("second", "owner"));
@@ -5869,7 +5887,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// The rule is restored...
assertThat(newRuleId).isEqualTo(ruleId);
AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
- newRuleId);
+ newRuleId, CUSTOM_PKG_UID);
assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
// ... but it is NOT active
@@ -5923,7 +5941,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// The rule is restored...
assertThat(newRuleId).isEqualTo(ruleId);
AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
- newRuleId);
+ newRuleId, CUSTOM_PKG_UID);
assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
// ... but it is NEITHER active NOR snoozed.
@@ -6005,22 +6023,22 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
// Null condition -> STATE_FALSE
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID))
.isEqualTo(Condition.STATE_FALSE);
mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, CONDITION_TRUE, ORIGIN_APP,
CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID))
.isEqualTo(Condition.STATE_TRUE);
mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, CONDITION_FALSE, ORIGIN_APP,
CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID))
.isEqualTo(Condition.STATE_FALSE);
mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id, ORIGIN_APP, "",
CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID))
.isEqualTo(Condition.STATE_UNKNOWN);
}
@@ -6036,8 +6054,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID);
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, "systemRule"))
- .isEqualTo(Condition.STATE_UNKNOWN);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, "systemRule",
+ CUSTOM_PKG_UID)).isEqualTo(Condition.STATE_UNKNOWN);
}
@Test
@@ -6063,7 +6081,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_MODES_API)
- public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() {
+ public void setAutomaticZenRuleStateFromConditionProvider_conditionForNotOwnedRule_ignored() {
// Assume existence of an other-package-owned rule that is currently ACTIVE.
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
@@ -6075,7 +6093,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
// Should be ignored.
- mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, otherRule.conditionId,
+ mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT,
+ otherRule.conditionId,
new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
ORIGIN_APP, CUSTOM_PKG_UID);
@@ -6182,7 +6201,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
// From user, update that rule's interruption filter.
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
.setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
.build();
@@ -6214,7 +6234,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
// From user, update something in that rule, but not the interruption filter.
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
.setName("Renamed")
.build();
@@ -6315,7 +6336,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
String ruleId = ZenModeConfig.implicitRuleId(mContext.getPackageName());
// User chooses a new name.
- AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
new AutomaticZenRule.Builder(azr).setName("User chose this").build(),
ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
@@ -6414,7 +6436,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
// From user, update that rule's policy.
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
ZenPolicy userUpdateZenPolicy = new ZenPolicy.Builder().disallowAllSounds()
.allowAlarms(true).build();
AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
@@ -6456,7 +6479,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
// From user, update something in that rule, but not the ZenPolicy.
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
.setName("Rule renamed, not touching policy")
.build();
@@ -6509,7 +6533,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
String ruleId = ZenModeConfig.implicitRuleId(mContext.getPackageName());
// User chooses a new name.
- AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+ AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID);
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
new AutomaticZenRule.Builder(azr).setName("User chose this").build(),
ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
@@ -6645,7 +6670,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new AutomaticZenRule.Builder("Rule", CONDITION_ID).setIconResId(resourceId).build(),
ORIGIN_APP, "reason", CUSTOM_PKG_UID);
AutomaticZenRule storedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
- ruleId);
+ ruleId, CUSTOM_PKG_UID);
assertThat(storedRule.getIconResId()).isEqualTo(0);
}
@@ -7087,8 +7112,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id))
- .isEqualTo(STATE_TRUE);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id,
+ CUSTOM_PKG_UID)).isEqualTo(STATE_TRUE);
assertThat(implicitRule.isActive()).isTrue();
assertThat(implicitRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
}
@@ -7108,8 +7133,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id))
- .isEqualTo(STATE_FALSE);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id,
+ CUSTOM_PKG_UID)).isEqualTo(STATE_FALSE);
assertThat(implicitRule.isActive()).isFalse();
assertThat(implicitRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
}
@@ -7177,7 +7202,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, SYSTEM_UID))
.isEqualTo(STATE_TRUE);
ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
@@ -7192,14 +7217,14 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
if (Flags.modesUi()) {
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
- .isEqualTo(STATE_TRUE);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID)).isEqualTo(STATE_TRUE);
zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
assertThat(zenRule.condition).isNull();
} else {
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
- .isEqualTo(STATE_FALSE);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
+ SYSTEM_UID)).isEqualTo(STATE_FALSE);
}
}
@@ -7218,7 +7243,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
new Condition(rule.getConditionId(), "snooze", STATE_FALSE, SOURCE_USER_ACTION),
ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+ assertThat(
+ mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID))
.isEqualTo(STATE_FALSE);
ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
@@ -7232,7 +7258,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
- assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+ assertThat(
+ mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID))
.isEqualTo(STATE_TRUE);
zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
index b8d554b405d1..98a4fb3c473f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
@@ -184,12 +184,12 @@ public class AppCompatResizeOverridesTest extends WindowTestsBase {
void checkShouldOverrideForceResizeApp(boolean expected) {
Assert.assertEquals(expected, activity().top().mAppCompatController
- .getAppCompatResizeOverrides().shouldOverrideForceResizeApp());
+ .getResizeOverrides().shouldOverrideForceResizeApp());
}
void checkShouldOverrideForceNonResizeApp(boolean expected) {
Assert.assertEquals(expected, activity().top().mAppCompatController
- .getAppCompatResizeOverrides().shouldOverrideForceNonResizeApp());
+ .getResizeOverrides().shouldOverrideForceNonResizeApp());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index b6e393d7be0c..03d904283e83 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -342,8 +342,8 @@ public class AppTransitionTests extends WindowTestsBase {
public void testCancelRemoteAnimationWhenFreeze() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
doReturn(false).when(dc).onDescendantOrientationChanged(any());
- final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
- dc, "exiting app");
+ final WindowState exitingAppWindow = newWindowBuilder("exiting app",
+ TYPE_BASE_APPLICATION).setDisplay(dc).build();
final ActivityRecord exitingActivity = exitingAppWindow.mActivityRecord;
// Wait until everything in animation handler get executed to prevent the exiting window
// from being removed during WindowSurfacePlacer Traversal.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
index 14276ae21899..7033d79d0ee1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -266,10 +266,10 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
prepareSecondaryDisplay();
- final WindowState defaultDisplayWindow = createWindow(/* parent= */ null,
- TYPE_BASE_APPLICATION, mDisplayContent, "DefaultDisplayWindow");
- final WindowState secondaryDisplayWindow = createWindow(/* parent= */ null,
- TYPE_BASE_APPLICATION, mSecondaryDisplayContent, "SecondaryDisplayWindow");
+ final WindowState defaultDisplayWindow = newWindowBuilder("DefaultDisplayWindow",
+ TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
+ final WindowState secondaryDisplayWindow = newWindowBuilder("SecondaryDisplayWindow",
+ TYPE_BASE_APPLICATION).setDisplay(mSecondaryDisplayContent).build();
makeWindowVisibleAndNotDrawn(defaultDisplayWindow, secondaryDisplayWindow);
// Mark as display switching only for the default display as we filter out
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 bd15bc42e811..347d1bc1becc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -379,13 +379,11 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay);
assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer);
- final WindowState firstActivityWin =
- createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
- "firstActivityWin");
+ final WindowState firstActivityWin = newWindowBuilder("firstActivityWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build();
spyOn(firstActivityWin);
- final WindowState secondActivityWin =
- createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity,
- "firstActivityWin");
+ final WindowState secondActivityWin = newWindowBuilder("secondActivityWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(mSecondActivity).build();
spyOn(secondActivityWin);
// firstActivityWin should be the target
@@ -424,13 +422,11 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
setupImeWindow();
final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer();
final WindowToken imeToken = tokenOfType(TYPE_INPUT_METHOD);
- final WindowState firstActivityWin =
- createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
- "firstActivityWin");
+ final WindowState firstActivityWin = newWindowBuilder("firstActivityWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build();
spyOn(firstActivityWin);
- final WindowState secondActivityWin =
- createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity,
- "secondActivityWin");
+ final WindowState secondActivityWin = newWindowBuilder("secondActivityWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(mSecondActivity).build();
spyOn(secondActivityWin);
// firstActivityWin should be the target
@@ -464,9 +460,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay);
assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer);
- final WindowState firstActivityWin =
- createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
- "firstActivityWin");
+ final WindowState firstActivityWin = newWindowBuilder("firstActivityWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build();
spyOn(firstActivityWin);
// firstActivityWin should be the target
doReturn(true).when(firstActivityWin).canBeImeTarget();
@@ -499,9 +494,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay);
// firstActivityWin should be the target
- final WindowState firstActivityWin =
- createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
- "firstActivityWin");
+ final WindowState firstActivityWin = newWindowBuilder("firstActivityWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build();
spyOn(firstActivityWin);
doReturn(true).when(firstActivityWin).canBeImeTarget();
WindowState imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */);
@@ -560,8 +554,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
}
private void setupImeWindow() {
- final WindowState imeWindow = createWindow(null /* parent */,
- TYPE_INPUT_METHOD, mDisplay, "mImeWindow");
+ final WindowState imeWindow = newWindowBuilder("mImeWindow", TYPE_INPUT_METHOD).setDisplay(
+ mDisplay).build();
imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
mDisplay.mInputMethodWindow = imeWindow;
}
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 dc4adcc4315b..299717393028 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -878,8 +878,10 @@ public class TaskFragmentTest extends WindowTestsBase {
.build();
final ActivityRecord activity0 = tf0.getTopMostActivity();
final ActivityRecord activity1 = tf1.getTopMostActivity();
- final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, activity0, "win0");
- final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity1, "win1");
+ final WindowState win0 = newWindowBuilder("win0", TYPE_BASE_APPLICATION).setWindowToken(
+ activity0).build();
+ final WindowState win1 = newWindowBuilder("win1", TYPE_BASE_APPLICATION).setWindowToken(
+ activity1).build();
doReturn(false).when(mDisplayContent).shouldImeAttachedToApp();
mDisplayContent.setImeInputTarget(win0);
@@ -1174,8 +1176,8 @@ public class TaskFragmentTest extends WindowTestsBase {
}
private WindowState createAppWindow(ActivityRecord app, String name) {
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name,
- 0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow());
+ final WindowState win = newWindowBuilder(name, TYPE_BASE_APPLICATION).setWindowToken(
+ app).setClientWindow(new TestIWindow()).build();
mWm.mWindowMap.put(win.mClient.asBinder(), win);
return win;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index f145b40d2292..f9250f9ecf5d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -63,7 +63,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
@Test
public void testAppRemoved() {
- final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+ final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
mCache.putSnapshot(window.getTask(), createSnapshot());
assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
mCache.onAppRemoved(window.mActivityRecord);
@@ -72,7 +72,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
@Test
public void testAppDied() {
- final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+ final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
mCache.putSnapshot(window.getTask(), createSnapshot());
assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
mCache.onAppDied(window.mActivityRecord);
@@ -81,7 +81,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
@Test
public void testTaskRemoved() {
- final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+ final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
mCache.putSnapshot(window.getTask(), createSnapshot());
assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
mCache.onIdRemoved(window.getTask().mTaskId);
@@ -90,7 +90,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
@Test
public void testReduced_notCached() {
- final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+ final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
@@ -105,7 +105,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
@Test
public void testRestoreFromDisk() {
- final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+ final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
@@ -117,7 +117,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
@Test
public void testClearCache() {
- final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+ final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
mCache.putSnapshot(window.getTask(), mSnapshot);
assertEquals(mSnapshot, mCache.getSnapshot(window.getTask().mTaskId,
false /* isLowResolution */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index c6b2a6b8d42f..1bca53aff034 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -74,8 +74,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
@Test
public void testGetClosingApps_closing() {
- final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
- "closingWindow");
+ final WindowState closingWindow = newWindowBuilder("closingWindow",
+ FIRST_APPLICATION_WINDOW).build();
closingWindow.mActivityRecord.commitVisibility(
false /* visible */, true /* performLayout */);
final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
@@ -88,8 +88,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
@Test
public void testGetClosingApps_notClosing() {
- final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
- "closingWindow");
+ final WindowState closingWindow = newWindowBuilder("closingWindow",
+ FIRST_APPLICATION_WINDOW).build();
final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
FIRST_APPLICATION_WINDOW, "openingWindow");
closingWindow.mActivityRecord.commitVisibility(
@@ -105,8 +105,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
@Test
public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
- final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
- "closingWindow");
+ final WindowState closingWindow = newWindowBuilder("closingWindow",
+ FIRST_APPLICATION_WINDOW).build();
closingWindow.mActivityRecord.commitVisibility(
false /* visible */, true /* performLayout */);
final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
@@ -133,19 +133,19 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
@Test
public void testGetSnapshotMode() {
- final WindowState disabledWindow = createWindow(null,
- FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow");
+ final WindowState disabledWindow = newWindowBuilder("disabledWindow",
+ FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build();
disabledWindow.mActivityRecord.setRecentsScreenshotEnabled(false);
assertEquals(SNAPSHOT_MODE_APP_THEME,
mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
- final WindowState normalWindow = createWindow(null,
- FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow");
+ final WindowState normalWindow = newWindowBuilder("normalWindow",
+ FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build();
assertEquals(SNAPSHOT_MODE_REAL,
mWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask()));
- final WindowState secureWindow = createWindow(null,
- FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow");
+ final WindowState secureWindow = newWindowBuilder("secureWindow",
+ FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build();
secureWindow.mAttrs.flags |= FLAG_SECURE;
assertEquals(SNAPSHOT_MODE_APP_THEME,
mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
@@ -297,8 +297,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
spyOn(mWm.mTaskSnapshotController);
doReturn(false).when(mWm.mTaskSnapshotController).shouldDisableSnapshots();
- final WindowState normalWindow = createWindow(null,
- FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow");
+ final WindowState normalWindow = newWindowBuilder("normalWindow",
+ FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build();
final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
.setTopActivityComponent(normalWindow.mActivityRecord.mActivityComponent).build();
doReturn(snapshot).when(mWm.mTaskSnapshotController).snapshot(any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
index 9bde0663d4a3..51ea498811fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -41,7 +41,7 @@ import java.io.File;
* Test class for {@link TaskSnapshotPersister} and {@link AppSnapshotLoader}
*
* Build/Install/Run:
- * atest TaskSnapshotPersisterLoaderTest
+ * atest TaskSnapshotLowResDisabledTest
*/
@MediumTest
@Presubmit
@@ -126,7 +126,7 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
@Test
public void testReduced_notCached() {
- final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+ final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 1fa657822189..5ed2df30518b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -504,8 +504,8 @@ public class WindowContainerTests extends WindowTestsBase {
assertTrue(child.isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION));
assertFalse(child.isAnimating(PARENTS, ANIMATION_TYPE_SCREEN_ROTATION));
- final WindowState windowState = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
- mDisplayContent, "TestWindowState");
+ final WindowState windowState = newWindowBuilder("TestWindowState",
+ TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
WindowContainer parent = windowState.getParent();
spyOn(windowState.mSurfaceAnimator);
doReturn(true).when(windowState.mSurfaceAnimator).isAnimating();
@@ -1045,8 +1045,8 @@ public class WindowContainerTests extends WindowTestsBase {
// An animating window with mRemoveOnExit can be removed by handleCompleteDeferredRemoval
// once it no longer animates.
- final WindowState exitingWindow = createWindow(null, TYPE_APPLICATION_OVERLAY,
- displayContent, "exiting window");
+ final WindowState exitingWindow = newWindowBuilder("exiting window",
+ TYPE_APPLICATION_OVERLAY).setDisplay(displayContent).build();
exitingWindow.startAnimation(exitingWindow.getPendingTransaction(),
mock(AnimationAdapter.class), false /* hidden */,
SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
@@ -1063,7 +1063,7 @@ public class WindowContainerTests extends WindowTestsBase {
final ActivityRecord r = new TaskBuilder(mSupervisor).setCreateActivity(true)
.setDisplay(displayContent).build().getTopMostActivity();
// Add a window and make the activity animating so the removal of activity is deferred.
- createWindow(null, TYPE_BASE_APPLICATION, r, "win");
+ newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(r).build();
doReturn(true).when(r).isAnimating(anyInt(), anyInt());
displayContent.remove();
@@ -1216,7 +1216,8 @@ public class WindowContainerTests extends WindowTestsBase {
public void testFreezeInsets() {
final Task task = createTask(mDisplayContent);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
+ activity).build();
// Set visibility to false, verify the main window of the task will be set the frozen
// insets state immediately.
@@ -1233,7 +1234,8 @@ public class WindowContainerTests extends WindowTestsBase {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
+ activity).build();
task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
spyOn(win);
doReturn(true).when(task).okToAnimate();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index 72935cb546d9..8606581539ff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -49,9 +49,10 @@ public class WindowContainerTraversalTests extends WindowTestsBase {
@SetupWindows(addWindows = { W_DOCK_DIVIDER, W_INPUT_METHOD })
@Test
public void testDockedDividerPosition() {
- final WindowState splitScreenWindow = createWindow(null,
- WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
- mDisplayContent, "splitScreenWindow");
+ final WindowState splitScreenWindow = newWindowBuilder("splitScreenWindow",
+ TYPE_BASE_APPLICATION).setWindowingMode(
+ WINDOWING_MODE_MULTI_WINDOW).setActivityType(ACTIVITY_TYPE_STANDARD).setDisplay(
+ mDisplayContent).build();
mDisplayContent.setImeLayeringTarget(splitScreenWindow);
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 50e0e181cd68..513ba1d49258 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -154,9 +154,11 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testIsParentWindowHidden() {
- final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
- final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
- final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
+ final WindowState parentWindow = newWindowBuilder("parentWindow", TYPE_APPLICATION).build();
+ final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+ parentWindow).build();
+ final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+ parentWindow).build();
// parentWindow is initially set to hidden.
assertTrue(parentWindow.mHidden);
@@ -172,10 +174,12 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testIsChildWindow() {
- final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
- final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
- final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
- final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow");
+ final WindowState parentWindow = newWindowBuilder("parentWindow", TYPE_APPLICATION).build();
+ final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+ parentWindow).build();
+ final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+ parentWindow).build();
+ final WindowState randomWindow = newWindowBuilder("randomWindow", TYPE_APPLICATION).build();
assertFalse(parentWindow.isChildWindow());
assertTrue(child1.isChildWindow());
@@ -185,12 +189,15 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testHasChild() {
- final WindowState win1 = createWindow(null, TYPE_APPLICATION, "win1");
- final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, "win11");
- final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, "win12");
- final WindowState win2 = createWindow(null, TYPE_APPLICATION, "win2");
- final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW, "win21");
- final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow");
+ final WindowState win1 = newWindowBuilder("win1", TYPE_APPLICATION).build();
+ final WindowState win11 = newWindowBuilder("win11", FIRST_SUB_WINDOW).setParent(
+ win1).build();
+ final WindowState win12 = newWindowBuilder("win12", FIRST_SUB_WINDOW).setParent(
+ win1).build();
+ final WindowState win2 = newWindowBuilder("win2", TYPE_APPLICATION).build();
+ final WindowState win21 = newWindowBuilder("win21", FIRST_SUB_WINDOW).setParent(
+ win2).build();
+ final WindowState randomWindow = newWindowBuilder("randomWindow", TYPE_APPLICATION).build();
assertTrue(win1.hasChild(win11));
assertTrue(win1.hasChild(win12));
@@ -206,9 +213,11 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testGetParentWindow() {
- final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
- final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
- final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
+ final WindowState parentWindow = newWindowBuilder("parentWindow", TYPE_APPLICATION).build();
+ final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+ parentWindow).build();
+ final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+ parentWindow).build();
assertNull(parentWindow.getParentWindow());
assertEquals(parentWindow, child1.getParentWindow());
@@ -217,8 +226,8 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testOverlayWindowHiddenWhenSuspended() {
- final WindowState overlayWindow = spy(createWindow(null, TYPE_APPLICATION_OVERLAY,
- "overlayWindow"));
+ final WindowState overlayWindow = spy(
+ newWindowBuilder("overlayWindow", TYPE_APPLICATION_OVERLAY).build());
overlayWindow.setHiddenWhileSuspended(true);
verify(overlayWindow).hide(true /* doAnimation */, true /* requestAnim */);
overlayWindow.setHiddenWhileSuspended(false);
@@ -227,9 +236,11 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testGetTopParentWindow() {
- final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
- final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
- final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
+ final WindowState root = newWindowBuilder("root", TYPE_APPLICATION).build();
+ final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+ root).build();
+ final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+ child1).build();
assertEquals(root, root.getTopParentWindow());
assertEquals(root, child1.getTopParentWindow());
@@ -244,7 +255,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testIsOnScreen_hiddenByPolicy() {
- final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+ final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).build();
window.setHasSurface(true);
assertTrue(window.isOnScreen());
window.hide(false /* doAnimation */, false /* requestAnim */);
@@ -273,8 +284,8 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCanBeImeTarget() {
- final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
- final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow");
+ final WindowState appWindow = newWindowBuilder("appWindow", TYPE_APPLICATION).build();
+ final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_INPUT_METHOD).build();
// Setting FLAG_NOT_FOCUSABLE prevents the window from being an IME target.
appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
@@ -328,16 +339,17 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testGetWindow() {
- final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
- final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild");
- final WindowState mediaOverlayChild = createWindow(root,
- TYPE_APPLICATION_MEDIA_OVERLAY, "mediaOverlayChild");
- final WindowState attachedDialogChild = createWindow(root,
- TYPE_APPLICATION_ATTACHED_DIALOG, "attachedDialogChild");
- final WindowState subPanelChild = createWindow(root,
- TYPE_APPLICATION_SUB_PANEL, "subPanelChild");
- final WindowState aboveSubPanelChild = createWindow(root,
- TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild");
+ final WindowState root = newWindowBuilder("root", TYPE_APPLICATION).build();
+ final WindowState mediaChild = newWindowBuilder("mediaChild",
+ TYPE_APPLICATION_MEDIA).setParent(root).build();
+ final WindowState mediaOverlayChild = newWindowBuilder("mediaOverlayChild",
+ TYPE_APPLICATION_MEDIA_OVERLAY).setParent(root).build();
+ final WindowState attachedDialogChild = newWindowBuilder("attachedDialogChild",
+ TYPE_APPLICATION_ATTACHED_DIALOG).setParent(root).build();
+ final WindowState subPanelChild = newWindowBuilder("subPanelChild",
+ TYPE_APPLICATION_SUB_PANEL).setParent(root).build();
+ final WindowState aboveSubPanelChild = newWindowBuilder("aboveSubPanelChild",
+ TYPE_APPLICATION_ABOVE_SUB_PANEL).setParent(root).build();
final LinkedList<WindowState> windows = new LinkedList<>();
@@ -358,7 +370,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testDestroySurface() {
- final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
win.mHasSurface = win.mAnimatingExit = true;
win.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class);
win.onExitAnimationDone();
@@ -384,8 +396,10 @@ public class WindowStateTests extends WindowTestsBase {
// Call prepareWindowToDisplayDuringRelayout for a window without FLAG_TURN_SCREEN_ON before
// calling setCurrentLaunchCanTurnScreenOn for windows with flag in the same activity.
final ActivityRecord activity = createActivityRecord(mDisplayContent);
- final WindowState first = createWindow(null, TYPE_APPLICATION, activity, "first");
- final WindowState second = createWindow(null, TYPE_APPLICATION, activity, "second");
+ final WindowState first = newWindowBuilder("first", TYPE_APPLICATION).setWindowToken(
+ activity).build();
+ final WindowState second = newWindowBuilder("second", TYPE_APPLICATION).setWindowToken(
+ activity).build();
testPrepareWindowToDisplayDuringRelayout(first, false /* expectedWakeupCalled */,
true /* expectedCurrentLaunchCanTurnScreenOn */);
@@ -423,10 +437,10 @@ public class WindowStateTests extends WindowTestsBase {
// Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an
// activity. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup
final WindowToken windowToken = createTestWindowToken(FIRST_SUB_WINDOW, mDisplayContent);
- final WindowState firstWindow = createWindow(null, TYPE_APPLICATION, windowToken,
- "firstWindow");
- final WindowState secondWindow = createWindow(null, TYPE_APPLICATION, windowToken,
- "secondWindow");
+ final WindowState firstWindow = newWindowBuilder("firstWindow",
+ TYPE_APPLICATION).setWindowToken(windowToken).build();
+ final WindowState secondWindow = newWindowBuilder("secondWindow",
+ TYPE_APPLICATION).setWindowToken(windowToken).build();
firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
@@ -459,7 +473,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCanAffectSystemUiFlags() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
app.mActivityRecord.setVisible(true);
assertTrue(app.canAffectSystemUiFlags());
app.mActivityRecord.setVisible(false);
@@ -471,7 +485,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCanAffectSystemUiFlags_starting() {
- final WindowState app = createWindow(null, TYPE_APPLICATION_STARTING, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION_STARTING).build();
app.mActivityRecord.setVisible(true);
app.mStartingData = new SnapshotStartingData(mWm, null, 0);
assertFalse(app.canAffectSystemUiFlags());
@@ -481,7 +495,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCanAffectSystemUiFlags_disallow() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
app.mActivityRecord.setVisible(true);
assertTrue(app.canAffectSystemUiFlags());
app.getTask().setCanAffectSystemUiFlags(false);
@@ -538,9 +552,11 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testIsSelfOrAncestorWindowAnimating() {
- final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
- final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
- final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
+ final WindowState root = newWindowBuilder("root", TYPE_APPLICATION).build();
+ final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+ root).build();
+ final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+ child1).build();
assertFalse(child2.isSelfOrAncestorWindowAnimatingExit());
child2.mAnimatingExit = true;
assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
@@ -551,7 +567,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testDeferredRemovalByAnimating() {
- final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+ final WindowState appWindow = newWindowBuilder("appWindow", TYPE_APPLICATION).build();
makeWindowVisible(appWindow);
spyOn(appWindow.mWinAnimator);
doReturn(true).when(appWindow.mWinAnimator).getShown();
@@ -571,8 +587,9 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testOnExitAnimationDone() {
- final WindowState parent = createWindow(null, TYPE_APPLICATION, "parent");
- final WindowState child = createWindow(parent, TYPE_APPLICATION_PANEL, "child");
+ final WindowState parent = newWindowBuilder("parent", TYPE_APPLICATION).build();
+ final WindowState child = newWindowBuilder("child", TYPE_APPLICATION_PANEL).setParent(
+ parent).build();
final SurfaceControl.Transaction t = parent.getPendingTransaction();
child.startAnimation(t, mock(AnimationAdapter.class), false /* hidden */,
SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
@@ -609,7 +626,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testLayoutSeqResetOnReparent() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
app.mLayoutSeq = 1;
mDisplayContent.mLayoutSeq = 1;
@@ -622,7 +639,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testDisplayIdUpdatedOnReparent() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
// fake a different display
app.mInputWindowHandle.setDisplayId(mDisplayContent.getDisplayId() + 1);
app.onDisplayChanged(mDisplayContent);
@@ -633,7 +650,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testApplyWithNextDraw() {
- final WindowState win = createWindow(null, TYPE_APPLICATION_OVERLAY, "app");
+ final WindowState win = newWindowBuilder("app", TYPE_APPLICATION_OVERLAY).build();
final SurfaceControl.Transaction[] handledT = { null };
// The normal case that the draw transaction is applied with finishing drawing.
win.applyWithNextDraw(t -> handledT[0] = t);
@@ -657,7 +674,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testSeamlesslyRotateWindow() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
final SurfaceControl.Transaction t = spy(StubTransaction.class);
makeWindowVisible(app);
@@ -707,7 +724,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testVisibilityChangeSwitchUser() {
- final WindowState window = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState window = newWindowBuilder("app", TYPE_APPLICATION).build();
window.mHasSurface = true;
spyOn(window);
doReturn(false).when(window).showForAllUsers();
@@ -729,8 +746,9 @@ public class WindowStateTests extends WindowTestsBase {
final CompatModePackages cmp = mWm.mAtmService.mCompatModePackages;
spyOn(cmp);
doReturn(overrideScale).when(cmp).getCompatScale(anyString(), anyInt());
- final WindowState w = createWindow(null, TYPE_APPLICATION_OVERLAY, "win");
- final WindowState child = createWindow(w, TYPE_APPLICATION_PANEL, "child");
+ final WindowState w = newWindowBuilder("win", TYPE_APPLICATION_OVERLAY).build();
+ final WindowState child = newWindowBuilder("child", TYPE_APPLICATION_PANEL).setParent(
+ w).build();
assertTrue(w.hasCompatScale());
assertTrue(child.hasCompatScale());
@@ -788,7 +806,8 @@ public class WindowStateTests extends WindowTestsBase {
// Child window without scale (e.g. different app) should apply inverse scale of parent.
doReturn(1f).when(cmp).getCompatScale(anyString(), anyInt());
- final WindowState child2 = createWindow(w, TYPE_APPLICATION_SUB_PANEL, "child2");
+ final WindowState child2 = newWindowBuilder("child2", TYPE_APPLICATION_SUB_PANEL).setParent(
+ w).build();
makeWindowVisible(w, child2);
clearInvocations(t);
child2.prepareSurfaces();
@@ -798,10 +817,10 @@ public class WindowStateTests extends WindowTestsBase {
@SetupWindows(addWindows = { W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE })
@Test
public void testRequestDrawIfNeeded() {
- final WindowState startingApp = createWindow(null /* parent */,
- TYPE_BASE_APPLICATION, "startingApp");
- final WindowState startingWindow = createWindow(null /* parent */,
- TYPE_APPLICATION_STARTING, startingApp.mToken, "starting");
+ final WindowState startingApp = newWindowBuilder("startingApp",
+ TYPE_BASE_APPLICATION).build();
+ final WindowState startingWindow = newWindowBuilder("starting",
+ TYPE_APPLICATION_STARTING).setWindowToken(startingApp.mToken).build();
startingApp.mActivityRecord.mStartingWindow = startingWindow;
final WindowState keyguardHostWindow = mNotificationShadeWindow;
final WindowState allDrawnApp = mAppWindow;
@@ -878,7 +897,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testRequestResizeForBlastSync() {
- final WindowState win = createWindow(null, TYPE_APPLICATION, "window");
+ final WindowState win = newWindowBuilder("window", TYPE_APPLICATION).build();
makeWindowVisible(win);
makeLastConfigReportedToClient(win, true /* visible */);
win.mLayoutSeq = win.getDisplayContent().mLayoutSeq;
@@ -926,8 +945,8 @@ public class WindowStateTests extends WindowTestsBase {
final Task task = createTask(mDisplayContent);
final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
- final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION, embeddedActivity,
- "App window");
+ final WindowState win = newWindowBuilder("App window", TYPE_APPLICATION).setWindowToken(
+ embeddedActivity).build();
doReturn(true).when(embeddedActivity).isVisible();
embeddedActivity.setVisibleRequested(true);
makeWindowVisible(win);
@@ -949,14 +968,14 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCantReceiveTouchWhenAppTokenHiddenRequested() {
- final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+ final WindowState win0 = newWindowBuilder("win0", TYPE_APPLICATION).build();
win0.mActivityRecord.setVisibleRequested(false);
assertFalse(win0.canReceiveTouchInput());
}
@Test
public void testCantReceiveTouchWhenNotFocusable() {
- final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+ final WindowState win0 = newWindowBuilder("win0", TYPE_APPLICATION).build();
final Task rootTask = win0.mActivityRecord.getRootTask();
spyOn(rootTask);
when(rootTask.shouldIgnoreInput()).thenReturn(true);
@@ -969,7 +988,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testUpdateInputWindowHandle() {
- final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
win.mAttrs.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
win.mAttrs.flags = FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH;
final InputWindowHandle handle = new InputWindowHandle(
@@ -1026,7 +1045,7 @@ public class WindowStateTests extends WindowTestsBase {
@DisableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX)
@Test
public void testTouchRegionUsesLetterboxBoundsIfTransformedBoundsAndLetterboxScrolling() {
- final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
// Transformed bounds used for size of touchable region if letterbox inner bounds are empty.
final Rect transformedBounds = new Rect(0, 0, 300, 500);
@@ -1051,7 +1070,7 @@ public class WindowStateTests extends WindowTestsBase {
@DisableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX)
@Test
public void testTouchRegionUsesLetterboxBoundsIfNullTransformedBoundsAndLetterboxScrolling() {
- final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
// Fragment bounds used for size of touchable region if letterbox inner bounds are empty
// and Transform bounds are null.
@@ -1083,7 +1102,7 @@ public class WindowStateTests extends WindowTestsBase {
@EnableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX)
@Test
public void testTouchRegionUsesTransformedBoundsIfLetterboxScrolling() {
- final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
// Transformed bounds used for size of touchable region if letterbox inner bounds are empty.
final Rect transformedBounds = new Rect(0, 0, 300, 500);
@@ -1109,7 +1128,7 @@ public class WindowStateTests extends WindowTestsBase {
public void testHasActiveVisibleWindow() {
final int uid = ActivityBuilder.DEFAULT_FAKE_UID;
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app", uid);
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).setOwnerId(uid).build();
app.mActivityRecord.setVisible(false);
app.mActivityRecord.setVisibility(false);
assertFalse(mAtm.hasActiveVisibleWindow(uid));
@@ -1120,15 +1139,17 @@ public class WindowStateTests extends WindowTestsBase {
// Make the activity invisible and add a visible toast. The uid should have no active
// visible window because toast can be misused by legacy app to bypass background check.
app.mActivityRecord.setVisibility(false);
- final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay", uid);
- final WindowState toast = createWindow(null, TYPE_TOAST, app.mToken, "toast", uid);
+ final WindowState overlay = newWindowBuilder("overlay",
+ TYPE_APPLICATION_OVERLAY).setOwnerId(uid).build();
+ final WindowState toast = newWindowBuilder("toast", TYPE_TOAST).setWindowToken(
+ app.mToken).setOwnerId(uid).build();
toast.onSurfaceShownChanged(true);
assertFalse(mAtm.hasActiveVisibleWindow(uid));
// Though starting window should belong to system. Make sure it is ignored to avoid being
// allow-list unexpectedly, see b/129563343.
- final WindowState starting =
- createWindow(null, TYPE_APPLICATION_STARTING, app.mToken, "starting", uid);
+ final WindowState starting = newWindowBuilder("starting",
+ TYPE_APPLICATION_STARTING).setWindowToken(app.mToken).setOwnerId(uid).build();
starting.onSurfaceShownChanged(true);
assertFalse(mAtm.hasActiveVisibleWindow(uid));
@@ -1145,8 +1166,8 @@ public class WindowStateTests extends WindowTestsBase {
@SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
@Test
public void testNeedsRelativeLayeringToIme_notAttached() {
- WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken,
- "SameTokenWindow");
+ WindowState sameTokenWindow = newWindowBuilder("SameTokenWindow",
+ TYPE_BASE_APPLICATION).setWindowToken(mAppWindow.mToken).build();
mDisplayContent.setImeLayeringTarget(mAppWindow);
makeWindowVisible(mImeWindow);
sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -1158,8 +1179,8 @@ public class WindowStateTests extends WindowTestsBase {
@SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
@Test
public void testNeedsRelativeLayeringToIme_startingWindow() {
- WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING,
- mAppWindow.mToken, "SameTokenWindow");
+ WindowState sameTokenWindow = newWindowBuilder("SameTokenWindow",
+ TYPE_APPLICATION_STARTING).setWindowToken(mAppWindow.mToken).build();
mDisplayContent.setImeLayeringTarget(mAppWindow);
makeWindowVisible(mImeWindow);
sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -1169,9 +1190,9 @@ public class WindowStateTests extends WindowTestsBase {
@UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD})
@Test
public void testNeedsRelativeLayeringToIme_systemDialog() {
- WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
- mDisplayContent,
- "SystemDialog", true);
+ WindowState systemDialogWindow = newWindowBuilder("SystemDialog",
+ TYPE_SECURE_SYSTEM_OVERLAY).setDisplay(
+ mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build();
mDisplayContent.setImeLayeringTarget(mAppWindow);
mAppWindow.getTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
makeWindowVisible(mImeWindow);
@@ -1182,20 +1203,21 @@ public class WindowStateTests extends WindowTestsBase {
@UseTestDisplay(addWindows = {W_INPUT_METHOD})
@Test
public void testNeedsRelativeLayeringToIme_notificationShadeShouldNotHideSystemDialog() {
- WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
- mDisplayContent,
- "SystemDialog", true);
+ WindowState systemDialogWindow = newWindowBuilder("SystemDialog",
+ TYPE_SECURE_SYSTEM_OVERLAY).setDisplay(
+ mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build();
mDisplayContent.setImeLayeringTarget(systemDialogWindow);
makeWindowVisible(mImeWindow);
- WindowState notificationShade = createWindow(null, TYPE_NOTIFICATION_SHADE,
- mDisplayContent, "NotificationShade", true);
+ WindowState notificationShade = newWindowBuilder("NotificationShade",
+ TYPE_NOTIFICATION_SHADE).setDisplay(
+ mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build();
notificationShade.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
assertFalse(notificationShade.needsRelativeLayeringToIme());
}
@Test
public void testSetFreezeInsetsState() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
spyOn(app);
doReturn(true).when(app).isVisible();
@@ -1216,7 +1238,7 @@ public class WindowStateTests extends WindowTestsBase {
verify(app).notifyInsetsChanged();
// Verify that invisible non-activity window won't dispatch insets changed.
- final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay");
+ final WindowState overlay = newWindowBuilder("overlay", TYPE_APPLICATION_OVERLAY).build();
makeWindowVisible(overlay);
assertTrue(overlay.isReadyToDispatchInsetsState());
overlay.mHasSurface = false;
@@ -1244,9 +1266,9 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testAdjustImeInsetsVisibilityWhenSwitchingApps() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
- final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
+ final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).build();
+ final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_APPLICATION).build();
spyOn(imeWindow);
doReturn(true).when(imeWindow).isVisible();
mDisplayContent.mInputMethodWindow = imeWindow;
@@ -1279,10 +1301,11 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testAdjustImeInsetsVisibilityWhenSwitchingApps_toAppInMultiWindowMode() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- final WindowState app2 = createWindow(null, WINDOWING_MODE_MULTI_WINDOW,
- ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app2");
- final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
+ final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).setWindowingMode(
+ WINDOWING_MODE_MULTI_WINDOW).setActivityType(ACTIVITY_TYPE_STANDARD).setDisplay(
+ mDisplayContent).build();
+ final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_APPLICATION).build();
spyOn(imeWindow);
doReturn(true).when(imeWindow).isVisible();
mDisplayContent.mInputMethodWindow = imeWindow;
@@ -1321,8 +1344,8 @@ public class WindowStateTests extends WindowTestsBase {
@SetupWindows(addWindows = W_ACTIVITY)
@Test
public void testUpdateImeControlTargetWhenLeavingMultiWindow() {
- WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
- mAppWindow.mToken, "app");
+ WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).setWindowToken(
+ mAppWindow.mToken).build();
mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
spyOn(app);
@@ -1349,8 +1372,8 @@ public class WindowStateTests extends WindowTestsBase {
@SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE })
@Test
public void testNotificationShadeHasImeInsetsWhenMultiWindow() {
- WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
- mAppWindow.mToken, "app");
+ WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).setWindowToken(
+ mAppWindow.mToken).build();
// Simulate entering multi-window mode and windowing mode is multi-window.
app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -1376,7 +1399,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testRequestedVisibility() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
app.mActivityRecord.setVisible(false);
app.mActivityRecord.setVisibility(false);
assertFalse(app.isVisibleRequested());
@@ -1391,7 +1414,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testKeepClearAreas() {
- final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+ final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).build();
makeWindowVisible(window);
final Rect keepClearArea1 = new Rect(0, 0, 10, 10);
@@ -1433,7 +1456,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testUnrestrictedKeepClearAreas() {
- final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+ final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).build();
makeWindowVisible(window);
final Rect keepClearArea1 = new Rect(0, 0, 10, 10);
@@ -1481,8 +1504,9 @@ public class WindowStateTests extends WindowTestsBase {
final InputMethodManagerInternal immi = InputMethodManagerInternal.get();
spyOn(immi);
- final WindowState imeTarget = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
- createActivityRecord(mDisplayContent), "imeTarget");
+ final WindowState imeTarget = newWindowBuilder("imeTarget",
+ TYPE_BASE_APPLICATION).setWindowToken(
+ createActivityRecord(mDisplayContent)).build();
imeTarget.mActivityRecord.setVisibleRequested(true);
makeWindowVisible(imeTarget);
@@ -1562,8 +1586,8 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testIsSecureLocked_flagSecureSet() {
- WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window",
- 1 /* ownerId */);
+ WindowState window = newWindowBuilder("test-window", TYPE_APPLICATION).setOwnerId(
+ 1).build();
window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE;
assertTrue(window.isSecureLocked());
@@ -1571,8 +1595,8 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testIsSecureLocked_flagSecureNotSet() {
- WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window",
- 1 /* ownerId */);
+ WindowState window = newWindowBuilder("test-window", TYPE_APPLICATION).setOwnerId(
+ 1).build();
assertFalse(window.isSecureLocked());
}
@@ -1581,8 +1605,8 @@ public class WindowStateTests extends WindowTestsBase {
public void testIsSecureLocked_disableSecureWindows() {
assumeTrue(Build.IS_DEBUGGABLE);
- WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window",
- 1 /* ownerId */);
+ WindowState window = newWindowBuilder("test-window", TYPE_APPLICATION).setOwnerId(
+ 1).build();
window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE;
ContentResolver cr = useFakeSettingsProvider();
@@ -1617,8 +1641,10 @@ public class WindowStateTests extends WindowTestsBase {
String testPackage = "test";
int ownerId1 = 20;
int ownerId2 = 21;
- final WindowState window1 = createWindow(null, TYPE_APPLICATION, "window1", ownerId1);
- final WindowState window2 = createWindow(null, TYPE_APPLICATION, "window2", ownerId2);
+ final WindowState window1 = newWindowBuilder("window1", TYPE_APPLICATION).setOwnerId(
+ ownerId1).build();
+ final WindowState window2 = newWindowBuilder("window2", TYPE_APPLICATION).setOwnerId(
+ ownerId2).build();
// Setting packagename for targeted feature
window1.mAttrs.packageName = testPackage;
@@ -1638,7 +1664,8 @@ public class WindowStateTests extends WindowTestsBase {
public void testIsSecureLocked_sensitiveContentBlockOrClearScreenCaptureForApp() {
String testPackage = "test";
int ownerId = 20;
- final WindowState window = createWindow(null, TYPE_APPLICATION, "window", ownerId);
+ final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).setOwnerId(
+ ownerId).build();
window.mAttrs.packageName = testPackage;
assertFalse(window.isSecureLocked());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index f226b9d29ca0..a02c3db1e636 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -74,11 +74,16 @@ public class WindowTokenTests extends WindowTestsBase {
assertEquals(0, token.getWindowsCount());
- final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
- final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
- final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3");
+ final WindowState window1 = newWindowBuilder("window1", TYPE_APPLICATION).setWindowToken(
+ token).build();
+ final WindowState window11 = newWindowBuilder("window11", FIRST_SUB_WINDOW).setParent(
+ window1).setWindowToken(token).build();
+ final WindowState window12 = newWindowBuilder("window12", FIRST_SUB_WINDOW).setParent(
+ window1).setWindowToken(token).build();
+ final WindowState window2 = newWindowBuilder("window2", TYPE_APPLICATION).setWindowToken(
+ token).build();
+ final WindowState window3 = newWindowBuilder("window3", TYPE_APPLICATION).setWindowToken(
+ token).build();
token.addWindow(window1);
// NOTE: Child windows will not be added to the token as window containers can only
@@ -105,8 +110,10 @@ public class WindowTokenTests extends WindowTestsBase {
public void testAddWindow_assignsLayers() {
final TestWindowToken token1 = createTestWindowToken(0, mDisplayContent);
final TestWindowToken token2 = createTestWindowToken(0, mDisplayContent);
- final WindowState window1 = createWindow(null, TYPE_STATUS_BAR, token1, "window1");
- final WindowState window2 = createWindow(null, TYPE_STATUS_BAR, token2, "window2");
+ final WindowState window1 = newWindowBuilder("window1", TYPE_STATUS_BAR).setWindowToken(
+ token1).build();
+ final WindowState window2 = newWindowBuilder("window2", TYPE_STATUS_BAR).setWindowToken(
+ token2).build();
token1.addWindow(window1);
token2.addWindow(window2);
@@ -122,8 +129,10 @@ public class WindowTokenTests extends WindowTestsBase {
assertEquals(token, dc.getWindowToken(token.token));
- final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
- final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
+ final WindowState window1 = newWindowBuilder("window1", TYPE_APPLICATION).setWindowToken(
+ token).build();
+ final WindowState window2 = newWindowBuilder("window2", TYPE_APPLICATION).setWindowToken(
+ token).build();
window2.removeImmediately();
// The token should still be mapped in the display content since it still has a child.
@@ -147,8 +156,10 @@ public class WindowTokenTests extends WindowTestsBase {
// Verify that the token is on the display
assertNotNull(mDisplayContent.getWindowToken(token.token));
- final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1");
- final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2");
+ final WindowState window1 = newWindowBuilder("window1", TYPE_TOAST).setWindowToken(
+ token).build();
+ final WindowState window2 = newWindowBuilder("window2", TYPE_TOAST).setWindowToken(
+ token).build();
mDisplayContent.removeWindowToken(token.token, true /* animateExit */);
// Verify that the token is no longer mapped on the display
@@ -231,7 +242,8 @@ public class WindowTokenTests extends WindowTestsBase {
assertNull(fromClientToken.mSurfaceControl);
- createWindow(null, TYPE_APPLICATION_OVERLAY, fromClientToken, "window");
+ newWindowBuilder("window", TYPE_APPLICATION_OVERLAY).setWindowToken(
+ fromClientToken).build();
assertNotNull(fromClientToken.mSurfaceControl);
final WindowToken nonClientToken = new WindowToken.Builder(mDisplayContent.mWmService,
@@ -285,7 +297,7 @@ public class WindowTokenTests extends WindowTestsBase {
// Simulate an app window to be the IME layering target, assume the app window has no
// frozen insets state by default.
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
mDisplayContent.setImeLayeringTarget(app);
assertNull(app.getFrozenInsetsState());
assertTrue(app.isImeLayeringTarget());
@@ -299,7 +311,8 @@ public class WindowTokenTests extends WindowTestsBase {
@Test
public void testRemoveWindowToken_noAnimateExitWhenSet() {
final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
- final WindowState win = createWindow(null, TYPE_APPLICATION, token, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).setWindowToken(
+ token).build();
makeWindowVisible(win);
assertTrue(win.isOnScreen());
spyOn(win);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 4f60106db93d..84e21181a7b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -221,7 +221,7 @@ public class ZOrderingTests extends WindowTestsBase {
}
WindowState createWindow(String name) {
- return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name);
+ return newWindowBuilder(name, TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
}
@Test
@@ -263,12 +263,12 @@ public class ZOrderingTests extends WindowTestsBase {
@Test
public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() {
final WindowState imeAppTarget = createWindow("imeAppTarget");
- final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
- "imeAppTargetChildAboveWindow");
- final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
- "imeAppTargetChildBelowWindow");
+ final WindowState imeAppTargetChildAboveWindow = newWindowBuilder(
+ "imeAppTargetChildAboveWindow", TYPE_APPLICATION_ATTACHED_DIALOG).setParent(
+ imeAppTarget).setWindowToken(imeAppTarget.mToken).build();
+ final WindowState imeAppTargetChildBelowWindow = newWindowBuilder(
+ "imeAppTargetChildBelowWindow", TYPE_APPLICATION_MEDIA_OVERLAY).setParent(
+ imeAppTarget).setWindowToken(imeAppTarget.mToken).build();
mDisplayContent.setImeLayeringTarget(imeAppTarget);
makeWindowVisible(mImeWindow);
@@ -313,9 +313,9 @@ public class ZOrderingTests extends WindowTestsBase {
@Test
public void testAssignWindowLayers_ForImeNonAppImeTarget() {
- final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
- mDisplayContent, "imeSystemOverlayTarget",
- true /* ownerCanAddInternalSystemWindow */);
+ final WindowState imeSystemOverlayTarget = newWindowBuilder("imeSystemOverlayTarget",
+ TYPE_SYSTEM_OVERLAY).setDisplay(mDisplayContent).setOwnerCanAddInternalSystemWindow(
+ true).build();
mDisplayContent.setImeLayeringTarget(imeSystemOverlayTarget);
mDisplayContent.assignChildLayers(mTransaction);
@@ -354,18 +354,19 @@ public class ZOrderingTests extends WindowTestsBase {
@Test
public void testStackLayers() {
final WindowState anyWindow1 = createWindow("anyWindow");
- final WindowState pinnedStackWindow = createWindow(null, WINDOWING_MODE_PINNED,
- ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
- "pinnedStackWindow");
- final WindowState dockedStackWindow = createWindow(null,
- WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
- mDisplayContent, "dockedStackWindow");
- final WindowState assistantStackWindow = createWindow(null,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
- mDisplayContent, "assistantStackWindow");
- final WindowState homeActivityWindow = createWindow(null, WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION,
- mDisplayContent, "homeActivityWindow");
+ final WindowState pinnedStackWindow = newWindowBuilder("pinnedStackWindow",
+ TYPE_BASE_APPLICATION).setWindowingMode(WINDOWING_MODE_PINNED).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setDisplay(mDisplayContent).build();
+ final WindowState dockedStackWindow = newWindowBuilder("dockedStackWindow",
+ TYPE_BASE_APPLICATION).setWindowingMode(
+ WINDOWING_MODE_MULTI_WINDOW).setActivityType(ACTIVITY_TYPE_STANDARD).setDisplay(
+ mDisplayContent).build();
+ final WindowState assistantStackWindow = newWindowBuilder("assistantStackWindow",
+ TYPE_BASE_APPLICATION).setWindowingMode(WINDOWING_MODE_FULLSCREEN).setActivityType(
+ ACTIVITY_TYPE_ASSISTANT).setDisplay(mDisplayContent).build();
+ final WindowState homeActivityWindow = newWindowBuilder("homeActivityWindow",
+ TYPE_BASE_APPLICATION).setWindowingMode(WINDOWING_MODE_FULLSCREEN).setActivityType(
+ ACTIVITY_TYPE_HOME).setDisplay(mDisplayContent).build();
final WindowState anyWindow2 = createWindow("anyWindow2");
mDisplayContent.assignChildLayers(mTransaction);
@@ -383,13 +384,12 @@ public class ZOrderingTests extends WindowTestsBase {
@Test
public void testAssignWindowLayers_ForSysUiPanels() {
- final WindowState navBarPanel =
- createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel");
- final WindowState statusBarPanel =
- createWindow(null, TYPE_STATUS_BAR_ADDITIONAL, mDisplayContent,
- "StatusBarAdditional");
- final WindowState statusBarSubPanel =
- createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, mDisplayContent, "StatusBarSubPanel");
+ final WindowState navBarPanel = newWindowBuilder("NavBarPanel",
+ TYPE_NAVIGATION_BAR_PANEL).setDisplay(mDisplayContent).build();
+ final WindowState statusBarPanel = newWindowBuilder("StatusBarAdditional",
+ TYPE_STATUS_BAR_ADDITIONAL).setDisplay(mDisplayContent).build();
+ final WindowState statusBarSubPanel = newWindowBuilder("StatusBarSubPanel",
+ TYPE_STATUS_BAR_SUB_PANEL).setDisplay(mDisplayContent).build();
mDisplayContent.assignChildLayers(mTransaction);
// Ime should be above all app windows and below system windows if it is targeting an app
@@ -401,15 +401,16 @@ public class ZOrderingTests extends WindowTestsBase {
@Test
public void testAssignWindowLayers_ForImeOnPopupImeLayeringTarget() {
- final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION,
- mAppWindow.mActivityRecord, "imeAppTarget");
+ final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
+ TYPE_APPLICATION).setWindowToken(mAppWindow.mActivityRecord).build();
mDisplayContent.setImeInputTarget(imeAppTarget);
mDisplayContent.setImeLayeringTarget(imeAppTarget);
mDisplayContent.setImeControlTarget(imeAppTarget);
// Set a popup IME layering target and keeps the original IME control target behinds it.
- final WindowState popupImeTargetWin = createWindow(imeAppTarget,
- TYPE_APPLICATION_SUB_PANEL, mAppWindow.mActivityRecord, "popupImeTargetWin");
+ final WindowState popupImeTargetWin = newWindowBuilder("popupImeTargetWin",
+ TYPE_APPLICATION_SUB_PANEL).setParent(imeAppTarget).setWindowToken(
+ mAppWindow.mActivityRecord).build();
mDisplayContent.setImeLayeringTarget(popupImeTargetWin);
mDisplayContent.updateImeParent();
@@ -424,11 +425,11 @@ public class ZOrderingTests extends WindowTestsBase {
// then we can drop all negative layering on the windowing side.
final WindowState anyWindow = createWindow("anyWindow");
- final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent,
- "TypeApplicationMediaChild");
- final WindowState mediaOverlayChild = createWindow(anyWindow,
- TYPE_APPLICATION_MEDIA_OVERLAY,
- mDisplayContent, "TypeApplicationMediaOverlayChild");
+ final WindowState child = newWindowBuilder("TypeApplicationMediaChild",
+ TYPE_APPLICATION_MEDIA).setParent(anyWindow).setDisplay(mDisplayContent).build();
+ final WindowState mediaOverlayChild = newWindowBuilder("TypeApplicationMediaOverlayChild",
+ TYPE_APPLICATION_MEDIA_OVERLAY).setParent(anyWindow).setDisplay(
+ mDisplayContent).build();
mDisplayContent.assignChildLayers(mTransaction);
@@ -440,14 +441,17 @@ public class ZOrderingTests extends WindowTestsBase {
public void testAssignWindowLayers_ForPostivelyZOrderedSubtype() {
final WindowState anyWindow = createWindow("anyWindow");
final ArrayList<WindowState> childList = new ArrayList<>();
- childList.add(createWindow(anyWindow, TYPE_APPLICATION_PANEL, mDisplayContent,
- "TypeApplicationPanelChild"));
- childList.add(createWindow(anyWindow, TYPE_APPLICATION_SUB_PANEL, mDisplayContent,
- "TypeApplicationSubPanelChild"));
- childList.add(createWindow(anyWindow, TYPE_APPLICATION_ATTACHED_DIALOG, mDisplayContent,
- "TypeApplicationAttachedDialogChild"));
- childList.add(createWindow(anyWindow, TYPE_APPLICATION_ABOVE_SUB_PANEL, mDisplayContent,
- "TypeApplicationAboveSubPanelPanelChild"));
+ childList.add(newWindowBuilder("TypeApplicationPanelChild",
+ TYPE_APPLICATION_PANEL).setParent(anyWindow).setDisplay(mDisplayContent).build());
+ childList.add(newWindowBuilder("TypeApplicationSubPanelChild",
+ TYPE_APPLICATION_SUB_PANEL).setParent(anyWindow).setDisplay(
+ mDisplayContent).build());
+ childList.add(newWindowBuilder("TypeApplicationAttachedDialogChild",
+ TYPE_APPLICATION_ATTACHED_DIALOG).setParent(anyWindow).setDisplay(
+ mDisplayContent).build());
+ childList.add(newWindowBuilder("TypeApplicationAboveSubPanelPanelChild",
+ TYPE_APPLICATION_ABOVE_SUB_PANEL).setParent(anyWindow).setDisplay(
+ mDisplayContent).build());
final LayerRecordingTransaction t = mTransaction;
mDisplayContent.assignChildLayers(t);
@@ -469,8 +473,8 @@ public class ZOrderingTests extends WindowTestsBase {
// Create a popupWindow
assertWindowHigher(mImeWindow, mAppWindow);
- final WindowState popupWindow = createWindow(mAppWindow, TYPE_APPLICATION_PANEL,
- mDisplayContent, "PopupWindow");
+ final WindowState popupWindow = newWindowBuilder("PopupWindow",
+ TYPE_APPLICATION_PANEL).setParent(mAppWindow).setDisplay(mDisplayContent).build();
spyOn(popupWindow);
mDisplayContent.assignChildLayers(mTransaction);
@@ -492,8 +496,9 @@ public class ZOrderingTests extends WindowTestsBase {
makeWindowVisible(mImeWindow);
// Create a popupWindow
- final WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
- mDisplayContent, "SystemDialog", true);
+ final WindowState systemDialogWindow = newWindowBuilder("SystemDialog",
+ TYPE_SECURE_SYSTEM_OVERLAY).setDisplay(
+ mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build();
systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
spyOn(systemDialogWindow);
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
index d49214ab718b..a9ae5f7dfc3f 100644
--- a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
+++ b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
@@ -16,6 +16,10 @@
package com.android.server.texttospeech;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_SCHEDULE_LIKE_TOP_APP;
+
import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
import android.annotation.NonNull;
@@ -95,7 +99,7 @@ final class TextToSpeechManagerPerUserService extends
ITextToSpeechSessionCallback callback) {
super(context,
new Intent(TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE).setPackage(engine),
- Context.BIND_AUTO_CREATE | Context.BIND_SCHEDULE_LIKE_TOP_APP,
+ BIND_AUTO_CREATE | BIND_SCHEDULE_LIKE_TOP_APP | BIND_FOREGROUND_SERVICE,
userId,
ITextToSpeechService.Stub::asInterface);
mEngine = engine;
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index b5dfb631609c..e18fad3eda79 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -78,6 +78,9 @@ public interface SatelliteTransmissionUpdateCallback {
/**
* Called when framework receives a request to send a datagram.
*
+ * Informs external apps that device is working on sending a datagram out and is in the process
+ * of checking if all the conditions required to send datagrams are met.
+ *
* @param datagramType The type of the requested datagram.
*/
@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 71f303311047..cadb0bdb41e1 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -72,6 +72,7 @@ android_ravenwood_test {
"tests/**/*.java",
],
auto_gen_config: true,
+ team: "trendy_team_ravenwood",
}
// Make the current.txt available for use by the cts/tests/signature and /vendor tests.
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
index df92898d76b1..9640a84eb9ca 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
@@ -29,6 +29,7 @@ public class JankUtils {
AppJankStats jankStats = new AppJankStats(
/*App Uid*/APP_ID,
/*Widget Id*/"test widget id",
+ /*navigationComponent*/null,
/*Widget Category*/AppJankStats.WIDGET_CATEGORY_SCROLL,
/*Widget State*/AppJankStats.WIDGET_STATE_SCROLLING,
/*Total Frames*/100,
diff --git a/tests/InputScreenshotTest/robotests/Android.bp b/tests/InputScreenshotTest/robotests/Android.bp
index b2414a85c095..63a13849ee7f 100644
--- a/tests/InputScreenshotTest/robotests/Android.bp
+++ b/tests/InputScreenshotTest/robotests/Android.bp
@@ -66,7 +66,6 @@ android_robolectric_test {
"android.test.mock.stubs.system",
"truth",
],
- upstream: true,
java_resource_dirs: ["config"],
instrumentation_for: "InputRoboApp",
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index 9f35c7b7fa33..e294da101fb7 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -65,6 +65,7 @@ android_ravenwood_test {
"src/com/android/internal/util/ParcellingTests.java",
],
auto_gen_config: true,
+ team: "trendy_team_ravenwood",
}
java_test_helper_library {
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
index 449d93dd8c0b..20315561cceb 100644
--- a/tools/aapt2/cmd/Command.cpp
+++ b/tools/aapt2/cmd/Command.cpp
@@ -53,61 +53,67 @@ std::string GetSafePath(StringPiece arg) {
void Command::AddRequiredFlag(StringPiece name, StringPiece description, std::string* value,
uint32_t flags) {
- auto func = [value, flags](StringPiece arg) -> bool {
+ auto func = [value, flags](StringPiece arg, std::ostream*) -> bool {
*value = (flags & Command::kPath) ? GetSafePath(arg) : std::string(arg);
return true;
};
- flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func));
+ flags_.emplace_back(
+ Flag(name, description, /* required */ true, /* num_args */ 1, std::move(func)));
}
void Command::AddRequiredFlagList(StringPiece name, StringPiece description,
std::vector<std::string>* value, uint32_t flags) {
- auto func = [value, flags](StringPiece arg) -> bool {
+ auto func = [value, flags](StringPiece arg, std::ostream*) -> bool {
value->push_back((flags & Command::kPath) ? GetSafePath(arg) : std::string(arg));
return true;
};
- flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func));
+ flags_.emplace_back(
+ Flag(name, description, /* required */ true, /* num_args */ 1, std::move(func)));
}
void Command::AddOptionalFlag(StringPiece name, StringPiece description,
std::optional<std::string>* value, uint32_t flags) {
- auto func = [value, flags](StringPiece arg) -> bool {
+ auto func = [value, flags](StringPiece arg, std::ostream*) -> bool {
*value = (flags & Command::kPath) ? GetSafePath(arg) : std::string(arg);
return true;
};
- flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
+ flags_.emplace_back(
+ Flag(name, description, /* required */ false, /* num_args */ 1, std::move(func)));
}
void Command::AddOptionalFlagList(StringPiece name, StringPiece description,
std::vector<std::string>* value, uint32_t flags) {
- auto func = [value, flags](StringPiece arg) -> bool {
+ auto func = [value, flags](StringPiece arg, std::ostream*) -> bool {
value->push_back((flags & Command::kPath) ? GetSafePath(arg) : std::string(arg));
return true;
};
- flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
+ flags_.emplace_back(
+ Flag(name, description, /* required */ false, /* num_args */ 1, std::move(func)));
}
void Command::AddOptionalFlagList(StringPiece name, StringPiece description,
std::unordered_set<std::string>* value) {
- auto func = [value](StringPiece arg) -> bool {
+ auto func = [value](StringPiece arg, std::ostream* out_error) -> bool {
value->emplace(arg);
return true;
};
- flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
+ flags_.emplace_back(
+ Flag(name, description, /* required */ false, /* num_args */ 1, std::move(func)));
}
void Command::AddOptionalSwitch(StringPiece name, StringPiece description, bool* value) {
- auto func = [value](StringPiece arg) -> bool {
+ auto func = [value](StringPiece arg, std::ostream* out_error) -> bool {
*value = true;
return true;
};
- flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 0, func));
+ flags_.emplace_back(
+ Flag(name, description, /* required */ false, /* num_args */ 0, std::move(func)));
}
void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand, bool experimental) {
@@ -172,19 +178,74 @@ void Command::Usage(std::ostream* out) {
argline = " ";
}
}
- *out << " " << std::setw(kWidth) << std::left << "-h"
- << "Displays this help menu\n";
out->flush();
}
-int Command::Execute(const std::vector<StringPiece>& args, std::ostream* out_error) {
+const std::string& Command::addEnvironmentArg(const Flag& flag, const char* env) {
+ if (*env && flag.num_args > 0) {
+ return environment_args_.emplace_back(flag.name + '=' + env);
+ }
+ return flag.name;
+}
+
+//
+// Looks for the flags specified in the environment and adds them to |args|.
+// Expected format:
+// - _AAPT2_UPPERCASE_NAME are added before all of the command line flags, so it's
+// a default for the flag that may get overridden by the command line.
+// - AAPT2_UPPERCASE_NAME_ are added after them, making this to be the final value
+// even if there was something on the command line.
+// - All dashes in the flag name get replaced with underscores, the rest of it is
+// intact.
+//
+// E.g.
+// --set-some-flag becomes either _AAPT2_SET_SOME_FLAG or AAPT2_SET_SOME_FLAG_
+// --set-param=2 is _AAPT2_SET_SOME_FLAG=2
+//
+// Values get passed as it, with no processing or quoting.
+//
+// This way one can make sure aapt2 has the flags they need even when it is
+// launched in a way they can't control, e.g. deep inside a build.
+//
+void Command::parseFlagsFromEnvironment(std::vector<StringPiece>& args) {
+ // If the first argument is a subcommand then skip it and prepend the flags past that (the root
+ // command should only have a single '-h' flag anyway).
+ const int insert_pos = args.empty() ? 0 : args.front().starts_with('-') ? 0 : 1;
+
+ std::string env_name;
+ for (const Flag& flag : flags_) {
+ // First, the prefix version.
+ env_name.assign("_AAPT2_");
+ // Append the uppercased flag name, skipping all dashes in front and replacing them with
+ // underscores later.
+ auto name_start = flag.name.begin();
+ while (name_start != flag.name.end() && *name_start == '-') {
+ ++name_start;
+ }
+ std::transform(name_start, flag.name.end(), std::back_inserter(env_name),
+ [](char c) { return c == '-' ? '_' : toupper(c); });
+ if (auto prefix_env = getenv(env_name.c_str())) {
+ args.insert(args.begin() + insert_pos, addEnvironmentArg(flag, prefix_env));
+ }
+ // Now reuse the same name variable to construct a suffix version: append the
+ // underscore and just skip the one in front.
+ env_name += '_';
+ if (auto suffix_env = getenv(env_name.c_str() + 1)) {
+ args.push_back(addEnvironmentArg(flag, suffix_env));
+ }
+ }
+}
+
+int Command::Execute(std::vector<StringPiece>& args, std::ostream* out_error) {
TRACE_NAME_ARGS("Command::Execute", args);
std::vector<std::string> file_args;
+ parseFlagsFromEnvironment(args);
+
for (size_t i = 0; i < args.size(); i++) {
StringPiece arg = args[i];
if (*(arg.data()) != '-') {
- // Continue parsing as the subcommand if the first argument matches one of the subcommands
+ // Continue parsing as a subcommand if the first argument matches one of the subcommands
if (i == 0) {
for (auto& subcommand : subcommands_) {
if (arg == subcommand->name_ || (!subcommand->short_name_.empty()
@@ -211,37 +272,67 @@ int Command::Execute(const std::vector<StringPiece>& args, std::ostream* out_err
return 1;
}
+ static constexpr auto matchShortArg = [](std::string_view arg, const Flag& flag) static {
+ return flag.name.starts_with("--") &&
+ arg.compare(0, 2, std::string_view(flag.name.c_str() + 1, 2)) == 0;
+ };
+
bool match = false;
for (Flag& flag : flags_) {
- // Allow both "--arg value" and "--arg=value" syntax.
+ // Allow both "--arg value" and "--arg=value" syntax, and look for the cases where we can
+ // safely deduce the "--arg" flag from the short "-a" version when there's no value expected
+ bool matched_current = false;
if (arg.starts_with(flag.name) &&
(arg.size() == flag.name.size() || (flag.num_args > 0 && arg[flag.name.size()] == '='))) {
- if (flag.num_args > 0) {
- if (arg.size() == flag.name.size()) {
- i++;
- if (i >= args.size()) {
- *out_error << flag.name << " missing argument.\n\n";
- Usage(out_error);
- return 1;
- }
- arg = args[i];
- } else {
- arg.remove_prefix(flag.name.size() + 1);
- // Disallow empty arguments after '='.
- if (arg.empty()) {
- *out_error << flag.name << " has empty argument.\n\n";
- Usage(out_error);
- return 1;
- }
+ matched_current = true;
+ } else if (flag.num_args == 0 && matchShortArg(arg, flag)) {
+ matched_current = true;
+ // It matches, now need to make sure no other flag would match as well.
+ // This is really inefficient, but we don't expect to have enough flags for it to matter
+ // (famous last words).
+ for (const Flag& other_flag : flags_) {
+ if (&other_flag == &flag) {
+ continue;
+ }
+ if (matchShortArg(arg, other_flag)) {
+ matched_current = false; // ambiguous, skip this match
+ break;
+ }
+ }
+ }
+ if (!matched_current) {
+ continue;
+ }
+
+ if (flag.num_args > 0) {
+ if (arg.size() == flag.name.size()) {
+ i++;
+ if (i >= args.size()) {
+ *out_error << flag.name << " missing argument.\n\n";
+ Usage(out_error);
+ return 1;
}
- flag.action(arg);
+ arg = args[i];
} else {
- flag.action({});
+ arg.remove_prefix(flag.name.size() + 1);
+ // Disallow empty arguments after '='.
+ if (arg.empty()) {
+ *out_error << flag.name << " has empty argument.\n\n";
+ Usage(out_error);
+ return 1;
+ }
+ }
+ if (!flag.action(arg, out_error)) {
+ return 1;
+ }
+ } else {
+ if (!flag.action({}, out_error)) {
+ return 1;
}
- flag.found = true;
- match = true;
- break;
}
+ flag.found = true;
+ match = true;
+ break;
}
if (!match) {
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
index 1416e980ed19..767ca9b0de9f 100644
--- a/tools/aapt2/cmd/Command.h
+++ b/tools/aapt2/cmd/Command.h
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#ifndef AAPT_COMMAND_H
-#define AAPT_COMMAND_H
+#pragma once
+#include <deque>
#include <functional>
+#include <memory>
#include <optional>
#include <ostream>
#include <string>
@@ -30,10 +31,17 @@ namespace aapt {
class Command {
public:
- explicit Command(android::StringPiece name) : name_(name), full_subcommand_name_(name){};
+ explicit Command(android::StringPiece name) : Command(name, {}) {
+ }
explicit Command(android::StringPiece name, android::StringPiece short_name)
- : name_(name), short_name_(short_name), full_subcommand_name_(name){};
+ : name_(name), short_name_(short_name), full_subcommand_name_(name) {
+ flags_.emplace_back("--help", "Displays this help menu", false, 0,
+ [this](android::StringPiece arg, std::ostream* out) {
+ Usage(out);
+ return false;
+ });
+ }
Command(Command&&) = default;
Command& operator=(Command&&) = default;
@@ -76,41 +84,51 @@ class Command {
// Parses the command line arguments, sets the flag variable values, and runs the action of
// the command. If the arguments fail to parse to the command and its subcommands, then the action
// will not be run and the usage will be printed instead.
- int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError);
+ int Execute(std::vector<android::StringPiece>& args, std::ostream* out_error);
+
+ // Same, but for a temporary vector of args.
+ int Execute(std::vector<android::StringPiece>&& args, std::ostream* out_error) {
+ return Execute(args, out_error);
+ }
// The action to preform when the command is executed.
virtual int Action(const std::vector<std::string>& args) = 0;
private:
struct Flag {
- explicit Flag(android::StringPiece name, android::StringPiece description,
- const bool is_required, const size_t num_args,
- std::function<bool(android::StringPiece value)>&& action)
+ explicit Flag(android::StringPiece name, android::StringPiece description, bool is_required,
+ const size_t num_args,
+ std::function<bool(android::StringPiece value, std::ostream* out_err)>&& action)
: name(name),
description(description),
- is_required(is_required),
+ action(std::move(action)),
num_args(num_args),
- action(std::move(action)) {
+ is_required(is_required) {
}
- const std::string name;
- const std::string description;
- const bool is_required;
- const size_t num_args;
- const std::function<bool(android::StringPiece value)> action;
+ std::string name;
+ std::string description;
+ std::function<bool(android::StringPiece value, std::ostream* out_error)> action;
+ size_t num_args;
+ bool is_required;
bool found = false;
};
+ const std::string& addEnvironmentArg(const Flag& flag, const char* env);
+ void parseFlagsFromEnvironment(std::vector<android::StringPiece>& args);
+
std::string name_;
std::string short_name_;
- std::string description_ = "";
+ std::string description_;
std::string full_subcommand_name_;
std::vector<Flag> flags_;
std::vector<std::unique_ptr<Command>> subcommands_;
std::vector<std::unique_ptr<Command>> experimental_subcommands_;
+ // A collection of arguments loaded from environment variables, with stable positions
+ // in memory - we add them to the vector of string views so the pointers may not change,
+ // with or without short string buffer utilization in std::string.
+ std::deque<std::string> environment_args_;
};
} // namespace aapt
-
-#endif // AAPT_COMMAND_H
diff --git a/tools/aapt2/cmd/Command_test.cpp b/tools/aapt2/cmd/Command_test.cpp
index 20d87e0025c3..2a3cb2a0c65d 100644
--- a/tools/aapt2/cmd/Command_test.cpp
+++ b/tools/aapt2/cmd/Command_test.cpp
@@ -118,4 +118,45 @@ TEST(CommandTest, OptionsWithValues) {
EXPECT_NE(0, command.Execute({"--flag1"s, "2"s}, &std::cerr));
}
+TEST(CommandTest, ShortOptions) {
+ TestCommand command;
+ bool flag = false;
+ command.AddOptionalSwitch("--flag", "", &flag);
+
+ ASSERT_EQ(0, command.Execute({"--flag"s}, &std::cerr));
+ EXPECT_TRUE(flag);
+
+ // Short version of a switch should work.
+ flag = false;
+ ASSERT_EQ(0, command.Execute({"-f"s}, &std::cerr));
+ EXPECT_TRUE(flag);
+
+ // Ambiguous names shouldn't parse via short options.
+ command.AddOptionalSwitch("--flag-2", "", &flag);
+ ASSERT_NE(0, command.Execute({"-f"s}, &std::cerr));
+
+ // But when we have a proper flag like that it should still work.
+ flag = false;
+ command.AddOptionalSwitch("-f", "", &flag);
+ ASSERT_EQ(0, command.Execute({"-f"s}, &std::cerr));
+ EXPECT_TRUE(flag);
+
+ // A regular short flag works fine as well.
+ flag = false;
+ command.AddOptionalSwitch("-d", "", &flag);
+ ASSERT_EQ(0, command.Execute({"-d"s}, &std::cerr));
+ EXPECT_TRUE(flag);
+
+ // A flag with a value only works via its long name syntax.
+ std::optional<std::string> val;
+ command.AddOptionalFlag("--with-val", "", &val);
+ ASSERT_EQ(0, command.Execute({"--with-val"s, "1"s}, &std::cerr));
+ EXPECT_TRUE(val);
+ EXPECT_STREQ("1", val->c_str());
+
+ // Make sure the flags that require a value can't be parsed via short syntax, -w=blah
+ // looks weird.
+ ASSERT_NE(0, command.Execute({"-w"s, "2"s}, &std::cerr));
+}
+
} // namespace aapt \ No newline at end of file