summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp8
-rw-r--r--apex/jobscheduler/framework/aconfig/job.aconfig8
-rw-r--r--apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java10
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl1
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobScheduler.java28
-rw-r--r--apex/jobscheduler/service/aconfig/device_idle.aconfig10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java32
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java204
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java110
-rw-r--r--api/Android.bp1
-rw-r--r--config/preloaded-classes-denylist11
-rw-r--r--core/api/current.txt21
-rw-r--r--core/api/system-current.txt9
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/app/ActivityManager.java13
-rw-r--r--core/java/android/app/ActivityTaskManager.java5
-rw-r--r--core/java/android/app/ActivityThread.java57
-rw-r--r--core/java/android/app/AppCompatCallbacks.java1
-rw-r--r--core/java/android/app/AppCompatTaskInfo.java7
-rw-r--r--core/java/android/app/AppOpsManager.java176
-rw-r--r--core/java/android/app/ForegroundServiceTypePolicy.java1
-rw-r--r--core/java/android/app/INotificationManager.aidl3
-rw-r--r--core/java/android/app/NotificationManager.java14
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java77
-rw-r--r--core/java/android/app/TaskInfo.java10
-rw-r--r--core/java/android/app/admin/DevicePolicyIdentifiers.java6
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig10
-rw-r--r--core/java/android/app/compat/ChangeIdStateCache.java13
-rw-r--r--core/java/android/app/compat/ChangeIdStateQuery.java1
-rw-r--r--core/java/android/app/compat/CompatChanges.java1
-rw-r--r--core/java/android/app/compat/PackageOverride.java1
-rw-r--r--core/java/android/app/wallpaper/WallpaperDescription.java45
-rw-r--r--core/java/android/content/pm/IOnAppsChangedListener.aidl2
-rw-r--r--core/java/android/content/pm/LauncherApps.java46
-rw-r--r--core/java/android/content/pm/LauncherUserInfo.java47
-rw-r--r--core/java/android/content/pm/ServiceInfo.java2
-rw-r--r--core/java/android/content/pm/flags.aconfig9
-rw-r--r--core/java/android/content/pm/multiuser.aconfig7
-rw-r--r--core/java/android/content/res/flags.aconfig12
-rw-r--r--core/java/android/hardware/DataSpace.java35
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java116
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java40
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java143
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java7
-rw-r--r--core/java/android/hardware/display/IVirtualDisplayCallback.aidl5
-rw-r--r--core/java/android/hardware/display/VirtualDisplay.java22
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java63
-rw-r--r--core/java/android/hardware/input/AidlInputGestureData.aidl21
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl8
-rw-r--r--core/java/android/hardware/input/InputGestureData.java102
-rw-r--r--core/java/android/hardware/input/InputManager.java14
-rw-r--r--core/java/android/hardware/input/InputSettings.java17
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig9
-rw-r--r--core/java/android/hardware/location/OWNERS2
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java4
-rw-r--r--core/java/android/os/AggregateBatteryConsumer.java14
-rw-r--r--core/java/android/os/BatteryConsumer.java30
-rw-r--r--core/java/android/os/BatteryUsageStats.java2
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java57
-rw-r--r--core/java/android/os/PowerComponents.java12
-rw-r--r--core/java/android/os/UidBatteryConsumer.java17
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl1
-rw-r--r--core/java/android/permission/flags.aconfig53
-rw-r--r--core/java/android/print/OWNERS1
-rw-r--r--core/java/android/printservice/OWNERS1
-rw-r--r--core/java/android/security/responsible_apis_flags.aconfig1
-rw-r--r--core/java/android/service/notification/flags.aconfig9
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java6
-rw-r--r--core/java/android/text/flags/flags.aconfig7
-rw-r--r--core/java/android/util/Log.java6
-rw-r--r--core/java/android/view/ImeBackAnimationController.java11
-rw-r--r--core/java/android/view/InsetsController.java13
-rw-r--r--core/java/android/view/RoundScrollbarRenderer.java4
-rw-r--r--core/java/android/view/SurfaceControl.java3
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java64
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java8
-rw-r--r--core/java/android/view/inputmethod/InputMethodSubtype.java125
-rw-r--r--core/java/android/widget/TextView.java38
-rw-r--r--core/java/android/window/ImeOnBackInvokedDispatcher.java28
-rw-r--r--core/java/android/window/TransitionFilter.java13
-rw-r--r--core/java/android/window/TransitionInfo.java9
-rw-r--r--core/java/com/android/internal/compat/AndroidBuildClassifier.java1
-rw-r--r--core/java/com/android/internal/compat/ChangeReporter.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeConfig.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeInfo.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverrideConfig.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java1
-rw-r--r--core/java/com/android/internal/compat/OverrideAllowedState.java1
-rw-r--r--core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java22
-rw-r--r--core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java22
-rw-r--r--core/jni/android_view_SurfaceControl.cpp6
-rw-r--r--core/res/Android.bp1
-rw-r--r--core/res/AndroidManifest.xml10
-rw-r--r--core/res/res/drawable/ic_zen_mode_icon_star_badge.xml (renamed from core/res/res/drawable/ic_zen_mode_type_unknown.xml)0
-rw-r--r--core/res/res/layout/input_method_switch_item_new.xml23
-rw-r--r--core/res/res/values/attrs.xml4
-rw-r--r--core/res/res/values/public-staging.xml2
-rw-r--r--core/res/res/values/strings.xml5
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java35
-rw-r--r--core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java6
-rw-r--r--core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java22
-rw-r--r--graphics/java/android/graphics/ImageFormat.java18
-rw-r--r--graphics/java/android/graphics/Paint.java22
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java4
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt96
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt27
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.aidl (renamed from libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl)2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java (renamed from libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java)168
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt20
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java107
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java64
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt47
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt31
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java331
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt (renamed from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt)46
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java189
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java20
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt42
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt68
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt8
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/hwui/ImageDecoder.cpp4
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp1
-rw-r--r--libs/hwui/hwui/Paint.h2
-rw-r--r--libs/hwui/hwui/PaintImpl.cpp11
-rw-r--r--libs/hwui/jni/BitmapFactory.cpp4
-rw-r--r--libs/hwui/jni/BitmapRegionDecoder.cpp4
-rw-r--r--libs/hwui/jni/ImageDecoder.cpp4
-rw-r--r--libs/hwui/libhwui.map.txt1
-rw-r--r--libs/hwui/utils/StatsUtils.cpp102
-rw-r--r--libs/hwui/utils/StatsUtils.h33
-rw-r--r--media/java/android/media/ImageReader.java12
-rw-r--r--media/java/android/media/ImageUtils.java17
-rw-r--r--media/java/android/media/quality/MediaQualityContract.java44
-rw-r--r--media/java/android/media/quality/MediaQualityManager.java51
-rw-r--r--media/java/android/media/quality/PictureProfile.java171
-rw-r--r--media/java/android/media/tv/TvInputServiceExtensionManager.java229
-rw-r--r--native/graphics/jni/Android.bp9
-rw-r--r--native/graphics/jni/imagedecoder.cpp41
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java3
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java4
-rw-r--r--packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java25
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt2
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt10
-rw-r--r--packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedInterface.kt49
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelperProvider.kt24
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java65
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java4
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig17
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java3
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt63
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt39
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt47
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt30
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt18
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt39
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt3
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt84
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt21
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt3
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt20
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt35
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt4
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt7
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt7
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt5
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt8
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt7
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt14
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt8
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt12
-rw-r--r--packages/SystemUI/docs/demo_mode.md1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java548
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt49
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt182
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt142
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt219
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt103
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt56
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt74
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt648
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt13
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java3
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_notes.xml23
-rw-r--r--packages/SystemUI/res/layout/clipboard_overlay.xml32
-rw-r--r--packages/SystemUI/res/layout/screen_record_dialog.xml164
-rw-r--r--packages/SystemUI/res/layout/volume_ringer_drawer.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/res/values/tiles_states_strings.xml10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java28
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java284
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProviderImpl.kt (renamed from packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt)13
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayOverrideModule.kt (renamed from packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt172
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt165
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModel.kt118
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt111
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/model/NotesTileModel.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java192
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt142
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt136
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderScope.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt22
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withAnimator.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json)0
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withSpring.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json)127
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withAnimator.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json)0
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withSpring.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json)127
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withAnimator.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json)0
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withSpring.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json)127
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withAnimator.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json)0
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withSpring.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json)127
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java1594
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt186
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt238
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt199
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java574
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt43
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt59
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/notes/NotesTileKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStoreKosmos.kt41
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeDarkIconDispatcherStore.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeLightBarControllerStore.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSysUiDarkIconDispatcherStore.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreKosmos.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt5
-rw-r--r--packages/Vcn/framework-b/Android.bp44
-rw-r--r--packages/Vcn/framework-b/api/current.txt1
-rw-r--r--packages/Vcn/framework-b/api/module-lib-current.txt1
-rw-r--r--packages/Vcn/framework-b/api/module-lib-removed.txt1
-rw-r--r--packages/Vcn/framework-b/api/removed.txt1
-rw-r--r--packages/Vcn/framework-b/api/system-current.txt1
-rw-r--r--packages/Vcn/framework-b/api/system-removed.txt1
-rw-r--r--packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java25
-rw-r--r--packages/Vcn/service-b/Android.bp36
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java25
-rw-r--r--packages/overlays/Android.bp1
-rw-r--r--ravenwood/Android.bp11
-rw-r--r--ravenwood/Framework.bp3
-rw-r--r--ravenwood/TEST_MAPPING6
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java2
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java84
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java12
-rw-r--r--ravenwood/runtime-jni/ravenwood_initializer.cpp50
-rw-r--r--ravenwood/runtime-jni/ravenwood_runtime.cpp20
-rwxr-xr-xravenwood/scripts/run-ravenwood-tests.sh4
-rw-r--r--ravenwood/tests/bivalenttest/Android.bp3
-rw-r--r--ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt56
-rw-r--r--ravenwood/texts/ravenwood-annotation-allowed-classes.txt6
-rw-r--r--ravenwood/texts/ravenwood-services-policies.txt11
-rw-r--r--services/Android.bp1
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java213
-rw-r--r--services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java225
-rw-r--r--services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java13
-rw-r--r--services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java27
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java24
-rw-r--r--services/core/Android.bp8
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java11
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java21
-rw-r--r--services/core/java/com/android/server/am/AnrHelper.java12
-rw-r--r--services/core/java/com/android/server/am/AppStartInfoTracker.java11
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java3
-rw-r--r--services/core/java/com/android/server/am/FgsTempAllowList.java17
-rw-r--r--services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java27
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java80
-rw-r--r--services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java28
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java1
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java44
-rw-r--r--services/core/java/com/android/server/compat/OverrideValidatorImpl.java1
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java18
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompatNative.java1
-rw-r--r--services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java1
-rw-r--r--services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java1
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java29
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java28
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java24
-rw-r--r--services/core/java/com/android/server/input/KeyGestureController.java23
-rw-r--r--services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java26
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java21
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java33
-rw-r--r--services/core/java/com/android/server/media/MediaSession2Record.java5
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java78
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecordImpl.java4
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java37
-rw-r--r--services/core/java/com/android/server/notification/GroupHelper.java174
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java66
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java181
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java2
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/om/OverlayActorEnforcer.java9
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java90
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java120
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java1
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java26
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java7
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java38
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java11
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java57
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java6
-rw-r--r--services/core/services-jarjar-rules.txt2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java1089
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java13
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java16
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java41
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java12
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java4
-rw-r--r--services/tests/mockingservicestests/Android.bp4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java66
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java55
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java52
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java4
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java68
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java35
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java66
-rw-r--r--services/tests/servicestests/Android.bp36
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java365
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java298
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java58
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java146
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java33
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java4
-rw-r--r--tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java26
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt98
-rw-r--r--tests/testables/src/android/testing/TestableLooper.java18
-rw-r--r--tests/utils/testutils/java/android/os/test/TestLooper.java34
546 files changed, 12528 insertions, 7910 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 26fbd270eb46..497619ae0613 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -348,6 +348,7 @@ java_aconfig_library {
aconfig_declarations {
name: "android.security.flags-aconfig",
package: "android.security",
+ exportable: true,
container: "system",
srcs: ["core/java/android/security/*.aconfig"],
}
@@ -365,6 +366,13 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.security.flags-aconfig-java-export",
+ aconfig_declarations: "android.security.flags-aconfig",
+ mode: "exported",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
cc_aconfig_library {
name: "android_security_flags_aconfig_c_lib",
aconfig_declarations: "android.security.flags-aconfig",
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index 79aef1e6a19a..47a85498f51b 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -45,3 +45,11 @@ flag {
description: "Introduce a new getPendingJobReasons() API which returns reasons why a job may not have executed. Also deprecate the existing getPendingJobReason() API."
bug: "372031023"
}
+
+flag {
+ name: "get_pending_job_reasons_history_api"
+ is_exported: true
+ namespace: "backstage_power"
+ description: "Introduce a new getPendingJobReasonsHistory() API which returns a limited historical view of getPendingJobReasons()."
+ bug: "372031023"
+}
diff --git a/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java b/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java
index 3cfddc6d8e2b..fb5ef8771c26 100644
--- a/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java
+++ b/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java
@@ -173,6 +173,16 @@ public class JobSchedulerImpl extends JobScheduler {
}
@Override
+ @NonNull
+ public int[] getPendingJobReasons(int jobId) {
+ try {
+ return mBinder.getPendingJobReasons(mNamespace, jobId);
+ } catch (RemoteException e) {
+ return new int[] { PENDING_JOB_REASON_UNDEFINED };
+ }
+ }
+
+ @Override
public boolean canRunUserInitiatedJobs() {
try {
return mBinder.canRunUserInitiatedJobs(mContext.getOpPackageName());
diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl
index 416a2d8c0002..21051b520d84 100644
--- a/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl
+++ b/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl
@@ -39,6 +39,7 @@ interface IJobScheduler {
ParceledListSlice<JobInfo> getAllPendingJobsInNamespace(String namespace);
JobInfo getPendingJob(String namespace, int jobId);
int getPendingJobReason(String namespace, int jobId);
+ int[] getPendingJobReasons(String namespace, int jobId);
boolean canRunUserInitiatedJobs(String packageName);
boolean hasRunUserInitiatedJobsPermission(String packageName, int userId);
List<JobInfo> getStartedJobs();
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index ad54cd397413..bfdd15e9b0cd 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -16,6 +16,7 @@
package android.app.job;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -238,6 +239,13 @@ public abstract class JobScheduler {
* to defer this job.
*/
public static final int PENDING_JOB_REASON_USER = 15;
+ /**
+ * The override deadline has not transpired.
+ *
+ * @see JobInfo.Builder#setOverrideDeadline(long)
+ */
+ @FlaggedApi(Flags.FLAG_GET_PENDING_JOB_REASONS_API)
+ public static final int PENDING_JOB_REASON_CONSTRAINT_DEADLINE = 16;
/** @hide */
@IntDef(prefix = {"PENDING_JOB_REASON_"}, value = {
@@ -259,6 +267,7 @@ public abstract class JobScheduler {
PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION,
PENDING_JOB_REASON_QUOTA,
PENDING_JOB_REASON_USER,
+ PENDING_JOB_REASON_CONSTRAINT_DEADLINE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PendingJobReason {
@@ -458,6 +467,10 @@ public abstract class JobScheduler {
/**
* Returns a reason why the job is pending and not currently executing. If there are multiple
* reasons why a job may be pending, this will only return one of them.
+ *
+ * @apiNote
+ * To know all the potential reasons why the job may be pending,
+ * use {@link #getPendingJobReasons(int)} instead.
*/
@PendingJobReason
public int getPendingJobReason(int jobId) {
@@ -465,6 +478,21 @@ public abstract class JobScheduler {
}
/**
+ * Returns potential reasons why the job with the given {@code jobId} may be pending
+ * and not currently executing.
+ *
+ * The returned array will include {@link PendingJobReason reasons} composed of both
+ * explicitly set constraints on the job and implicit constraints imposed by the system.
+ * The results can be used to debug why a given job may not be currently executing.
+ */
+ @FlaggedApi(Flags.FLAG_GET_PENDING_JOB_REASONS_API)
+ @NonNull
+ @PendingJobReason
+ public int[] getPendingJobReasons(int jobId) {
+ return new int[] { PENDING_JOB_REASON_UNDEFINED };
+ }
+
+ /**
* Returns {@code true} if the calling app currently holds the
* {@link android.Manifest.permission#RUN_USER_INITIATED_JOBS} permission, allowing it to run
* user-initiated jobs.
diff --git a/apex/jobscheduler/service/aconfig/device_idle.aconfig b/apex/jobscheduler/service/aconfig/device_idle.aconfig
index c4d0d1850a18..426031fbeb9c 100644
--- a/apex/jobscheduler/service/aconfig/device_idle.aconfig
+++ b/apex/jobscheduler/service/aconfig/device_idle.aconfig
@@ -17,3 +17,13 @@ flag {
description: "Disable wakelocks for background apps while Light Device Idle is active"
bug: "326607666"
}
+
+flag {
+ name: "use_cpu_time_for_temp_allowlist"
+ namespace: "backstage_power"
+ description: "Use CPU time for temporary allowlists"
+ bug: "376561328"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 3e650da2e66f..41fd4a29cfd1 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -620,8 +620,8 @@ public class DeviceIdleController extends SystemService
* the network and acquire wakelocks. Times are in milliseconds.
*/
@GuardedBy("this")
- private final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes
- = new SparseArray<>();
+ @VisibleForTesting
+ final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes = new SparseArray<>();
private NetworkPolicyManagerInternal mNetworkPolicyManagerInternal;
@@ -1941,7 +1941,8 @@ public class DeviceIdleController extends SystemService
private static final int MSG_REPORT_IDLE_ON_LIGHT = 3;
private static final int MSG_REPORT_IDLE_OFF = 4;
private static final int MSG_REPORT_ACTIVE = 5;
- private static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
+ @VisibleForTesting
+ static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
@VisibleForTesting
static final int MSG_REPORT_STATIONARY_STATUS = 7;
private static final int MSG_FINISH_IDLE_OP = 8;
@@ -2511,6 +2512,11 @@ public class DeviceIdleController extends SystemService
return SystemClock.elapsedRealtime();
}
+ /** Returns the current elapsed realtime in milliseconds. */
+ long getUptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+
LocationManager getLocationManager() {
if (mLocationManager == null) {
mLocationManager = mContext.getSystemService(LocationManager.class);
@@ -3264,7 +3270,8 @@ public class DeviceIdleController extends SystemService
void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int uid,
long duration, @TempAllowListType int tempAllowListType, boolean sync,
@ReasonCode int reasonCode, @Nullable String reason) {
- final long timeNow = SystemClock.elapsedRealtime();
+ final long timeNow = Flags.useCpuTimeForTempAllowlist() ? mInjector.getUptimeMillis()
+ : mInjector.getElapsedRealtime();
boolean informWhitelistChanged = false;
int appId = UserHandle.getAppId(uid);
synchronized (this) {
@@ -3350,7 +3357,8 @@ public class DeviceIdleController extends SystemService
}
void checkTempAppWhitelistTimeout(int uid) {
- final long timeNow = SystemClock.elapsedRealtime();
+ final long timeNow = Flags.useCpuTimeForTempAllowlist() ? mInjector.getUptimeMillis()
+ : mInjector.getElapsedRealtime();
final int appId = UserHandle.getAppId(uid);
if (DEBUG) {
Slog.d(TAG, "checkTempAppWhitelistTimeout: uid=" + uid + ", timeNow=" + timeNow);
@@ -5219,6 +5227,17 @@ public class DeviceIdleController extends SystemService
}
}
+ pw.println(" Flags:");
+ pw.print(" ");
+ pw.print(Flags.FLAG_USE_CPU_TIME_FOR_TEMP_ALLOWLIST);
+ pw.print("=");
+ pw.println(Flags.useCpuTimeForTempAllowlist());
+ pw.print(" ");
+ pw.print(Flags.FLAG_REMOVE_IDLE_LOCATION);
+ pw.print("=");
+ pw.println(Flags.removeIdleLocation());
+ pw.println();
+
synchronized (this) {
mConstants.dump(pw);
@@ -5449,7 +5468,8 @@ public class DeviceIdleController extends SystemService
pw.println(" Temp whitelist schedule:");
prefix = " ";
}
- final long timeNow = SystemClock.elapsedRealtime();
+ final long timeNow = Flags.useCpuTimeForTempAllowlist() ? mInjector.getUptimeMillis()
+ : mInjector.getElapsedRealtime();
for (int i = 0; i < size; i++) {
pw.print(prefix);
pw.print("UID=");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index ba8e3e8b48fc..8f44698ffd8d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1550,7 +1550,7 @@ class JobConcurrencyManager {
mActivePkgStats.add(
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
packageStats);
- mService.resetPendingJobReasonCache(jobStatus);
+ mService.resetPendingJobReasonsCache(jobStatus);
}
if (mService.getPendingJobQueue().remove(jobStatus)) {
mService.mJobPackageTracker.noteNonpending(jobStatus);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 4c1951ae9d39..f569388ef3c1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -460,10 +460,10 @@ public class JobSchedulerService extends com.android.server.SystemService
private final ArraySet<JobStatus> mChangedJobList = new ArraySet<>();
/**
- * Cached pending job reasons. Mapping from UID -> namespace -> job ID -> reason.
+ * Cached pending job reasons. Mapping from UID -> namespace -> job ID -> reasons.
*/
- @GuardedBy("mPendingJobReasonCache") // Use its own lock to avoid blocking JS processing
- private final SparseArrayMap<String, SparseIntArray> mPendingJobReasonCache =
+ @GuardedBy("mPendingJobReasonsCache") // Use its own lock to avoid blocking JS processing
+ private final SparseArrayMap<String, SparseArray<int[]>> mPendingJobReasonsCache =
new SparseArrayMap<>();
/**
@@ -2021,139 +2021,123 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
- @JobScheduler.PendingJobReason
- private int getPendingJobReason(int uid, String namespace, int jobId) {
- int reason;
+ @NonNull
+ private int[] getPendingJobReasons(int uid, String namespace, int jobId) {
+ int[] reasons;
// Some apps may attempt to query this frequently, so cache the reason under a separate lock
// so that the rest of JS processing isn't negatively impacted.
- synchronized (mPendingJobReasonCache) {
- SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid, namespace);
- if (jobIdToReason != null) {
- reason = jobIdToReason.get(jobId, JobScheduler.PENDING_JOB_REASON_UNDEFINED);
- if (reason != JobScheduler.PENDING_JOB_REASON_UNDEFINED) {
- return reason;
+ synchronized (mPendingJobReasonsCache) {
+ SparseArray<int[]> jobIdToReasons = mPendingJobReasonsCache.get(uid, namespace);
+ if (jobIdToReasons != null) {
+ reasons = jobIdToReasons.get(jobId);
+ if (reasons != null) {
+ return reasons;
}
}
}
synchronized (mLock) {
- reason = getPendingJobReasonLocked(uid, namespace, jobId);
+ reasons = getPendingJobReasonsLocked(uid, namespace, jobId);
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReason("
- + uid + "," + namespace + "," + jobId + ")=" + reason);
+ Slog.v(TAG, "getPendingJobReasons("
+ + uid + "," + namespace + "," + jobId + ")=" + Arrays.toString(reasons));
}
}
- synchronized (mPendingJobReasonCache) {
- SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid, namespace);
- if (jobIdToReason == null) {
- jobIdToReason = new SparseIntArray();
- mPendingJobReasonCache.add(uid, namespace, jobIdToReason);
+ synchronized (mPendingJobReasonsCache) {
+ SparseArray<int[]> jobIdToReasons = mPendingJobReasonsCache.get(uid, namespace);
+ if (jobIdToReasons == null) {
+ jobIdToReasons = new SparseArray<>();
+ mPendingJobReasonsCache.add(uid, namespace, jobIdToReasons);
}
- jobIdToReason.put(jobId, reason);
+ jobIdToReasons.put(jobId, reasons);
}
- return reason;
+ return reasons;
}
@VisibleForTesting
@JobScheduler.PendingJobReason
int getPendingJobReason(JobStatus job) {
- return getPendingJobReason(job.getUid(), job.getNamespace(), job.getJobId());
+ // keep original method to enable unit testing with flags
+ return getPendingJobReasons(job.getUid(), job.getNamespace(), job.getJobId())[0];
+ }
+
+ @VisibleForTesting
+ @NonNull
+ int[] getPendingJobReasons(JobStatus job) {
+ return getPendingJobReasons(job.getUid(), job.getNamespace(), job.getJobId());
}
- @JobScheduler.PendingJobReason
@GuardedBy("mLock")
- private int getPendingJobReasonLocked(int uid, String namespace, int jobId) {
+ @NonNull
+ private int[] getPendingJobReasonsLocked(int uid, String namespace, int jobId) {
// Very similar code to isReadyToBeExecutedLocked.
-
- JobStatus job = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
+ final JobStatus job = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
if (job == null) {
// Job doesn't exist.
- return JobScheduler.PENDING_JOB_REASON_INVALID_JOB_ID;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_INVALID_JOB_ID };
}
-
if (isCurrentlyRunningLocked(job)) {
- return JobScheduler.PENDING_JOB_REASON_EXECUTING;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_EXECUTING };
}
+ final String debugPrefix = "getPendingJobReasonsLocked: " + job.toShortString();
final boolean jobReady = job.isReady();
-
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " ready=" + jobReady);
+ Slog.v(TAG, debugPrefix + " ready=" + jobReady);
}
-
if (!jobReady) {
- return job.getPendingJobReason();
+ return job.getPendingJobReasons();
}
final boolean userStarted = areUsersStartedLocked(job);
-
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " userStarted=" + userStarted);
+ Slog.v(TAG, debugPrefix + " userStarted=" + userStarted);
}
if (!userStarted) {
- return JobScheduler.PENDING_JOB_REASON_USER;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_USER };
}
final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " backingUp=" + backingUp);
+ Slog.v(TAG, debugPrefix + " backingUp=" + backingUp);
}
-
if (backingUp) {
// TODO: Should we make a special reason for this?
- return JobScheduler.PENDING_JOB_REASON_APP;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_APP };
}
- JobRestriction restriction = checkIfRestricted(job);
+ final JobRestriction restriction = checkIfRestricted(job);
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " restriction=" + restriction);
+ Slog.v(TAG, debugPrefix + " restriction=" + restriction);
}
if (restriction != null) {
- return restriction.getPendingReason();
+ // Currently this will return _DEVICE_STATE because of thermal reasons.
+ // TODO (b/372031023): does it make sense to move this along with the
+ // pendingJobReasons() call above and also get the pending reasons from
+ // all of the restriction controllers?
+ return new int[] { restriction.getPendingReason() };
}
- // The following can be a little more expensive (especially jobActive, since we need to
- // go through the array of all potentially active jobs), so we are doing them
- // later... but still before checking with the package manager!
+ // The following can be a little more expensive, so we are doing it later,
+ // but still before checking with the package manager!
final boolean jobPending = mPendingJobQueue.contains(job);
-
-
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " pending=" + jobPending);
+ Slog.v(TAG, debugPrefix + " pending=" + jobPending);
}
-
if (jobPending) {
- // We haven't started the job for some reason. Presumably, there are too many jobs
- // running.
- return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
- }
-
- final boolean jobActive = mConcurrencyManager.isJobRunningLocked(job);
-
- if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " active=" + jobActive);
- }
- if (jobActive) {
- return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
+ // We haven't started the job - presumably, there are too many jobs running.
+ return new int[] { JobScheduler.PENDING_JOB_REASON_DEVICE_STATE };
}
// Validate that the defined package+service is still present & viable.
final boolean componentUsable = isComponentUsable(job);
-
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " componentUsable=" + componentUsable);
+ Slog.v(TAG, debugPrefix + " componentUsable=" + componentUsable);
}
if (!componentUsable) {
- return JobScheduler.PENDING_JOB_REASON_APP;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_APP };
}
- return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_UNDEFINED };
}
private JobInfo getPendingJob(int uid, @Nullable String namespace, int jobId) {
@@ -2195,15 +2179,16 @@ public class JobSchedulerService extends com.android.server.SystemService
// The app process will be killed soon. There's no point keeping its jobs in
// the pending queue to try and start them.
if (mPendingJobQueue.remove(job)) {
- synchronized (mPendingJobReasonCache) {
- SparseIntArray jobIdToReason = mPendingJobReasonCache.get(
+ synchronized (mPendingJobReasonsCache) {
+ SparseArray<int[]> jobIdToReason = mPendingJobReasonsCache.get(
job.getUid(), job.getNamespace());
if (jobIdToReason == null) {
- jobIdToReason = new SparseIntArray();
- mPendingJobReasonCache.add(job.getUid(), job.getNamespace(),
+ jobIdToReason = new SparseArray<>();
+ mPendingJobReasonsCache.add(job.getUid(), job.getNamespace(),
jobIdToReason);
}
- jobIdToReason.put(job.getJobId(), JobScheduler.PENDING_JOB_REASON_USER);
+ jobIdToReason.put(job.getJobId(),
+ new int[] { JobScheduler.PENDING_JOB_REASON_USER });
}
}
}
@@ -2229,8 +2214,8 @@ public class JobSchedulerService extends com.android.server.SystemService
synchronized (mLock) {
mJobs.removeJobsOfUnlistedUsers(umi.getUserIds());
}
- synchronized (mPendingJobReasonCache) {
- mPendingJobReasonCache.clear();
+ synchronized (mPendingJobReasonsCache) {
+ mPendingJobReasonsCache.clear();
}
}
@@ -2875,7 +2860,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean update = lastJob != null;
mJobs.add(jobStatus);
// Clear potentially cached INVALID_JOB_ID reason.
- resetPendingJobReasonCache(jobStatus);
+ resetPendingJobReasonsCache(jobStatus);
if (mReadyToRock) {
for (int i = 0; i < mControllers.size(); i++) {
StateController controller = mControllers.get(i);
@@ -2897,9 +2882,9 @@ public class JobSchedulerService extends com.android.server.SystemService
// Deal with any remaining work items in the old job.
jobStatus.stopTrackingJobLocked(incomingJob);
- synchronized (mPendingJobReasonCache) {
- SparseIntArray reasonCache =
- mPendingJobReasonCache.get(jobStatus.getUid(), jobStatus.getNamespace());
+ synchronized (mPendingJobReasonsCache) {
+ SparseArray<int[]> reasonCache =
+ mPendingJobReasonsCache.get(jobStatus.getUid(), jobStatus.getNamespace());
if (reasonCache != null) {
reasonCache.delete(jobStatus.getJobId());
}
@@ -2927,11 +2912,11 @@ public class JobSchedulerService extends com.android.server.SystemService
return removed;
}
- /** Remove the pending job reason for this job from the cache. */
- void resetPendingJobReasonCache(@NonNull JobStatus jobStatus) {
- synchronized (mPendingJobReasonCache) {
- final SparseIntArray reasons =
- mPendingJobReasonCache.get(jobStatus.getUid(), jobStatus.getNamespace());
+ /** Remove the pending job reasons for this job from the cache. */
+ void resetPendingJobReasonsCache(@NonNull JobStatus jobStatus) {
+ synchronized (mPendingJobReasonsCache) {
+ final SparseArray<int[]> reasons =
+ mPendingJobReasonsCache.get(jobStatus.getUid(), jobStatus.getNamespace());
if (reasons != null) {
reasons.delete(jobStatus.getJobId());
}
@@ -3313,18 +3298,18 @@ public class JobSchedulerService extends com.android.server.SystemService
public void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs) {
if (changedJobs == null) {
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
- synchronized (mPendingJobReasonCache) {
- mPendingJobReasonCache.clear();
+ synchronized (mPendingJobReasonsCache) {
+ mPendingJobReasonsCache.clear();
}
} else if (changedJobs.size() > 0) {
synchronized (mLock) {
mChangedJobList.addAll(changedJobs);
}
mHandler.obtainMessage(MSG_CHECK_CHANGED_JOB_LIST).sendToTarget();
- synchronized (mPendingJobReasonCache) {
+ synchronized (mPendingJobReasonsCache) {
for (int i = changedJobs.size() - 1; i >= 0; --i) {
final JobStatus job = changedJobs.valueAt(i);
- resetPendingJobReasonCache(job);
+ resetPendingJobReasonsCache(job);
}
}
}
@@ -3893,23 +3878,21 @@ public class JobSchedulerService extends com.android.server.SystemService
// Update the pending reason for any jobs that aren't going to be run.
final int numRunnableJobs = runnableJobs.size();
if (numRunnableJobs > 0 && numRunnableJobs != jobsToRun.size()) {
- synchronized (mPendingJobReasonCache) {
+ synchronized (mPendingJobReasonsCache) {
for (int i = 0; i < numRunnableJobs; ++i) {
final JobStatus job = runnableJobs.get(i);
if (jobsToRun.contains(job)) {
- // We're running this job. Skip updating the pending reason.
- continue;
+ continue; // we're running this job - skip updating the pending reason.
}
- SparseIntArray reasons =
- mPendingJobReasonCache.get(job.getUid(), job.getNamespace());
+ SparseArray<int[]> reasons =
+ mPendingJobReasonsCache.get(job.getUid(), job.getNamespace());
if (reasons == null) {
- reasons = new SparseIntArray();
- mPendingJobReasonCache.add(job.getUid(), job.getNamespace(), reasons);
+ reasons = new SparseArray<>();
+ mPendingJobReasonsCache.add(job.getUid(), job.getNamespace(), reasons);
}
- // We're force batching these jobs, so consider it an optimization
- // policy reason.
- reasons.put(job.getJobId(),
- JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION);
+ // we're force batching these jobs - note it as optimization.
+ reasons.put(job.getJobId(), new int[]
+ { JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION });
}
}
}
@@ -5123,12 +5106,16 @@ public class JobSchedulerService extends com.android.server.SystemService
@Override
public int getPendingJobReason(String namespace, int jobId) throws RemoteException {
- final int uid = Binder.getCallingUid();
+ return getPendingJobReasons(validateNamespace(namespace), jobId)[0];
+ }
+ @Override
+ public int[] getPendingJobReasons(String namespace, int jobId) throws RemoteException {
+ final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- return JobSchedulerService.this.getPendingJobReason(
- uid, validateNamespace(namespace), jobId);
+ return JobSchedulerService.this.getPendingJobReasons(
+ uid, validateNamespace(namespace), jobId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5867,6 +5854,9 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND,
android.app.job.Flags.ignoreImportantWhileForeground());
pw.println();
+ pw.print(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API,
+ android.app.job.Flags.getPendingJobReasonsApi());
+ pw.println();
pw.decreaseIndent();
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 68303e86055c..a4a302450849 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -439,6 +439,9 @@ public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
case android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND:
pw.println(android.app.job.Flags.ignoreImportantWhileForeground());
break;
+ case android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API:
+ pw.println(android.app.job.Flags.getPendingJobReasonsApi());
+ break;
default:
pw.println("Unknown flag: " + flagName);
break;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 1dc5a714337c..58579eb0db47 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -2067,12 +2067,11 @@ public final class JobStatus {
}
/**
- * If {@link #isReady()} returns false, this will return a single reason why the job isn't
- * ready. If {@link #isReady()} returns true, this will return
- * {@link JobScheduler#PENDING_JOB_REASON_UNDEFINED}.
+ * This will return all potential reasons why the job is pending.
*/
- @JobScheduler.PendingJobReason
- public int getPendingJobReason() {
+ @NonNull
+ public int[] getPendingJobReasons() {
+ final ArrayList<Integer> reasons = new ArrayList<>();
final int unsatisfiedConstraints = ~satisfiedConstraints
& (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS);
if ((CONSTRAINT_BACKGROUND_NOT_RESTRICTED & unsatisfiedConstraints) != 0) {
@@ -2084,78 +2083,99 @@ public final class JobStatus {
// (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of
// battery saver state.
if (mIsUserBgRestricted) {
- return JobScheduler.PENDING_JOB_REASON_BACKGROUND_RESTRICTION;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_BACKGROUND_RESTRICTION);
+ } else {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_DEVICE_STATE);
+ }
+ }
+ if ((CONSTRAINT_DEVICE_NOT_DOZING & unsatisfiedConstraints) != 0) {
+ if (!reasons.contains(JobScheduler.PENDING_JOB_REASON_DEVICE_STATE)) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_DEVICE_STATE);
}
- return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
}
+
if ((CONSTRAINT_BATTERY_NOT_LOW & unsatisfiedConstraints) != 0) {
if ((CONSTRAINT_BATTERY_NOT_LOW & requiredConstraints) != 0) {
// The developer requested this constraint, so it makes sense to return the
// explicit constraint reason.
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW);
+ } else {
+ // Hard-coding right now since the current dynamic constraint sets don't overlap
+ // TODO: return based on active dynamic constraint sets when they start overlapping
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_APP_STANDBY);
}
- // Hard-coding right now since the current dynamic constraint sets don't overlap
- // TODO: return based on active dynamic constraint sets when they start overlapping
- return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
}
if ((CONSTRAINT_CHARGING & unsatisfiedConstraints) != 0) {
if ((CONSTRAINT_CHARGING & requiredConstraints) != 0) {
// The developer requested this constraint, so it makes sense to return the
// explicit constraint reason.
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CHARGING;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CHARGING);
+ } else {
+ // Hard-coding right now since the current dynamic constraint sets don't overlap
+ // TODO: return based on active dynamic constraint sets when they start overlapping
+ if (!reasons.contains(JobScheduler.PENDING_JOB_REASON_APP_STANDBY)) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_APP_STANDBY);
+ }
}
- // Hard-coding right now since the current dynamic constraint sets don't overlap
- // TODO: return based on active dynamic constraint sets when they start overlapping
- return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
- }
- if ((CONSTRAINT_CONNECTIVITY & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY;
- }
- if ((CONSTRAINT_CONTENT_TRIGGER & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER;
- }
- if ((CONSTRAINT_DEVICE_NOT_DOZING & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
- }
- if ((CONSTRAINT_FLEXIBLE & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION;
}
if ((CONSTRAINT_IDLE & unsatisfiedConstraints) != 0) {
if ((CONSTRAINT_IDLE & requiredConstraints) != 0) {
// The developer requested this constraint, so it makes sense to return the
// explicit constraint reason.
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE);
+ } else {
+ // Hard-coding right now since the current dynamic constraint sets don't overlap
+ // TODO: return based on active dynamic constraint sets when they start overlapping
+ if (!reasons.contains(JobScheduler.PENDING_JOB_REASON_APP_STANDBY)) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_APP_STANDBY);
+ }
}
- // Hard-coding right now since the current dynamic constraint sets don't overlap
- // TODO: return based on active dynamic constraint sets when they start overlapping
- return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
+ }
+
+ if ((CONSTRAINT_CONNECTIVITY & unsatisfiedConstraints) != 0) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY);
+ }
+ if ((CONSTRAINT_CONTENT_TRIGGER & unsatisfiedConstraints) != 0) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER);
+ }
+ if ((CONSTRAINT_FLEXIBLE & unsatisfiedConstraints) != 0) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION);
}
if ((CONSTRAINT_PREFETCH & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_PREFETCH;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_PREFETCH);
}
if ((CONSTRAINT_STORAGE_NOT_LOW & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW);
}
if ((CONSTRAINT_TIMING_DELAY & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY);
}
if ((CONSTRAINT_WITHIN_QUOTA & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_QUOTA;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_QUOTA);
}
-
- if (getEffectiveStandbyBucket() == NEVER_INDEX) {
- Slog.wtf(TAG, "App in NEVER bucket querying pending job reason");
- // The user hasn't officially launched this app.
- return JobScheduler.PENDING_JOB_REASON_USER;
+ if (android.app.job.Flags.getPendingJobReasonsApi()) {
+ if ((CONSTRAINT_DEADLINE & unsatisfiedConstraints) != 0) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_DEADLINE);
+ }
}
- if (serviceProcessName != null) {
- return JobScheduler.PENDING_JOB_REASON_APP;
+
+ if (reasons.isEmpty()) {
+ if (getEffectiveStandbyBucket() == NEVER_INDEX) {
+ Slog.wtf(TAG, "App in NEVER bucket querying pending job reason");
+ // The user hasn't officially launched this app.
+ reasons.add(JobScheduler.PENDING_JOB_REASON_USER);
+ } else if (serviceProcessName != null) {
+ reasons.add(JobScheduler.PENDING_JOB_REASON_APP);
+ } else {
+ reasons.add(JobScheduler.PENDING_JOB_REASON_UNDEFINED);
+ }
}
- if (!isReady()) {
- Slog.wtf(TAG, "Unknown reason job isn't ready");
+ final int[] reasonsArr = new int[reasons.size()];
+ for (int i = 0; i < reasonsArr.length; i++) {
+ reasonsArr[i] = reasons.get(i);
}
- return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
+ return reasonsArr;
}
/** @return whether or not the @param constraint is satisfied */
diff --git a/api/Android.bp b/api/Android.bp
index ff674c7f8bd3..0ac85e28de1a 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -73,6 +73,7 @@ combined_apis {
"framework-bluetooth",
"framework-configinfrastructure",
"framework-connectivity",
+ "framework-connectivity-b",
"framework-connectivity-t",
"framework-devicelock",
"framework-graphics",
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index a413bbd68f60..16f069394639 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -1,13 +1,16 @@
android.content.AsyncTaskLoader$LoadTask
+android.media.MediaCodecInfo$CodecCapabilities$FeatureList
android.net.ConnectivityThread$Singleton
android.os.FileObserver
android.os.NullVibrator
+android.permission.PermissionManager
+android.provider.MediaStore
android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
+android.view.HdrRenderState
android.widget.Magnifier
+com.android.internal.jank.InteractionJankMonitor$InstanceHolder
+com.android.internal.util.LatencyTracker$SLatencyTrackerHolder
gov.nist.core.net.DefaultNetworkLayer
-android.net.rtp.AudioGroup
-android.net.rtp.AudioStream
-android.net.rtp.RtpStream
java.util.concurrent.ThreadLocalRandom
java.util.ImmutableCollections
-com.android.internal.jank.InteractionJankMonitor$InstanceHolder
+sun.nio.fs.UnixChannelFactory
diff --git a/core/api/current.txt b/core/api/current.txt
index d9ab273888db..15eebbe2be0e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -241,6 +241,7 @@ package android {
field public static final String PROVIDE_REMOTE_CREDENTIALS = "android.permission.PROVIDE_REMOTE_CREDENTIALS";
field @FlaggedApi("android.security.aapm_api") public static final String QUERY_ADVANCED_PROTECTION_MODE = "android.permission.QUERY_ADVANCED_PROTECTION_MODE";
field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
+ field @FlaggedApi("android.permission.flags.ranging_permission_enabled") public static final String RANGING = "android.permission.RANGING";
field public static final String READ_ASSISTANT_APP_SEARCH_DATA = "android.permission.READ_ASSISTANT_APP_SEARCH_DATA";
field public static final String READ_BASIC_PHONE_STATE = "android.permission.READ_BASIC_PHONE_STATE";
field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR";
@@ -1074,6 +1075,7 @@ package android {
field public static final int layout = 16842994; // 0x10100f2
field public static final int layoutAnimation = 16842988; // 0x10100ec
field public static final int layoutDirection = 16843698; // 0x10103b2
+ field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int layoutLabel;
field public static final int layoutMode = 16843738; // 0x10103da
field public static final int layout_above = 16843140; // 0x1010184
field public static final int layout_alignBaseline = 16843142; // 0x1010186
@@ -8023,6 +8025,7 @@ package android.app.admin {
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final String CONTENT_PROTECTION_POLICY = "contentProtection";
field public static final String KEYGUARD_DISABLED_FEATURES_POLICY = "keyguardDisabledFeatures";
field public static final String LOCK_TASK_POLICY = "lockTask";
+ field @FlaggedApi("android.app.admin.flags.set_mte_policy_coexistence") public static final String MEMORY_TAGGING_POLICY = "memoryTagging";
field public static final String PACKAGES_SUSPENDED_POLICY = "packagesSuspended";
field public static final String PACKAGE_UNINSTALL_BLOCKED_POLICY = "packageUninstallBlocked";
field public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
@@ -9245,6 +9248,7 @@ package android.app.job {
method @Nullable public String getNamespace();
method @Nullable public abstract android.app.job.JobInfo getPendingJob(int);
method public int getPendingJobReason(int);
+ method @FlaggedApi("android.app.job.get_pending_job_reasons_api") @NonNull public int[] getPendingJobReasons(int);
method @NonNull public java.util.Map<java.lang.String,java.util.List<android.app.job.JobInfo>> getPendingJobsInAllNamespaces();
method public abstract int schedule(@NonNull android.app.job.JobInfo);
field public static final int PENDING_JOB_REASON_APP = 1; // 0x1
@@ -9254,6 +9258,7 @@ package android.app.job {
field public static final int PENDING_JOB_REASON_CONSTRAINT_CHARGING = 5; // 0x5
field public static final int PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY = 6; // 0x6
field public static final int PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER = 7; // 0x7
+ field @FlaggedApi("android.app.job.get_pending_job_reasons_api") public static final int PENDING_JOB_REASON_CONSTRAINT_DEADLINE = 16; // 0x10
field public static final int PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE = 8; // 0x8
field public static final int PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY = 9; // 0x9
field public static final int PENDING_JOB_REASON_CONSTRAINT_PREFETCH = 10; // 0xa
@@ -9687,10 +9692,8 @@ package android.app.wallpaper {
method @Nullable public String getId();
method @Nullable public android.net.Uri getThumbnail();
method @Nullable public CharSequence getTitle();
- method @NonNull public static android.app.wallpaper.WallpaperDescription readFromStream(@NonNull java.io.InputStream) throws java.io.IOException;
method @NonNull public android.app.wallpaper.WallpaperDescription.Builder toBuilder();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- method public void writeToStream(@NonNull java.io.OutputStream) throws java.io.IOException;
field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpaper.WallpaperDescription> CREATOR;
}
@@ -12732,6 +12735,7 @@ package android.content.pm {
method public abstract void onPackagesUnavailable(String[], android.os.UserHandle, boolean);
method public void onPackagesUnsuspended(String[], android.os.UserHandle);
method public void onShortcutsChanged(@NonNull String, @NonNull java.util.List<android.content.pm.ShortcutInfo>, @NonNull android.os.UserHandle);
+ method @FlaggedApi("android.multiuser.add_launcher_user_config") public void onUserConfigChanged(@NonNull android.content.pm.LauncherUserInfo);
}
public static final class LauncherApps.PinItemRequest implements android.os.Parcelable {
@@ -12767,10 +12771,12 @@ package android.content.pm {
@FlaggedApi("android.os.allow_private_profile") public final class LauncherUserInfo implements android.os.Parcelable {
method @FlaggedApi("android.os.allow_private_profile") public int describeContents();
+ method @FlaggedApi("android.multiuser.add_launcher_user_config") @NonNull public android.os.Bundle getUserConfig();
method @FlaggedApi("android.os.allow_private_profile") public int getUserSerialNumber();
method @FlaggedApi("android.os.allow_private_profile") @NonNull public String getUserType();
method @FlaggedApi("android.os.allow_private_profile") public void writeToParcel(@NonNull android.os.Parcel, int);
field @FlaggedApi("android.os.allow_private_profile") @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherUserInfo> CREATOR;
+ field @FlaggedApi("android.multiuser.add_launcher_user_config") public static final String PRIVATE_SPACE_ENTRYPOINT_HIDDEN = "private_space_entrypoint_hidden";
}
public final class ModuleInfo implements android.os.Parcelable {
@@ -13701,7 +13707,7 @@ package android.content.pm {
field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
field public static final int FLAG_USE_APP_ZYGOTE = 8; // 0x8
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CAMERA}, anyOf={android.Manifest.permission.CAMERA}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40
- field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
+ field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING, android.Manifest.permission.RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
@@ -16418,6 +16424,7 @@ package android.graphics {
field public static final int FLEX_RGBA_8888 = 42; // 0x2a
field public static final int FLEX_RGB_888 = 41; // 0x29
field public static final int HEIC = 1212500294; // 0x48454946
+ field @FlaggedApi("com.android.internal.camera.flags.camera_heif_gainmap") public static final int HEIC_ULTRAHDR = 4102; // 0x1006
field public static final int JPEG = 256; // 0x100
field public static final int JPEG_R = 4101; // 0x1005
field public static final int NV16 = 16; // 0x10
@@ -16844,6 +16851,7 @@ package android.graphics {
field @FlaggedApi("com.android.text.flags.letter_spacing_justification") public static final int TEXT_RUN_FLAG_LEFT_EDGE = 8192; // 0x2000
field @FlaggedApi("com.android.text.flags.letter_spacing_justification") public static final int TEXT_RUN_FLAG_RIGHT_EDGE = 16384; // 0x4000
field public static final int UNDERLINE_TEXT_FLAG = 8; // 0x8
+ field @FlaggedApi("com.android.text.flags.vertical_text_layout") public static final int VERTICAL_TEXT_FLAG = 4096; // 0x1000
}
public enum Paint.Align {
@@ -18708,6 +18716,7 @@ package android.hardware {
field public static final int DATASPACE_DISPLAY_P3 = 143261696; // 0x88a0000
field public static final int DATASPACE_DYNAMIC_DEPTH = 4098; // 0x1002
field public static final int DATASPACE_HEIF = 4100; // 0x1004
+ field @FlaggedApi("com.android.internal.camera.flags.camera_heif_gainmap") public static final int DATASPACE_HEIF_ULTRAHDR = 4102; // 0x1006
field public static final int DATASPACE_JFIF = 146931712; // 0x8c20000
field public static final int DATASPACE_JPEG_R = 4101; // 0x1005
field public static final int DATASPACE_SCRGB = 411107328; // 0x18810000
@@ -55573,6 +55582,7 @@ package android.view.accessibility {
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+ field @FlaggedApi("android.view.accessibility.a11y_character_in_window_api") public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
field public static final int FLAG_PREFETCH_ANCESTORS = 1; // 0x1
field public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 16; // 0x10
@@ -56975,6 +56985,9 @@ package android.view.inputmethod {
method public String getExtraValueOf(String);
method public int getIconResId();
method @NonNull public String getLanguageTag();
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @NonNull public CharSequence getLayoutDisplayName(@NonNull android.content.Context, @NonNull android.content.pm.ApplicationInfo);
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @NonNull public CharSequence getLayoutLabelNonLocalized();
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @StringRes public int getLayoutLabelResource();
method @Deprecated @NonNull public String getLocale();
method public String getMode();
method @NonNull public CharSequence getNameOverride();
@@ -56994,6 +57007,8 @@ package android.view.inputmethod {
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(String);
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @NonNull public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLayoutLabelNonLocalized(@NonNull CharSequence);
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @NonNull public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLayoutLabelResource(@StringRes int);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
method @NonNull public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setPhysicalKeyboardHint(@Nullable android.icu.util.ULocale, @NonNull String);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 3fde7497a090..2a01ca082832 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -696,6 +696,7 @@ package android.app {
field public static final String OPSTR_PLAY_AUDIO = "android:play_audio";
field public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
+ field @FlaggedApi("android.permission.flags.ranging_permission_enabled") public static final String OPSTR_RANGING = "android:ranging";
field @FlaggedApi("android.view.contentprotection.flags.rapid_clear_notifications_by_listener_app_op_enabled") public static final String OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER = "android:rapid_clear_notifications_by_listener";
field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final String OPSTR_READ_HEART_RATE = "android:read_heart_rate";
@@ -5293,13 +5294,19 @@ package android.hardware.display {
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
+ public abstract static class VirtualDisplay.Callback {
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public void onRequestedBrightnessChanged(@FloatRange(from=0.0f, to=1.0f) float);
+ }
+
public final class VirtualDisplayConfig implements android.os.Parcelable {
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @FloatRange(from=0.0f, to=1.0f) public float getDefaultBrightness();
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @Nullable public android.view.DisplayCutout getDisplayCutout();
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") public boolean isIgnoreActivitySizeRestrictions();
}
public static final class VirtualDisplayConfig.Builder {
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDefaultBrightness(@FloatRange(from=0.0f, to=1.0f) float);
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setIgnoreActivitySizeRestrictions(boolean);
@@ -7027,7 +7034,7 @@ package android.hardware.soundtrigger {
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAllowMultipleTriggers(boolean);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAudioCapabilities(int);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setCaptureRequested(boolean);
- method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setData(@Nullable byte[]);
+ method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setData(@NonNull byte[]);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setKeyphrases(@NonNull java.util.Collection<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 98d6f58e6bcf..117351943587 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -402,6 +402,7 @@ package android.app {
method @FlaggedApi("android.service.notification.notification_classification") @NonNull public java.util.Set<java.lang.String> getUnsupportedAdjustmentTypes();
method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String);
method @FlaggedApi("android.app.modes_api") public boolean removeAutomaticZenRule(@NonNull String, boolean);
+ method @FlaggedApi("android.service.notification.notification_classification") public void setAssistantAdjustmentKeyTypeState(int, boolean);
method @FlaggedApi("android.app.api_rich_ongoing") public void setCanPostPromotedNotifications(@NonNull String, int, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 36fc65a76d53..b447897733e1 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2764,14 +2764,19 @@ public class ActivityManager {
/**
* Information of organized child tasks.
*
+ * @deprecated No longer used
* @hide
*/
+ @Deprecated
public ArrayList<RecentTaskInfo> childrenTaskInfos = new ArrayList<>();
/**
* Information about the last snapshot taken for this task.
+ *
+ * @deprecated No longer used
* @hide
*/
+ @Deprecated
public PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData();
public RecentTaskInfo() {
@@ -2793,7 +2798,7 @@ public class ActivityManager {
lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR);
lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR);
lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR);
- super.readFromParcel(source);
+ super.readTaskFromParcel(source);
}
@Override
@@ -2804,7 +2809,7 @@ public class ActivityManager {
dest.writeTypedObject(lastSnapshotData.taskSize, flags);
dest.writeTypedObject(lastSnapshotData.contentInsets, flags);
dest.writeTypedObject(lastSnapshotData.bufferSize, flags);
- super.writeToParcel(dest, flags);
+ super.writeTaskToParcel(dest, flags);
}
public static final @android.annotation.NonNull Creator<RecentTaskInfo> CREATOR
@@ -2988,13 +2993,13 @@ public class ActivityManager {
public void readFromParcel(Parcel source) {
id = source.readInt();
- super.readFromParcel(source);
+ super.readTaskFromParcel(source);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
- super.writeToParcel(dest, flags);
+ super.writeTaskToParcel(dest, flags);
}
public static final @android.annotation.NonNull Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 799df1f9227a..16dcf2ad7e45 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -534,10 +534,9 @@ public class ActivityTaskManager {
dest.writeIntArray(childTaskUserIds);
dest.writeInt(visible ? 1 : 0);
dest.writeInt(position);
- super.writeToParcel(dest, flags);
+ super.writeTaskToParcel(dest, flags);
}
- @Override
void readFromParcel(Parcel source) {
bounds = source.readTypedObject(Rect.CREATOR);
childTaskIds = source.createIntArray();
@@ -546,7 +545,7 @@ public class ActivityTaskManager {
childTaskUserIds = source.createIntArray();
visible = source.readInt() > 0;
position = source.readInt();
- super.readFromParcel(source);
+ super.readTaskFromParcel(source);
}
public static final @NonNull Creator<RootTaskInfo> CREATOR = new Creator<>() {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ca98da76b78f..60b8f80d8f2d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3100,6 +3100,19 @@ public final class ActivityThread extends ClientTransactionHandler
mResourcesManager = ResourcesManager.getInstance();
}
+ /**
+ * Creates and initialize a new system activity thread, to be used for testing. This does not
+ * call {@link #attach}, so it does not modify static state.
+ */
+ @VisibleForTesting
+ @NonNull
+ public static ActivityThread createSystemActivityThreadForTesting() {
+ final var thread = new ActivityThread();
+ thread.mSystemThread = true;
+ initializeSystemThread(thread);
+ return thread;
+ }
+
@UnsupportedAppUsage
public ApplicationThread getApplicationThread()
{
@@ -6806,6 +6819,16 @@ public final class ActivityThread extends ClientTransactionHandler
LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths);
resApk.updateApplicationInfo(ai, oldPaths);
}
+ if (android.content.res.Flags.systemContextHandleAppInfoChanged() && mSystemThread) {
+ final var systemContext = getSystemContext();
+ if (systemContext.getPackageName().equals(ai.packageName)) {
+ // The system package is not tracked directly, but still needs to receive updates to
+ // its application info.
+ final ArrayList<String> oldPaths = new ArrayList<>();
+ LoadedApk.makePaths(this, systemContext.getApplicationInfo(), oldPaths);
+ systemContext.mPackageInfo.updateApplicationInfo(ai, oldPaths);
+ }
+ }
ResourcesImpl beforeImpl = getApplication().getResources().getImpl();
@@ -8560,17 +8583,7 @@ public final class ActivityThread extends ClientTransactionHandler
// we can't display an alert, we just want to die die die.
android.ddm.DdmHandleAppName.setAppName("system_process",
UserHandle.myUserId());
- try {
- mInstrumentation = new Instrumentation();
- mInstrumentation.basicInit(this);
- ContextImpl context = ContextImpl.createAppContext(
- this, getSystemContext().mPackageInfo);
- mInitialApplication = context.mPackageInfo.makeApplicationInner(true, null);
- mInitialApplication.onCreate();
- } catch (Exception e) {
- throw new RuntimeException(
- "Unable to instantiate Application():" + e.toString(), e);
- }
+ initializeSystemThread(this);
}
ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> {
@@ -8595,6 +8608,28 @@ public final class ActivityThread extends ClientTransactionHandler
ViewRootImpl.addConfigCallback(configChangedCallback);
}
+ /**
+ * Initializes the given system activity thread, setting up its instrumentation and initial
+ * application. This only has an effect if the given thread is a {@link #mSystemThread}.
+ *
+ * @param thread the given system activity thread to initialize.
+ */
+ private static void initializeSystemThread(@NonNull ActivityThread thread) {
+ if (!thread.mSystemThread) {
+ return;
+ }
+ try {
+ thread.mInstrumentation = new Instrumentation();
+ thread.mInstrumentation.basicInit(thread);
+ ContextImpl context = ContextImpl.createAppContext(
+ thread, thread.getSystemContext().mPackageInfo);
+ thread.mInitialApplication = context.mPackageInfo.makeApplicationInner(true, null);
+ thread.mInitialApplication.onCreate();
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to instantiate Application():" + e, e);
+ }
+ }
+
@UnsupportedAppUsage
public static ActivityThread systemMain() {
ThreadedRenderer.initForSystemProcess();
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
index 4bfa3b340ec9..cf01f50c8026 100644
--- a/core/java/android/app/AppCompatCallbacks.java
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -28,6 +28,7 @@ import java.util.Arrays;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class AppCompatCallbacks implements Compatibility.BehaviorChangeDelegate {
private final long[] mDisabledChanges;
private final long[] mLoggableChanges;
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 8370c2e522f3..68794588afc5 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -167,10 +167,11 @@ public class AppCompatTaskInfo implements Parcelable {
}
/**
- * @return {@code true} if top activity is pillarboxed.
+ * @return {@code true} if the top activity bounds are letterboxed with width <= height.
*/
- public boolean isTopActivityPillarboxed() {
- return topActivityLetterboxWidth < topActivityLetterboxHeight;
+ public boolean isTopActivityPillarboxShaped() {
+ return isTopActivityLetterboxed()
+ && topActivityLetterboxWidth <= topActivityLetterboxHeight;
}
/**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 0629b8a58b42..38c8583dd024 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -63,6 +63,7 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.IpcDataCache;
import android.os.Looper;
import android.os.PackageTagsList;
import android.os.Parcel;
@@ -78,12 +79,14 @@ import android.permission.PermissionGroupUsage;
import android.permission.PermissionUsageHelper;
import android.permission.flags.Flags;
import android.provider.DeviceConfig;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.Pools;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Immutable;
@@ -1611,9 +1614,16 @@ public class AppOpsManager {
/** @hide Access to read skin temperature. */
public static final int OP_READ_SKIN_TEMPERATURE = AppOpEnums.APP_OP_READ_SKIN_TEMPERATURE;
+ /**
+ * Allows an app to range with nearby devices using any ranging technology available.
+ *
+ * @hide
+ */
+ public static final int OP_RANGING = AppOpEnums.APP_OP_RANGING;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 151;
+ public static final int _NUM_OP = 152;
/**
* All app ops represented as strings.
@@ -1768,6 +1778,7 @@ public class AppOpsManager {
OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS,
OPSTR_READ_HEART_RATE,
OPSTR_READ_SKIN_TEMPERATURE,
+ OPSTR_RANGING,
})
public @interface AppOpString {}
@@ -2515,6 +2526,11 @@ public class AppOpsManager {
@FlaggedApi(Flags.FLAG_PLATFORM_SKIN_TEMPERATURE_ENABLED)
public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature";
+ /** @hide Access to ranging */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_RANGING_PERMISSION_ENABLED)
+ public static final String OPSTR_RANGING = "android:ranging";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2586,6 +2602,7 @@ public class AppOpsManager {
OP_BLUETOOTH_ADVERTISE,
OP_UWB_RANGING,
OP_NEARBY_WIFI_DEVICES,
+ Flags.rangingPermissionEnabled() ? OP_RANGING : OP_NONE,
// Notifications
OP_POST_NOTIFICATION,
// Health
@@ -3108,6 +3125,10 @@ public class AppOpsManager {
Flags.platformSkinTemperatureEnabled()
? HealthPermissions.READ_SKIN_TEMPERATURE : null)
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+ new AppOpInfo.Builder(OP_RANGING, OPSTR_RANGING, "RANGING")
+ .setPermission(Flags.rangingPermissionEnabled()?
+ Manifest.permission.RANGING : null)
+ .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
};
// The number of longs needed to form a full bitmask of app ops
@@ -7797,6 +7818,116 @@ public class AppOpsManager {
}
}
+ private static final String APP_OP_MODE_CACHING_API = "getAppOpMode";
+ private static final String APP_OP_MODE_CACHING_NAME = "appOpModeCache";
+ private static final int APP_OP_MODE_CACHING_SIZE = 2048;
+
+ private static final IpcDataCache.QueryHandler<AppOpModeQuery, Integer> sGetAppOpModeQuery =
+ new IpcDataCache.QueryHandler<>() {
+ @Override
+ public Integer apply(AppOpModeQuery query) {
+ IAppOpsService service = getService();
+ try {
+ return service.checkOperationRawForDevice(query.op, query.uid,
+ query.packageName, query.attributionTag, query.virtualDeviceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public boolean shouldBypassCache(@NonNull AppOpModeQuery query) {
+ // If the flag to enable the new caching behavior is off, bypass the cache.
+ return !Flags.appopModeCachingEnabled();
+ }
+ };
+
+ // A LRU cache on binder clients that caches AppOp mode by uid, packageName, virtualDeviceId
+ // and attributionTag.
+ private static final IpcDataCache<AppOpModeQuery, Integer> sAppOpModeCache =
+ new IpcDataCache<>(APP_OP_MODE_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+ APP_OP_MODE_CACHING_API, APP_OP_MODE_CACHING_NAME, sGetAppOpModeQuery);
+
+ // Ops that we don't want to cache due to:
+ // 1) Discrepancy of attributionTag support in checkOp and noteOp that determines if a package
+ // can bypass user restriction of an op: b/240617242. COARSE_LOCATION and FINE_LOCATION are
+ // the only two ops that are impacted.
+ private static final SparseBooleanArray OPS_WITHOUT_CACHING = new SparseBooleanArray();
+ static {
+ OPS_WITHOUT_CACHING.put(OP_COARSE_LOCATION, true);
+ OPS_WITHOUT_CACHING.put(OP_FINE_LOCATION, true);
+ }
+
+ private static boolean isAppOpModeCachingEnabled(int opCode) {
+ if (!Flags.appopModeCachingEnabled()) {
+ return false;
+ }
+ return !OPS_WITHOUT_CACHING.get(opCode, false);
+ }
+
+ /**
+ * @hide
+ */
+ public static void invalidateAppOpModeCache() {
+ if (Flags.appopModeCachingEnabled()) {
+ IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, APP_OP_MODE_CACHING_API);
+ }
+ }
+
+ /**
+ * Bypass AppOpModeCache in the local process
+ *
+ * @hide
+ */
+ public static void disableAppOpModeCache() {
+ if (Flags.appopModeCachingEnabled()) {
+ sAppOpModeCache.disableLocal();
+ }
+ }
+
+ private static final class AppOpModeQuery {
+ final int op;
+ final int uid;
+ final String packageName;
+ final int virtualDeviceId;
+ final String attributionTag;
+ final String methodName;
+
+ AppOpModeQuery(int op, int uid, @Nullable String packageName, int virtualDeviceId,
+ @Nullable String attributionTag, @Nullable String methodName) {
+ this.op = op;
+ this.uid = uid;
+ this.packageName = packageName;
+ this.virtualDeviceId = virtualDeviceId;
+ this.attributionTag = attributionTag;
+ this.methodName = methodName;
+ }
+
+ @Override
+ public String toString() {
+ return TextUtils.formatSimple("AppOpModeQuery(op=%d, uid=%d, packageName=%s, "
+ + "virtualDeviceId=%d, attributionTag=%s, methodName=%s", op, uid,
+ packageName, virtualDeviceId, attributionTag, methodName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(op, uid, packageName, virtualDeviceId, attributionTag);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null) return false;
+ if (this.getClass() != o.getClass()) return false;
+
+ AppOpModeQuery other = (AppOpModeQuery) o;
+ return op == other.op && uid == other.uid && Objects.equals(packageName,
+ other.packageName) && virtualDeviceId == other.virtualDeviceId
+ && Objects.equals(attributionTag, other.attributionTag);
+ }
+ }
+
AppOpsManager(Context context, IAppOpsService service) {
mContext = context;
mService = service;
@@ -8851,12 +8982,16 @@ public class AppOpsManager {
private int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName,
int virtualDeviceId) {
try {
- if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
- return mService.checkOperationRaw(op, uid, packageName, null);
+ int mode;
+ if (isAppOpModeCachingEnabled(op)) {
+ mode = sAppOpModeCache.query(
+ new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null,
+ "unsafeCheckOpRawNoThrow"));
} else {
- return mService.checkOperationRawForDevice(
+ mode = mService.checkOperationRawForDevice(
op, uid, packageName, null, virtualDeviceId);
}
+ return mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -9041,7 +9176,7 @@ public class AppOpsManager {
SyncNotedAppOp syncOp;
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
- collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+ collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
} else {
syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
@@ -9284,8 +9419,21 @@ public class AppOpsManager {
@UnsupportedAppUsage
public int checkOp(int op, int uid, String packageName) {
try {
- int mode = mService.checkOperationForDevice(op, uid, packageName,
- Context.DEVICE_ID_DEFAULT);
+ int mode;
+ if (isAppOpModeCachingEnabled(op)) {
+ mode = sAppOpModeCache.query(
+ new AppOpModeQuery(op, uid, packageName, Context.DEVICE_ID_DEFAULT, null,
+ "checkOp"));
+ if (mode == MODE_FOREGROUND) {
+ // We only cache raw mode. If the mode is FOREGROUND, we need another binder
+ // call to fetch translated value based on the process state.
+ mode = mService.checkOperationForDevice(op, uid, packageName,
+ Context.DEVICE_ID_DEFAULT);
+ }
+ } else {
+ mode = mService.checkOperationForDevice(op, uid, packageName,
+ Context.DEVICE_ID_DEFAULT);
+ }
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
@@ -9324,13 +9472,19 @@ public class AppOpsManager {
private int checkOpNoThrow(int op, int uid, String packageName, int virtualDeviceId) {
try {
int mode;
- if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
- mode = mService.checkOperation(op, uid, packageName);
+ if (isAppOpModeCachingEnabled(op)) {
+ mode = sAppOpModeCache.query(
+ new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null,
+ "checkOpNoThrow"));
+ if (mode == MODE_FOREGROUND) {
+ // We only cache raw mode. If the mode is FOREGROUND, we need another binder
+ // call to fetch translated value based on the process state.
+ mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId);
+ }
} else {
mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId);
}
-
- return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode;
+ return mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index d1e517bbd03c..16444dc5adde 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -398,6 +398,7 @@ public abstract class ForegroundServiceTypePolicy {
new RegularPermission(Manifest.permission.NFC),
new RegularPermission(Manifest.permission.TRANSMIT_IR),
new RegularPermission(Manifest.permission.UWB_RANGING),
+ new RegularPermission(Manifest.permission.RANGING),
new UsbDevicePermission(),
new UsbAccessoryPermission(),
}, false),
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a97fa18a3599..0654ac2f33ce 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -267,4 +267,7 @@ interface INotificationManager
void setAdjustmentTypeSupportedState(in INotificationListener token, String key, boolean supported);
List<String> getUnsupportedAdjustmentTypes();
+
+ int[] getAllowedAdjustmentKeyTypes();
+ void setAssistantAdjustmentKeyTypeState(int type, boolean enabled);
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 768b70c28632..c49b02210dd4 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1848,6 +1848,20 @@ public class NotificationManager {
/**
* @hide
*/
+ @TestApi
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void setAssistantAdjustmentKeyTypeState(@Adjustment.Types int type, boolean enabled) {
+ INotificationManager service = getService();
+ try {
+ service.setAssistantAdjustmentKeyTypeState(type, enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
public List<String> getEnabledNotificationListenerPackages() {
INotificationManager service = getService();
try {
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 038dcdb7efc9..f432a22b14ac 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -717,12 +717,10 @@ public class PropertyInvalidatedCache<Query, Result> {
// The shared memory.
private volatile NonceStore mStore;
- // The index of the nonce in shared memory.
+ // The index of the nonce in shared memory. This changes from INVALID only when the local
+ // object is completely initialized.
private volatile int mHandle = NonceStore.INVALID_NONCE_INDEX;
- // True if the string has been stored, ever.
- private volatile boolean mRecorded = false;
-
// A short name that is saved in shared memory. This is the portion of the property name
// that follows the prefix.
private final String mShortName;
@@ -736,48 +734,63 @@ public class PropertyInvalidatedCache<Query, Result> {
}
}
+ // Initialize the mStore and mHandle variables. This function does nothing if the
+ // variables are already initialized. Synchronization ensures that initialization happens
+ // no more than once. The function returns the new value of mHandle.
+ //
+ // If the "update" boolean is true, then the property is registered with the nonce store
+ // before the associated handle is fetched.
+ private int initialize(boolean update) {
+ synchronized (mLock) {
+ int handle = mHandle;
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ if (mStore == null) {
+ mStore = NonceStore.getInstance();
+ if (mStore == null) {
+ return NonceStore.INVALID_NONCE_INDEX;
+ }
+ }
+ if (update) {
+ mStore.storeName(mShortName);
+ }
+ handle = mStore.getHandleForName(mShortName);
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ return NonceStore.INVALID_NONCE_INDEX;
+ }
+ // The handle must be valid.
+ mHandle = handle;
+ }
+ return handle;
+ }
+ }
+
// Fetch the nonce from shared memory. If the shared memory is not available, return
// UNSET. If the shared memory is available but the nonce name is not known (it may not
// have been invalidated by the server yet), return UNSET.
@Override
long getNonceInternal() {
- if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
- if (mStore == null) {
- mStore = NonceStore.getInstance();
- if (mStore == null) {
- return NONCE_UNSET;
- }
- }
- mHandle = mStore.getHandleForName(mShortName);
- if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+ int handle = mHandle;
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ handle = initialize(false);
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
return NONCE_UNSET;
}
}
- return mStore.getNonce(mHandle);
+ return mStore.getNonce(handle);
}
- // Set the nonce in shared mmory. If the shared memory is not available, throw an
- // exception. Otherwise, if the nonce name has never been recorded, record it now and
- // fetch the handle for the name. If the handle cannot be created, throw an exception.
+ // Set the nonce in shared memory. If the shared memory is not available or if the nonce
+ // cannot be registered in shared memory, throw an exception.
@Override
void setNonceInternal(long value) {
- if (mHandle == NonceStore.INVALID_NONCE_INDEX || !mRecorded) {
- if (mStore == null) {
- mStore = NonceStore.getInstance();
- if (mStore == null) {
- throw new IllegalStateException("setNonce: shared memory not ready");
- }
- }
- // Always store the name before fetching the handle. storeName() is idempotent
- // but does take a little time, so this code calls it just once.
- mStore.storeName(mShortName);
- mRecorded = true;
- mHandle = mStore.getHandleForName(mShortName);
- if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
- throw new IllegalStateException("setNonce: shared memory store failed");
+ int handle = mHandle;
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ handle = initialize(true);
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ throw new IllegalStateException("unable to assign nonce handle: " + mName);
}
}
- mStore.setNonce(mHandle, value);
+ mStore.setNonce(handle, value);
}
}
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index c4a6decd982f..aac963ade726 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -364,8 +364,9 @@ public class TaskInfo {
// Do nothing
}
- private TaskInfo(Parcel source) {
- readFromParcel(source);
+ /** @hide */
+ public TaskInfo(Parcel source) {
+ readTaskFromParcel(source);
}
/**
@@ -524,7 +525,7 @@ public class TaskInfo {
/**
* Reads the TaskInfo from a parcel.
*/
- void readFromParcel(Parcel source) {
+ void readTaskFromParcel(Parcel source) {
userId = source.readInt();
taskId = source.readInt();
effectiveUid = source.readInt();
@@ -577,8 +578,9 @@ public class TaskInfo {
/**
* Writes the TaskInfo to a parcel.
+ * @hide
*/
- void writeToParcel(Parcel dest, int flags) {
+ public void writeTaskToParcel(Parcel dest, int flags) {
dest.writeInt(userId);
dest.writeInt(taskId);
dest.writeInt(effectiveUid);
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index c0e435c04d3c..35149b5a3135 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -191,6 +191,12 @@ public final class DevicePolicyIdentifiers {
public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
/**
+ * String identifier for {@link DevicePolicyManager#setMtePolicy(int)}.
+ */
+ @FlaggedApi(android.app.admin.flags.Flags.FLAG_SET_MTE_POLICY_COEXISTENCE)
+ public static final String MEMORY_TAGGING_POLICY = "memoryTagging";
+
+ /**
* @hide
*/
public static final String USER_RESTRICTION_PREFIX = "userRestriction_";
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 3aaca25eca15..04a9d13420ee 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -276,6 +276,16 @@ flag {
}
flag {
+ name: "suspend_packages_coexistence"
+ namespace: "enterprise"
+ description: "Migrate setPackagesSuspended for unmanaged mode"
+ bug: "335624297"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "backup_connected_apps_settings"
namespace: "enterprise"
description: "backup and restore connected work and personal apps user settings across devices"
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index db663f8ed4c4..7d21cbf955d9 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -31,13 +31,24 @@ import com.android.internal.compat.IPlatformCompat;
* Handles caching of calls to {@link com.android.internal.compat.IPlatformCompat}
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ChangeIdStateCache
extends PropertyInvalidatedCache<ChangeIdStateQuery, Boolean> {
private static final String CACHE_KEY = createSystemCacheKey("is_compat_change_enabled");
private static final int MAX_ENTRIES = 2048;
- private static boolean sDisabled = false;
+ private static boolean sDisabled = getDefaultDisabled();
private volatile IPlatformCompat mPlatformCompat;
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static boolean getDefaultDisabled() {
+ return false;
+ }
+
+ private static boolean getDefaultDisabled$ravenwood() {
+ return true; // TODO(b/376676753) Disable the cache for now.
+ }
+
/** @hide */
public ChangeIdStateCache() {
super(MAX_ENTRIES, CACHE_KEY);
diff --git a/core/java/android/app/compat/ChangeIdStateQuery.java b/core/java/android/app/compat/ChangeIdStateQuery.java
index 7598d6c90d3d..26d9ab65417e 100644
--- a/core/java/android/app/compat/ChangeIdStateQuery.java
+++ b/core/java/android/app/compat/ChangeIdStateQuery.java
@@ -35,6 +35,7 @@ import java.util.Objects;
* @hide
*/
@Immutable
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
final class ChangeIdStateQuery {
static final int QUERY_BY_PACKAGE_NAME = 0;
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index d7b2ab4351a4..643d4c96f7b9 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -39,6 +39,7 @@ import java.util.Set;
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatChanges {
private static final ChangeIdStateCache QUERY_CACHE = new ChangeIdStateCache();
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index ebc2945fb1a0..ffc1eec05667 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -36,6 +36,7 @@ import java.util.Objects;
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PackageOverride {
/** @hide */
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index c3d6340be41f..8ffda7242b37 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -18,8 +18,6 @@ package android.app.wallpaper;
import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
import android.annotation.FlaggedApi;
import android.app.WallpaperInfo;
import android.content.ComponentName;
@@ -31,7 +29,6 @@ import android.text.Html;
import android.text.Spanned;
import android.text.SpannedString;
import android.util.Log;
-import android.util.Xml;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -43,8 +40,6 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -154,46 +149,6 @@ public final class WallpaperDescription implements Parcelable {
return Objects.hash(mComponent, mId);
}
- ////// Stream read/write
-
- /**
- * Writes the content of the {@link WallpaperDescription} to a {@link OutputStream}.
- *
- * <p>The content can be read by {@link #readFromStream}. This method is intended for use by
- * trusted apps only, and the format is not guaranteed to be stable.</p>
- */
- public void writeToStream(@NonNull OutputStream outputStream) throws IOException {
- TypedXmlSerializer serializer = Xml.newFastSerializer();
- serializer.setOutput(outputStream, UTF_8.name());
- serializer.startTag(null, "description");
- try {
- saveToXml(serializer);
- } catch (XmlPullParserException e) {
- throw new IOException(e);
- }
- serializer.endTag(null, "description");
- serializer.flush();
- }
-
- /**
- * Reads a {@link PersistableBundle} from an {@link InputStream}.
- *
- * <p>The stream must be generated by {@link #writeToStream}. This method is intended for use by
- * trusted apps only, and the format is not guaranteed to be stable.</p>
- */
- @NonNull
- public static WallpaperDescription readFromStream(@NonNull InputStream inputStream)
- throws IOException {
- try {
- TypedXmlPullParser parser = Xml.newFastPullParser();
- parser.setInput(inputStream, UTF_8.name());
- parser.next();
- return WallpaperDescription.restoreFromXml(parser);
- } catch (XmlPullParserException e) {
- throw new IOException(e);
- }
- }
-
////// XML storage
/** @hide */
diff --git a/core/java/android/content/pm/IOnAppsChangedListener.aidl b/core/java/android/content/pm/IOnAppsChangedListener.aidl
index 830cbe0e0dd0..ade58c45b024 100644
--- a/core/java/android/content/pm/IOnAppsChangedListener.aidl
+++ b/core/java/android/content/pm/IOnAppsChangedListener.aidl
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.pm.LauncherUserInfo;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import android.os.UserHandle;
@@ -34,4 +35,5 @@ oneway interface IOnAppsChangedListener {
void onPackagesUnsuspended(in UserHandle user, in String[] packageNames);
void onShortcutChanged(in UserHandle user, String packageName, in ParceledListSlice shortcuts);
void onPackageLoadingProgressChanged(in UserHandle user, String packageName, float progress);
+ void onUserConfigChanged(in LauncherUserInfo launcherUserInfo);
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 26f919f99ee9..26b835689b67 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -182,6 +182,8 @@ public class LauncherApps {
*/
public static final int FLAG_CACHE_PEOPLE_TILE_SHORTCUTS = 2;
+ private static final String LAUNCHER_USER_INFO_EXTRA_KEY = "launcher_user_info";
+
/** @hide */
@IntDef(flag = false, prefix = { "FLAG_CACHE_" }, value = {
FLAG_CACHE_NOTIFICATION_SHORTCUTS,
@@ -349,6 +351,19 @@ public class LauncherApps {
*/
public void onPackageLoadingProgressChanged(@NonNull String packageName,
@NonNull UserHandle user, float progress) {}
+
+ /**
+ * Indicates {@link LauncherUserInfo} configs for a user have changed. The new
+ * {@link LauncherUserInfo} is given as a parameter.
+ *
+ * {@link LauncherUserInfo#getUserConfig} to get the updated user configs.
+ *
+ * @param launcherUserInfo The LauncherUserInfo of the user/profile whose configs have
+ * changed.
+ */
+ @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+ public void onUserConfigChanged(@NonNull LauncherUserInfo launcherUserInfo) {
+ }
}
/**
@@ -2168,6 +2183,21 @@ public class LauncherApps {
}
}
}
+
+ public void onUserConfigChanged(LauncherUserInfo launcherUserInfo) {
+ if (DEBUG) {
+ if (Flags.allowPrivateProfile()
+ && android.multiuser.Flags.addLauncherUserConfig()) {
+ Log.d(TAG, "OnUserConfigChanged for user type " + launcherUserInfo.getUserType()
+ + ", new userConfig: " + launcherUserInfo.getUserConfig());
+ }
+ }
+ synchronized (LauncherApps.this) {
+ for (CallbackMessageHandler callback : mCallbacks) {
+ callback.postOnUserConfigChanged(launcherUserInfo);
+ }
+ }
+ }
};
/**
@@ -2224,6 +2254,7 @@ public class LauncherApps {
private static final int MSG_UNSUSPENDED = 7;
private static final int MSG_SHORTCUT_CHANGED = 8;
private static final int MSG_LOADING_PROGRESS_CHANGED = 9;
+ private static final int MSG_USER_CONFIG_CHANGED = 10;
private final LauncherApps.Callback mCallback;
@@ -2278,6 +2309,14 @@ public class LauncherApps {
mCallback.onPackageLoadingProgressChanged(info.packageName, info.user,
info.mLoadingProgress);
break;
+ case MSG_USER_CONFIG_CHANGED:
+ if (Flags.allowPrivateProfile()
+ && android.multiuser.Flags.addLauncherUserConfig()) {
+ mCallback.onUserConfigChanged(Objects.requireNonNull(
+ info.launcherExtras.getParcelable(LAUNCHER_USER_INFO_EXTRA_KEY,
+ LauncherUserInfo.class)));
+ }
+ break;
}
}
@@ -2353,6 +2392,13 @@ public class LauncherApps {
info.mLoadingProgress = progress;
obtainMessage(MSG_LOADING_PROGRESS_CHANGED, info).sendToTarget();
}
+
+ public void postOnUserConfigChanged(LauncherUserInfo launcherUserInfo) {
+ CallbackInfo info = new CallbackInfo();
+ info.launcherExtras = new Bundle();
+ info.launcherExtras.putParcelable(LAUNCHER_USER_INFO_EXTRA_KEY, launcherUserInfo);
+ obtainMessage(MSG_USER_CONFIG_CHANGED, info).sendToTarget();
+ }
}
/**
diff --git a/core/java/android/content/pm/LauncherUserInfo.java b/core/java/android/content/pm/LauncherUserInfo.java
index 8426f54d4754..574af5902e89 100644
--- a/core/java/android/content/pm/LauncherUserInfo.java
+++ b/core/java/android/content/pm/LauncherUserInfo.java
@@ -18,6 +18,7 @@ package android.content.pm;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.os.Bundle;
import android.os.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,11 +32,25 @@ import android.os.UserManager;
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
public final class LauncherUserInfo implements Parcelable {
+ /**
+ * A boolean extra indicating whether the private space entrypoint should be hidden when locked.
+ *
+ * @see #getUserConfig
+ */
+ @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+ public static final String PRIVATE_SPACE_ENTRYPOINT_HIDDEN =
+ "private_space_entrypoint_hidden";
+
private final String mUserType;
// Serial number for the user, should be same as in the {@link UserInfo} object.
private final int mUserSerialNumber;
+ // Additional configs for the user, e.g., whether to hide the private space entrypoint when
+ // locked.
+ private final Bundle mUserConfig;
+
+
/**
* Returns type of the user as defined in {@link UserManager}. e.g.,
* {@link UserManager.USER_TYPE_PROFILE_MANAGED} or {@link UserManager.USER_TYPE_PROFILE_ClONE}
@@ -50,6 +65,17 @@ public final class LauncherUserInfo implements Parcelable {
}
/**
+ * Returns additional configs for this launcher user
+ *
+ * @see #PRIVATE_SPACE_ENTRYPOINT_HIDDEN
+ */
+ @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+ @NonNull
+ public Bundle getUserConfig() {
+ return mUserConfig;
+ }
+
+ /**
* Returns serial number of user as returned by
* {@link UserManager#getSerialNumberForUser(UserHandle)}
*
@@ -63,6 +89,7 @@ public final class LauncherUserInfo implements Parcelable {
private LauncherUserInfo(@NonNull Parcel in) {
mUserType = in.readString16NoHelper();
mUserSerialNumber = in.readInt();
+ mUserConfig = in.readBundle(Bundle.class.getClassLoader());
}
@Override
@@ -70,6 +97,7 @@ public final class LauncherUserInfo implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString16NoHelper(mUserType);
dest.writeInt(mUserSerialNumber);
+ dest.writeBundle(mUserConfig);
}
@Override
@@ -99,23 +127,36 @@ public final class LauncherUserInfo implements Parcelable {
private final String mUserType;
private final int mUserSerialNumber;
+ private final Bundle mUserConfig;
+
+
+ @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+ public Builder(@NonNull String userType, int userSerialNumber, @NonNull Bundle config) {
+ this.mUserType = userType;
+ this.mUserSerialNumber = userSerialNumber;
+ this.mUserConfig = config;
+ }
public Builder(@NonNull String userType, int userSerialNumber) {
this.mUserType = userType;
this.mUserSerialNumber = userSerialNumber;
+ this.mUserConfig = new Bundle();
}
/**
* Builds the LauncherUserInfo object
*/
- @NonNull public LauncherUserInfo build() {
- return new LauncherUserInfo(this.mUserType, this.mUserSerialNumber);
+ @NonNull
+ public LauncherUserInfo build() {
+ return new LauncherUserInfo(this.mUserType, this.mUserSerialNumber, this.mUserConfig);
}
} // End builder
- private LauncherUserInfo(@NonNull String userType, int userSerialNumber) {
+ private LauncherUserInfo(@NonNull String userType, int userSerialNumber,
+ @NonNull Bundle config) {
this.mUserType = userType;
this.mUserSerialNumber = userSerialNumber;
+ this.mUserConfig = config;
}
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 5b0cee75e591..4285b0a2b91a 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -251,6 +251,7 @@ public class ServiceInfo extends ComponentInfo
* {@link android.Manifest.permission#NFC},
* {@link android.Manifest.permission#TRANSMIT_IR},
* {@link android.Manifest.permission#UWB_RANGING},
+ * {@link android.Manifest.permission#RANGING},
* or has been granted the access to one of the attached USB devices/accessories.
*/
@RequiresPermission(
@@ -267,6 +268,7 @@ public class ServiceInfo extends ComponentInfo
Manifest.permission.NFC,
Manifest.permission.TRANSMIT_IR,
Manifest.permission.UWB_RANGING,
+ Manifest.permission.RANGING,
},
conditional = true
)
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 6f70586881be..fff980fa8585 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -349,3 +349,12 @@ flag {
bug: "364760703"
is_fixed_read_only: true
}
+
+flag {
+ name: "cloud_compilation_pm"
+ is_exported: true
+ namespace: "package_manager_service"
+ description: "Feature flag to enable the Cloud Compilation support on the package manager side."
+ bug: "377474232"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 528bde80cd3d..3d89ce12dec4 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -543,3 +543,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "add_launcher_user_config"
+ namespace: "profile_experiences"
+ description: "Add support for LauncherUserInfo configs"
+ bug: "346553745"
+}
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index e98fc0c9d02c..26ecbd1982d5 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -83,3 +83,15 @@ flag {
bug: "364035303"
}
+flag {
+ name: "system_context_handle_app_info_changed"
+ is_exported: true
+ namespace: "resource_manager"
+ description: "Feature flag for allowing system context to handle application info changes"
+ bug: "362420029"
+ # This flag is read at boot time.
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/hardware/DataSpace.java b/core/java/android/hardware/DataSpace.java
index 611738435f7e..1cd9244333c5 100644
--- a/core/java/android/hardware/DataSpace.java
+++ b/core/java/android/hardware/DataSpace.java
@@ -420,18 +420,38 @@ public final class DataSpace {
public static final int DATASPACE_HEIF = 4100;
/**
- * ISO/IEC TBD
+ * Ultra HDR
*
- * JPEG image with embedded recovery map following the Jpeg/R specification.
+ * JPEG image with embedded HDR gain map following the Ultra HDR specification and
+ * starting with Android version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM V}
+ * ISO/CD 21496‐1
*
- * <p>This value must always remain aligned with the public ImageFormat Jpeg/R definition and is
- * valid with formats:
- * HAL_PIXEL_FORMAT_BLOB: JPEG image encoded by Jpeg/R encoder according to ISO/IEC TBD.
- * The image contains a standard SDR JPEG and a recovery map. Jpeg/R decoders can use the
- * map to recover the input image.</p>
+ * <p>This value is valid with formats:</p>
+ * <ul>
+ * <li>HAL_PIXEL_FORMAT_BLOB: JPEG image encoded by Jpeg/R encoder according to
+ * ISO/CD 21496‐1</li>
+ * </ul>
+ * <p>
+ * The image contains a standard SDR JPEG and a gain map. Ultra HDR decoders can use the
+ * gain map to boost the brightness of the rendered image.</p>
*/
public static final int DATASPACE_JPEG_R = 4101;
+ /**
+ * ISO/IEC 23008-12:2024
+ *
+ * High Efficiency Image File Format (HEIF) with embedded HDR gain map
+ *
+ * <p>This value is valid with formats:</p>
+ * <ul>
+ * <li>HAL_PIXEL_FORMAT_BLOB: A HEIC image encoded by HEVC encoder
+ * according to ISO/IEC 23008-12:2024 that includes an HDR gain map and
+ * metadata according to ISO/CD 21496‐1.</li>
+ * </ul>
+ */
+ @FlaggedApi(com.android.internal.camera.flags.Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final int DATASPACE_HEIF_ULTRAHDR = 4102;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
@@ -660,6 +680,7 @@ public final class DataSpace {
DATASPACE_DEPTH,
DATASPACE_DYNAMIC_DEPTH,
DATASPACE_HEIF,
+ DATASPACE_HEIF_ULTRAHDR,
DATASPACE_JPEG_R,
DATASPACE_UNKNOWN,
DATASPACE_SCRGB_LINEAR,
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 16d82caa6c1a..a37648f7e45d 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -5775,6 +5775,122 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
/**
+ * <p>The available HEIC (ISO/IEC 23008-12/24) UltraHDR stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ * <p>The configurations are listed as <code>(format, width, height, input?)</code> tuples.</p>
+ * <p>All the static, control, and dynamic metadata tags related to JPEG apply to HEIC formats.
+ * Configuring JPEG and HEIC streams at the same time is not supported.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_STREAM_CONFIGURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.heic.availableHeicUltraHdrStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for HEIC UltraHDR output formats.</p>
+ * <p>This should correspond to the frame duration when only that
+ * stream is active, with all processing (typically in android.*.mode)
+ * set to either OFF or FAST.</p>
+ * <p>When multiple streams are used in a request, the minimum frame
+ * duration will be max(individual stream min durations).</p>
+ * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
+ * android.scaler.availableStallDurations for more details about
+ * calculating the max frame rate.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_MIN_FRAME_DURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicUltraHdrMinFrameDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for HEIC UltraHDR streams.</p>
+ * <p>A stall duration is how much extra time would get added
+ * to the normal minimum frame duration for a repeating request
+ * that has streams with non-zero stall.</p>
+ * <p>This functions similarly to
+ * android.scaler.availableStallDurations for HEIC UltraHDR
+ * streams.</p>
+ * <p>All HEIC output stream formats may have a nonzero stall
+ * duration.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_STALL_DURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicUltraHdrStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>The available HEIC (ISO/IEC 23008-12/24) UltraHDR stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream) for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Refer to android.heic.availableHeicStreamConfigurations for details.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.heic.availableHeicUltraHdrStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for HEIC UltraHDR output formats for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Refer to android.heic.availableHeicMinFrameDurations for details.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicUltraHdrMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for HEIC UltraHDR streams for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Refer to android.heic.availableHeicStallDurations for details.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicUltraHdrStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
* <p>The direction of the camera faces relative to the vehicle body frame and the
* passenger seats.</p>
* <p>This enum defines the lens facing characteristic of the cameras on the automotive
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index ef7f3f8ab58b..e22c263e893d 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1385,6 +1385,9 @@ public class CameraMetadataNative implements Parcelable {
/*jpegRconfiguration*/ null,
/*jpegRminduration*/ null,
/*jpegRstallduration*/ null,
+ /*heicUltraHDRconfiguration*/ null,
+ /*heicUltraHDRminduration*/ null,
+ /*heicUltraHDRstallduration*/ null,
/*highspeedvideoconfigurations*/ null,
/*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]);
break;
@@ -1402,6 +1405,9 @@ public class CameraMetadataNative implements Parcelable {
/*jpegRconfiguration*/ null,
/*jpegRminduration*/ null,
/*jpegRstallduration*/ null,
+ /*heicUltraHDRconfiguration*/ null,
+ /*heicUltraHDRminduration*/ null,
+ /*heicUltraHDRstallduration*/ null,
highSpeedVideoConfigurations,
/*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]);
break;
@@ -1419,6 +1425,9 @@ public class CameraMetadataNative implements Parcelable {
/*jpegRconfiguration*/ null,
/*jpegRminduration*/ null,
/*jpegRstallduration*/ null,
+ /*heicUltraHDRcconfiguration*/ null,
+ /*heicUltraHDRminduration*/ null,
+ /*heicUltraHDRstallduration*/ null,
/*highSpeedVideoConfigurations*/ null,
inputOutputFormatsMap, listHighResolution, supportsPrivate[i]);
break;
@@ -1436,6 +1445,9 @@ public class CameraMetadataNative implements Parcelable {
/*jpegRconfiguration*/ null,
/*jpegRminduration*/ null,
/*jpegRstallduration*/ null,
+ /*heicUltraHDRcconfiguration*/ null,
+ /*heicUltraHDRminduration*/ null,
+ /*heicUltraHDRstallduration*/ null,
/*highSpeedVideoConfigurations*/ null,
/*inputOutputFormatsMap*/ null, listHighResolution, supportsPrivate[i]);
}
@@ -1607,6 +1619,17 @@ public class CameraMetadataNative implements Parcelable {
CameraCharacteristics.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS);
StreamConfigurationDuration[] heicStallDurations = getBase(
CameraCharacteristics.HEIC_AVAILABLE_HEIC_STALL_DURATIONS);
+ StreamConfiguration[] heicUltraHDRConfigurations = null;
+ StreamConfigurationDuration[] heicUltraHDRMinFrameDurations = null;
+ StreamConfigurationDuration[] heicUltraHDRStallDurations = null;
+ if (Flags.cameraHeifGainmap()) {
+ heicUltraHDRConfigurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_STREAM_CONFIGURATIONS);
+ heicUltraHDRMinFrameDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_MIN_FRAME_DURATIONS);
+ heicUltraHDRStallDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_STALL_DURATIONS);
+ }
StreamConfiguration[] jpegRConfigurations = getBase(
CameraCharacteristics.JPEGR_AVAILABLE_JPEG_R_STREAM_CONFIGURATIONS);
StreamConfigurationDuration[] jpegRMinFrameDurations = getBase(
@@ -1625,7 +1648,8 @@ public class CameraMetadataNative implements Parcelable {
dynamicDepthStallDurations, heicConfigurations,
heicMinFrameDurations, heicStallDurations,
jpegRConfigurations, jpegRMinFrameDurations, jpegRStallDurations,
- highSpeedVideoConfigurations, inputOutputFormatsMap,
+ heicUltraHDRConfigurations, heicUltraHDRMinFrameDurations,
+ heicUltraHDRStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap,
listHighResolution);
}
@@ -1662,6 +1686,17 @@ public class CameraMetadataNative implements Parcelable {
CameraCharacteristics.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
StreamConfigurationDuration[] heicStallDurations = getBase(
CameraCharacteristics.HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfiguration[] heicUltraHDRConfigurations = null;
+ StreamConfigurationDuration[] heicUltraHDRMinFrameDurations = null;
+ StreamConfigurationDuration[] heicUltraHDRStallDurations = null;
+ if (Flags.cameraHeifGainmap()) {
+ heicUltraHDRConfigurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+ heicUltraHDRMinFrameDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+ heicUltraHDRStallDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+ }
StreamConfiguration[] jpegRConfigurations = getBase(
CameraCharacteristics.JPEGR_AVAILABLE_JPEG_R_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
StreamConfigurationDuration[] jpegRMinFrameDurations = getBase(
@@ -1681,7 +1716,8 @@ public class CameraMetadataNative implements Parcelable {
dynamicDepthStallDurations, heicConfigurations,
heicMinFrameDurations, heicStallDurations,
jpegRConfigurations, jpegRMinFrameDurations, jpegRStallDurations,
- highSpeedVideoConfigurations, inputOutputFormatsMap,
+ heicUltraHDRConfigurations, heicUltraHDRMinFrameDurations,
+ heicUltraHDRStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap,
listHighResolution, false);
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index e3dbb2bbbf90..ec028bf6dcbb 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkArrayElementsNotNull;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
+import android.hardware.DataSpace;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
@@ -31,6 +32,8 @@ import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
+import com.android.internal.camera.flags.Flags;
+
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
@@ -100,6 +103,12 @@ public final class StreamConfigurationMap {
* {@link StreamConfigurationDuration}
* @param jpegRStallDurations a non-{@code null} array of Jpeg/R
* {@link StreamConfigurationDuration}
+ * @param heicUltraHDRConfigurations a non-{@code null} array of Heic UltraHDR
+ * {@link StreamConfiguration}
+ * @param heicUltraHDRMinFrameDurations a non-{@code null} array of Heic UltraHDR
+ * {@link StreamConfigurationDuration}
+ * @param heicUltraHDRStallDurations a non-{@code null} array of Heic UltraHDR
+ * {@link StreamConfigurationDuration}
* @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if
* camera device does not support high speed video recording
* @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE
@@ -125,6 +134,9 @@ public final class StreamConfigurationMap {
StreamConfiguration[] jpegRConfigurations,
StreamConfigurationDuration[] jpegRMinFrameDurations,
StreamConfigurationDuration[] jpegRStallDurations,
+ StreamConfiguration[] heicUltraHDRConfigurations,
+ StreamConfigurationDuration[] heicUltraHDRMinFrameDurations,
+ StreamConfigurationDuration[] heicUltraHDRStallDurations,
HighSpeedVideoConfiguration[] highSpeedVideoConfigurations,
ReprocessFormatsMap inputOutputFormatsMap,
boolean listHighResolution) {
@@ -134,8 +146,9 @@ public final class StreamConfigurationMap {
dynamicDepthStallDurations,
heicConfigurations, heicMinFrameDurations, heicStallDurations,
jpegRConfigurations, jpegRMinFrameDurations, jpegRStallDurations,
- highSpeedVideoConfigurations, inputOutputFormatsMap, listHighResolution,
- /*enforceImplementationDefined*/ true);
+ heicUltraHDRConfigurations, heicUltraHDRMinFrameDurations,
+ heicUltraHDRStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap,
+ listHighResolution, /*enforceImplementationDefined*/ true);
}
/**
@@ -168,6 +181,12 @@ public final class StreamConfigurationMap {
* {@link StreamConfigurationDuration}
* @param jpegRStallDurations a non-{@code null} array of Jpeg/R
* {@link StreamConfigurationDuration}
+ * @param heicUltraHDRConfigurations an array of Heic UltraHDR
+ * {@link StreamConfiguration}, {@code null} if camera doesn't support the format
+ * @param heicUltraHDRMinFrameDurations an array of Heic UltraHDR
+ * {@link StreamConfigurationDuration}, {@code null} if camera doesn't support the format
+ * @param heicUltraHDRStallDurations an array of Heic UltraHDR
+ * {@link StreamConfigurationDuration}, {@code null} if camera doesn't support the format
* @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if
* camera device does not support high speed video recording
* @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE
@@ -195,6 +214,9 @@ public final class StreamConfigurationMap {
StreamConfiguration[] jpegRConfigurations,
StreamConfigurationDuration[] jpegRMinFrameDurations,
StreamConfigurationDuration[] jpegRStallDurations,
+ StreamConfiguration[] heicUltraHDRConfigurations,
+ StreamConfigurationDuration[] heicUltraHDRMinFrameDurations,
+ StreamConfigurationDuration[] heicUltraHDRStallDurations,
HighSpeedVideoConfiguration[] highSpeedVideoConfigurations,
ReprocessFormatsMap inputOutputFormatsMap,
boolean listHighResolution,
@@ -259,6 +281,18 @@ public final class StreamConfigurationMap {
"heicStallDurations");
}
+ if (heicUltraHDRConfigurations == null || (!Flags.cameraHeifGainmap())) {
+ mHeicUltraHDRConfigurations = new StreamConfiguration[0];
+ mHeicUltraHDRMinFrameDurations = new StreamConfigurationDuration[0];
+ mHeicUltraHDRStallDurations = new StreamConfigurationDuration[0];
+ } else {
+ mHeicUltraHDRConfigurations = checkArrayElementsNotNull(heicUltraHDRConfigurations,
+ "heicUltraHDRConfigurations");
+ mHeicUltraHDRMinFrameDurations = checkArrayElementsNotNull(
+ heicUltraHDRMinFrameDurations, "heicUltraHDRMinFrameDurations");
+ mHeicUltraHDRStallDurations = checkArrayElementsNotNull(heicUltraHDRStallDurations,
+ "heicUltraHDRStallDurations");
+ }
if (jpegRConfigurations == null) {
mJpegRConfigurations = new StreamConfiguration[0];
@@ -336,6 +370,19 @@ public final class StreamConfigurationMap {
mHeicOutputFormats.get(config.getFormat()) + 1);
}
+ if (Flags.cameraHeifGainmap()) {
+ // For each Heic UlrtaHDR format, track how many sizes there are available to configure
+ for (StreamConfiguration config : mHeicUltraHDRConfigurations) {
+ if (!config.isOutput()) {
+ // Ignoring input Heic UltraHDR configs
+ continue;
+ }
+
+ mHeicUltraHDROutputFormats.put(config.getFormat(),
+ mHeicUltraHDROutputFormats.get(config.getFormat()) + 1);
+ }
+ }
+
// For each Jpeg/R format, track how many sizes there are available to configure
for (StreamConfiguration config : mJpegRConfigurations) {
if (!config.isOutput()) {
@@ -483,6 +530,11 @@ public final class StreamConfigurationMap {
int internalFormat = imageFormatToInternal(format);
int dataspace = imageFormatToDataspace(format);
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ return mHeicUltraHDROutputFormats.indexOfKey(internalFormat) >= 0;
+ }
+ }
if (dataspace == HAL_DATASPACE_DEPTH) {
return mDepthOutputFormats.indexOfKey(internalFormat) >= 0;
} else if (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) {
@@ -607,6 +659,11 @@ public final class StreamConfigurationMap {
surfaceDataspace == HAL_DATASPACE_HEIF ? mHeicConfigurations :
surfaceDataspace == HAL_DATASPACE_JPEG_R ? mJpegRConfigurations :
mConfigurations;
+ if (Flags.cameraHeifGainmap()) {
+ if (surfaceDataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ configs = mHeicUltraHDRConfigurations;
+ }
+ }
for (StreamConfiguration config : configs) {
if (config.getFormat() == surfaceFormat && config.isOutput()) {
// Matching format, either need exact size match, or a flexible consumer
@@ -646,6 +703,11 @@ public final class StreamConfigurationMap {
dataspace == HAL_DATASPACE_HEIF ? mHeicConfigurations :
dataspace == HAL_DATASPACE_JPEG_R ? mJpegRConfigurations :
mConfigurations;
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR ) {
+ configs = mHeicUltraHDRConfigurations;
+ }
+ }
for (StreamConfiguration config : configs) {
if ((config.getFormat() == internalFormat) && config.isOutput() &&
config.getSize().equals(size)) {
@@ -1176,6 +1238,10 @@ public final class StreamConfigurationMap {
Arrays.equals(mHeicConfigurations, other.mHeicConfigurations) &&
Arrays.equals(mHeicMinFrameDurations, other.mHeicMinFrameDurations) &&
Arrays.equals(mHeicStallDurations, other.mHeicStallDurations) &&
+ Arrays.equals(mHeicUltraHDRConfigurations, other.mHeicUltraHDRConfigurations) &&
+ Arrays.equals(mHeicUltraHDRMinFrameDurations,
+ other.mHeicUltraHDRMinFrameDurations) &&
+ Arrays.equals(mHeicUltraHDRStallDurations, other.mHeicUltraHDRStallDurations) &&
Arrays.equals(mJpegRConfigurations, other.mJpegRConfigurations) &&
Arrays.equals(mJpegRMinFrameDurations, other.mJpegRMinFrameDurations) &&
Arrays.equals(mJpegRStallDurations, other.mJpegRStallDurations) &&
@@ -1197,8 +1263,9 @@ public final class StreamConfigurationMap {
mDynamicDepthConfigurations, mDynamicDepthMinFrameDurations,
mDynamicDepthStallDurations, mHeicConfigurations,
mHeicMinFrameDurations, mHeicStallDurations,
- mJpegRConfigurations, mJpegRMinFrameDurations, mJpegRStallDurations,
- mHighSpeedVideoConfigurations);
+ mHeicUltraHDRConfigurations, mHeicUltraHDRMinFrameDurations,
+ mHeicUltraHDRStallDurations, mJpegRConfigurations, mJpegRMinFrameDurations,
+ mJpegRStallDurations, mHighSpeedVideoConfigurations);
}
// Check that the argument is supported by #getOutputFormats or #getInputFormats
@@ -1209,6 +1276,13 @@ public final class StreamConfigurationMap {
int internalDataspace = imageFormatToDataspace(format);
if (output) {
+ if (Flags.cameraHeifGainmap()) {
+ if (internalDataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ if (mHeicUltraHDROutputFormats.indexOfKey(internalFormat) >= 0) {
+ return format;
+ }
+ }
+ }
if (internalDataspace == HAL_DATASPACE_DEPTH) {
if (mDepthOutputFormats.indexOfKey(internalFormat) >= 0) {
return format;
@@ -1429,6 +1503,7 @@ public final class StreamConfigurationMap {
* <li>ImageFormat.DEPTH_POINT_CLOUD => HAL_PIXEL_FORMAT_BLOB
* <li>ImageFormat.DEPTH_JPEG => HAL_PIXEL_FORMAT_BLOB
* <li>ImageFormat.HEIC => HAL_PIXEL_FORMAT_BLOB
+ * <li>ImageFormat.HEIC_ULTRAHDR => HAL_PIXEL_FORMAT_BLOB
* <li>ImageFormat.JPEG_R => HAL_PIXEL_FORMAT_BLOB
* <li>ImageFormat.DEPTH16 => HAL_PIXEL_FORMAT_Y16
* </ul>
@@ -1451,6 +1526,11 @@ public final class StreamConfigurationMap {
* if {@code format} was {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}
*/
static int imageFormatToInternal(int format) {
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ return HAL_PIXEL_FORMAT_BLOB;
+ }
+ }
switch (format) {
case ImageFormat.JPEG:
case ImageFormat.DEPTH_POINT_CLOUD:
@@ -1480,6 +1560,7 @@ public final class StreamConfigurationMap {
* <li>ImageFormat.DEPTH16 => HAL_DATASPACE_DEPTH
* <li>ImageFormat.DEPTH_JPEG => HAL_DATASPACE_DYNAMIC_DEPTH
* <li>ImageFormat.HEIC => HAL_DATASPACE_HEIF
+ * <li>ImageFormat.HEIC_ULTRAHDR => DATASPACE_HEIF_ULTRAHDR
* <li>ImageFormat.JPEG_R => HAL_DATASPACE_JPEG_R
* <li>ImageFormat.YUV_420_888 => HAL_DATASPACE_JFIF
* <li>ImageFormat.RAW_SENSOR => HAL_DATASPACE_ARBITRARY
@@ -1508,6 +1589,11 @@ public final class StreamConfigurationMap {
* if {@code format} was {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}
*/
static int imageFormatToDataspace(int format) {
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ return DataSpace.DATASPACE_HEIF_ULTRAHDR;
+ }
+ }
switch (format) {
case ImageFormat.JPEG:
return HAL_DATASPACE_V0_JFIF;
@@ -1584,13 +1670,21 @@ public final class StreamConfigurationMap {
dataspace == HAL_DATASPACE_JPEG_R ? mJpegROutputFormats :
highRes ? mHighResOutputFormats :
mOutputFormats;
-
+ boolean isDataSpaceHeifUltraHDR = false;
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ formatsMap = mHeicUltraHDROutputFormats;
+ isDataSpaceHeifUltraHDR = true;
+ }
+ }
int sizesCount = formatsMap.get(format);
if ( ((!output || (dataspace == HAL_DATASPACE_DEPTH || dataspace == HAL_DATASPACE_JPEG_R ||
dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ||
- dataspace == HAL_DATASPACE_HEIF)) && sizesCount == 0) ||
+ dataspace == HAL_DATASPACE_HEIF ||
+ isDataSpaceHeifUltraHDR)) && sizesCount == 0) ||
(output && (dataspace != HAL_DATASPACE_DEPTH && dataspace != HAL_DATASPACE_JPEG_R &&
dataspace != HAL_DATASPACE_DYNAMIC_DEPTH &&
+ !isDataSpaceHeifUltraHDR &&
dataspace != HAL_DATASPACE_HEIF) &&
mAllOutputFormats.get(format) == 0)) {
return null;
@@ -1604,12 +1698,14 @@ public final class StreamConfigurationMap {
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicConfigurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRConfigurations :
+ (isDataSpaceHeifUltraHDR) ? mHeicUltraHDRConfigurations :
mConfigurations;
StreamConfigurationDuration[] minFrameDurations =
(dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations :
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthMinFrameDurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicMinFrameDurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRMinFrameDurations :
+ (isDataSpaceHeifUltraHDR) ? mHeicUltraHDRMinFrameDurations :
mMinFrameDurations;
for (StreamConfiguration config : configurations) {
@@ -1639,7 +1735,8 @@ public final class StreamConfigurationMap {
// Dynamic depth streams can have both fast and also high res modes.
if ((sizeIndex != sizesCount) && (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ||
- dataspace == HAL_DATASPACE_HEIF) || (dataspace == HAL_DATASPACE_JPEG_R)) {
+ dataspace == HAL_DATASPACE_HEIF) || (dataspace == HAL_DATASPACE_JPEG_R) ||
+ isDataSpaceHeifUltraHDR) {
if (sizeIndex > sizesCount) {
throw new AssertionError(
@@ -1682,6 +1779,11 @@ public final class StreamConfigurationMap {
if (mHeicOutputFormats.size() > 0) {
formats[i++] = ImageFormat.HEIC;
}
+ if (Flags.cameraHeifGainmap()) {
+ if (mHeicUltraHDROutputFormats.size() > 0) {
+ formats[i++] = ImageFormat.HEIC_ULTRAHDR;
+ }
+ }
if (mJpegROutputFormats.size() > 0) {
formats[i++] = ImageFormat.JPEG_R;
}
@@ -1725,12 +1827,19 @@ public final class StreamConfigurationMap {
* @see #DURATION_STALL
* */
private StreamConfigurationDuration[] getDurations(int duration, int dataspace) {
+ boolean isDataSpaceHeifUltraHDR = false;
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ isDataSpaceHeifUltraHDR = true;
+ }
+ }
switch (duration) {
case DURATION_MIN_FRAME:
return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations :
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ?
mDynamicDepthMinFrameDurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicMinFrameDurations :
+ isDataSpaceHeifUltraHDR ? mHeicUltraHDRMinFrameDurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRMinFrameDurations :
mMinFrameDurations;
@@ -1738,6 +1847,7 @@ public final class StreamConfigurationMap {
return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthStallDurations :
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthStallDurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicStallDurations :
+ isDataSpaceHeifUltraHDR ? mHeicUltraHDRStallDurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRStallDurations :
mStallDurations;
default:
@@ -1754,6 +1864,7 @@ public final class StreamConfigurationMap {
size += mDynamicDepthOutputFormats.size();
size += mHeicOutputFormats.size();
size += mJpegROutputFormats.size();
+ size += mHeicUltraHDROutputFormats.size();
}
return size;
@@ -1774,11 +1885,18 @@ public final class StreamConfigurationMap {
}
private boolean isSupportedInternalConfiguration(int format, int dataspace, Size size) {
+ boolean isDataSpaceHeifUltraHDR = false;
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ isDataSpaceHeifUltraHDR = true;
+ }
+ }
StreamConfiguration[] configurations =
(dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations :
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicConfigurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRConfigurations :
+ isDataSpaceHeifUltraHDR ? mHeicUltraHDRConfigurations :
mConfigurations;
for (int i = 0; i < configurations.length; i++) {
@@ -1954,6 +2072,11 @@ public final class StreamConfigurationMap {
* @hide
*/
public static String formatToString(int format) {
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ return "HEIC_ULTRAHDR";
+ }
+ }
switch (format) {
case ImageFormat.YV12:
return "YV12";
@@ -2078,6 +2201,10 @@ public final class StreamConfigurationMap {
private final StreamConfigurationDuration[] mHeicMinFrameDurations;
private final StreamConfigurationDuration[] mHeicStallDurations;
+ private final StreamConfiguration[] mHeicUltraHDRConfigurations;
+ private final StreamConfigurationDuration[] mHeicUltraHDRMinFrameDurations;
+ private final StreamConfigurationDuration[] mHeicUltraHDRStallDurations;
+
private final StreamConfiguration[] mJpegRConfigurations;
private final StreamConfigurationDuration[] mJpegRMinFrameDurations;
private final StreamConfigurationDuration[] mJpegRStallDurations;
@@ -2103,6 +2230,8 @@ public final class StreamConfigurationMap {
private final SparseIntArray mDynamicDepthOutputFormats = new SparseIntArray();
/** internal format -> num heic output sizes mapping, for HAL_DATASPACE_HEIF */
private final SparseIntArray mHeicOutputFormats = new SparseIntArray();
+ /** internal format -> num heic output sizes mapping, for DATASPACE_HEIF_GAINMAP */
+ private final SparseIntArray mHeicUltraHDROutputFormats = new SparseIntArray();
/** internal format -> num Jpeg/R output sizes mapping, for HAL_DATASPACE_JPEG_R */
private final SparseIntArray mJpegROutputFormats = new SparseIntArray();
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 3c6841cd8aeb..56307ae53a0c 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -1432,6 +1432,13 @@ public final class DisplayManagerGlobal {
mExecutor.execute(mCallback::onStopped);
}
}
+
+ @Override // Binder call
+ public void onRequestedBrightnessChanged(float brightness) {
+ if (mCallback != null) {
+ mExecutor.execute(() -> mCallback.onRequestedBrightnessChanged(brightness));
+ }
+ }
}
/**
diff --git a/core/java/android/hardware/display/IVirtualDisplayCallback.aidl b/core/java/android/hardware/display/IVirtualDisplayCallback.aidl
index c3490d177be2..9cc0364f2729 100644
--- a/core/java/android/hardware/display/IVirtualDisplayCallback.aidl
+++ b/core/java/android/hardware/display/IVirtualDisplayCallback.aidl
@@ -38,4 +38,9 @@ oneway interface IVirtualDisplayCallback {
* of the application to release() the virtual display.
*/
void onStopped();
+
+ /**
+ * Called when the virtual display's requested brightness has changed.
+ */
+ void onRequestedBrightnessChanged(float brightness);
}
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 32b640583734..3b573ea98c27 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -16,6 +16,8 @@
package android.hardware.display;
import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.SystemApi;
import android.view.Display;
import android.view.Surface;
@@ -164,5 +166,25 @@ public final class VirtualDisplay {
* of the application to release() the virtual display.
*/
public void onStopped() { }
+
+ /**
+ * Called when the requested brightness of the display has changed.
+ *
+ * <p>The system may adjust the display's brightness based on user or app activity. This
+ * callback will only be invoked if the display has an explicitly specified default
+ * brightness value.</p>
+ *
+ * <p>Value of {@code 0.0} indicates the minimum supported brightness and value of
+ * {@code 1.0} indicates the maximum supported brightness.</p>
+ *
+ * @see android.view.View#setKeepScreenOn(boolean)
+ * @see android.view.WindowManager.LayoutParams#screenBrightness
+ * @see VirtualDisplayConfig.Builder#setDefaultBrightness(float)
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @SystemApi
+ public void onRequestedBrightnessChanged(
+ @FloatRange(from = 0.0f, to = 1.0f) float brightness) {}
}
}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 49944c76eb99..57d9d28a9d47 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -29,6 +29,7 @@ import android.media.projection.MediaProjection;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PowerManager;
import android.util.ArraySet;
import android.view.Display;
import android.view.DisplayCutout;
@@ -61,6 +62,7 @@ public final class VirtualDisplayConfig implements Parcelable {
private final boolean mIsHomeSupported;
private final DisplayCutout mDisplayCutout;
private final boolean mIgnoreActivitySizeRestrictions;
+ private final float mDefaultBrightness;
private VirtualDisplayConfig(
@NonNull String name,
@@ -76,7 +78,8 @@ public final class VirtualDisplayConfig implements Parcelable {
float requestedRefreshRate,
boolean isHomeSupported,
@Nullable DisplayCutout displayCutout,
- boolean ignoreActivitySizeRestrictions) {
+ boolean ignoreActivitySizeRestrictions,
+ @FloatRange(from = 0.0f, to = 1.0f) float defaultBrightness) {
mName = name;
mWidth = width;
mHeight = height;
@@ -91,6 +94,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mIsHomeSupported = isHomeSupported;
mDisplayCutout = displayCutout;
mIgnoreActivitySizeRestrictions = ignoreActivitySizeRestrictions;
+ mDefaultBrightness = defaultBrightness;
}
/**
@@ -157,6 +161,22 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Returns the default brightness of the display.
+ *
+ * <p>Value of {@code 0.0} indicates the minimum supported brightness and value of {@code 1.0}
+ * indicates the maximum supported brightness.</p>
+ *
+ * @see Builder#setDefaultBrightness(float)
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @SystemApi
+ public @FloatRange(from = 0.0f, to = 1.0f) float getDefaultBrightness() {
+ return mDefaultBrightness;
+ }
+
+
+ /**
* Returns the unique identifier for the display. Shouldn't be displayed to the user.
* @hide
*/
@@ -245,6 +265,7 @@ public final class VirtualDisplayConfig implements Parcelable {
dest.writeBoolean(mIsHomeSupported);
DisplayCutout.ParcelableWrapper.writeCutoutToParcel(mDisplayCutout, dest, flags);
dest.writeBoolean(mIgnoreActivitySizeRestrictions);
+ dest.writeFloat(mDefaultBrightness);
}
@Override
@@ -272,7 +293,8 @@ public final class VirtualDisplayConfig implements Parcelable {
&& mRequestedRefreshRate == that.mRequestedRefreshRate
&& mIsHomeSupported == that.mIsHomeSupported
&& mIgnoreActivitySizeRestrictions == that.mIgnoreActivitySizeRestrictions
- && Objects.equals(mDisplayCutout, that.mDisplayCutout);
+ && Objects.equals(mDisplayCutout, that.mDisplayCutout)
+ && mDefaultBrightness == that.mDefaultBrightness;
}
@Override
@@ -281,7 +303,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
mRequestedRefreshRate, mIsHomeSupported, mDisplayCutout,
- mIgnoreActivitySizeRestrictions);
+ mIgnoreActivitySizeRestrictions, mDefaultBrightness);
return hashCode;
}
@@ -303,6 +325,7 @@ public final class VirtualDisplayConfig implements Parcelable {
+ " mIsHomeSupported=" + mIsHomeSupported
+ " mDisplayCutout=" + mDisplayCutout
+ " mIgnoreActivitySizeRestrictions=" + mIgnoreActivitySizeRestrictions
+ + " mDefaultBrightness=" + mDefaultBrightness
+ ")";
}
@@ -321,6 +344,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mIsHomeSupported = in.readBoolean();
mDisplayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(in);
mIgnoreActivitySizeRestrictions = in.readBoolean();
+ mDefaultBrightness = in.readFloat();
}
@NonNull
@@ -355,6 +379,7 @@ public final class VirtualDisplayConfig implements Parcelable {
private boolean mIsHomeSupported = false;
private DisplayCutout mDisplayCutout = null;
private boolean mIgnoreActivitySizeRestrictions = false;
+ private float mDefaultBrightness = 0.0f;
/**
* Creates a new Builder.
@@ -547,6 +572,35 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Sets the default brightness of the display.
+ *
+ * <p>The system will use this brightness value whenever the display should be bright, i.e.
+ * it is powered on and not dimmed due to user activity or app activity.</p>
+ *
+ * <p>Value of {@code 0.0} indicates the minimum supported brightness and value of
+ * {@code 1.0} indicates the maximum supported brightness.</p>
+ *
+ * <p>If unset, defaults to {@code 0.0}</p>
+ *
+ * @see android.view.View#setKeepScreenOn(boolean)
+ * @see Builder#setDefaultBrightness(float)
+ * @see VirtualDisplay.Callback#onRequestedBrightnessChanged(float)
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @SystemApi
+ @NonNull
+ public Builder setDefaultBrightness(@FloatRange(from = 0.0f, to = 1.0f) float brightness) {
+ if (brightness < PowerManager.BRIGHTNESS_MIN
+ || brightness > PowerManager.BRIGHTNESS_MAX) {
+ throw new IllegalArgumentException(
+ "Virtual display default brightness must be in range [0.0, 1.0]");
+ }
+ mDefaultBrightness = brightness;
+ return this;
+ }
+
+ /**
* Builds the {@link VirtualDisplayConfig} instance.
*/
@NonNull
@@ -565,7 +619,8 @@ public final class VirtualDisplayConfig implements Parcelable {
mRequestedRefreshRate,
mIsHomeSupported,
mDisplayCutout,
- mIgnoreActivitySizeRestrictions);
+ mIgnoreActivitySizeRestrictions,
+ mDefaultBrightness);
}
}
}
diff --git a/core/java/android/hardware/input/AidlInputGestureData.aidl b/core/java/android/hardware/input/AidlInputGestureData.aidl
index 137f672bf59c..e33ec53dd208 100644
--- a/core/java/android/hardware/input/AidlInputGestureData.aidl
+++ b/core/java/android/hardware/input/AidlInputGestureData.aidl
@@ -19,13 +19,26 @@ package android.hardware.input;
/** @hide */
@JavaDerive(equals=true)
parcelable AidlInputGestureData {
- int keycode;
- int modifierState;
- int gestureType;
+ Trigger trigger;
- // App launch parameters: Only set if gestureType is KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+ int gestureType;
+ // App launch parameters (Only set if gestureType is LAUNCH_APPLICATION)
String appLaunchCategory;
String appLaunchRole;
String appLaunchPackageName;
String appLaunchClassName;
+
+ parcelable KeyTrigger {
+ int keycode;
+ int modifierState;
+ }
+
+ parcelable TouchpadGestureTrigger {
+ int gestureType;
+ }
+
+ union Trigger {
+ KeyTrigger key;
+ TouchpadGestureTrigger touchpadGesture;
+ }
}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 39dddb723cb9..1b96224f03da 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -266,19 +266,19 @@ interface IInputManager {
@PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
- int addCustomInputGesture(in AidlInputGestureData data);
+ int addCustomInputGesture(int userId, in AidlInputGestureData data);
@PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
- int removeCustomInputGesture(in AidlInputGestureData data);
+ int removeCustomInputGesture(int userId, in AidlInputGestureData data);
@PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
- void removeAllCustomInputGestures();
+ void removeAllCustomInputGestures(int userId);
- AidlInputGestureData[] getCustomInputGestures();
+ AidlInputGestureData[] getCustomInputGestures(int userId);
AidlInputGestureData[] getAppLaunchBookmarks();
}
diff --git a/core/java/android/hardware/input/InputGestureData.java b/core/java/android/hardware/input/InputGestureData.java
index 5ab73cee9641..ee0a2a9cf88c 100644
--- a/core/java/android/hardware/input/InputGestureData.java
+++ b/core/java/android/hardware/input/InputGestureData.java
@@ -35,20 +35,40 @@ import java.util.Objects;
*/
public final class InputGestureData {
+ public static final int TOUCHPAD_GESTURE_TYPE_UNKNOWN = 0;
+ public static final int TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP = 1;
+
@NonNull
private final AidlInputGestureData mInputGestureData;
- public InputGestureData(AidlInputGestureData inputGestureData) {
+ public InputGestureData(@NonNull AidlInputGestureData inputGestureData) {
this.mInputGestureData = inputGestureData;
validate();
}
/** Returns the trigger information for this input gesture */
public Trigger getTrigger() {
- if (mInputGestureData.keycode != KeyEvent.KEYCODE_UNKNOWN) {
- return new KeyTrigger(mInputGestureData.keycode, mInputGestureData.modifierState);
+ switch (mInputGestureData.trigger.getTag()) {
+ case AidlInputGestureData.Trigger.Tag.key: {
+ AidlInputGestureData.KeyTrigger trigger = mInputGestureData.trigger.getKey();
+ if (trigger == null) {
+ throw new RuntimeException("InputGestureData is corrupted, null key trigger!");
+ }
+ return createKeyTrigger(trigger.keycode, trigger.modifierState);
+ }
+ case AidlInputGestureData.Trigger.Tag.touchpadGesture: {
+ AidlInputGestureData.TouchpadGestureTrigger trigger =
+ mInputGestureData.trigger.getTouchpadGesture();
+ if (trigger == null) {
+ throw new RuntimeException(
+ "InputGestureData is corrupted, null touchpad trigger!");
+ }
+ return createTouchpadTrigger(trigger.gestureType);
+ }
+ default:
+ throw new RuntimeException("InputGestureData is corrupted, invalid trigger type!");
+
}
- throw new RuntimeException("InputGestureData is corrupted, invalid trigger type!");
}
/** Returns the action to perform for this input gesture */
@@ -127,9 +147,15 @@ public final class InputGestureData {
"No app launch data for system action launch application");
}
AidlInputGestureData data = new AidlInputGestureData();
+ data.trigger = new AidlInputGestureData.Trigger();
if (mTrigger instanceof KeyTrigger keyTrigger) {
- data.keycode = keyTrigger.getKeycode();
- data.modifierState = keyTrigger.getModifierState();
+ data.trigger.setKey(new AidlInputGestureData.KeyTrigger());
+ data.trigger.getKey().keycode = keyTrigger.getKeycode();
+ data.trigger.getKey().modifierState = keyTrigger.getModifierState();
+ } else if (mTrigger instanceof TouchpadTrigger touchpadTrigger) {
+ data.trigger.setTouchpadGesture(new AidlInputGestureData.TouchpadGestureTrigger());
+ data.trigger.getTouchpadGesture().gestureType =
+ touchpadTrigger.getTouchpadGestureType();
} else {
throw new IllegalArgumentException("Invalid trigger type!");
}
@@ -163,30 +189,12 @@ public final class InputGestureData {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InputGestureData that = (InputGestureData) o;
- return mInputGestureData.keycode == that.mInputGestureData.keycode
- && mInputGestureData.modifierState == that.mInputGestureData.modifierState
- && mInputGestureData.gestureType == that.mInputGestureData.gestureType
- && Objects.equals(mInputGestureData.appLaunchCategory, that.mInputGestureData.appLaunchCategory)
- && Objects.equals(mInputGestureData.appLaunchRole, that.mInputGestureData.appLaunchRole)
- && Objects.equals(mInputGestureData.appLaunchPackageName, that.mInputGestureData.appLaunchPackageName)
- && Objects.equals(mInputGestureData.appLaunchPackageName, that.mInputGestureData.appLaunchPackageName);
+ return Objects.equals(mInputGestureData, that.mInputGestureData);
}
@Override
public int hashCode() {
- int _hash = 1;
- _hash = 31 * _hash + mInputGestureData.keycode;
- _hash = 31 * _hash + mInputGestureData.modifierState;
- _hash = 31 * _hash + mInputGestureData.gestureType;
- _hash = 31 * _hash + (mInputGestureData.appLaunchCategory != null
- ? mInputGestureData.appLaunchCategory.hashCode() : 0);
- _hash = 31 * _hash + (mInputGestureData.appLaunchRole != null
- ? mInputGestureData.appLaunchRole.hashCode() : 0);
- _hash = 31 * _hash + (mInputGestureData.appLaunchPackageName != null
- ? mInputGestureData.appLaunchPackageName.hashCode() : 0);
- _hash = 31 * _hash + (mInputGestureData.appLaunchPackageName != null
- ? mInputGestureData.appLaunchPackageName.hashCode() : 0);
- return _hash;
+ return mInputGestureData.hashCode();
}
public interface Trigger {
@@ -197,6 +205,11 @@ public final class InputGestureData {
return new KeyTrigger(keycode, modifierState);
}
+ /** Creates a input gesture trigger based on a touchpad gesture */
+ public static Trigger createTouchpadTrigger(int touchpadGestureType) {
+ return new TouchpadTrigger(touchpadGestureType);
+ }
+
/** Key based input gesture trigger */
public static class KeyTrigger implements Trigger {
private static final int SHORTCUT_META_MASK =
@@ -242,6 +255,43 @@ public final class InputGestureData {
}
}
+ /** Touchpad based input gesture trigger */
+ public static class TouchpadTrigger implements Trigger {
+ private final int mTouchpadGestureType;
+
+ private TouchpadTrigger(int touchpadGestureType) {
+ if (touchpadGestureType != TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP) {
+ throw new IllegalArgumentException(
+ "Invalid touchpadGestureType = " + touchpadGestureType);
+ }
+ mTouchpadGestureType = touchpadGestureType;
+ }
+
+ public int getTouchpadGestureType() {
+ return mTouchpadGestureType;
+ }
+
+ @Override
+ public String toString() {
+ return "TouchpadTrigger{" +
+ "mTouchpadGestureType=" + mTouchpadGestureType +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TouchpadTrigger that = (TouchpadTrigger) o;
+ return mTouchpadGestureType == that.mTouchpadGestureType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mTouchpadGestureType);
+ }
+ }
+
/** Data for action to perform when input gesture is triggered */
public record Action(@KeyGestureEvent.KeyGestureType int keyGestureType,
@Nullable AppLaunchData appLaunchData) {
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 2051dbe7fb2e..9050ae235ce7 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -34,6 +34,7 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.compat.annotation.ChangeId;
@@ -1487,12 +1488,13 @@ public final class InputManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
@CustomInputGestureResult
+ @UserHandleAware
public int addCustomInputGesture(@NonNull InputGestureData inputGestureData) {
if (!enableCustomizableInputGestures()) {
return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
}
try {
- return mIm.addCustomInputGesture(inputGestureData.getAidlData());
+ return mIm.addCustomInputGesture(mContext.getUserId(), inputGestureData.getAidlData());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1509,12 +1511,14 @@ public final class InputManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
@CustomInputGestureResult
+ @UserHandleAware
public int removeCustomInputGesture(@NonNull InputGestureData inputGestureData) {
if (!enableCustomizableInputGestures()) {
return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
}
try {
- return mIm.removeCustomInputGesture(inputGestureData.getAidlData());
+ return mIm.removeCustomInputGesture(mContext.getUserId(),
+ inputGestureData.getAidlData());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1525,12 +1529,13 @@ public final class InputManager {
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ @UserHandleAware
public void removeAllCustomInputGestures() {
if (!enableCustomizableInputGestures()) {
return;
}
try {
- mIm.removeAllCustomInputGestures();
+ mIm.removeAllCustomInputGestures(mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1540,13 +1545,14 @@ public final class InputManager {
*
* @hide
*/
+ @UserHandleAware
public List<InputGestureData> getCustomInputGestures() {
List<InputGestureData> result = new ArrayList<>();
if (!enableCustomizableInputGestures()) {
return result;
}
try {
- for (AidlInputGestureData data : mIm.getCustomInputGestures()) {
+ for (AidlInputGestureData data : mIm.getCustomInputGestures(mContext.getUserId())) {
result.add(new InputGestureData(data));
}
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index c4566981d3b5..96f6ad117035 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -30,6 +30,7 @@ import static com.android.hardware.input.Flags.mouseSwapPrimaryButton;
import static com.android.hardware.input.Flags.touchpadTapDragging;
import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut;
import static com.android.hardware.input.Flags.touchpadVisualizer;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
import static com.android.input.flags.Flags.enableInputFilterRustImpl;
import static com.android.input.flags.Flags.keyboardRepeatKeys;
@@ -386,7 +387,7 @@ public class InputSettings {
* @hide
*/
public static boolean isTouchpadThreeFingerTapShortcutFeatureFlagEnabled() {
- return enableCustomizableInputGestures() && touchpadThreeFingerTapShortcut();
+ return isCustomizableInputGesturesFeatureFlagEnabled() && touchpadThreeFingerTapShortcut();
}
/**
@@ -1132,4 +1133,18 @@ public class InputSettings {
Settings.Secure.KEY_REPEAT_DELAY_MS, delayTimeMillis,
UserHandle.USER_CURRENT);
}
+
+ /**
+ * Whether "Customizable key gestures" feature flag is enabled.
+ *
+ * <p>
+ * ‘Customizable key gestures’ is a feature which allows users to customize key based
+ * shortcuts on the physical keyboard.
+ * </p>
+ *
+ * @hide
+ */
+ public static boolean isCustomizableInputGesturesFeatureFlagEnabled() {
+ return enableCustomizableInputGestures() && useKeyGestureEventHandler();
+ }
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index f9cb94aca54e..38e32c61c99e 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -141,7 +141,7 @@ flag {
flag {
name: "keyboard_a11y_shortcut_control"
namespace: "input"
- description: "Adds shortcuts to toggle and control a11y features"
+ description: "Adds shortcuts to toggle and control a11y keyboard features"
bug: "373458181"
}
@@ -165,3 +165,10 @@ flag {
description: "Turns three-finger touchpad taps into a customizable shortcut."
bug: "365063048"
}
+
+flag {
+ name: "enable_talkback_and_magnifier_key_gestures"
+ namespace: "input"
+ description: "Adds key gestures for talkback and magnifier"
+ bug: "375277034"
+} \ No newline at end of file
diff --git a/core/java/android/hardware/location/OWNERS b/core/java/android/hardware/location/OWNERS
index 747f90947b9c..340d6f2eb08c 100644
--- a/core/java/android/hardware/location/OWNERS
+++ b/core/java/android/hardware/location/OWNERS
@@ -9,4 +9,4 @@ wyattriley@google.com
yuhany@google.com
# ContextHub team
-per-file *ContextHub*,*NanoApp* = file:platform/system/chre:/OWNERS
+per-file Android.bp,*Hub*,*NanoApp* = file:platform/system/chre:/OWNERS
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index c6fd0ee3c80d..85cf9491287c 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -1750,8 +1750,8 @@ public class SoundTrigger {
* internals, typically during enrollment.
* @return the same Builder instance.
*/
- public @NonNull Builder setData(@Nullable byte[] data) {
- mData = data;
+ public @NonNull Builder setData(@NonNull byte[] data) {
+ mData = requireNonNull(data, "Data must not be null");
return this;
}
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index c7f8878f104e..f0e12ca644dd 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -85,7 +85,7 @@ public final class AggregateBatteryConsumer extends BatteryConsumer {
throw new XmlPullParserException("Invalid XML parser state");
}
- consumerBuilder.setConsumedPower(
+ consumerBuilder.addConsumedPower(
parser.getAttributeDouble(null, BatteryUsageStats.XML_ATTR_POWER));
while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
@@ -132,11 +132,19 @@ public final class AggregateBatteryConsumer extends BatteryConsumer {
}
/**
+ * Adds the total power included in this aggregate.
+ */
+ public Builder addConsumedPower(double consumedPowerMah) {
+ mData.putDouble(COLUMN_INDEX_CONSUMED_POWER,
+ mData.getDouble(COLUMN_INDEX_CONSUMED_POWER) + consumedPowerMah);
+ return this;
+ }
+
+ /**
* Adds power and usage duration from the supplied AggregateBatteryConsumer.
*/
public void add(AggregateBatteryConsumer aggregateBatteryConsumer) {
- setConsumedPower(mData.getDouble(COLUMN_INDEX_CONSUMED_POWER)
- + aggregateBatteryConsumer.getConsumedPower());
+ addConsumedPower(aggregateBatteryConsumer.getConsumedPower());
mPowerComponentsBuilder.addPowerAndDuration(aggregateBatteryConsumer.mPowerComponents);
}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index b0ecca790b80..14b67f64b6da 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -1064,7 +1064,9 @@ public abstract class BatteryConsumer {
* @param componentId The ID of the power component, e.g.
* {@link BatteryConsumer#POWER_COMPONENT_CPU}.
* @param componentPower Amount of consumed power in mAh.
+ * @deprecated use {@link #addConsumedPower}
*/
+ @Deprecated
@NonNull
public T setConsumedPower(@PowerComponentId int componentId, double componentPower) {
return setConsumedPower(componentId, componentPower, POWER_MODEL_POWER_PROFILE);
@@ -1076,7 +1078,9 @@ public abstract class BatteryConsumer {
* @param componentId The ID of the power component, e.g.
* {@link BatteryConsumer#POWER_COMPONENT_CPU}.
* @param componentPower Amount of consumed power in mAh.
+ * @deprecated use {@link #addConsumedPower}
*/
+ @Deprecated
@SuppressWarnings("unchecked")
@NonNull
public T setConsumedPower(@PowerComponentId int componentId, double componentPower,
@@ -1104,6 +1108,21 @@ public abstract class BatteryConsumer {
@SuppressWarnings("unchecked")
@NonNull
+ public T addConsumedPower(@PowerComponentId int componentId, double componentPower) {
+ mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
+ componentPower, POWER_MODEL_UNDEFINED);
+ return (T) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @NonNull
+ public T addConsumedPower(Key key, double componentPower) {
+ mPowerComponentsBuilder.addConsumedPower(key, componentPower, POWER_MODEL_UNDEFINED);
+ return (T) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @NonNull
public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
mPowerComponentsBuilder.addConsumedPower(key, componentPower, powerModel);
return (T) this;
@@ -1115,7 +1134,9 @@ public abstract class BatteryConsumer {
* @param componentId The ID of the power component, e.g.
* {@link UidBatteryConsumer#POWER_COMPONENT_CPU}.
* @param componentUsageTimeMillis Amount of time in microseconds.
+ * @deprecated use {@link #addUsageDurationMillis}
*/
+ @Deprecated
@SuppressWarnings("unchecked")
@NonNull
public T setUsageDurationMillis(@PowerComponentId int componentId,
@@ -1126,6 +1147,7 @@ public abstract class BatteryConsumer {
return (T) this;
}
+ @Deprecated
@SuppressWarnings("unchecked")
@NonNull
public T setUsageDurationMillis(Key key, long componentUsageTimeMillis) {
@@ -1133,6 +1155,14 @@ public abstract class BatteryConsumer {
return (T) this;
}
+ @NonNull
+ public T addUsageDurationMillis(@PowerComponentId int componentId,
+ long componentUsageTimeMillis) {
+ mPowerComponentsBuilder.addUsageDurationMillis(
+ getKey(componentId, PROCESS_STATE_UNSPECIFIED), componentUsageTimeMillis);
+ return (T) this;
+ }
+
@SuppressWarnings("unchecked")
@NonNull
public T addUsageDurationMillis(Key key, long componentUsageTimeMillis) {
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 5ae425f184c7..72e4cef2f6eb 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -209,7 +209,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
}
builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(totalPowerMah);
+ .addConsumedPower(totalPowerMah);
mAggregateBatteryConsumers =
new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 66f4198ad31c..6cfbf4ebf661 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -93,9 +93,7 @@ public final class MessageQueue {
* system processes and provides a higher level of concurrency and higher enqueue throughput
* than the legacy implementation.
*/
- private static boolean sUseConcurrent;
-
- private static boolean sUseConcurrentInitialized = false;
+ private boolean mUseConcurrent;
@RavenwoodRedirect
private native static long nativeInit();
@@ -112,10 +110,7 @@ public final class MessageQueue {
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
- if (!sUseConcurrentInitialized) {
- sUseConcurrent = UserHandle.isCore(Process.myUid());
- sUseConcurrentInitialized = true;
- }
+ mUseConcurrent = UserHandle.isCore(Process.myUid());
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
@@ -158,7 +153,7 @@ public final class MessageQueue {
* @return True if the looper is idle.
*/
public boolean isIdle() {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
final long now = SystemClock.uptimeMillis();
if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) {
@@ -208,7 +203,7 @@ public final class MessageQueue {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mIdleHandlersLock) {
mIdleHandlers.add(handler);
}
@@ -229,7 +224,7 @@ public final class MessageQueue {
* @param handler The IdleHandler to be removed.
*/
public void removeIdleHandler(@NonNull IdleHandler handler) {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mIdleHandlersLock) {
mIdleHandlers.remove(handler);
}
@@ -252,7 +247,7 @@ public final class MessageQueue {
* @hide
*/
public boolean isPolling() {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
// If the loop is quitting then it must not be idling.
// We can assume mPtr != 0 when sQuitting is false.
return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr);
@@ -303,7 +298,7 @@ public final class MessageQueue {
throw new IllegalArgumentException("listener must not be null");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mFileDescriptorRecordsLock) {
updateOnFileDescriptorEventListenerLocked(fd, events, listener);
}
@@ -331,7 +326,7 @@ public final class MessageQueue {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mFileDescriptorRecordsLock) {
updateOnFileDescriptorEventListenerLocked(fd, 0, null);
}
@@ -388,7 +383,7 @@ public final class MessageQueue {
final int oldWatchedEvents;
final OnFileDescriptorEventListener listener;
final int seq;
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mFileDescriptorRecordsLock) {
record = mFileDescriptorRecords.get(fd);
if (record == null) {
@@ -708,7 +703,7 @@ public final class MessageQueue {
@UnsupportedAppUsage
Message next() {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return nextConcurrent();
}
@@ -834,7 +829,7 @@ public final class MessageQueue {
throw new IllegalStateException("Main thread not allowed to quit.");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mIdleHandlersLock) {
if (sQuitting.compareAndSet(this, false, true)) {
if (safe) {
@@ -898,7 +893,7 @@ public final class MessageQueue {
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
final int token = mNextBarrierTokenAtomic.getAndIncrement();
// b/376573804: apps and tests may expect to be able to use reflection
@@ -991,7 +986,7 @@ public final class MessageQueue {
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
boolean removed;
MessageNode first;
final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token);
@@ -1058,7 +1053,7 @@ public final class MessageQueue {
throw new IllegalArgumentException("Message must have a target.");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
@@ -1187,7 +1182,7 @@ public final class MessageQueue {
if (h == null) {
return false;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject,
false);
}
@@ -1219,7 +1214,7 @@ public final class MessageQueue {
if (h == null) {
return false;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals,
false);
@@ -1253,7 +1248,7 @@ public final class MessageQueue {
if (h == null) {
return false;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject,
false);
}
@@ -1285,7 +1280,7 @@ public final class MessageQueue {
if (h == null) {
return false;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false);
}
synchronized (this) {
@@ -1304,7 +1299,7 @@ public final class MessageQueue {
if (h == null) {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true);
return;
}
@@ -1355,7 +1350,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true);
return;
}
@@ -1407,7 +1402,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
return;
}
@@ -1470,7 +1465,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
return;
}
@@ -1532,7 +1527,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true);
return;
}
@@ -1594,7 +1589,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
return;
}
@@ -1742,7 +1737,7 @@ public final class MessageQueue {
@NeverCompile
void dump(Printer pw, String prefix, Handler h) {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
long now = SystemClock.uptimeMillis();
int n = 0;
@@ -1803,7 +1798,7 @@ public final class MessageQueue {
@NeverCompile
void dumpDebug(ProtoOutputStream proto, long fieldId) {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
final long messageQueueToken = proto.start(fieldId);
StackNode node = (StackNode) sState.getVolatile(this);
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index f4e3f3b6e430..d116e0737c46 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -439,8 +439,8 @@ class PowerComponents {
}
final BatteryConsumer.Key key = builder.mData.layout.getKey(componentId,
processState, screenState, powerState);
- builder.setConsumedPower(key, powerMah, model);
- builder.setUsageDurationMillis(key, durationMs);
+ builder.addConsumedPower(key, powerMah, model);
+ builder.addUsageDurationMillis(key, durationMs);
break;
}
}
@@ -468,6 +468,10 @@ class PowerComponents {
}
}
+ /**
+ * @deprecated use {@link #addConsumedPower(BatteryConsumer.Key, double, int)}
+ */
+ @Deprecated
@NonNull
public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower,
int powerModel) {
@@ -489,6 +493,10 @@ class PowerComponents {
return this;
}
+ /**
+ * @deprecated use {@link #addUsageDurationMillis(BatteryConsumer.Key, long)}
+ */
+ @Deprecated
@NonNull
public Builder setUsageDurationMillis(BatteryConsumer.Key key,
long componentUsageDurationMillis) {
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index f893739fcf28..976bfe41ba45 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -210,12 +210,6 @@ public final class UidBatteryConsumer extends BatteryConsumer {
serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE,
packageWithHighestDrain);
}
- serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND,
- getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND));
- serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND,
- getTimeInProcessStateMs(PROCESS_STATE_BACKGROUND));
- serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE,
- getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE));
mPowerComponents.writeToXml(serializer);
serializer.endTag(null, BatteryUsageStats.XML_TAG_UID);
}
@@ -235,13 +229,6 @@ public final class UidBatteryConsumer extends BatteryConsumer {
consumerBuilder.setPackageWithHighestDrain(
parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE));
- consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND,
- parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND));
- consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND,
- parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND));
- consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
- parser.getAttributeLong(null,
- BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE));
while (!(eventType == XmlPullParser.END_TAG
&& parser.getName().equals(BatteryUsageStats.XML_TAG_UID))
&& eventType != XmlPullParser.END_DOCUMENT) {
@@ -335,7 +322,11 @@ public final class UidBatteryConsumer extends BatteryConsumer {
/**
* Sets the duration, in milliseconds, that this UID was active in a particular process
* state, such as foreground service.
+ *
+ * @deprecated time in process is now derived from the
+ * {@link BatteryConsumer#POWER_COMPONENT_BASE} duration
*/
+ @Deprecated
@NonNull
public Builder setTimeInProcessStateMs(@ProcessState int state, long timeInProcessStateMs) {
Key key = getKey(POWER_COMPONENT_BASE, state);
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 1ab48a22cbbd..09b96da39a84 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -181,6 +181,5 @@ interface IStorageManager {
* device's useful lifetime remains. If no information is available, -1
* is returned.
*/
- @EnforcePermission("READ_PRIVILEGED_PHONE_STATE")
int getInternalStorageRemainingLifetime() = 99;
}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 6a4932211f27..9e0d0e195a96 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -53,6 +53,24 @@ flag {
}
flag {
+ name: "enhanced_confirmation_in_call_apis_enabled"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "enable enhanced confirmation incall apis"
+ bug: "310220212"
+}
+
+flag {
+ name: "unknown_call_package_install_blocking_enabled"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "enable the blocking of certain app installs during an unknown call"
+ bug: "310220212"
+}
+
+flag {
name: "op_enable_mobile_data_by_user"
is_exported: true
namespace: "permissions"
@@ -332,3 +350,38 @@ flag {
description: "Enables ExtServices to leverage TextClassifier for OTP detection"
bug: "351976749"
}
+
+flag {
+ name: "health_connect_backup_restore_permission_enabled"
+ is_fixed_read_only: true
+ namespace: "health_connect"
+ description: "This flag protects the permission that is required to call Health Connect backup and restore apis"
+ bug: "376014879" # android_fr bug
+}
+
+flag {
+ name: "enable_aiai_proxied_text_classifiers"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "Enables the AiAi to utilize the default OTP text classifier that is also used by ExtServices"
+ bug: "377229653"
+}
+
+flag {
+ name: "enable_sqlite_appops_accesses"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "Enables SQlite for recording discrete and historical AppOp accesses"
+ bug: "377584611"
+}
+
+flag {
+ name: "ranging_permission_enabled"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "uwb"
+ description: "This fixed read-only flag is used to enable new ranging permission for all ranging use cases."
+ bug: "370977414"
+}
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index 0809de25b45c..ce79f5d0c669 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -2,3 +2,4 @@
anothermark@google.com
kumarashishg@google.com
+bmgordon@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index 0809de25b45c..ce79f5d0c669 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -2,3 +2,4 @@
anothermark@google.com
kumarashishg@google.com
+bmgordon@google.com
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 5995760a41ec..66e1f38621ae 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -67,6 +67,7 @@ flag {
name: "aapm_api"
namespace: "responsible_apis"
description: "Android Advanced Protection Mode Service and Manager"
+ is_exported: true
bug: "352420507"
is_fixed_read_only: true
}
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 34e311f8c932..d065939bc19d 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -65,4 +65,11 @@ flag {
namespace: "systemui"
description: "Allows the NAS to create and modify conversation notifications"
bug: "373599715"
-} \ No newline at end of file
+}
+
+flag {
+ name: "notification_regroup_on_classification"
+ namespace: "systemui"
+ description: "This flag controls regrouping after notification classification"
+ bug: "372775153"
+}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 2e660fc1f157..7d79fd3d44ea 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -1164,7 +1164,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
public boolean startRecognition(@RecognitionFlags int recognitionFlags) {
if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")");
synchronized (mLock) {
- return startRecognitionLocked(recognitionFlags, null /* data */) == STATUS_OK;
+ return startRecognitionLocked(recognitionFlags, /* data= */new byte[0]) == STATUS_OK;
}
}
@@ -1496,8 +1496,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
@GuardedBy("mLock")
- private int startRecognitionLocked(int recognitionFlags,
- @Nullable byte[] data) {
+ @SuppressWarnings("FlaggedApi") // RecognitionConfig.Builder is available internally.
+ private int startRecognitionLocked(int recognitionFlags, @NonNull byte[] data) {
if (DBG) {
Slog.d(TAG, "startRecognition("
+ recognitionFlags
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index e830d89d3116..02923eda308e 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -202,3 +202,10 @@ flag {
description: "Deprecate the Paint#elegantTextHeight API and stick it to true"
bug: "349519475"
}
+
+flag {
+ name: "vertical_text_layout"
+ namespace: "text"
+ description: "Make Paint class work for vertical layout text."
+ bug: "355296926"
+}
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 8358b9a51adb..1dd9d46fdfb7 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -75,8 +75,7 @@ import java.net.UnknownHostException;
@android.ravenwood.annotation.RavenwoodClassLoadHook(
"com.android.platform.test.ravenwood.runtimehelper.ClassLoadHook.onClassLoaded")
// Uncomment the following annotation to switch to the Java substitution version.
-//@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
-// "com.android.platform.test.ravenwood.nativesubstitution.Log_host")
+@android.ravenwood.annotation.RavenwoodRedirectionClass("Log_host")
public final class Log {
/** @hide */
@IntDef({ASSERT, ERROR, WARN, INFO, DEBUG, VERBOSE})
@@ -250,6 +249,7 @@ public final class Log {
* tag limit of concern after this API level.
*/
@FastNative
+ @android.ravenwood.annotation.RavenwoodRedirect
public static native boolean isLoggable(@Nullable String tag, @Level int level);
/**
@@ -425,6 +425,7 @@ public final class Log {
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodRedirect
public static native int println_native(int bufID, int priority, String tag, String msg);
/**
@@ -452,6 +453,7 @@ public final class Log {
* Return the maximum payload the log daemon accepts without truncation.
* @return LOGGER_ENTRY_MAX_PAYLOAD.
*/
+ @android.ravenwood.annotation.RavenwoodRedirect
private static native int logger_entry_max_payload_native();
/**
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index b80146505a1b..19e0913fbc65 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -33,6 +33,7 @@ import android.util.Log;
import android.view.animation.BackGestureInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.view.inputmethod.Flags;
import android.view.inputmethod.ImeTracker;
import android.window.BackEvent;
import android.window.OnBackAnimationCallback;
@@ -142,9 +143,15 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
// control has been cancelled by the system. This can happen in multi-window mode for
// example (i.e. split-screen or activity-embedding)
notifyHideIme();
- return;
+ } else {
+ startPostCommitAnim(/*hideIme*/ true);
+ }
+ if (Flags.refactorInsetsController()) {
+ // Unregister all IME back callbacks so that back events are sent to the next callback
+ // even while the hide animation is playing
+ mInsetsController.getHost().getInputMethodManager().getImeOnBackInvokedDispatcher()
+ .preliminaryClear();
}
- startPostCommitAnim(/*hideIme*/ true);
}
private void setPreCommitProgress(float progress) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 25d2246424de..26ca813a9caa 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1344,6 +1344,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
boolean fromPredictiveBack) {
final boolean visible = layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
+ if (Flags.refactorInsetsController() && !fromPredictiveBack && !visible
+ && (types & ime()) != 0 && (mRequestedVisibleTypes & ime()) != 0) {
+ // Clear IME back callbacks if a IME hide animation is requested
+ mHost.getInputMethodManager().getImeOnBackInvokedDispatcher().preliminaryClear();
+ }
// Basically, we accept the requested visibilities from the upstream callers...
setRequestedVisibleTypes(visible ? types : 0, types);
@@ -1921,6 +1926,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
final @InsetsType int requestedVisibleTypes =
(mRequestedVisibleTypes & ~mask) | (visibleTypes & mask);
if (mRequestedVisibleTypes != requestedVisibleTypes) {
+ if (Flags.refactorInsetsController() && (mRequestedVisibleTypes & ime()) == 0
+ && (requestedVisibleTypes & ime()) != 0) {
+ // In case the IME back callbacks have been preliminarily cleared before, let's
+ // reregister them. This can happen if an IME hide animation was interrupted and the
+ // IME is requested to be shown again.
+ getHost().getInputMethodManager().getImeOnBackInvokedDispatcher()
+ .undoPreliminaryClear();
+ }
ProtoLog.d(IME_INSETS_CONTROLLER, "Setting requestedVisibleTypes to %d (was %d)",
requestedVisibleTypes, mRequestedVisibleTypes);
mRequestedVisibleTypes = requestedVisibleTypes;
diff --git a/core/java/android/view/RoundScrollbarRenderer.java b/core/java/android/view/RoundScrollbarRenderer.java
index 59c2598f00f0..5e1eadae0953 100644
--- a/core/java/android/view/RoundScrollbarRenderer.java
+++ b/core/java/android/view/RoundScrollbarRenderer.java
@@ -35,7 +35,9 @@ import android.view.flags.Flags;
* @hide
*/
public class RoundScrollbarRenderer {
- private static final String BLUECHIP_ENABLED_SYSPROP = "persist.cw_build.bluechip.enabled";
+ /** @hide */
+ public static final String BLUECHIP_ENABLED_SYSPROP = "persist.cw_build.bluechip.enabled";
+
// The range of the scrollbar position represented as an angle in degrees.
private static final float SCROLLBAR_ANGLE_RANGE = 28.8f;
private static final float MAX_SCROLLBAR_ANGLE_SWIPE = 26.3f; // 90%
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b8b22e283175..df54d310059f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -312,6 +312,7 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeNotifyShutdown();
private static native void nativeSetLuts(long transactionObj, long nativeObject,
float[] buffers, int[] slots, int[] dimensions, int[] sizes, int[] samplingKeys);
+ private static native void nativeEnableDebugLogCallPoints(long transactionObj);
/**
* Transforms that can be applied to buffers as they are displayed to a window.
@@ -4605,7 +4606,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * TODO(b/366484871): To be removed once we have some logging in native
* This is called when BlastBufferQueue.mergeWithNextTransaction() is called from java, and
* for the purposes of logging that path.
*/
@@ -4616,6 +4616,7 @@ public final class SurfaceControl implements Parcelable {
if (mCalls != null) {
mCalls.clear();
}
+ nativeEnableDebugLogCallPoints(mNativeObject);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 5e5f33ef41f8..fe4f0cd73fff 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -857,8 +857,10 @@ public class AccessibilityNodeInfo implements Parcelable {
* <p>
* The data can be retrieved from the {@code Bundle} returned by {@link #getExtras()} using this
* string as a key for {@link Bundle#getParcelableArray(String, Class)}. The
- * {@link android.graphics.RectF} will be null for characters that either do not exist or are
- * off the screen.
+ * {@link android.graphics.RectF} will be {@code null} for characters that either do not exist
+ * or are off the screen.
+ * <p>
+ * Note that character locations returned are modified by changes in display magnification.
*
* {@see #refreshWithExtraData(String, Bundle)}
*/
@@ -866,6 +868,36 @@ public class AccessibilityNodeInfo implements Parcelable {
"android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
/**
+ * Key used to request and locate extra data for text character location in
+ * window coordinates. This key requests that an array of
+ * {@link android.graphics.RectF}s be added to the extras. This request is made
+ * with {@link #refreshWithExtraData(String, Bundle)}. The arguments taken by
+ * this request are two integers:
+ * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX} and
+ * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}. The starting index
+ * must be valid inside the CharSequence returned by {@link #getText()}, and
+ * the length must be positive.
+ * <p>
+ * Providers may advertise that they support text characters in window coordinates using
+ * {@link #setAvailableExtraData(List)}. Services may check if an implementation supports text
+ * characters in window coordinates with {@link #getAvailableExtraData()}.
+ * <p>
+ * The data can be retrieved from the {@code Bundle} returned by
+ * {@link #getExtras()} using this string as a key for
+ * {@link Bundle#getParcelableArray(String, Class)}. The
+ * {@link android.graphics.RectF} will be {@code null} for characters that either do
+ * not exist or are outside of the window bounds.
+ * <p>
+ * Note that character locations in window bounds are not modified by
+ * changes in display magnification.
+ *
+ * {@see #refreshWithExtraData(String, Bundle)}
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_CHARACTER_IN_WINDOW_API)
+ public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY =
+ "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY";
+
+ /**
* Integer argument specifying the start index of the requested text location data. Must be
* valid inside the CharSequence returned by {@link #getText()}.
*
@@ -5460,26 +5492,6 @@ public class AccessibilityNodeInfo implements Parcelable {
}
}
- private static String getExpandedStateSymbolicName(int state) {
- if (Flags.a11yExpansionStateApi()) {
- switch (state) {
- case EXPANDED_STATE_UNDEFINED:
- return "EXPANDED_STATE_UNDEFINED";
- case EXPANDED_STATE_COLLAPSED:
- return "EXPANDED_STATE_COLLAPSED";
- case EXPANDED_STATE_PARTIAL:
- return "EXPANDED_STATE_PARTIAL";
- case EXPANDED_STATE_FULL:
- return "EXPANDED_STATE_FULL";
- default:
- throw new IllegalArgumentException("Unknown expanded state: " + state);
- }
- } else {
- // TODO(b/362782158) Remove when flag is removed.
- return "";
- }
- }
-
private static boolean canPerformRequestOverConnection(int connectionId,
int windowId, long accessibilityNodeId) {
final boolean hasWindowId = windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
@@ -5573,20 +5585,12 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append("; maxTextLength: ").append(mMaxTextLength);
builder.append("; stateDescription: ").append(mStateDescription);
builder.append("; contentDescription: ").append(mContentDescription);
- if (Flags.supplementalDescription()) {
- builder.append("; supplementalDescription: ").append(mSupplementalDescription);
- }
builder.append("; tooltipText: ").append(mTooltipText);
builder.append("; containerTitle: ").append(mContainerTitle);
builder.append("; viewIdResName: ").append(mViewIdResourceName);
builder.append("; uniqueId: ").append(mUniqueId);
- builder.append("; expandedState: ").append(getExpandedStateSymbolicName(mExpandedState));
-
builder.append("; checkable: ").append(isCheckable());
builder.append("; checked: ").append(isChecked());
- if (Flags.a11yIsRequiredApi()) {
- builder.append("; required: ").append(isFieldRequired());
- }
builder.append("; focusable: ").append(isFocusable());
builder.append("; focused: ").append(isFocused());
builder.append("; selected: ").append(isSelected());
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7dc77b175c79..73f9d9fc23dc 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -3667,6 +3667,14 @@ public final class InputMethodManager {
}
/**
+ * Returns the ImeOnBackInvokedDispatcher.
+ * @hide
+ */
+ public ImeOnBackInvokedDispatcher getImeOnBackInvokedDispatcher() {
+ return mImeDispatcher;
+ }
+
+ /**
* Check the next served view if needs to start input.
*/
@GuardedBy("mH")
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index be91cfb9ebf8..a67ae7c96a54 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -17,8 +17,10 @@
package android.view.inputmethod;
import android.annotation.AnyThread;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -87,8 +89,17 @@ public final class InputMethodSubtype implements Parcelable {
private final boolean mIsAsciiCapable;
private final int mSubtypeHashCode;
private final int mSubtypeIconResId;
+ /** The subtype name resource identifier. */
private final int mSubtypeNameResId;
+ /** The untranslatable name of the subtype. */
+ @NonNull
private final CharSequence mSubtypeNameOverride;
+ /** The layout label string resource identifier. */
+ @StringRes
+ private final int mLayoutLabelResId;
+ /** The non-localized layout label. */
+ @NonNull
+ private final CharSequence mLayoutLabelNonLocalized;
private final String mPkLanguageTag;
private final String mPkLayoutType;
private final int mSubtypeId;
@@ -176,6 +187,7 @@ public final class InputMethodSubtype implements Parcelable {
mSubtypeNameResId = subtypeNameResId;
return this;
}
+ /** The subtype name resource identifier. */
private int mSubtypeNameResId = 0;
/**
@@ -191,9 +203,56 @@ public final class InputMethodSubtype implements Parcelable {
mSubtypeNameOverride = nameOverride;
return this;
}
+ /** The untranslatable name of the subtype. */
+ @NonNull
private CharSequence mSubtypeNameOverride = "";
/**
+ * Sets the layout label string resource identifier.
+ *
+ * @param layoutLabelResId the layout label string resource identifier.
+ *
+ * @see #getLayoutDisplayName
+ */
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ @NonNull
+ public InputMethodSubtypeBuilder setLayoutLabelResource(
+ @StringRes int layoutLabelResId) {
+ if (!Flags.imeSwitcherRevampApi()) {
+ return this;
+ }
+ mLayoutLabelResId = layoutLabelResId;
+ return this;
+ }
+ /** The layout label string resource identifier. */
+ @StringRes
+ private int mLayoutLabelResId = 0;
+
+ /**
+ * Sets the non-localized layout label. This is used as the layout display name if the
+ * {@link #getLayoutLabelResource layoutLabelResource} is not set ({@code 0}).
+ *
+ * @param layoutLabelNonLocalized the non-localized layout label.
+ *
+ * @see #getLayoutDisplayName
+ */
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ @NonNull
+ public InputMethodSubtypeBuilder setLayoutLabelNonLocalized(
+ @NonNull CharSequence layoutLabelNonLocalized) {
+ if (!Flags.imeSwitcherRevampApi()) {
+ return this;
+ }
+ Objects.requireNonNull(layoutLabelNonLocalized,
+ "layoutLabelNonLocalized cannot be null");
+ mLayoutLabelNonLocalized = layoutLabelNonLocalized;
+ return this;
+ }
+ /** The non-localized layout label. */
+ @NonNull
+ private CharSequence mLayoutLabelNonLocalized = "";
+
+ /**
* Sets the physical keyboard hint information, such as language and layout.
*
* The system can use the hint information to automatically configure the physical keyboard
@@ -350,6 +409,8 @@ public final class InputMethodSubtype implements Parcelable {
private InputMethodSubtype(InputMethodSubtypeBuilder builder) {
mSubtypeNameResId = builder.mSubtypeNameResId;
mSubtypeNameOverride = builder.mSubtypeNameOverride;
+ mLayoutLabelResId = builder.mLayoutLabelResId;
+ mLayoutLabelNonLocalized = builder.mLayoutLabelNonLocalized;
mPkLanguageTag = builder.mPkLanguageTag;
mPkLayoutType = builder.mPkLayoutType;
mSubtypeIconResId = builder.mSubtypeIconResId;
@@ -376,6 +437,9 @@ public final class InputMethodSubtype implements Parcelable {
mSubtypeNameResId = source.readInt();
CharSequence cs = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mSubtypeNameOverride = cs != null ? cs : "";
+ mLayoutLabelResId = source.readInt();
+ cs = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ mLayoutLabelNonLocalized = cs != null ? cs : "";
s = source.readString8();
mPkLanguageTag = s != null ? s : "";
s = source.readString8();
@@ -412,6 +476,24 @@ public final class InputMethodSubtype implements Parcelable {
}
/**
+ * Returns the layout label string resource identifier.
+ */
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ @StringRes
+ public int getLayoutLabelResource() {
+ return mLayoutLabelResId;
+ }
+
+ /**
+ * Returns the non-localized layout label.
+ */
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ @NonNull
+ public CharSequence getLayoutLabelNonLocalized() {
+ return mLayoutLabelNonLocalized;
+ }
+
+ /**
* Returns the physical keyboard BCP-47 language tag.
*
* @attr ref android.R.styleable#InputMethod_Subtype_physicalKeyboardHintLanguageTag
@@ -643,9 +725,47 @@ public final class InputMethodSubtype implements Parcelable {
try {
return String.format(subtypeNameString, replacementString);
} catch (IllegalFormatException e) {
- Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
+ Slog.w(TAG, "Found illegal format in subtype name(" + subtypeName + "): " + e);
+ return "";
+ }
+ }
+
+ /**
+ * Returns the layout display name.
+ *
+ * <p>If {@code layoutLabelResource} is non-zero (specified through
+ * {@link InputMethodSubtypeBuilder#setLayoutLabelResource setLayoutLabelResource}), the
+ * text generated from that resource will be returned. The localized string resource of the
+ * label should be capitalized for inclusion in UI lists.
+ *
+ * <p>If {@code layoutLabelResource} is zero, the framework returns the non-localized
+ * layout label, if specified through
+ * {@link InputMethodSubtypeBuilder#setLayoutLabelNonLocalized setLayoutLabelNonLocalized}.
+ *
+ * @param context The context used for getting the
+ * {@link android.content.pm.PackageManager PackageManager}.
+ * @param imeAppInfo The {@link ApplicationInfo} of the input method.
+ * @return the layout display name.
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ public CharSequence getLayoutDisplayName(@NonNull Context context,
+ @NonNull ApplicationInfo imeAppInfo) {
+ if (!Flags.imeSwitcherRevampApi()) {
+ return "";
+ }
+ Objects.requireNonNull(context, "context cannot be null");
+ Objects.requireNonNull(imeAppInfo, "imeAppInfo cannot be null");
+ if (mLayoutLabelResId == 0) {
+ return mLayoutLabelNonLocalized;
+ }
+
+ final CharSequence subtypeLayoutName = context.getPackageManager().getText(
+ imeAppInfo.packageName, mLayoutLabelResId, imeAppInfo);
+ if (TextUtils.isEmpty(subtypeLayoutName)) {
return "";
}
+ return subtypeLayoutName;
}
@Nullable
@@ -778,6 +898,8 @@ public final class InputMethodSubtype implements Parcelable {
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeInt(mSubtypeNameResId);
TextUtils.writeToParcel(mSubtypeNameOverride, dest, parcelableFlags);
+ dest.writeInt(mLayoutLabelResId);
+ TextUtils.writeToParcel(mLayoutLabelNonLocalized, dest, parcelableFlags);
dest.writeString8(mPkLanguageTag);
dest.writeString8(mPkLayoutType);
dest.writeInt(mSubtypeIconResId);
@@ -794,6 +916,7 @@ public final class InputMethodSubtype implements Parcelable {
void dump(@NonNull Printer pw, @NonNull String prefix) {
pw.println(prefix + "mSubtypeNameOverride=" + mSubtypeNameOverride
+ + " mLayoutLabelNonLocalized=" + mLayoutLabelNonLocalized
+ " mPkLanguageTag=" + mPkLanguageTag
+ " mPkLayoutType=" + mPkLayoutType
+ " mSubtypeId=" + mSubtypeId
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ef941da0e32d..d7750bd412a3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -26,6 +26,9 @@ import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDER
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY;
+import static android.view.accessibility.Flags.FLAG_A11Y_CHARACTER_IN_WINDOW_API;
+import static android.view.accessibility.Flags.a11yCharacterInWindowApi;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
import static android.view.inputmethod.EditorInfo.STYLUS_HANDWRITING_ENABLED_ANDROIDX_EXTRAS_KEY;
import static android.view.inputmethod.Flags.initiationWithoutInputConnection;
@@ -492,6 +495,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/** Accessibility action start id for "smart" actions. @hide */
static final int ACCESSIBILITY_ACTION_SMART_START_ID = 0x10001000;
+ // Stable extra data keys supported by TextView.
+ private static final List<String> ACCESSIBILITY_EXTRA_DATA_KEYS = List.of(
+ EXTRA_DATA_RENDERING_INFO_KEY,
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
+ );
+
+ // Flagged and stable extra data keys supported by TextView.
+ @FlaggedApi(FLAG_A11Y_CHARACTER_IN_WINDOW_API)
+ private static final List<String> ACCESSIBILITY_EXTRA_DATA_KEYS_FLAGGED = List.of(
+ EXTRA_DATA_RENDERING_INFO_KEY,
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY,
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY
+ );
+
/**
* @hide
*/
@@ -14207,10 +14224,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
- info.setAvailableExtraData(Arrays.asList(
- EXTRA_DATA_RENDERING_INFO_KEY,
- EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
- ));
+ if (a11yCharacterInWindowApi()) {
+ info.setAvailableExtraData(ACCESSIBILITY_EXTRA_DATA_KEYS_FLAGGED);
+ } else {
+ info.setAvailableExtraData(ACCESSIBILITY_EXTRA_DATA_KEYS);
+ }
info.setTextSelectable(isTextSelectable() || isTextEditable());
} else {
info.setAvailableExtraData(Arrays.asList(
@@ -14275,7 +14293,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void addExtraDataToAccessibilityNodeInfo(
AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
- if (arguments != null && extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
+ boolean isCharacterLocationKey = extraDataKey.equals(
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
+ boolean isCharacterLocationInWindowKey = (a11yCharacterInWindowApi() && extraDataKey.equals(
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY));
+ if (arguments != null && (isCharacterLocationKey || isCharacterLocationInWindowKey)) {
int positionInfoStartIndex = arguments.getInt(
EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX, -1);
int positionInfoLength = arguments.getInt(
@@ -14297,7 +14319,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
RectF bounds = cursorAnchorInfo
.getCharacterBounds(positionInfoStartIndex + i);
if (bounds != null) {
- mapRectFromViewToScreenCoords(bounds, true);
+ if (isCharacterLocationKey) {
+ mapRectFromViewToScreenCoords(bounds, true);
+ } else if (isCharacterLocationInWindowKey) {
+ mapRectFromViewToWindowCoords(bounds, true);
+ }
boundingRects[i] = bounds;
}
}
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index bd01899a649b..c67b9cac250b 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -203,6 +203,34 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
mImeCallbacks.remove(callback);
}
+ /**
+ * Unregisters all callbacks on the receiving dispatcher but keeps a reference of the callbacks
+ * in case the clearance is reverted in
+ * {@link ImeOnBackInvokedDispatcher#undoPreliminaryClear()}.
+ */
+ public void preliminaryClear() {
+ // Unregister previously registered callbacks if there's any.
+ if (getReceivingDispatcher() != null) {
+ for (ImeOnBackInvokedCallback callback : mImeCallbacks) {
+ getReceivingDispatcher().unregisterOnBackInvokedCallback(callback);
+ }
+ }
+ }
+
+ /**
+ * Reregisters all callbacks on the receiving dispatcher that have previously been cleared by
+ * calling {@link ImeOnBackInvokedDispatcher#preliminaryClear()}. This can happen if an IME hide
+ * animation is interrupted causing the IME to reappear.
+ */
+ public void undoPreliminaryClear() {
+ if (getReceivingDispatcher() != null) {
+ for (ImeOnBackInvokedCallback callback : mImeCallbacks) {
+ getReceivingDispatcher().registerOnBackInvokedCallbackUnchecked(callback,
+ callback.mPriority);
+ }
+ }
+ }
+
/** Clears all registered callbacks on the instance. */
public void clear() {
// Unregister previously registered callbacks if there's any.
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 8bb4c526b20d..61fc6226f822 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -17,6 +17,7 @@
package android.window;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TransitionType;
import android.annotation.IntDef;
@@ -189,6 +190,8 @@ public final class TransitionFilter implements Parcelable {
public Boolean mCustomAnimation = null;
public IBinder mTaskFragmentToken = null;
+ public int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+
public Requirement() {
}
@@ -206,6 +209,7 @@ public final class TransitionFilter implements Parcelable {
final int customAnimRaw = in.readInt();
mCustomAnimation = customAnimRaw == 0 ? null : Boolean.valueOf(customAnimRaw == 2);
mTaskFragmentToken = in.readStrongBinder();
+ mWindowingMode = in.readInt();
}
/** Go through changes and find if at-least one change matches this filter */
@@ -270,6 +274,12 @@ public final class TransitionFilter implements Parcelable {
continue;
}
}
+ if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
+ if (change.getTaskInfo() == null
+ || change.getTaskInfo().getWindowingMode() != mWindowingMode) {
+ continue;
+ }
+ }
return true;
}
return false;
@@ -322,6 +332,7 @@ public final class TransitionFilter implements Parcelable {
int customAnimRaw = mCustomAnimation == null ? 0 : (mCustomAnimation ? 2 : 1);
dest.writeInt(customAnimRaw);
dest.writeStrongBinder(mTaskFragmentToken);
+ dest.writeInt(mWindowingMode);
}
@NonNull
@@ -369,6 +380,8 @@ public final class TransitionFilter implements Parcelable {
if (mTaskFragmentToken != null) {
out.append(" taskFragmentToken=").append(mTaskFragmentToken);
}
+ out.append(" windowingMode="
+ + WindowConfiguration.windowingModeToString(mWindowingMode));
out.append("}");
return out.toString();
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 14505f527195..0f2dd10d7f47 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -169,8 +169,11 @@ public final class TransitionInfo implements Parcelable {
/** This change represents its start configuration for the duration of the animation. */
public static final int FLAG_CONFIG_AT_END = 1 << 22;
+ /** This change represents one of a Task Display Area. */
+ public static final int FLAG_IS_TASK_DISPLAY_AREA = 1 << 23;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 23;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 24;
/** The change belongs to a window that won't contain activities. */
public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -205,6 +208,7 @@ public final class TransitionInfo implements Parcelable {
FLAG_MOVED_TO_TOP,
FLAG_SYNC,
FLAG_CONFIG_AT_END,
+ FLAG_IS_TASK_DISPLAY_AREA,
FLAG_FIRST_CUSTOM
}, flag = true)
public @interface ChangeFlags {}
@@ -553,6 +557,9 @@ public final class TransitionInfo implements Parcelable {
if ((flags & FLAG_MOVED_TO_TOP) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("MOVE_TO_TOP");
}
+ if ((flags & FLAG_IS_TASK_DISPLAY_AREA) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|").append("FLAG_IS_TASK_DISPLAY_AREA");
+ }
return sb.toString();
}
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
index 364db06976a0..19f8889996dc 100644
--- a/core/java/com/android/internal/compat/AndroidBuildClassifier.java
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -22,6 +22,7 @@ import android.os.Build;
* Platform private class for determining the type of Android build installed.
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AndroidBuildClassifier {
public boolean isDebuggableBuild() {
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index f61157175f9d..f714098e8bc4 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -42,6 +42,7 @@ import java.util.function.Function;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ChangeReporter {
private static final String TAG = "CompatChangeReporter";
private static final Function<Integer, Set<ChangeReport>> NEW_CHANGE_REPORT_SET =
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
index 182dba71d0d7..8fd914aecf55 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
@@ -28,6 +28,7 @@ import java.util.Set;
* Parcelable containing compat config overrides for a given application.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityChangeConfig implements Parcelable {
private final ChangeConfig mChangeConfig;
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index 03fe4551c249..505fd2319a6b 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -25,6 +25,7 @@ import android.os.Parcelable;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CompatibilityChangeInfo implements Parcelable {
private final long mChangeId;
private final @Nullable String mName;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
index 9a02b7b7aae9..32206c9950dd 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
@@ -28,6 +28,7 @@ import java.util.Map;
* Parcelable containing compat config overrides for a given application.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverrideConfig implements Parcelable {
public final Map<Long, PackageOverride> overrides;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
index 8652bb6d05e4..998b48a8a76e 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
@@ -26,6 +26,7 @@ import java.util.Map;
* Parcelable containing compat config overrides by application.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverridesByPackageConfig implements Parcelable {
public final Map<String, CompatibilityOverrideConfig> packageNameToOverrides;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
index b408d6440070..c0e2217d509e 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
@@ -29,6 +29,7 @@ import java.util.Map;
* IDs.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverridesToRemoveByPackageConfig implements Parcelable {
public final Map<String, CompatibilityOverridesToRemoveConfig> packageNameToOverridesToRemove;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
index e85afefdc39a..10461ec0b4c6 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
@@ -30,6 +30,7 @@ import java.util.Set;
* <p>This class is separate from CompatibilityOverrideConfig since we only need change IDs.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverridesToRemoveConfig implements Parcelable {
public final Set<Long> changeIds;
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index e408be2ab471..f018c3a830bd 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -27,6 +27,7 @@ import java.lang.annotation.RetentionPolicy;
/**
* This class contains all the possible override allowed states.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class OverrideAllowedState implements Parcelable {
@IntDef({
ALLOWED,
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index a69d2e4f4dca..3303d875c427 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -15,6 +15,12 @@
*/
package com.android.internal.ravenwood;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
import android.ravenwood.annotation.RavenwoodRedirectionClass;
@@ -78,4 +84,20 @@ public final class RavenwoodEnvironment {
public String getRavenwoodRuntimePath() {
throw notSupportedOnDevice();
}
+
+ /** @hide */
+ public static class CompatIdsForTest {
+ // Enabled by default
+ @ChangeId
+ public static final long TEST_COMPAT_ID_1 = 368131859L;
+
+ @Disabled
+ @ChangeId public static final long TEST_COMPAT_ID_2 = 368131701L;
+
+ @EnabledAfter(targetSdkVersion = S)
+ @ChangeId public static final long TEST_COMPAT_ID_3 = 368131659L;
+
+ @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
+ @ChangeId public static final long TEST_COMPAT_ID_4 = 368132057L;
+ }
}
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index b6383d9f0754..38685b652c50 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -530,8 +530,26 @@ public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup {
int rootViewTopOnWindow = mTmpCoords[1];
int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
- mCoordsOnWindow.set(
- Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
+ // In some cases, app can have specific Window for Android UI components such as EditText.
+ // In this case, Window bounds != App bounds. Hence, instead of ensuring non-negative
+ // PopupWindow coords, app bounds should be used to limit the coords. For instance,
+ // ____ <- |
+ // | | |W1 & App bounds
+ // |___| |
+ // |W2 | | W2 has smaller bounds and contain EditText where PopupWindow will be opened.
+ // ---- <-|
+ // Here, we'll open PopupWindow upwards, but as PopupWindow is anchored based on W2, it
+ // will have negative Y coords. This negative Y is safe to use because it's still within app
+ // bounds. However, if it gets out of app bounds, we should clamp it to 0.
+ Rect appBounds = mContext
+ .getResources().getConfiguration().windowConfiguration.getAppBounds();
+ mCoordsOnWindow.set(x - windowLeftOnScreen, y - windowTopOnScreen);
+ if (rootViewLeftOnScreen + mCoordsOnWindow.x < appBounds.left) {
+ mCoordsOnWindow.x = 0;
+ }
+ if (rootViewTopOnScreen + mCoordsOnWindow.y < appBounds.top) {
+ mCoordsOnWindow.y = 0;
+ }
}
/**
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index df87a69f02ce..56292c3d0fb2 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -2403,6 +2403,11 @@ SurfaceComposerClient::Transaction* android_view_SurfaceTransaction_getNativeSur
}
}
+static void nativeEnableDebugLogCallPoints(JNIEnv* env, jclass clazz, jlong transactionObj) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ transaction->enableDebugLogCallPoints();
+}
+
static const JNINativeMethod sSurfaceControlMethods[] = {
// clang-format off
{"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J",
@@ -2649,6 +2654,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
{"nativeNotifyShutdown", "()V",
(void*)nativeNotifyShutdown },
{"nativeSetLuts", "(JJ[F[I[I[I[I)V", (void*)nativeSetLuts },
+ {"nativeEnableDebugLogCallPoints", "(J)V", (void*)nativeEnableDebugLogCallPoints },
// clang-format on
};
diff --git a/core/res/Android.bp b/core/res/Android.bp
index f6ca8218926c..66c2e12f7cdf 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -171,6 +171,7 @@ android_app {
"android.security.flags-aconfig",
"com.android.hardware.input.input-aconfig",
"aconfig_trade_in_mode_flags",
+ "ranging_aconfig_flags",
],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0479318ad9ed..5913992004b8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2412,6 +2412,16 @@
android:label="@string/permlab_nearby_wifi_devices"
android:protectionLevel="dangerous" />
+ <!-- Required to be able to range to devices using generic ranging module.
+ @FlaggedApi("android.permission.flags.ranging_permission_enabled")
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.RANGING"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:description="@string/permdesc_ranging"
+ android:label="@string/permlab_ranging"
+ android:protectionLevel="dangerous"
+ android:featureFlag="android.permission.flags.ranging_permission_enabled"/>
+
<!-- @SystemApi @TestApi Allows an application to suspend other apps, which will prevent the
user from using them until they are unsuspended.
@hide
diff --git a/core/res/res/drawable/ic_zen_mode_type_unknown.xml b/core/res/res/drawable/ic_zen_mode_icon_star_badge.xml
index 04df5f91fd68..04df5f91fd68 100644
--- a/core/res/res/drawable/ic_zen_mode_type_unknown.xml
+++ b/core/res/res/drawable/ic_zen_mode_icon_star_badge.xml
diff --git a/core/res/res/layout/input_method_switch_item_new.xml b/core/res/res/layout/input_method_switch_item_new.xml
index f8710cc45358..7b241aff3fb1 100644
--- a/core/res/res/layout/input_method_switch_item_new.xml
+++ b/core/res/res/layout/input_method_switch_item_new.xml
@@ -18,18 +18,20 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_item"
android:layout_width="match_parent"
- android:layout_height="72dp"
+ android:layout_height="wrap_content"
+ android:minHeight="72dp"
android:background="@drawable/input_method_switch_item_background"
android:gravity="center_vertical"
android:orientation="horizontal"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="8dp"
android:paddingStart="20dp"
- android:paddingEnd="24dp">
+ android:paddingEnd="24dp"
+ android:paddingVertical="8dp">
<LinearLayout
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start|center_vertical"
android:orientation="vertical">
@@ -39,11 +41,26 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
+ android:marqueeRepeatLimit="1"
android:singleLine="true"
android:fontFamily="google-sans-text"
android:textColor="@color/input_method_switch_on_item"
android:textAppearance="?attr/textAppearanceListItem"/>
+ <TextView
+ android:id="@+id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
+ android:fontFamily="google-sans-text"
+ android:textColor="?attr/materialColorOnSurfaceVariant"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ android:textAllCaps="true"
+ android:visibility="gone"/>
+
</LinearLayout>
<ImageView
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 092d2a72580a..e6dedce8feaf 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4418,6 +4418,10 @@
<declare-styleable name="InputMethod_Subtype">
<!-- The name of the subtype. -->
<attr name="label" />
+ <!-- The layout label of the subtype.
+ {@link android.view.inputmethod.InputMethodSubtype#getLayoutDisplayName} returns the
+ value specified in this attribute. -->
+ <attr name="layoutLabel" format="reference" />
<!-- The icon of the subtype. -->
<attr name="icon" />
<!-- The locale of the subtype. This string should be a locale (for example en_US and fr_FR)
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 0c28ea406aa2..70cc5f14391d 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -125,6 +125,8 @@
<public name="supplementalDescription"/>
<!-- @FlaggedApi("android.security.enable_intent_matching_flags") -->
<public name="intentMatchingFlags"/>
+ <!-- @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) -->
+ <public name="layoutLabel"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01b60000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e23e665e7335..c13fdb17dfe3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1766,6 +1766,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=140]-->
<string name="permdesc_nearby_wifi_devices">Allows the app to advertise, connect, and determine the relative position of nearby Wi\u2011Fi devices</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50]-->
+ <string name="permlab_ranging">determine relative position between nearby devices</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=120]-->
+ <string name="permdesc_ranging">Allow the app to determine relative position between nearby devices</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_preferredPaymentInfo">Preferred NFC Payment Service Information</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fec8bbbfeb83..aa08d5e2313e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5647,7 +5647,6 @@
<java-symbol type="drawable" name="ic_zen_mode_type_schedule_calendar" />
<java-symbol type="drawable" name="ic_zen_mode_type_schedule_time" />
<java-symbol type="drawable" name="ic_zen_mode_type_theater" />
- <java-symbol type="drawable" name="ic_zen_mode_type_unknown" />
<java-symbol type="drawable" name="ic_zen_mode_type_special_dnd" />
<!-- System notification for background user sound -->
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 24f6ceaf786c..8d045f87063b 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -26,6 +26,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -59,6 +60,7 @@ import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -67,7 +69,11 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Looper;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -129,6 +135,9 @@ public class ActivityThreadTest {
@Rule(order = 1)
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private ActivityWindowInfoListener mActivityWindowInfoListener;
private WindowTokenClientController mOriginalWindowTokenClientController;
private Configuration mOriginalAppConfig;
@@ -912,6 +921,32 @@ public class ActivityThreadTest {
}
/**
+ * Verifies that {@link ActivityThread#handleApplicationInfoChanged} does send updates to the
+ * system context, when given the system application info.
+ */
+ @RequiresFlagsEnabled(android.content.res.Flags.FLAG_SYSTEM_CONTEXT_HANDLE_APP_INFO_CHANGED)
+ @Test
+ public void testHandleApplicationInfoChanged_systemContext() {
+ Looper.prepare();
+ final var systemThread = ActivityThread.createSystemActivityThreadForTesting();
+
+ final Context systemContext = systemThread.getSystemContext();
+ final var appInfo = systemContext.getApplicationInfo();
+ // sourceDir must not be null, and contain at least a '/', for handleApplicationInfoChanged.
+ appInfo.sourceDir = "/";
+
+ // Create a copy of the application info.
+ final var newAppInfo = new ApplicationInfo(appInfo);
+ newAppInfo.sourceDir = "/";
+ assertWithMessage("New application info is a separate instance")
+ .that(systemContext.getApplicationInfo()).isNotSameInstanceAs(newAppInfo);
+
+ systemThread.handleApplicationInfoChanged(newAppInfo);
+ assertWithMessage("Application info was updated successfully")
+ .that(systemContext.getApplicationInfo()).isSameInstanceAs(newAppInfo);
+ }
+
+ /**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
* Configuration, int, ActivityWindowInfo)} to try to push activity configuration to the
* activity for the given sequence number.
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 00ffda867d6a..a47a3e0e6c2a 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -161,7 +161,7 @@ public class ImeBackAnimationControllerTest {
mBackAnimationController.onBackInvoked();
// verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
// getInputMethodManager is called from ImeBackAnimationController)
- verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
+ verify(mViewRootInsetsControllerHost, times(2)).getInputMethodManager();
// verify that ImeBackAnimationController does not take control over IME insets
verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(),
anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
@@ -180,7 +180,7 @@ public class ImeBackAnimationControllerTest {
mBackAnimationController.onBackInvoked();
// verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
// getInputMethodManager is called from ImeBackAnimationController)
- verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
+ verify(mViewRootInsetsControllerHost, times(2)).getInputMethodManager();
// verify that ImeBackAnimationController does not take control over IME insets
verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(),
anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
@@ -300,7 +300,7 @@ public class ImeBackAnimationControllerTest {
mBackAnimationController.onBackInvoked();
// verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
// getInputMethodManager is called from ImeBackAnimationController)
- verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
+ verify(mViewRootInsetsControllerHost, times(2)).getInputMethodManager();
});
}
diff --git a/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java b/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java
index 262bd5cd6c01..0f17f9cdddc8 100644
--- a/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java
+++ b/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java
@@ -16,7 +16,11 @@
package android.view;
+import static android.view.RoundScrollbarRenderer.BLUECHIP_ENABLED_SYSPROP;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -30,11 +34,8 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.os.SystemProperties;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.flags.Flags;
import androidx.test.core.app.ApplicationProvider;
@@ -42,7 +43,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -66,9 +66,6 @@ public class RoundScrollbarRendererTest {
private static final float DEFAULT_ALPHA = 0.5f;
private static final Rect BOUNDS = new Rect(0, 0, 200, 200);
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Mock private Canvas mCanvas;
@Captor private ArgumentCaptor<Paint> mPaintCaptor;
private RoundScrollbarRenderer mScrollbar;
@@ -88,8 +85,8 @@ public class RoundScrollbarRendererTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_USE_REFACTORED_ROUND_SCROLLBAR)
public void testScrollbarDrawn_legacy() {
+ assumeFalse(usingRefactoredScrollbar());
mScrollbar.drawRoundScrollbars(mCanvas, DEFAULT_ALPHA, BOUNDS, /* drawToLeft= */ false);
// The arc will be drawn twice, i.e. once for track and once for thumb
@@ -105,8 +102,8 @@ public class RoundScrollbarRendererTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_USE_REFACTORED_ROUND_SCROLLBAR)
public void testScrollbarDrawn() {
+ assumeTrue(usingRefactoredScrollbar());
mScrollbar.drawRoundScrollbars(mCanvas, DEFAULT_ALPHA, BOUNDS, /* drawToLeft= */ false);
// The arc will be drawn thrice, i.e. twice for track and once for thumb
@@ -143,4 +140,9 @@ public class RoundScrollbarRendererTest {
return super.computeVerticalScrollExtent();
}
}
+
+ private static boolean usingRefactoredScrollbar() {
+ return Flags.useRefactoredRoundScrollbar()
+ && SystemProperties.getBoolean(BLUECHIP_ENABLED_SYSPROP, false);
+ }
}
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 93d94c9cd7eb..b4899f975f43 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -19,6 +19,8 @@ package android.graphics;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import com.android.internal.camera.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -63,6 +65,7 @@ public class ImageFormat {
RAW_DEPTH10,
PRIVATE,
HEIC,
+ HEIC_ULTRAHDR,
JPEG_R
})
public @interface Format {
@@ -832,6 +835,16 @@ public class ImageFormat {
public static final int HEIC = 0x48454946;
/**
+ * High Efficiency Image File Format (HEIF) with embedded HDR gain map
+ *
+ * <p>This format defines the HEIC brand of High Efficiency Image File
+ * Format as described in ISO/IEC 23008-12:2024 with HDR gain map according
+ * to ISO/CD 21496‐1.</p>
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final int HEIC_ULTRAHDR = 0x1006;
+
+ /**
* Use this function to retrieve the number of bits per pixel of an
* ImageFormat.
*
@@ -926,6 +939,11 @@ public class ImageFormat {
if (android.media.codec.Flags.p210FormatSupport() && format == YCBCR_P210) {
return true;
}
+ if (Flags.cameraHeifGainmap()){
+ if (format == HEIC_ULTRAHDR) {
+ return true;
+ }
+ }
return false;
}
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b7a1c13c75c7..8bb32568ec5a 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -19,7 +19,7 @@ package android.graphics;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
import static com.android.text.flags.Flags.FLAG_DEPRECATE_ELEGANT_TEXT_HEIGHT_API;
-
+import static com.android.text.flags.Flags.FLAG_VERTICAL_TEXT_LAYOUT;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
@@ -35,6 +35,7 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
+import android.graphics.text.TextRunShaper;
import android.os.Build;
import android.os.LocaleList;
import android.text.GraphicsOperations;
@@ -269,7 +270,24 @@ public class Paint {
public static final int EMBEDDED_BITMAP_TEXT_FLAG = 0x400;
/** @hide bit mask for the flag forcing freetype's autohinter on for text */
public static final int AUTO_HINTING_TEXT_FLAG = 0x800;
- /** @hide bit mask for the flag enabling vertical rendering for text */
+
+ /**
+ * A flat that controls text to be written in vertical orientation
+ *
+ * <p>
+ * This flag is used for telling the underlying text layout engine that the text is for vertical
+ * direction. By enabling this flag, text measurement, drawing and shaping APIs works for
+ * vertical text layout. For example, {@link Canvas#drawText(String, float, float, Paint)} draws
+ * text from top to bottom. {@link Paint#measureText(String)} returns vertical advances instead
+ * of horizontal advances. {@link TextRunShaper} shapes text vertically and report glyph IDs for
+ * vertical layout.
+ *
+ * <p>
+ * Do not set this flag for making {@link android.text.Layout}. The {@link android.text.Layout}
+ * class and its subclasses are designed for horizontal text only and does not work for vertical
+ * text.
+ */
+ @FlaggedApi(FLAG_VERTICAL_TEXT_LAYOUT)
public static final int VERTICAL_TEXT_FLAG = 0x1000;
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 438532725686..4d7be39ca5a4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -16,6 +16,7 @@
package androidx.window.extensions.area;
+import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
@@ -569,7 +570,8 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
private boolean isDeviceFolded() {
if (Flags.deviceStatePropertyApi()) {
return mCurrentDeviceState.hasProperty(
- PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+ && !mCurrentDeviceState.hasProperty(PROPERTY_EMULATED_ONLY);
} else {
return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState.getIdentifier());
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
index 2fbf089d99d6..00d9a931cebe 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
@@ -19,12 +19,15 @@ package com.android.wm.shell.bubbles.bar
import android.app.ActivityManager
import android.content.Context
import android.content.pm.LauncherApps
+import android.graphics.PointF
import android.os.Handler
import android.os.UserManager
import android.view.IWindowManager
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
+import androidx.core.animation.AnimatorTestRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -48,6 +51,7 @@ import com.android.wm.shell.bubbles.BubbleTaskViewFactory
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.FakeBubbleFactory
import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
+import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.bubbles.properties.BubbleProperties
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.common.DisplayController
@@ -57,6 +61,7 @@ import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.shared.TransactionPool
+import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -66,8 +71,10 @@ import com.android.wm.shell.taskview.TaskViewTaskController
import com.android.wm.shell.taskview.TaskViewTransitions
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
+import org.junit.After
import java.util.Collections
import org.junit.Before
+import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -79,18 +86,28 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class BubbleBarLayerViewTest {
+ companion object {
+ @JvmField @ClassRule
+ val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
+ }
+
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var bubbleBarLayerView: BubbleBarLayerView
private lateinit var uiEventLoggerFake: UiEventLoggerFake
+ private lateinit var bubbleController: BubbleController
+
+ private lateinit var bubblePositioner: BubblePositioner
+
private lateinit var bubble: Bubble
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
ProtoLog.init()
+ PhysicsAnimatorTestUtils.prepareForTest()
uiEventLoggerFake = UiEventLoggerFake()
val bubbleLogger = BubbleLogger(uiEventLoggerFake)
@@ -100,7 +117,7 @@ class BubbleBarLayerViewTest {
val windowManager = context.getSystemService(WindowManager::class.java)
- val bubblePositioner = BubblePositioner(context, windowManager)
+ bubblePositioner = BubblePositioner(context, windowManager)
bubblePositioner.setShowingInBubbleBar(true)
val bubbleData =
@@ -113,7 +130,7 @@ class BubbleBarLayerViewTest {
bgExecutor,
)
- val bubbleController =
+ bubbleController =
createBubbleController(
bubbleData,
windowManager,
@@ -151,6 +168,12 @@ class BubbleBarLayerViewTest {
bubble = FakeBubbleFactory.createChatBubbleWithViewInfo(context, viewInfo = viewInfo)
}
+ @After
+ fun tearDown() {
+ PhysicsAnimatorTestUtils.tearDown()
+ getInstrumentation().waitForIdleSync()
+ }
+
private fun createBubbleController(
bubbleData: BubbleData,
windowManager: WindowManager?,
@@ -224,6 +247,70 @@ class BubbleBarLayerViewTest {
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @Test
+ fun testEventLogging_dragExpandedViewLeft() {
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ // Drag from right to left
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, rightEdge())
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, leftEdge())
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, leftEdge())
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
+ @Test
+ fun testEventLogging_dragExpandedViewRight() {
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.LEFT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ // Drag from left to right
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, leftEdge())
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, rightEdge())
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, rightEdge())
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
+ private fun leftEdge(): PointF {
+ val screenSize = bubblePositioner.availableRect
+ return PointF(screenSize.left.toFloat(), screenSize.height() / 2f)
+ }
+
+ private fun rightEdge(): PointF {
+ val screenSize = bubblePositioner.availableRect
+ return PointF(screenSize.right.toFloat(), screenSize.height() / 2f)
+ }
+
+ private fun waitForExpandedViewAnimation() {
+ // wait for idle to allow the animation to start
+ getInstrumentation().waitForIdleSync()
+ getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(200) }
+ PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
+ AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
+ }
+
private inner class FakeBubbleTaskViewFactory(private val mainExecutor: ShellExecutor) :
BubbleTaskViewFactory {
override fun create(): BubbleTaskView {
@@ -290,4 +377,9 @@ class BubbleBarLayerViewTest {
}
}
}
+
+ private fun View.dispatchTouchEvent(eventTime: Long, action: Int, point: PointF) {
+ val event = MotionEvent.obtain(0L, eventTime, action, point.x, point.y, 0)
+ getInstrumentation().runOnMainSync { dispatchTouchEvent(event) }
+ }
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index ecb2b25a02f1..d4cbe6e10971 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -74,6 +74,7 @@ class BubbleExpandedViewPinControllerTest {
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ ProtoLog.init()
container = FrameLayout(context)
val windowManager = context.getSystemService(WindowManager::class.java)
positioner = BubblePositioner(context, windowManager)
@@ -85,7 +86,7 @@ class BubbleExpandedViewPinControllerTest {
isSmallTablet = false,
isLandscape = true,
isRtl = false,
- insets = Insets.of(10, 20, 30, 40)
+ insets = Insets.of(10, 20, 30, 40),
)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen =
@@ -407,12 +408,26 @@ class BubbleExpandedViewPinControllerTest {
assertThat(testListener.locationReleases).containsExactly(RIGHT)
}
+ /** Send drag start event when on left */
+ @Test
+ fun start_onLeft_sendStartEventOnLeft() {
+ getInstrumentation().runOnMainSync { controller.onDragStart(initialLocationOnLeft = true) }
+ assertThat(testListener.locationStart).containsExactly(LEFT)
+ }
+
+ /** Send drag start event when on right */
+ @Test
+ fun start_onRight_sendStartEventOnRight() {
+ getInstrumentation().runOnMainSync { controller.onDragStart(initialLocationOnLeft = false) }
+ assertThat(testListener.locationStart).containsExactly(RIGHT)
+ }
+
private fun getExpectedDropTargetBoundsOnLeft(): Rect =
Rect().also {
positioner.getBubbleBarExpandedViewBounds(
true /* onLeft */,
false /* isOverflowExpanded */,
- it
+ it,
)
}
@@ -421,7 +436,7 @@ class BubbleExpandedViewPinControllerTest {
positioner.getBubbleBarExpandedViewBounds(
false /* onLeft */,
false /* isOverflowExpanded */,
- it
+ it,
)
}
@@ -446,8 +461,14 @@ class BubbleExpandedViewPinControllerTest {
}
internal class TestLocationChangeListener : BaseBubblePinController.LocationChangeListener {
+ val locationStart = mutableListOf<BubbleBarLocation>()
val locationChanges = mutableListOf<BubbleBarLocation>()
val locationReleases = mutableListOf<BubbleBarLocation>()
+
+ override fun onStart(location: BubbleBarLocation) {
+ locationStart.add(location)
+ }
+
override fun onChange(location: BubbleBarLocation) {
locationChanges.add(location)
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.aidl
index e21bf8fb723c..93e635dd937c 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.aidl
@@ -16,4 +16,4 @@
package com.android.wm.shell.shared;
-parcelable GroupedRecentTaskInfo; \ No newline at end of file
+parcelable GroupedTaskInfo; \ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
index 65e079ef4f72..03e0ab0591a1 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
@@ -17,7 +17,8 @@
package com.android.wm.shell.shared;
import android.annotation.IntDef;
-import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,69 +28,91 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.shared.split.SplitBounds;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
- * Simple container for recent tasks. May contain either a single or pair of tasks.
+ * Simple container for recent tasks which should be presented as a single task within the
+ * Overview UI.
*/
-public class GroupedRecentTaskInfo implements Parcelable {
+public class GroupedTaskInfo implements Parcelable {
- public static final int TYPE_SINGLE = 1;
+ public static final int TYPE_FULLSCREEN = 1;
public static final int TYPE_SPLIT = 2;
public static final int TYPE_FREEFORM = 3;
@IntDef(prefix = {"TYPE_"}, value = {
- TYPE_SINGLE,
+ TYPE_FULLSCREEN,
TYPE_SPLIT,
TYPE_FREEFORM
})
public @interface GroupType {}
+ /**
+ * The type of this particular task info, can be one of TYPE_FULLSCREEN, TYPE_SPLIT or
+ * TYPE_FREEFORM.
+ */
+ @GroupType
+ protected final int mType;
+
+ /**
+ * The list of tasks associated with this single recent task info.
+ * TYPE_FULLSCREEN: Contains the stack of tasks associated with a single "task" in overview
+ * TYPE_SPLIT: Contains the two split roots of each side
+ * TYPE_FREEFORM: Contains the set of tasks currently in freeform mode
+ */
@NonNull
- private final ActivityManager.RecentTaskInfo[] mTasks;
+ protected final List<TaskInfo> mTasks;
+
+ /**
+ * Only set for TYPE_SPLIT.
+ *
+ * Information about the split bounds.
+ */
@Nullable
- private final SplitBounds mSplitBounds;
- @GroupType
- private final int mType;
- // TODO(b/348332802): move isMinimized inside each Task object instead once we have a
- // replacement for RecentTaskInfo
- private final int[] mMinimizedTaskIds;
+ protected final SplitBounds mSplitBounds;
/**
- * Create new for a single task
+ * Only set for TYPE_FREEFORM.
+ *
+ * TODO(b/348332802): move isMinimized inside each Task object instead once we have a
+ * replacement for RecentTaskInfo
*/
- public static GroupedRecentTaskInfo forSingleTask(
- @NonNull ActivityManager.RecentTaskInfo task) {
- return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task}, null,
- TYPE_SINGLE, null /* minimizedFreeformTasks */);
+ @Nullable
+ protected final int[] mMinimizedTaskIds;
+
+ /**
+ * Create new for a stack of fullscreen tasks
+ */
+ public static GroupedTaskInfo forFullscreenTasks(@NonNull TaskInfo task) {
+ return new GroupedTaskInfo(List.of(task), null, TYPE_FULLSCREEN,
+ null /* minimizedFreeformTasks */);
}
/**
* Create new for a pair of tasks in split screen
*/
- public static GroupedRecentTaskInfo forSplitTasks(@NonNull ActivityManager.RecentTaskInfo task1,
- @NonNull ActivityManager.RecentTaskInfo task2, @Nullable SplitBounds splitBounds) {
- return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task1, task2},
- splitBounds, TYPE_SPLIT, null /* minimizedFreeformTasks */);
+ public static GroupedTaskInfo forSplitTasks(@NonNull TaskInfo task1,
+ @NonNull TaskInfo task2, @Nullable SplitBounds splitBounds) {
+ return new GroupedTaskInfo(List.of(task1, task2), splitBounds, TYPE_SPLIT,
+ null /* minimizedFreeformTasks */);
}
/**
* Create new for a group of freeform tasks
*/
- public static GroupedRecentTaskInfo forFreeformTasks(
- @NonNull ActivityManager.RecentTaskInfo[] tasks,
- @NonNull Set<Integer> minimizedFreeformTasks) {
- return new GroupedRecentTaskInfo(
- tasks,
- null /* splitBounds */,
- TYPE_FREEFORM,
+ public static GroupedTaskInfo forFreeformTasks(
+ @NonNull List<TaskInfo> tasks,
+ @NonNull Set<Integer> minimizedFreeformTasks) {
+ return new GroupedTaskInfo(tasks, null /* splitBounds */, TYPE_FREEFORM,
minimizedFreeformTasks.stream().mapToInt(i -> i).toArray());
}
- private GroupedRecentTaskInfo(
- @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ private GroupedTaskInfo(
+ @NonNull List<TaskInfo> tasks,
@Nullable SplitBounds splitBounds,
@GroupType int type,
@Nullable int[] minimizedFreeformTaskIds) {
@@ -100,52 +123,56 @@ public class GroupedRecentTaskInfo implements Parcelable {
ensureAllMinimizedIdsPresent(tasks, minimizedFreeformTaskIds);
}
- private static void ensureAllMinimizedIdsPresent(
- @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ private void ensureAllMinimizedIdsPresent(
+ @NonNull List<TaskInfo> tasks,
@Nullable int[] minimizedFreeformTaskIds) {
if (minimizedFreeformTaskIds == null) {
return;
}
if (!Arrays.stream(minimizedFreeformTaskIds).allMatch(
- taskId -> Arrays.stream(tasks).anyMatch(task -> task.taskId == taskId))) {
+ taskId -> tasks.stream().anyMatch(task -> task.taskId == taskId))) {
throw new IllegalArgumentException("Minimized task IDs contain non-existent Task ID.");
}
}
- GroupedRecentTaskInfo(Parcel parcel) {
- mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR);
+ protected GroupedTaskInfo(@NonNull Parcel parcel) {
+ mTasks = new ArrayList();
+ final int numTasks = parcel.readInt();
+ for (int i = 0; i < numTasks; i++) {
+ mTasks.add(new TaskInfo(parcel));
+ }
mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
mType = parcel.readInt();
mMinimizedTaskIds = parcel.createIntArray();
}
/**
- * Get primary {@link ActivityManager.RecentTaskInfo}
+ * Get primary {@link RecentTaskInfo}
*/
@NonNull
- public ActivityManager.RecentTaskInfo getTaskInfo1() {
- return mTasks[0];
+ public TaskInfo getTaskInfo1() {
+ return mTasks.getFirst();
}
/**
- * Get secondary {@link ActivityManager.RecentTaskInfo}.
+ * Get secondary {@link RecentTaskInfo}.
*
* Used in split screen.
*/
@Nullable
- public ActivityManager.RecentTaskInfo getTaskInfo2() {
- if (mTasks.length > 1) {
- return mTasks[1];
+ public TaskInfo getTaskInfo2() {
+ if (mTasks.size() > 1) {
+ return mTasks.get(1);
}
return null;
}
/**
- * Get all {@link ActivityManager.RecentTaskInfo}s grouped together.
+ * Get all {@link RecentTaskInfo}s grouped together.
*/
@NonNull
- public List<ActivityManager.RecentTaskInfo> getTaskInfoList() {
- return Arrays.asList(mTasks);
+ public List<TaskInfo> getTaskInfoList() {
+ return mTasks;
}
/**
@@ -164,28 +191,46 @@ public class GroupedRecentTaskInfo implements Parcelable {
return mType;
}
+ @Nullable
public int[] getMinimizedTaskIds() {
return mMinimizedTaskIds;
}
@Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof GroupedTaskInfo)) {
+ return false;
+ }
+ GroupedTaskInfo other = (GroupedTaskInfo) obj;
+ return mType == other.mType
+ && Objects.equals(mTasks, other.mTasks)
+ && Objects.equals(mSplitBounds, other.mSplitBounds)
+ && Arrays.equals(mMinimizedTaskIds, other.mMinimizedTaskIds);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mTasks, mSplitBounds, Arrays.hashCode(mMinimizedTaskIds));
+ }
+
+ @Override
public String toString() {
StringBuilder taskString = new StringBuilder();
- for (int i = 0; i < mTasks.length; i++) {
+ for (int i = 0; i < mTasks.size(); i++) {
if (i == 0) {
taskString.append("Task");
} else {
taskString.append(", Task");
}
- taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks[i]));
+ taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks.get(i)));
}
if (mSplitBounds != null) {
taskString.append(", SplitBounds: ").append(mSplitBounds);
}
taskString.append(", Type=");
switch (mType) {
- case TYPE_SINGLE:
- taskString.append("TYPE_SINGLE");
+ case TYPE_FULLSCREEN:
+ taskString.append("TYPE_FULLSCREEN");
break;
case TYPE_SPLIT:
taskString.append("TYPE_SPLIT");
@@ -199,7 +244,7 @@ public class GroupedRecentTaskInfo implements Parcelable {
return taskString.toString();
}
- private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
+ private String getTaskInfo(TaskInfo taskInfo) {
if (taskInfo == null) {
return null;
}
@@ -213,7 +258,12 @@ public class GroupedRecentTaskInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeTypedArray(mTasks, 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++) {
+ mTasks.get(i).writeTaskToParcel(parcel, flags);
+ }
parcel.writeTypedObject(mSplitBounds, flags);
parcel.writeInt(mType);
parcel.writeIntArray(mMinimizedTaskIds);
@@ -224,13 +274,15 @@ public class GroupedRecentTaskInfo implements Parcelable {
return 0;
}
- public static final @android.annotation.NonNull Creator<GroupedRecentTaskInfo> CREATOR =
- new Creator<GroupedRecentTaskInfo>() {
- public GroupedRecentTaskInfo createFromParcel(Parcel source) {
- return new GroupedRecentTaskInfo(source);
+ public static final Creator<GroupedTaskInfo> CREATOR = new Creator() {
+ @Override
+ public GroupedTaskInfo createFromParcel(Parcel in) {
+ return new GroupedTaskInfo(in);
}
- public GroupedRecentTaskInfo[] newArray(int size) {
- return new GroupedRecentTaskInfo[size];
+
+ @Override
+ public GroupedTaskInfo[] newArray(int size) {
+ return new GroupedTaskInfo[size];
}
};
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt
index fc3dc1465dff..f93b35e868f6 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt
@@ -20,7 +20,7 @@ import android.os.Looper
import android.util.ArrayMap
import androidx.dynamicanimation.animation.FloatPropertyCompat
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils.prepareForTest
-import java.util.*
+import java.util.ArrayDeque
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.collections.ArrayList
@@ -74,14 +74,17 @@ object PhysicsAnimatorTestUtils {
@JvmStatic
fun tearDown() {
- val latch = CountDownLatch(1)
- animationThreadHandler.post {
+ if (Looper.myLooper() == animationThreadHandler.looper) {
animatorTestHelpers.keys.forEach { it.cancel() }
- latch.countDown()
+ } else {
+ val latch = CountDownLatch(1)
+ animationThreadHandler.post {
+ animatorTestHelpers.keys.forEach { it.cancel() }
+ latch.countDown()
+ }
+ latch.await(5, TimeUnit.SECONDS)
}
- latch.await()
-
animatorTestHelpers.clear()
animators.clear()
allAnimatedObjects.clear()
@@ -348,8 +351,9 @@ object PhysicsAnimatorTestUtils {
* Returns all of the values that have ever been reported to update listeners, per property.
*/
@Suppress("UNCHECKED_CAST")
- fun <T : Any> getAnimationUpdateFrames(animator: PhysicsAnimator<T>):
- UpdateFramesPerProperty<T> {
+ fun <T : Any> getAnimationUpdateFrames(
+ animator: PhysicsAnimator<T>
+ ): UpdateFramesPerProperty<T> {
return animatorTestHelpers[animator]?.getUpdates() as UpdateFramesPerProperty<T>
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
index 7086691e7431..bd129a28f049 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
@@ -56,6 +56,7 @@ abstract class BaseBubblePinController(private val screenSizeProvider: () -> Poi
onLeft = initialLocationOnLeft
screenCenterX = screenSizeProvider.invoke().x / 2
dismissZone = getExclusionRect()
+ listener?.onStart(if (initialLocationOnLeft) LEFT else RIGHT)
}
/** View has moved to [x] and [y] screen coordinates */
@@ -109,6 +110,7 @@ abstract class BaseBubblePinController(private val screenSizeProvider: () -> Poi
/** Get width for exclusion rect where dismiss takes over drag */
protected abstract fun getExclusionRectWidth(): Float
+
/** Get height for exclusion rect where dismiss takes over drag */
protected abstract fun getExclusionRectHeight(): Float
@@ -184,6 +186,9 @@ abstract class BaseBubblePinController(private val screenSizeProvider: () -> Poi
/** Receive updates on location changes */
interface LocationChangeListener {
+ /** Bubble bar dragging has started. Includes the initial location of the bar */
+ fun onStart(location: BubbleBarLocation) {}
+
/**
* Bubble bar has been dragged to a new [BubbleBarLocation]. And the drag is still in
* progress.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 402818c80b01..999ce17905ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -124,18 +124,7 @@ public class BubbleBarLayerView extends FrameLayout
mBubbleExpandedViewPinController = new BubbleExpandedViewPinController(
context, this, mPositioner);
- mBubbleExpandedViewPinController.setListener(
- new BaseBubblePinController.LocationChangeListener() {
- @Override
- public void onChange(@NonNull BubbleBarLocation bubbleBarLocation) {
- mBubbleController.animateBubbleBarLocation(bubbleBarLocation);
- }
-
- @Override
- public void onRelease(@NonNull BubbleBarLocation location) {
- mBubbleController.setBubbleBarLocation(location);
- }
- });
+ mBubbleExpandedViewPinController.setListener(new LocationChangeListener());
setOnClickListener(view -> hideModalOrCollapse());
}
@@ -238,11 +227,7 @@ public class BubbleBarLayerView extends FrameLayout
DragListener dragListener = inDismiss -> {
if (inDismiss && mExpandedBubble != null) {
mBubbleController.dismissBubble(mExpandedBubble.getKey(), DISMISS_USER_GESTURE);
- if (mExpandedBubble instanceof Bubble) {
- // Only a bubble can be dragged to dismiss
- mBubbleLogger.log((Bubble) mExpandedBubble,
- BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_EXP_VIEW);
- }
+ logBubbleEvent(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_EXP_VIEW);
}
};
mDragController = new BubbleBarExpandedViewDragController(
@@ -423,10 +408,47 @@ public class BubbleBarLayerView extends FrameLayout
}
}
+ /**
+ * Log the event only if {@link #mExpandedBubble} is a {@link Bubble}.
+ * <p>
+ * Skips logging if it is {@link BubbleOverflow}.
+ */
+ private void logBubbleEvent(BubbleLogger.Event event) {
+ if (mExpandedBubble != null && mExpandedBubble instanceof Bubble bubble) {
+ mBubbleLogger.log(bubble, event);
+ }
+ }
+
@Nullable
@VisibleForTesting
public BubbleBarExpandedViewDragController getDragController() {
return mDragController;
}
+ private class LocationChangeListener implements
+ BaseBubblePinController.LocationChangeListener {
+
+ private BubbleBarLocation mInitialLocation;
+
+ @Override
+ public void onStart(@NonNull BubbleBarLocation location) {
+ mInitialLocation = location;
+ }
+
+ @Override
+ public void onChange(@NonNull BubbleBarLocation bubbleBarLocation) {
+ mBubbleController.animateBubbleBarLocation(bubbleBarLocation);
+ }
+
+ @Override
+ public void onRelease(@NonNull BubbleBarLocation location) {
+ mBubbleController.setBubbleBarLocation(location);
+ if (location != mInitialLocation) {
+ BubbleLogger.Event event = location.isOnLeft(isLayoutRtl())
+ ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_EXP_VIEW
+ : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_EXP_VIEW;
+ logBubbleEvent(event);
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index b83b5f341dda..8ef20d1d6b93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -44,7 +44,8 @@ object PipUtils {
private const val TAG = "PipUtils"
// Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
- private const val EPSILON = 1e-7
+ // TODO b/377530560: Restore epsilon once a long term fix is merged for non-config-at-end issue.
+ private const val EPSILON = 0.05f
/**
* @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 886330f3264a..0200e18b5c50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -286,7 +286,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
// we need to ignore all the incoming TaskInfo until the education
// completes. If we come from a double tap we follow the normal flow.
final boolean topActivityPillarboxed =
- taskInfo.appCompatTaskInfo.isTopActivityPillarboxed();
+ taskInfo.appCompatTaskInfo.isTopActivityPillarboxShaped();
final boolean isFirstTimeHorizontalReachabilityEdu = topActivityPillarboxed
&& !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(taskInfo);
final boolean isFirstTimeVerticalReachabilityEdu = !topActivityPillarboxed
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 706a67821a30..a472f79c98e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -779,7 +779,8 @@ public abstract class WMShellModule {
ShellTaskOrganizer shellTaskOrganizer,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
ReturnToDragStartAnimator returnToDragStartAnimator,
- @DynamicOverride DesktopRepository desktopRepository) {
+ @DynamicOverride DesktopRepository desktopRepository,
+ DesktopModeEventLogger desktopModeEventLogger) {
return new DesktopTilingDecorViewModel(
context,
displayController,
@@ -789,7 +790,8 @@ public abstract class WMShellModule {
shellTaskOrganizer,
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
- desktopRepository
+ desktopRepository,
+ desktopModeEventLogger
);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 48bb2a8b4a74..cefcb757690f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -205,6 +205,11 @@ class DesktopMixedTransitionHandler(
finishTransaction: SurfaceControl.Transaction,
finishCallback: TransitionFinishCallback,
): Boolean {
+ val launchChange = findDesktopTaskChange(info, pending.launchingTask)
+ if (launchChange == null) {
+ logV("No launch Change, returning")
+ return false
+ }
// Check if there's also an immersive change during this launch.
val immersiveExitChange = pending.exitingImmersiveTask?.let { exitingTask ->
findDesktopTaskChange(info, exitingTask)
@@ -212,8 +217,6 @@ class DesktopMixedTransitionHandler(
val minimizeChange = pending.minimizingTask?.let { minimizingTask ->
findDesktopTaskChange(info, minimizingTask)
}
- val launchChange = findDesktopTaskChange(info, pending.launchingTask)
- ?: error("Should have pending launching task change")
var subAnimationCount = -1
var combinedWct: WindowContainerTransaction? = null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index edcc877ef58e..c7cf31081c8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -275,7 +275,7 @@ private fun TaskInfo.hasPortraitTopActivity(): Boolean {
}
// Then check if the activity is portrait when letterboxed
- appCompatTaskInfo.isTopActivityLetterboxed -> appCompatTaskInfo.isTopActivityPillarboxed
+ appCompatTaskInfo.isTopActivityLetterboxed -> appCompatTaskInfo.isTopActivityPillarboxShaped
// Then check if the activity is portrait
appBounds != null -> appBounds.height() > appBounds.width()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index f8d2011d0934..b618bf1215ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -53,6 +53,7 @@ import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.ProtoLog;
+import com.android.window.flags.Flags;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -72,6 +73,9 @@ import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
public class KeyguardTransitionHandler
implements Transitions.TransitionHandler, KeyguardChangeListener,
TaskStackListenerCallback {
+ private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS =
+ Flags.ensureKeyguardDoesTransitionStarting();
+
private static final String TAG = "KeyguardTransition";
private final Transitions mTransitions;
@@ -194,7 +198,7 @@ public class KeyguardTransitionHandler
// Occlude/unocclude animations are only played if the keyguard is locked.
if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
- if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0) {
+ if (isKeyguardOccluding(info)) {
if (hasOpeningDream(info)) {
return startAnimation(mOccludeByDreamTransition, "occlude-by-dream",
transition, info, startTransaction, finishTransaction, finishCallback);
@@ -202,7 +206,7 @@ public class KeyguardTransitionHandler
return startAnimation(mOccludeTransition, "occlude",
transition, info, startTransaction, finishTransaction, finishCallback);
}
- } else if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
+ } else if (isKeyguardUnoccluding(info)) {
return startAnimation(mUnoccludeTransition, "unocclude",
transition, info, startTransaction, finishTransaction, finishCallback);
}
@@ -325,6 +329,36 @@ public class KeyguardTransitionHandler
return false;
}
+ private static boolean isKeyguardOccluding(@NonNull TransitionInfo info) {
+ if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
+ return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0;
+ }
+
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.hasFlags(TransitionInfo.FLAG_IS_TASK_DISPLAY_AREA)
+ && change.getMode() == TRANSIT_TO_FRONT) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isKeyguardUnoccluding(@NonNull TransitionInfo info) {
+ if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
+ return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0;
+ }
+
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.hasFlags(TransitionInfo.FLAG_IS_TASK_DISPLAY_AREA)
+ && change.getMode() == TRANSIT_TO_BACK) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void finishAnimationImmediately(IBinder transition, StartedTransition playing) {
final IBinder fakeTransition = new Binder();
final TransitionInfo fakeInfo = new TransitionInfo(TRANSIT_SLEEP, 0x0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
index 24077a35d41c..026482004d51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
@@ -24,7 +24,6 @@ import android.view.Choreographer;
import android.view.SurfaceControl;
import com.android.wm.shell.R;
-import com.android.wm.shell.transition.Transitions;
/**
* Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
@@ -180,8 +179,7 @@ public class PipSurfaceTransactionHelper {
// destination are different.
final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH;
final Rect crop = mTmpDestinationRect;
- crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH
- : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH);
+ crop.set(0, 0, destW, destH);
// Inverse scale for crop to fit in screen coordinates.
crop.scale(1 / scale);
crop.offset(insets.left, insets.top);
@@ -200,8 +198,8 @@ public class PipSurfaceTransactionHelper {
}
}
mTmpTransform.setScale(scale, scale);
- mTmpTransform.postRotate(degrees);
mTmpTransform.postTranslate(positionX, positionY);
+ mTmpTransform.postRotate(degrees);
tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setCrop(leash, crop);
return this;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
index 3f9b0c30e314..fb1aba399585 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.pip2.animation;
+import static android.view.Surface.ROTATION_90;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.RectEvaluator;
@@ -73,6 +75,7 @@ public class PipExpandAnimator extends ValueAnimator {
mAnimationStartCallback.run();
}
if (mStartTransaction != null) {
+ onExpandAnimationUpdate(mStartTransaction, 0f);
mStartTransaction.apply();
}
}
@@ -81,13 +84,7 @@ public class PipExpandAnimator extends ValueAnimator {
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mFinishTransaction != null) {
- // finishTransaction might override some state (eg. corner radii) so we want to
- // manually set the state to the end of the animation
- mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash,
- mSourceRectHint, mBaseBounds, mAnimatedRect, getInsets(1f),
- false /* isInPipDirection */, 1f)
- .round(mFinishTransaction, mLeash, false /* applyCornerRadius */)
- .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */);
+ onExpandAnimationUpdate(mFinishTransaction, 1f);
}
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
@@ -102,14 +99,7 @@ public class PipExpandAnimator extends ValueAnimator {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
final float fraction = getAnimatedFraction();
-
- // TODO (b/350801661): implement fixed rotation
- Rect insets = getInsets(fraction);
- mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
- mBaseBounds, mAnimatedRect,
- insets, false /* isInPipDirection */, fraction)
- .round(tx, mLeash, false /* applyCornerRadius */)
- .shadow(tx, mLeash, false /* applyCornerRadius */);
+ onExpandAnimationUpdate(tx, fraction);
tx.apply();
}
};
@@ -167,6 +157,32 @@ public class PipExpandAnimator extends ValueAnimator {
mAnimationEndCallback = runnable;
}
+ private void onExpandAnimationUpdate(SurfaceControl.Transaction tx, float fraction) {
+ Rect insets = getInsets(fraction);
+ if (mRotation == Surface.ROTATION_0) {
+ mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mBaseBounds,
+ mAnimatedRect, insets, false /* isInPipDirection */, fraction);
+ } else {
+ // Fixed rotation case.
+ Rect start = mStartBounds;
+ Rect end = mEndBounds;
+ float degrees, x, y;
+ x = fraction * (end.left - start.left) + start.left;
+ y = fraction * (end.top - start.top) + start.top;
+
+ if (mRotation == ROTATION_90) {
+ degrees = 90 * fraction;
+ } else {
+ degrees = -90 * fraction;
+ }
+ mPipSurfaceTransactionHelper.rotateAndScaleWithCrop(tx, mLeash, mBaseBounds,
+ mAnimatedRect, insets, degrees, x, y,
+ true /* isExpanding */, mRotation == ROTATION_90);
+ }
+ mPipSurfaceTransactionHelper.round(tx, mLeash, false /* applyCornerRadius */)
+ .shadow(tx, mLeash, false /* applyShadowRadius */);
+ }
+
private Rect getInsets(float fraction) {
final Rect startInsets = mSourceRectHintInsets;
final Rect endInsets = mZeroInsets;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index 012dabbbb9f8..4558a9f141c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -30,6 +30,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+import com.android.wm.shell.shared.animation.Interpolators;
/**
* Animator that handles any resize related animation for PIP.
@@ -128,6 +129,7 @@ public class PipResizeAnimator extends ValueAnimator {
mRectEvaluator = new RectEvaluator(mAnimatedRect);
setObjectValues(startBounds, endBounds);
+ setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
addListener(mAnimatorListener);
addUpdateListener(mAnimatorUpdateListener);
setEvaluator(mRectEvaluator);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
index 58d2a8577d8c..44900ce1db8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
@@ -573,7 +573,7 @@ public class PhonePipMenuController implements PipMenuController,
@PipTransitionState.TransitionState int newState, Bundle extra) {
switch (newState) {
case PipTransitionState.ENTERED_PIP:
- attach(mPipTransitionState.mPinnedTaskLeash);
+ attach(mPipTransitionState.getPinnedTaskLeash());
break;
case PipTransitionState.EXITED_PIP:
detach();
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 9a93371621c9..d3f537b8f904 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
@@ -376,18 +376,20 @@ public class PipController implements ConfigurationChangeListener,
private void setLauncherKeepClearAreaHeight(boolean visible, int height) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"setLauncherKeepClearAreaHeight: visible=%b, height=%d", visible, height);
- if (visible) {
- Rect rect = new Rect(
- 0, mPipDisplayLayoutState.getDisplayBounds().bottom - height,
- mPipDisplayLayoutState.getDisplayBounds().right,
- mPipDisplayLayoutState.getDisplayBounds().bottom);
- mPipBoundsState.setNamedUnrestrictedKeepClearArea(
- PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, rect);
- } else {
- mPipBoundsState.setNamedUnrestrictedKeepClearArea(
- PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, null);
- }
- mPipTouchHandler.onShelfVisibilityChanged(visible, height);
+ mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
+ if (visible) {
+ Rect rect = new Rect(
+ 0, mPipDisplayLayoutState.getDisplayBounds().bottom - height,
+ mPipDisplayLayoutState.getDisplayBounds().right,
+ mPipDisplayLayoutState.getDisplayBounds().bottom);
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, rect);
+ } else {
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, null);
+ }
+ mPipTouchHandler.onShelfVisibilityChanged(visible, height);
+ });
}
@Override
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 17392bc521d8..3738353dd0a3 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
@@ -785,7 +785,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private void handleFlingTransition(SurfaceControl.Transaction startTx,
SurfaceControl.Transaction finishTx, Rect destinationBounds) {
- startTx.setPosition(mPipTransitionState.mPinnedTaskLeash,
+ startTx.setPosition(mPipTransitionState.getPinnedTaskLeash(),
destinationBounds.left, destinationBounds.top);
startTx.apply();
@@ -799,7 +799,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private void startResizeAnimation(SurfaceControl.Transaction startTx,
SurfaceControl.Transaction finishTx, Rect destinationBounds, int duration) {
- SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
Preconditions.checkState(pipLeash != null,
"No leash cached by mPipTransitionState=" + mPipTransitionState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index 751175f0f3e9..d98be55f28e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -531,7 +531,7 @@ public class PipResizeGestureHandler implements
// If resize transition was scheduled from this component, handle leash updates.
mWaitingForBoundsChangeTransition = false;
- SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
Preconditions.checkState(pipLeash != null,
"No leash cached by mPipTransitionState=" + mPipTransitionState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 8b25b11e3a47..607de0eccd77 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -118,7 +118,7 @@ public class PipScheduler {
public void removePipAfterAnimation() {
SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
PipAlphaAnimator animator = new PipAlphaAnimator(mContext,
- mPipTransitionState.mPinnedTaskLeash, tx, PipAlphaAnimator.FADE_OUT);
+ mPipTransitionState.getPinnedTaskLeash(), tx, PipAlphaAnimator.FADE_OUT);
animator.setAnimationEndCallback(this::scheduleRemovePipImmediately);
animator.start();
}
@@ -203,7 +203,7 @@ public class PipScheduler {
"%s: Attempted to user resize PIP to empty bounds, aborting.", TAG);
return;
}
- SurfaceControl leash = mPipTransitionState.mPinnedTaskLeash;
+ SurfaceControl leash = mPipTransitionState.getPinnedTaskLeash();
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
Matrix transformTensor = new Matrix();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 2c7584af1f07..2f9371536a16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -29,6 +29,8 @@ import android.view.SurfaceControl;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.Preconditions;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ShellExecutor;
@@ -36,6 +38,7 @@ import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip2.animation.PipResizeAnimator;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import java.util.ArrayList;
@@ -49,7 +52,8 @@ import java.util.List;
public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
PipTransitionState.PipTransitionStateChangedListener {
private static final int ASPECT_RATIO_CHANGE_DURATION = 250;
- private static final String ANIMATING_ASPECT_RATIO_CHANGE = "animating_aspect_ratio_change";
+ @VisibleForTesting
+ static final String ANIMATING_ASPECT_RATIO_CHANGE = "animating_aspect_ratio_change";
private final Context mContext;
private final PipTransitionState mPipTransitionState;
@@ -63,6 +67,8 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
private boolean mWaitingForAspectRatioChange = false;
private final List<PipParamsChangedCallback> mPipParamsChangedListeners = new ArrayList<>();
+ private PipResizeAnimatorSupplier mPipResizeAnimatorSupplier;
+
public PipTaskListener(Context context,
ShellTaskOrganizer shellTaskOrganizer,
PipTransitionState pipTransitionState,
@@ -84,6 +90,7 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP);
});
}
+ mPipResizeAnimatorSupplier = PipResizeAnimator::new;
}
void setPictureInPictureParams(@Nullable PictureInPictureParams params) {
@@ -121,6 +128,9 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
if (mPictureInPictureParams.equals(params)) {
return;
}
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "onTaskInfoChanged: %s, state=%s oldParams=%s newParams=%s",
+ taskInfo.topActivity, mPipTransitionState, mPictureInPictureParams, params);
setPictureInPictureParams(params);
float newAspectRatio = mPictureInPictureParams.getAspectRatioFloat();
if (PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) {
@@ -167,18 +177,18 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
- Preconditions.checkNotNull(mPipTransitionState.mPinnedTaskLeash,
+ Preconditions.checkNotNull(mPipTransitionState.getPinnedTaskLeash(),
"Leash is null for bounds transition.");
if (mWaitingForAspectRatioChange) {
- PipResizeAnimator animator = new PipResizeAnimator(mContext,
- mPipTransitionState.mPinnedTaskLeash, startTx, finishTx,
+ mWaitingForAspectRatioChange = false;
+ PipResizeAnimator animator = mPipResizeAnimatorSupplier.get(mContext,
+ mPipTransitionState.getPinnedTaskLeash(), startTx, finishTx,
destinationBounds,
mPipBoundsState.getBounds(), destinationBounds, duration,
0f /* delta */);
- animator.setAnimationEndCallback(() -> {
- mPipScheduler.scheduleFinishResizePip(destinationBounds);
- });
+ animator.setAnimationEndCallback(
+ () -> mPipScheduler.scheduleFinishResizePip(destinationBounds));
animator.start();
}
break;
@@ -192,4 +202,22 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
default void onActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) {
}
}
+
+ @VisibleForTesting
+ interface PipResizeAnimatorSupplier {
+ PipResizeAnimator get(@NonNull Context context,
+ @NonNull SurfaceControl leash,
+ @Nullable SurfaceControl.Transaction startTx,
+ @Nullable SurfaceControl.Transaction finishTx,
+ @NonNull Rect baseBounds,
+ @NonNull Rect startBounds,
+ @NonNull Rect endBounds,
+ int duration,
+ float delta);
+ }
+
+ @VisibleForTesting
+ void setPipResizeAnimatorSupplier(@NonNull PipResizeAnimatorSupplier supplier) {
+ mPipResizeAnimatorSupplier = supplier;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 19d293e829ad..65972fb7df48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -231,17 +231,15 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
// KCA triggered movement to wait for other transitions (e.g. due to IME changes).
return;
}
- mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
- boolean hasUserInteracted = (mPipBoundsState.hasUserMovedPip()
- || mPipBoundsState.hasUserResizedPip());
- int delta = mPipBoundsAlgorithm.getEntryDestinationBounds().top
- - mPipBoundsState.getBounds().top;
+ boolean hasUserInteracted = (mPipBoundsState.hasUserMovedPip()
+ || mPipBoundsState.hasUserResizedPip());
+ int delta = mPipBoundsAlgorithm.getEntryDestinationBounds().top
+ - mPipBoundsState.getBounds().top;
- if (!mIsImeShowing && !hasUserInteracted && delta != 0) {
- // If the user hasn't interacted with PiP, we respect the keep clear areas
- mMotionHelper.animateToOffset(mPipBoundsState.getBounds(), delta);
- }
- });
+ if (!mIsImeShowing && !hasUserInteracted && delta != 0) {
+ // If the user hasn't interacted with PiP, we respect the keep clear areas
+ mMotionHelper.animateToOffset(mPipBoundsState.getBounds(), delta);
+ }
};
if (PipUtils.isPip2ExperimentEnabled()) {
@@ -877,7 +875,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
mMovementWithinDismiss = touchState.getDownTouchPosition().y
>= mPipBoundsState.getMovementBounds().bottom;
mMotionHelper.setSpringingToTouch(false);
- mPipDismissTargetHandler.setTaskLeash(mPipTransitionState.mPinnedTaskLeash);
+ mPipDismissTargetHandler.setTaskLeash(mPipTransitionState.getPinnedTaskLeash());
// If the menu is still visible then just poke the menu
// so that it will timeout after the user stops touching it
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 6bf92f69cfb6..ea783e9cadb6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -325,9 +325,7 @@ public class PipTransition extends PipTransitionController implements
return false;
}
- SurfaceControl pipLeash = pipChange.getLeash();
- Preconditions.checkNotNull(pipLeash, "Leash is null for swipe-up transition.");
-
+ final SurfaceControl pipLeash = getLeash(pipChange);
final Rect destinationBounds = pipChange.getEndAbsBounds();
final SurfaceControl swipePipToHomeOverlay = mPipTransitionState.getSwipePipToHomeOverlay();
if (swipePipToHomeOverlay != null) {
@@ -349,7 +347,7 @@ public class PipTransition extends PipTransitionController implements
: startRotation - endRotation;
if (delta != ROTATION_0) {
mPipTransitionState.setInFixedRotation(true);
- handleBoundsTypeFixedRotation(pipChange, pipActivityChange, endRotation);
+ handleBoundsEnterFixedRotation(pipChange, pipActivityChange, endRotation);
}
prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange,
@@ -399,7 +397,7 @@ public class PipTransition extends PipTransitionController implements
final Rect adjustedSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint)
: PipUtils.getEnterPipWithOverlaySrcRectHint(startBounds, aspectRatio);
- final SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ final SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
// For opening type transitions, if there is a change of mode TO_FRONT/OPEN,
// make sure that change has alpha of 1f, since it's init state might be set to alpha=0f
@@ -414,15 +412,15 @@ public class PipTransition extends PipTransitionController implements
}
final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
- int startRotation = pipChange.getStartRotation();
- int endRotation = fixedRotationChange != null
+ final int startRotation = pipChange.getStartRotation();
+ final int endRotation = fixedRotationChange != null
? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED;
final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
: startRotation - endRotation;
if (delta != ROTATION_0) {
mPipTransitionState.setInFixedRotation(true);
- handleBoundsTypeFixedRotation(pipChange, pipActivityChange,
+ handleBoundsEnterFixedRotation(pipChange, pipActivityChange,
fixedRotationChange.getEndFixedRotation());
}
@@ -459,7 +457,7 @@ public class PipTransition extends PipTransitionController implements
animator.start();
}
- private void handleBoundsTypeFixedRotation(TransitionInfo.Change pipTaskChange,
+ private void handleBoundsEnterFixedRotation(TransitionInfo.Change pipTaskChange,
TransitionInfo.Change pipActivityChange, int endRotation) {
final Rect endBounds = pipTaskChange.getEndAbsBounds();
final Rect endActivityBounds = pipActivityChange.getEndAbsBounds();
@@ -492,6 +490,26 @@ public class PipTransition extends PipTransitionController implements
endBounds.top + activityEndOffset.y);
}
+ private void handleExpandFixedRotation(TransitionInfo.Change pipTaskChange, int endRotation) {
+ final Rect endBounds = pipTaskChange.getEndAbsBounds();
+ final int width = endBounds.width();
+ final int height = endBounds.height();
+ final int left = endBounds.left;
+ final int top = endBounds.top;
+ int newTop, newLeft;
+
+ if (endRotation == Surface.ROTATION_90) {
+ newLeft = top;
+ newTop = -(left + width);
+ } else {
+ newLeft = -(height + top);
+ newTop = left;
+ }
+ // Modify the endBounds, rotating and placing them potentially off-screen, so that
+ // as we translate and rotate around the origin, we place them right into the target.
+ endBounds.set(newLeft, newTop, newLeft + height, newTop + width);
+ }
+
private boolean startAlphaTypeEnterAnimation(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -503,7 +521,7 @@ public class PipTransition extends PipTransitionController implements
}
Rect destinationBounds = pipChange.getEndAbsBounds();
- SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition.");
// Start transition with 0 alpha at the entry bounds.
@@ -544,33 +562,51 @@ public class PipTransition extends PipTransitionController implements
}
}
- // for multi activity, we need to manually set the leash layer
- if (pipChange.getTaskInfo() == null) {
- TransitionInfo.Change parent = getChangeByToken(info, pipChange.getParent());
- if (parent != null) {
- startTransaction.setLayer(parent.getLeash(), Integer.MAX_VALUE - 1);
- }
+ // The parent change if we were in a multi-activity PiP; null if single activity PiP.
+ final TransitionInfo.Change parentBeforePip = pipChange.getTaskInfo() == null
+ ? getChangeByToken(info, pipChange.getParent()) : null;
+ if (parentBeforePip != null) {
+ // For multi activity, we need to manually set the leash layer
+ startTransaction.setLayer(parentBeforePip.getLeash(), Integer.MAX_VALUE - 1);
}
- Rect startBounds = pipChange.getStartAbsBounds();
- Rect endBounds = pipChange.getEndAbsBounds();
- SurfaceControl pipLeash = pipChange.getLeash();
- Preconditions.checkNotNull(pipLeash, "Leash is null for exit transition.");
+ final Rect startBounds = pipChange.getStartAbsBounds();
+ final Rect endBounds = pipChange.getEndAbsBounds();
+ final SurfaceControl pipLeash = getLeash(pipChange);
- Rect sourceRectHint = null;
- if (pipChange.getTaskInfo() != null
- && pipChange.getTaskInfo().pictureInPictureParams != null) {
+ PictureInPictureParams params = null;
+ if (pipChange.getTaskInfo() != null) {
// single activity
- sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
- } else if (mPipTaskListener.getPictureInPictureParams().hasSourceBoundsHint()) {
+ params = pipChange.getTaskInfo().pictureInPictureParams;
+ } else if (parentBeforePip != null && parentBeforePip.getTaskInfo() != null) {
// multi activity
- sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint();
+ params = parentBeforePip.getTaskInfo().pictureInPictureParams;
+ }
+ final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, endBounds,
+ startBounds);
+
+ final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
+ final int startRotation = pipChange.getStartRotation();
+ final int endRotation = fixedRotationChange != null
+ ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED;
+ final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
+ : endRotation - startRotation;
+
+ if (delta != ROTATION_0) {
+ handleExpandFixedRotation(pipChange, endRotation);
}
PipExpandAnimator animator = new PipExpandAnimator(mContext, pipLeash,
startTransaction, finishTransaction, endBounds, startBounds, endBounds,
- sourceRectHint, Surface.ROTATION_0);
- animator.setAnimationEndCallback(this::finishTransition);
+ sourceRectHint, delta);
+ animator.setAnimationEndCallback(() -> {
+ if (parentBeforePip != null) {
+ // TODO b/377362511: Animate local leash instead to also handle letterbox case.
+ // For multi-activity, set the crop to be null
+ finishTransaction.setCrop(pipLeash, null);
+ }
+ finishTransition();
+ });
animator.start();
return true;
}
@@ -717,6 +753,13 @@ public class PipTransition extends PipTransitionController implements
}
}
+ @NonNull
+ private SurfaceControl getLeash(TransitionInfo.Change change) {
+ SurfaceControl leash = change.getLeash();
+ Preconditions.checkNotNull(leash, "Leash is null for change=" + change);
+ return leash;
+ }
+
//
// Miscellaneous callbacks and listeners
//
@@ -754,17 +797,17 @@ public class PipTransition extends PipTransitionController implements
mPipTransitionState.mPipTaskToken = extra.getParcelable(
PIP_TASK_TOKEN, WindowContainerToken.class);
- mPipTransitionState.mPinnedTaskLeash = extra.getParcelable(
- PIP_TASK_LEASH, SurfaceControl.class);
+ mPipTransitionState.setPinnedTaskLeash(extra.getParcelable(
+ PIP_TASK_LEASH, SurfaceControl.class));
boolean hasValidTokenAndLeash = mPipTransitionState.mPipTaskToken != null
- && mPipTransitionState.mPinnedTaskLeash != null;
+ && mPipTransitionState.getPinnedTaskLeash() != null;
Preconditions.checkState(hasValidTokenAndLeash,
"Unexpected bundle for " + mPipTransitionState);
break;
case PipTransitionState.EXITED_PIP:
mPipTransitionState.mPipTaskToken = null;
- mPipTransitionState.mPinnedTaskLeash = null;
+ mPipTransitionState.setPinnedTaskLeash(null);
break;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index ccdd66b5d1a8..03e06f906015 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -142,7 +142,7 @@ public class PipTransitionState {
// pinned PiP task's leash
@Nullable
- SurfaceControl mPinnedTaskLeash;
+ private SurfaceControl mPinnedTaskLeash;
// Overlay leash potentially used during swipe PiP to home transition;
// if null while mInSwipePipToHomeTransition is true, then srcRectHint was invalid.
@@ -304,6 +304,14 @@ public class PipTransitionState {
mSwipePipToHomeAppBounds.setEmpty();
}
+ @Nullable SurfaceControl getPinnedTaskLeash() {
+ return mPinnedTaskLeash;
+ }
+
+ void setPinnedTaskLeash(@Nullable SurfaceControl leash) {
+ mPinnedTaskLeash = leash;
+ }
+
/**
* @return true if either in swipe or button-nav fixed rotation.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
index 799028a5507a..4a301cc0b603 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
@@ -24,7 +24,7 @@ import android.os.Bundle;
import com.android.wm.shell.recents.IRecentsAnimationRunner;
import com.android.wm.shell.recents.IRecentTasksListener;
-import com.android.wm.shell.shared.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
/**
* Interface that is exposed to remote callers to fetch recent tasks.
@@ -44,7 +44,7 @@ interface IRecentTasks {
/**
* Gets the set of recent tasks.
*/
- GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) = 3;
+ GroupedTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) = 3;
/**
* Gets the set of running tasks.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
index 371bdd5c6469..b58f0681c571 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -18,6 +18,8 @@ package com.android.wm.shell.recents;
import android.app.ActivityManager.RunningTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
+
/**
* Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
*/
@@ -44,8 +46,8 @@ oneway interface IRecentTasksListener {
void onRunningTaskChanged(in RunningTaskInfo taskInfo);
/** A task has moved to front. */
- oneway void onTaskMovedToFront(in RunningTaskInfo taskInfo);
+ void onTaskMovedToFront(in GroupedTaskInfo[] visibleTasks);
/** A task info has changed. */
- oneway void onTaskInfoChanged(in RunningTaskInfo taskInfo);
+ void onTaskInfoChanged(in RunningTaskInfo taskInfo);
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index 8c5d1e7e069d..364a087211c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -19,7 +19,7 @@ package com.android.wm.shell.recents;
import android.annotation.Nullable;
import android.graphics.Color;
-import com.android.wm.shell.shared.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.annotations.ExternalThread;
import java.util.List;
@@ -35,7 +35,7 @@ public interface RecentTasks {
* Gets the set of recent tasks.
*/
default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
- Consumer<List<GroupedRecentTaskInfo>> callback) {
+ Consumer<List<GroupedTaskInfo>> callback) {
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index faa20159f64a..9911669d2cb8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -24,10 +24,12 @@ import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_R
import android.Manifest;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.app.KeyguardManager;
import android.app.PendingIntent;
+import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -55,7 +57,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.shared.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -379,7 +381,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
return;
}
try {
- mListener.onTaskMovedToFront(taskInfo);
+ GroupedTaskInfo runningTask = GroupedTaskInfo.forFullscreenTasks(taskInfo);
+ mListener.onTaskMovedToFront(new GroupedTaskInfo[]{ runningTask });
} catch (RemoteException e) {
Slog.w(TAG, "Failed call onTaskMovedToFront", e);
}
@@ -407,27 +410,27 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
@VisibleForTesting
- ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
+ ArrayList<GroupedTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
// Note: the returned task list is from the most-recent to least-recent order
- final List<ActivityManager.RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
+ final List<RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
maxNum, flags, userId);
// Make a mapping of task id -> task info
- final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>();
+ final SparseArray<TaskInfo> rawMapping = new SparseArray<>();
for (int i = 0; i < rawList.size(); i++) {
- final ActivityManager.RecentTaskInfo taskInfo = rawList.get(i);
+ final TaskInfo taskInfo = rawList.get(i);
rawMapping.put(taskInfo.taskId, taskInfo);
}
- ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
+ ArrayList<TaskInfo> freeformTasks = new ArrayList<>();
Set<Integer> minimizedFreeformTasks = new HashSet<>();
int mostRecentFreeformTaskIndex = Integer.MAX_VALUE;
// Pull out the pairs as we iterate back in the list
- ArrayList<GroupedRecentTaskInfo> recentTasks = new ArrayList<>();
+ ArrayList<GroupedTaskInfo> recentTasks = new ArrayList<>();
for (int i = 0; i < rawList.size(); i++) {
- final ActivityManager.RecentTaskInfo taskInfo = rawList.get(i);
+ final RecentTaskInfo taskInfo = rawList.get(i);
if (!rawMapping.contains(taskInfo.taskId)) {
// If it's not in the mapping, then it was already paired with another task
continue;
@@ -460,20 +463,20 @@ public class RecentTasksController implements TaskStackListenerCallback,
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId, INVALID_TASK_ID);
if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
pairedTaskId)) {
- final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
+ final TaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
- recentTasks.add(GroupedRecentTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
+ recentTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
- recentTasks.add(GroupedRecentTaskInfo.forSingleTask(taskInfo));
+ recentTasks.add(GroupedTaskInfo.forFullscreenTasks(taskInfo));
}
}
// Add a special entry for freeform tasks
if (!freeformTasks.isEmpty()) {
recentTasks.add(mostRecentFreeformTaskIndex,
- GroupedRecentTaskInfo.forFreeformTasks(
- freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0]),
+ GroupedTaskInfo.forFreeformTasks(
+ freeformTasks,
minimizedFreeformTasks));
}
@@ -514,16 +517,16 @@ public class RecentTasksController implements TaskStackListenerCallback,
* {@param ignoreTaskToken} if it is non-null.
*/
@Nullable
- public ActivityManager.RecentTaskInfo findTaskInBackground(ComponentName componentName,
+ public RecentTaskInfo findTaskInBackground(ComponentName componentName,
int userId, @Nullable WindowContainerToken ignoreTaskToken) {
if (componentName == null) {
return null;
}
- List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+ List<RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
ActivityManager.getCurrentUser());
for (int i = 0; i < tasks.size(); i++) {
- final ActivityManager.RecentTaskInfo task = tasks.get(i);
+ final RecentTaskInfo task = tasks.get(i);
if (task.isVisible) {
continue;
}
@@ -541,12 +544,12 @@ public class RecentTasksController implements TaskStackListenerCallback,
* Find the background task that match the given taskId.
*/
@Nullable
- public ActivityManager.RecentTaskInfo findTaskInBackground(int taskId) {
- List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+ public RecentTaskInfo findTaskInBackground(int taskId) {
+ List<RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
ActivityManager.getCurrentUser());
for (int i = 0; i < tasks.size(); i++) {
- final ActivityManager.RecentTaskInfo task = tasks.get(i);
+ final RecentTaskInfo task = tasks.get(i);
if (task.isVisible) {
continue;
}
@@ -570,7 +573,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
pw.println(prefix + TAG);
pw.println(prefix + " mListener=" + mListener);
pw.println(prefix + "Tasks:");
- ArrayList<GroupedRecentTaskInfo> recentTasks = getRecentTasks(Integer.MAX_VALUE,
+ ArrayList<GroupedTaskInfo> recentTasks = getRecentTasks(Integer.MAX_VALUE,
ActivityManager.RECENT_IGNORE_UNAVAILABLE, ActivityManager.getCurrentUser());
for (int i = 0; i < recentTasks.size(); i++) {
pw.println(innerPrefix + recentTasks.get(i));
@@ -584,9 +587,9 @@ public class RecentTasksController implements TaskStackListenerCallback,
private class RecentTasksImpl implements RecentTasks {
@Override
public void getRecentTasks(int maxNum, int flags, int userId, Executor executor,
- Consumer<List<GroupedRecentTaskInfo>> callback) {
+ Consumer<List<GroupedTaskInfo>> callback) {
mMainExecutor.execute(() -> {
- List<GroupedRecentTaskInfo> tasks =
+ List<GroupedTaskInfo> tasks =
RecentTasksController.this.getRecentTasks(maxNum, flags, userId);
executor.execute(() -> callback.accept(tasks));
});
@@ -650,7 +653,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
@Override
- public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+ public void onTaskMovedToFront(GroupedTaskInfo[] taskInfo) {
mListener.call(l -> l.onTaskMovedToFront(taskInfo));
}
@@ -692,17 +695,20 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
@Override
- public GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId)
+ public GroupedTaskInfo[] getRecentTasks(int maxNum, int flags, int userId)
throws RemoteException {
if (mController == null) {
// The controller is already invalidated -- just return an empty task list for now
- return new GroupedRecentTaskInfo[0];
+ return new GroupedTaskInfo[0];
}
- final GroupedRecentTaskInfo[][] out = new GroupedRecentTaskInfo[][]{null};
+ final GroupedTaskInfo[][] out = new GroupedTaskInfo[][]{null};
executeRemoteCallWithTaskPermission(mController, "getRecentTasks",
- (controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId)
- .toArray(new GroupedRecentTaskInfo[0]),
+ (controller) -> {
+ List<GroupedTaskInfo> tasks = controller.getRecentTasks(
+ maxNum, flags, userId);
+ out[0] = tasks.toArray(new GroupedTaskInfo[0]);
+ },
true /* blocking */);
return out[0];
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index ec58292b352c..29e4b5bca5cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -395,10 +395,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
continue;
}
// No default animation for this, so just update bounds/position.
- final int rootIdx = TransitionUtil.rootIndexFor(change, info);
- startTransaction.setPosition(change.getLeash(),
- change.getEndAbsBounds().left - info.getRoot(rootIdx).getOffset().x,
- change.getEndAbsBounds().top - info.getRoot(rootIdx).getOffset().y);
+ if (change.getParent() == null) {
+ // For independent change without a parent, we have reparented it to the root
+ // leash in Transitions#setupAnimHierarchy.
+ final int rootIdx = TransitionUtil.rootIndexFor(change, info);
+ startTransaction.setPosition(change.getLeash(),
+ change.getEndAbsBounds().left - info.getRoot(rootIdx).getOffset().x,
+ change.getEndAbsBounds().top - info.getRoot(rootIdx).getOffset().y);
+ } else {
+ startTransaction.setPosition(change.getLeash(),
+ change.getEndRelOffset().x, change.getEndRelOffset().y);
+ }
// Seamless display transition doesn't need to animate.
if (isSeamlessDisplayChange) continue;
if (isTask || (change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index be4fd7c5eeec..7265fb8f8027 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -24,6 +24,8 @@ import static android.content.pm.PackageManager.FEATURE_PC;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
+
import android.app.ActivityManager.RunningTaskInfo;
import android.content.ContentResolver;
import android.content.Context;
@@ -195,7 +197,12 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
return;
}
- decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
+ if (enableDisplayFocusInShellTransitions()) {
+ // Pass the current global focus status to avoid updates outside of a ShellTransition.
+ decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
+ } else {
+ decoration.relayout(taskInfo, taskInfo.isFocused);
+ }
}
@Override
@@ -496,4 +503,4 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
return Settings.Global.getInt(resolver,
DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0;
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 17e3dd2fc68e..f2d8a782de34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -31,6 +31,7 @@ import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.statusBars;
import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
+import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing;
import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
@@ -468,7 +469,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
removeTaskFromEventReceiver(oldTaskInfo.displayId);
incrementEventReceiverTasks(taskInfo.displayId);
}
- decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
+ if (enableDisplayFocusInShellTransitions()) {
+ // Pass the current global focus status to avoid updates outside of a ShellTransition.
+ decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
+ } else {
+ decoration.relayout(taskInfo, taskInfo.isFocused);
+ }
mActivityOrientationChangeHandler.ifPresent(handler ->
handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
index e43c3a613157..61963cde2d06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
@@ -30,6 +30,7 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayChangeController
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
@@ -48,6 +49,7 @@ class DesktopTilingDecorViewModel(
private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
private val returnToDragStartAnimator: ReturnToDragStartAnimator,
private val taskRepository: DesktopRepository,
+ private val desktopModeEventLogger: DesktopModeEventLogger,
) : DisplayChangeController.OnDisplayChangingListener {
@VisibleForTesting
var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
@@ -80,6 +82,7 @@ class DesktopTilingDecorViewModel(
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
taskRepository,
+ desktopModeEventLogger,
)
tilingTransitionHandlerByDisplayId.put(displayId, newHandler)
newHandler
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
index 209eb5e501b2..6cdc517c9cb7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
@@ -23,6 +23,7 @@ import android.graphics.Rect
import android.graphics.Region
import android.os.Binder
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.RoundedCorner
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
@@ -39,6 +40,7 @@ import android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER
import android.view.WindowlessWindowManager
import com.android.wm.shell.R
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import java.util.function.Supplier
/**
@@ -141,8 +143,9 @@ class DesktopTilingDividerWindowManager(
t.setRelativeLayer(leash, relativeLeash, 1)
}
- override fun onDividerMoveStart(pos: Int) {
+ override fun onDividerMoveStart(pos: Int, motionEvent: MotionEvent) {
setSlippery(false)
+ transitionHandler.onDividerHandleDragStart(motionEvent)
}
/**
@@ -161,13 +164,13 @@ class DesktopTilingDividerWindowManager(
* Notifies the transition handler of tiling operations ending, which might result in resizing
* WindowContainerTransactions if the sizes of the tiled tasks changed.
*/
- override fun onDividerMovedEnd(pos: Int) {
+ override fun onDividerMovedEnd(pos: Int, motionEvent: MotionEvent) {
setSlippery(true)
val t = transactionSupplier.get()
t.setPosition(leash, pos.toFloat() - maxRoundedCornerRadius, dividerBounds.top.toFloat())
val dividerWidth = dividerBounds.width()
dividerBounds.set(pos, dividerBounds.top, pos + dividerWidth, dividerBounds.bottom)
- transitionHandler.onDividerHandleDragEnd(dividerBounds, t)
+ transitionHandler.onDividerHandleDragEnd(dividerBounds, t, motionEvent)
}
private fun getWindowManagerParams(): WindowManager.LayoutParams {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index c46767c3a51d..1c593c0362ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -25,6 +25,7 @@ import android.graphics.Rect
import android.os.IBinder
import android.os.UserHandle
import android.util.Slog
+import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_CHANGE
@@ -44,6 +45,8 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
@@ -70,6 +73,7 @@ class DesktopTilingWindowDecoration(
private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
private val returnToDragStartAnimator: ReturnToDragStartAnimator,
private val taskRepository: DesktopRepository,
+ private val desktopModeEventLogger: DesktopModeEventLogger,
private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
) :
Transitions.TransitionHandler,
@@ -218,6 +222,25 @@ class DesktopTilingWindowDecoration(
return tilingManager
}
+ fun onDividerHandleDragStart(motionEvent: MotionEvent) {
+ val leftTiledTask = leftTaskResizingHelper ?: return
+ val rightTiledTask = rightTaskResizingHelper ?: return
+
+ desktopModeEventLogger.logTaskResizingStarted(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ leftTiledTask.taskInfo,
+ displayController
+ )
+
+ desktopModeEventLogger.logTaskResizingStarted(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ rightTiledTask.taskInfo,
+ displayController
+ )
+ }
+
fun onDividerHandleMoved(dividerBounds: Rect, t: SurfaceControl.Transaction): Boolean {
val leftTiledTask = leftTaskResizingHelper ?: return false
val rightTiledTask = rightTaskResizingHelper ?: return false
@@ -266,10 +289,32 @@ class DesktopTilingWindowDecoration(
return true
}
- fun onDividerHandleDragEnd(dividerBounds: Rect, t: SurfaceControl.Transaction) {
+ fun onDividerHandleDragEnd(
+ dividerBounds: Rect,
+ t: SurfaceControl.Transaction,
+ motionEvent: MotionEvent,
+ ) {
val leftTiledTask = leftTaskResizingHelper ?: return
val rightTiledTask = rightTaskResizingHelper ?: return
+ desktopModeEventLogger.logTaskResizingEnded(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ leftTiledTask.taskInfo,
+ leftTiledTask.newBounds.height(),
+ leftTiledTask.newBounds.width(),
+ displayController
+ )
+
+ desktopModeEventLogger.logTaskResizingEnded(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ rightTiledTask.taskInfo,
+ rightTiledTask.newBounds.height(),
+ rightTiledTask.newBounds.width(),
+ displayController
+ )
+
if (leftTiledTask.newBounds == leftTiledTask.bounds) {
leftTiledTask.hideVeil()
rightTiledTask.hideVeil()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt
index b3b30ad4c09e..9799d01afc9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt
@@ -15,14 +15,16 @@
*/
package com.android.wm.shell.windowdecor.tiling
+import android.view.MotionEvent
+
/** Divider move callback to whichever entity that handles the moving logic. */
interface DividerMoveCallback {
/** Called on the divider move start gesture. */
- fun onDividerMoveStart(pos: Int)
+ fun onDividerMoveStart(pos: Int, motionEvent: MotionEvent)
/** Called on the divider moved by dragging it. */
fun onDividerMove(pos: Int): Boolean
/** Called on divider move gesture end. */
- fun onDividerMovedEnd(pos: Int)
+ fun onDividerMovedEnd(pos: Int, motionEvent: MotionEvent)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
index 89229051941c..111e28e450bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
@@ -206,7 +206,7 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
if (!isWithinHandleRegion(yTouchPosInDivider)) return true
- callback.onDividerMoveStart(touchPos)
+ callback.onDividerMoveStart(touchPos, event)
setTouching()
canResize = true
}
@@ -230,7 +230,7 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
if (!canResize) return true
if (moving && resized) {
dividerBounds.left = dividerBounds.left + lastAcceptedPos - startPos
- callback.onDividerMovedEnd(dividerBounds.left)
+ callback.onDividerMovedEnd(dividerBounds.left, event)
}
moving = false
canResize = false
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 df061e368071..b06c2dad4ffc 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
@@ -447,6 +447,37 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun startAnimation_pendingTransition_noLaunchChange_returnsFalse() {
+ val wct = WindowContainerTransaction()
+ val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val nonLaunchTaskChange = createChange(createTask(WINDOWING_MODE_FREEFORM))
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+ .thenReturn(transition)
+ mixedHandler.addPendingMixedTransition(
+ PendingMixedTransition.Launch(
+ transition = transition,
+ launchingTask = launchingTask.taskId,
+ minimizingTask = null,
+ exitingImmersiveTask = null,
+ )
+ )
+
+ val started = mixedHandler.startAnimation(
+ transition,
+ createTransitionInfo(
+ TRANSIT_OPEN,
+ listOf(nonLaunchTaskChange)
+ ),
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction(),
+ ) { }
+
+ assertFalse("Should not start animation without launching desktop task", started)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
index e19a10a78417..b816f0ef041e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
@@ -93,6 +93,17 @@ public class PipExpandAnimatorTest {
.thenReturn(mMockTransaction);
when(mMockTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat()))
.thenReturn(mMockTransaction);
+ // No-op on the mMockStartTransaction
+ when(mMockStartTransaction.setAlpha(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(mMockFinishTransaction);
+ when(mMockStartTransaction.setCrop(any(SurfaceControl.class), any(Rect.class)))
+ .thenReturn(mMockFinishTransaction);
+ when(mMockStartTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(mMockFinishTransaction);
+ when(mMockStartTransaction.setCornerRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(mMockFinishTransaction);
+ when(mMockStartTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(mMockFinishTransaction);
// Do the same for mMockFinishTransaction
when(mMockFinishTransaction.setAlpha(any(SurfaceControl.class), anyFloat()))
.thenReturn(mMockFinishTransaction);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
new file mode 100644
index 000000000000..89cb729d17b5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.phone;
+
+import static com.android.wm.shell.pip2.phone.PipTaskListener.ANIMATING_ASPECT_RATIO_CHANGE;
+
+import static org.junit.Assert.assertEquals;
+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.Mockito.when;
+import static org.mockito.kotlin.MatchersKt.eq;
+import static org.mockito.kotlin.VerificationKt.clearInvocations;
+import static org.mockito.kotlin.VerificationKt.times;
+import static org.mockito.kotlin.VerificationKt.verify;
+import static org.mockito.kotlin.VerificationKt.verifyZeroInteractions;
+
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
+import android.app.RemoteAction;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Rational;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.pip2.animation.PipResizeAnimator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit test against {@link PipTaskListener}.
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipTaskListenerTest {
+
+ @Mock private Context mMockContext;
+ @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
+ @Mock private PipTransitionState mMockPipTransitionState;
+ @Mock private SurfaceControl mMockLeash;
+ @Mock private PipScheduler mMockPipScheduler;
+ @Mock private PipBoundsState mMockPipBoundsState;
+ @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
+ @Mock private ShellExecutor mMockShellExecutor;
+
+ @Mock private Icon mMockIcon;
+ @Mock private PendingIntent mMockPendingIntent;
+
+ @Mock private PipTaskListener.PipParamsChangedCallback mMockPipParamsChangedCallback;
+
+ @Mock private PipResizeAnimator mMockPipResizeAnimator;
+
+ private ArgumentCaptor<List<RemoteAction>> mRemoteActionListCaptor;
+
+ private PipTaskListener mPipTaskListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRemoteActionListCaptor = ArgumentCaptor.forClass(List.class);
+ when(mMockPipTransitionState.getPinnedTaskLeash()).thenReturn(mMockLeash);
+ }
+
+ @Test
+ public void constructor_addPipTransitionStateChangedListener() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+
+ verify(mMockPipTransitionState).addPipTransitionStateChangedListener(eq(mPipTaskListener));
+ }
+
+ @Test
+ public void setPictureInPictureParams_updatePictureInPictureParams() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Rational aspectRatio = new Rational(4, 3);
+ String action1 = "action1";
+
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ PictureInPictureParams params = mPipTaskListener.getPictureInPictureParams();
+ assertEquals(aspectRatio, params.getAspectRatio());
+ assertTrue(params.hasSetActions());
+ assertEquals(1, params.getActions().size());
+ assertEquals(action1, params.getActions().get(0).getTitle());
+ }
+
+ @Test
+ public void setPictureInPictureParams_withActionsChanged_callbackActionsChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ String action1 = "action1";
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ action1 = "modified action1";
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ verify(mMockPipParamsChangedCallback).onActionsChanged(
+ mRemoteActionListCaptor.capture(), any());
+ assertEquals(1, mRemoteActionListCaptor.getValue().size());
+ assertEquals(action1, mRemoteActionListCaptor.getValue().get(0).getTitle());
+ }
+
+ @Test
+ public void setPictureInPictureParams_withoutActionsChanged_doesNotCallbackActionsChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ String action1 = "action1";
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ verifyZeroInteractions(mMockPipParamsChangedCallback);
+ }
+
+ @Test
+ public void onTaskInfoChanged_withActionsChanged_callbackActionsChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ when(mMockPipBoundsState.getAspectRatio()).thenReturn(aspectRatio.toFloat());
+ String action1 = "action1";
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ clearInvocations(mMockPipBoundsState);
+ action1 = "modified action1";
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ verify(mMockPipTransitionState, times(0))
+ .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+ verify(mMockPipParamsChangedCallback).onActionsChanged(
+ mRemoteActionListCaptor.capture(), any());
+ assertEquals(1, mRemoteActionListCaptor.getValue().size());
+ assertEquals(action1, mRemoteActionListCaptor.getValue().get(0).getTitle());
+ }
+
+ @Test
+ public void onTaskInfoChanged_withAspectRatioChanged_callbackAspectRatioChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ when(mMockPipBoundsState.getAspectRatio()).thenReturn(aspectRatio.toFloat());
+ String action1 = "action1";
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ clearInvocations(mMockPipBoundsState);
+ aspectRatio = new Rational(16, 9);
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ verify(mMockPipTransitionState).setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+ verifyZeroInteractions(mMockPipParamsChangedCallback);
+ }
+
+ @Test
+ public void onTaskInfoChanged_withoutParamsChanged_doesNotCallbackAspectRatioChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ when(mMockPipBoundsState.getAspectRatio()).thenReturn(aspectRatio.toFloat());
+ String action1 = "action1";
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verify(mMockPipTransitionState, times(0))
+ .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+ }
+
+ @Test
+ public void onPipTransitionStateChanged_scheduledBoundsChangeWithAspectRatioChange_schedule() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Bundle extras = new Bundle();
+ extras.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, true);
+
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED, PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extras);
+
+ verify(mMockPipScheduler).scheduleAnimateResizePip(any(), anyBoolean(), anyInt());
+ }
+
+ @Test
+ public void onPipTransitionStateChanged_scheduledBoundsChangeWithoutAspectRatioChange_noop() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Bundle extras = new Bundle();
+ extras.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, false);
+
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED,
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ extras);
+
+ verifyZeroInteractions(mMockPipScheduler);
+ }
+
+ @Test
+ public void onPipTransitionStateChanged_changingPipBoundsWaitAspectRatioChange_animate() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Bundle extras = new Bundle();
+ extras.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, true);
+ extras.putParcelable(PipTransition.PIP_DESTINATION_BOUNDS,
+ new Rect(0, 0, 100, 100));
+ when(mMockPipBoundsState.getBounds()).thenReturn(new Rect(0, 0, 200, 200));
+
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED,
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ extras);
+ mPipTaskListener.setPipResizeAnimatorSupplier(
+ (context, leash, startTx, finishTx, baseBounds, startBounds, endBounds,
+ duration, delta) -> mMockPipResizeAnimator);
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ PipTransitionState.CHANGING_PIP_BOUNDS,
+ extras);
+
+ verify(mMockPipResizeAnimator, times(1)).start();
+ }
+
+ @Test
+ public void onPipTransitionStateChanged_changingPipBoundsNotAspectRatioChange_noop() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Bundle extras = new Bundle();
+ extras.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, false);
+ extras.putParcelable(PipTransition.PIP_DESTINATION_BOUNDS,
+ new Rect(0, 0, 100, 100));
+ when(mMockPipBoundsState.getBounds()).thenReturn(new Rect(0, 0, 200, 200));
+
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED,
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ extras);
+ mPipTaskListener.setPipResizeAnimatorSupplier(
+ (context, leash, startTx, finishTx, baseBounds, startBounds, endBounds,
+ duration, delta) -> mMockPipResizeAnimator);
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ PipTransitionState.CHANGING_PIP_BOUNDS,
+ extras);
+
+ verify(mMockPipResizeAnimator, times(0)).start();
+ }
+
+ private PictureInPictureParams getPictureInPictureParams(Rational aspectRatio,
+ String... actions) {
+ final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
+ builder.setAspectRatio(aspectRatio);
+ final List<RemoteAction> remoteActions = new ArrayList<>();
+ for (String action : actions) {
+ remoteActions.add(new RemoteAction(mMockIcon, action, action, mMockPendingIntent));
+ }
+ if (!remoteActions.isEmpty()) {
+ builder.setActions(remoteActions);
+ }
+ return builder.build();
+ }
+
+ private ActivityManager.RunningTaskInfo getTaskInfo(Rational aspectRatio,
+ String... actions) {
+ final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.pictureInPictureParams = getPictureInPictureParams(aspectRatio, actions);
+ return taskInfo;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
index 0c100fca2036..2b30bc360d06 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.recents
import android.app.ActivityManager
+import android.app.TaskInfo
import android.graphics.Rect
import android.os.Parcel
import android.testing.AndroidTestingRunner
@@ -24,11 +25,10 @@ import android.window.IWindowContainerToken
import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.shared.GroupedRecentTaskInfo
-import com.android.wm.shell.shared.GroupedRecentTaskInfo.CREATOR
-import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_FREEFORM
-import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SINGLE
-import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SPLIT
+import com.android.wm.shell.shared.GroupedTaskInfo
+import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM
+import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN
+import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT
import com.android.wm.shell.shared.split.SplitBounds
import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50
import com.google.common.truth.Correspondence
@@ -39,15 +39,15 @@ import org.junit.runner.RunWith
import org.mockito.Mockito.mock
/**
- * Tests for [GroupedRecentTaskInfo]
+ * Tests for [GroupedTaskInfo]
*/
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class GroupedRecentTaskInfoTest : ShellTestCase() {
+class GroupedTaskInfoTest : ShellTestCase() {
@Test
fun testSingleTask_hasCorrectType() {
- assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_SINGLE)
+ assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_FULLSCREEN)
}
@Test
@@ -117,8 +117,9 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
recentTaskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
- assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SINGLE)
+ val recentTaskInfoParcel: GroupedTaskInfo =
+ GroupedTaskInfo.CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FULLSCREEN)
assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
assertThat(recentTaskInfoParcel.taskInfo2).isNull()
}
@@ -130,7 +131,8 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
recentTaskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ val recentTaskInfoParcel: GroupedTaskInfo =
+ GroupedTaskInfo.CREATOR.createFromParcel(parcel)
assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SPLIT)
assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
@@ -146,11 +148,12 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
recentTaskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ val recentTaskInfoParcel: GroupedTaskInfo =
+ GroupedTaskInfo.CREATOR.createFromParcel(parcel)
assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
assertThat(recentTaskInfoParcel.taskInfoList).hasSize(3)
// Only compare task ids
- val taskIdComparator = Correspondence.transforming<ActivityManager.RecentTaskInfo, Int>(
+ val taskIdComparator = Correspondence.transforming<TaskInfo, Int>(
{ it?.taskId }, "has taskId of"
)
assertThat(recentTaskInfoParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
@@ -167,7 +170,8 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ val recentTaskInfoParcel: GroupedTaskInfo =
+ GroupedTaskInfo.CREATOR.createFromParcel(parcel)
assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
assertThat(recentTaskInfoParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray())
}
@@ -177,24 +181,24 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
token = WindowContainerToken(mock(IWindowContainerToken::class.java))
}
- private fun singleTaskGroupInfo(): GroupedRecentTaskInfo {
+ private fun singleTaskGroupInfo(): GroupedTaskInfo {
val task = createTaskInfo(id = 1)
- return GroupedRecentTaskInfo.forSingleTask(task)
+ return GroupedTaskInfo.forFullscreenTasks(task)
}
- private fun splitTasksGroupInfo(): GroupedRecentTaskInfo {
+ private fun splitTasksGroupInfo(): GroupedTaskInfo {
val task1 = createTaskInfo(id = 1)
val task2 = createTaskInfo(id = 2)
val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_2_50_50)
- return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds)
+ return GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds)
}
private fun freeformTasksGroupInfo(
freeformTaskIds: Array<Int>,
minimizedTaskIds: Array<Int> = emptyArray()
- ): GroupedRecentTaskInfo {
- return GroupedRecentTaskInfo.forFreeformTasks(
- freeformTaskIds.map { createTaskInfo(it) }.toTypedArray(),
+ ): GroupedTaskInfo {
+ return GroupedTaskInfo.forFreeformTasks(
+ freeformTaskIds.map { createTaskInfo(it) }.toList(),
minimizedTaskIds.toSet())
}
}
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 9b73d53e0639..dede583ca970 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
@@ -46,6 +46,7 @@ import static org.mockito.Mockito.when;
import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityTaskManager;
import android.app.KeyguardManager;
import android.content.ComponentName;
@@ -71,7 +72,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopRepository;
-import com.android.wm.shell.shared.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.ShellSharedConstants;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.split.SplitBounds;
@@ -193,8 +194,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testAddRemoveSplitNotifyChange() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
setRawList(t1, t2);
mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, mock(SplitBounds.class));
@@ -207,8 +208,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testAddSameSplitBoundsInfoSkipNotifyChange() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
setRawList(t1, t2);
// Verify only one update if the split info is the same
@@ -223,13 +224,13 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
setRawList(t1, t2, t3);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
assertGroupedTasksListEquals(recentTasks,
t1.taskId, -1,
t2.taskId, -1,
@@ -238,12 +239,12 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_withPairs() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
- ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
- ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t5 = makeTaskInfo(5);
+ RecentTaskInfo t6 = makeTaskInfo(6);
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
@@ -255,8 +256,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
assertGroupedTasksListEquals(recentTasks,
t1.taskId, -1,
t2.taskId, t4.taskId,
@@ -267,14 +268,14 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_ReturnsRecentTasksAsynchronously() {
@SuppressWarnings("unchecked")
- final List<GroupedRecentTaskInfo>[] recentTasks = new List[1];
- Consumer<List<GroupedRecentTaskInfo>> consumer = argument -> recentTasks[0] = argument;
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
- ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
- ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
+ final List<GroupedTaskInfo>[] recentTasks = new List[1];
+ Consumer<List<GroupedTaskInfo>> consumer = argument -> recentTasks[0] = argument;
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t5 = makeTaskInfo(5);
+ RecentTaskInfo t6 = makeTaskInfo(6);
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
@@ -287,7 +288,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
mRecentTasksController.asRecentTasks()
- .getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run, consumer);
+ .getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run,
+ consumer);
mMainExecutor.flushAll();
assertGroupedTasksListEquals(recentTasks[0],
@@ -299,28 +301,28 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_hasActiveDesktopTasks_proto2Enabled_groupFreeformTasks() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
setRawList(t1, t2, t3, t4);
when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
// 2 freeform tasks should be grouped into one, 3 total recents entries
assertEquals(3, recentTasks.size());
- GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
- GroupedRecentTaskInfo singleGroup1 = recentTasks.get(1);
- GroupedRecentTaskInfo singleGroup2 = recentTasks.get(2);
+ GroupedTaskInfo freeformGroup = recentTasks.get(0);
+ GroupedTaskInfo singleGroup1 = recentTasks.get(1);
+ GroupedTaskInfo singleGroup2 = recentTasks.get(2);
// Check that groups have expected types
- assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup1.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup1.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup2.getType());
// Check freeform group entries
assertEquals(t1, freeformGroup.getTaskInfoList().get(0));
@@ -333,11 +335,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_hasActiveDesktopTasks_proto2Enabled_freeformTaskOrder() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
- ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t5 = makeTaskInfo(5);
setRawList(t1, t2, t3, t4, t5);
SplitBounds pair1Bounds =
@@ -347,19 +349,19 @@ public class RecentTasksControllerTest extends ShellTestCase {
when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
when(mDesktopRepository.isActiveTask(5)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
// 2 split screen tasks grouped, 2 freeform tasks grouped, 3 total recents entries
assertEquals(3, recentTasks.size());
- GroupedRecentTaskInfo splitGroup = recentTasks.get(0);
- GroupedRecentTaskInfo freeformGroup = recentTasks.get(1);
- GroupedRecentTaskInfo singleGroup = recentTasks.get(2);
+ GroupedTaskInfo splitGroup = recentTasks.get(0);
+ GroupedTaskInfo freeformGroup = recentTasks.get(1);
+ GroupedTaskInfo singleGroup = recentTasks.get(2);
// Check that groups have expected types
- assertEquals(GroupedRecentTaskInfo.TYPE_SPLIT, splitGroup.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_SPLIT, splitGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup.getType());
// Check freeform group entries
assertEquals(t3, freeformGroup.getTaskInfoList().get(0));
@@ -378,24 +380,24 @@ public class RecentTasksControllerTest extends ShellTestCase {
ExtendedMockito.doReturn(false)
.when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
setRawList(t1, t2, t3, t4);
when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
// Expect no grouping of tasks
assertEquals(4, recentTasks.size());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(0).getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(1).getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(2).getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(3).getType());
+ 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());
assertEquals(t1, recentTasks.get(0).getTaskInfo1());
assertEquals(t2, recentTasks.get(1).getTaskInfo1());
@@ -405,11 +407,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_proto2Enabled_includesMinimizedFreeformTasks() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
- ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t5 = makeTaskInfo(5);
setRawList(t1, t2, t3, t4, t5);
when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
@@ -417,19 +419,19 @@ public class RecentTasksControllerTest extends ShellTestCase {
when(mDesktopRepository.isActiveTask(5)).thenReturn(true);
when(mDesktopRepository.isMinimizedTask(3)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
// 3 freeform tasks should be grouped into one, 2 single tasks, 3 total recents entries
assertEquals(3, recentTasks.size());
- GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
- GroupedRecentTaskInfo singleGroup1 = recentTasks.get(1);
- GroupedRecentTaskInfo singleGroup2 = recentTasks.get(2);
+ GroupedTaskInfo freeformGroup = recentTasks.get(0);
+ GroupedTaskInfo singleGroup1 = recentTasks.get(1);
+ GroupedTaskInfo singleGroup2 = recentTasks.get(2);
// Check that groups have expected types
- assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup1.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup1.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup2.getType());
// Check freeform group entries
assertEquals(3, freeformGroup.getTaskInfoList().size());
@@ -445,8 +447,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
public void testGetRecentTasks_hasDesktopTasks_persistenceEnabled_freeformTaskHaveBoundsSet() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
t1.lastNonFullscreenBounds = new Rect(100, 200, 300, 400);
t2.lastNonFullscreenBounds = new Rect(150, 250, 350, 450);
@@ -455,11 +457,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
when(mDesktopRepository.isActiveTask(2)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
assertEquals(1, recentTasks.size());
- GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
+ GroupedTaskInfo freeformGroup = recentTasks.get(0);
// Check bounds
assertEquals(t1.lastNonFullscreenBounds, freeformGroup.getTaskInfoList().get(
@@ -478,9 +480,9 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testRemovedTaskRemovesSplit() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
setRawList(t1, t2, t3);
// Add a pair
@@ -500,7 +502,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testTaskWindowingModeChangedNotifiesChange() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t1 = makeTaskInfo(1);
setRawList(t1);
// Remove one of the tasks and ensure the pair is removed
@@ -607,7 +609,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
mRecentTasksControllerReal.onTaskMovedToFrontThroughTransition(taskInfo);
- verify(mRecentTasksListener).onTaskMovedToFront(taskInfo);
+ GroupedTaskInfo runningTask = GroupedTaskInfo.forFullscreenTasks(taskInfo);
+ verify(mRecentTasksListener).onTaskMovedToFront(eq(new GroupedTaskInfo[] { runningTask }));
}
@Test
@@ -656,8 +659,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
/**
* Helper to create a task with a given task id.
*/
- private ActivityManager.RecentTaskInfo makeTaskInfo(int taskId) {
- ActivityManager.RecentTaskInfo info = new ActivityManager.RecentTaskInfo();
+ private RecentTaskInfo makeTaskInfo(int taskId) {
+ RecentTaskInfo info = new RecentTaskInfo();
info.taskId = taskId;
info.lastNonFullscreenBounds = new Rect();
return info;
@@ -676,10 +679,10 @@ public class RecentTasksControllerTest extends ShellTestCase {
/**
* Helper to set the raw task list on the controller.
*/
- private ArrayList<ActivityManager.RecentTaskInfo> setRawList(
- ActivityManager.RecentTaskInfo... tasks) {
- ArrayList<ActivityManager.RecentTaskInfo> rawList = new ArrayList<>();
- for (ActivityManager.RecentTaskInfo task : tasks) {
+ private ArrayList<RecentTaskInfo> setRawList(
+ RecentTaskInfo... tasks) {
+ ArrayList<RecentTaskInfo> rawList = new ArrayList<>();
+ for (RecentTaskInfo task : tasks) {
rawList.add(task);
}
doReturn(rawList).when(mActivityTaskManager).getRecentTasks(anyInt(), anyInt(),
@@ -693,11 +696,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
* the grouped task list
*/
- private void assertGroupedTasksListEquals(List<GroupedRecentTaskInfo> recentTasks,
+ private void assertGroupedTasksListEquals(List<GroupedTaskInfo> recentTasks,
int... expectedTaskIds) {
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
- GroupedRecentTaskInfo pair = recentTasks.get(i);
+ GroupedTaskInfo pair = recentTasks.get(i);
int taskId1 = pair.getTaskInfo1().taskId;
flattenedTaskIds[2 * i] = taskId1;
flattenedTaskIds[2 * i + 1] = pair.getTaskInfo2() != null
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index ef3af8e7bdac..966651f19711 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -217,7 +217,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
// Put the same component to the top running task
ActivityManager.RunningTaskInfo topRunningTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
- doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
@@ -238,7 +237,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
// Put the same component to the top running task
ActivityManager.RunningTaskInfo topRunningTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
- doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any());
// Put the same component into a task in the background
ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 6cde0569796d..2442a55d78d0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+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_PINNED;
@@ -363,6 +364,25 @@ public class ShellTransitionTests extends ShellTestCase {
}
@Test
+ public void testTransitionFilterWindowingMode() {
+ TransitionFilter filter = new TransitionFilter();
+ filter.mRequirements =
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+ filter.mRequirements[0].mWindowingMode = WINDOWING_MODE_FREEFORM;
+ filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+
+ final TransitionInfo fullscreenStd = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN, createTaskInfo(
+ 1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD)).build();
+ assertFalse(filter.matches(fullscreenStd));
+
+ final TransitionInfo freeformStd = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN, createTaskInfo(
+ 1, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD)).build();
+ assertTrue(filter.matches(freeformStd));
+ }
+
+ @Test
public void testTransitionFilterMultiRequirement() {
// filter that requires at-least one opening and one closing app
TransitionFilter filter = new TransitionFilter();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 56267174ba75..956100d9bc03 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -1307,6 +1307,48 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
verify(decor).closeMaximizeMenu()
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
+ fun testOnTaskInfoChanged_enableShellTransitionsFlag() {
+ val task = createTask(
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ val taskSurface = SurfaceControl()
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task, taskSurface)
+ assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
+
+ decoration.mHasGlobalFocus = true
+ desktopModeWindowDecorViewModel.onTaskInfoChanged(task)
+ verify(decoration).relayout(task, true)
+
+ decoration.mHasGlobalFocus = false
+ desktopModeWindowDecorViewModel.onTaskInfoChanged(task)
+ verify(decoration).relayout(task, false)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
+ fun testOnTaskInfoChanged_disableShellTransitionsFlag() {
+ val task = createTask(
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ val taskSurface = SurfaceControl()
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task, taskSurface)
+ assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
+
+ task.isFocused = true
+ desktopModeWindowDecorViewModel.onTaskInfoChanged(task)
+ verify(decoration).relayout(task, true)
+
+ task.isFocused = false
+ desktopModeWindowDecorViewModel.onTaskInfoChanged(task)
+ verify(decoration).relayout(task, false)
+ }
+
private fun createOpenTaskDecoration(
@WindowingMode windowingMode: Int,
taskSurface: SurfaceControl = SurfaceControl(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
index 80ad1df44a1b..d44c01592ff7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -25,6 +25,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
@@ -52,6 +53,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
private val transitionsMock: Transitions = mock()
private val shellTaskOrganizerMock: ShellTaskOrganizer = mock()
private val desktopRepository: DesktopRepository = mock()
+ private val desktopModeEventLogger: DesktopModeEventLogger = mock()
private val toggleResizeDesktopTaskTransitionHandlerMock:
ToggleResizeDesktopTaskTransitionHandler =
mock()
@@ -74,6 +76,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
toggleResizeDesktopTaskTransitionHandlerMock,
returnToDragStartAnimatorMock,
desktopRepository,
+ desktopModeEventLogger,
)
whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index f371f5223419..057d8fa3adb0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -21,6 +21,7 @@ import android.content.res.Resources
import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
+import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_TO_FRONT
@@ -33,6 +34,8 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
@@ -91,7 +94,9 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
private val info: TransitionInfo = mock()
private val finishCallback: Transitions.TransitionFinishCallback = mock()
private val desktopRepository: DesktopRepository = mock()
+ private val desktopModeEventLogger: DesktopModeEventLogger = mock()
private val desktopTilingDividerWindowManager: DesktopTilingDividerWindowManager = mock()
+ private val motionEvent: MotionEvent = mock()
private lateinit var tilingDecoration: DesktopTilingWindowDecoration
private val split_divider_width = 10
@@ -112,6 +117,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
desktopRepository,
+ desktopModeEventLogger,
)
whenever(context.createContextAsUser(any(), any())).thenReturn(context)
}
@@ -371,13 +377,13 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
// End moving, no startTransition because bounds did not change.
tiledTaskHelper.newBounds.set(BOUNDS)
- tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction)
+ tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction, motionEvent)
verify(tiledTaskHelper, times(2)).hideVeil()
verify(transitions, never()).startTransition(any(), any(), any())
// Move then end again with bounds changing to ensure startTransition is called.
tilingDecoration.onDividerHandleMoved(BOUNDS, transaction)
- tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction)
+ tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction, motionEvent)
verify(transitions, times(1))
.startTransition(eq(TRANSIT_CHANGE), any(), eq(tilingDecoration))
// No hide veil until start animation is called.
@@ -389,6 +395,64 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
}
@Test
+ fun tiledTasksResizedUsingDividerHandle_shouldLogResizingEvents() {
+ // Setup
+ val task1 = createFreeformTask()
+ val task2 = createFreeformTask()
+ val stableBounds = STABLE_BOUNDS_MOCK
+ whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(stableBounds)
+ }
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+ desktopWindowDecoration.mTaskInfo = task1
+ task1.minWidth = 0
+ task1.minHeight = 0
+ initTiledTaskHelperMock(task1)
+ desktopWindowDecoration.mDecorWindowContext = context
+ whenever(resources.getBoolean(any())).thenReturn(true)
+
+ // Act
+ tilingDecoration.onAppTiled(
+ task1,
+ desktopWindowDecoration,
+ DesktopTasksController.SnapPosition.RIGHT,
+ BOUNDS,
+ )
+ tilingDecoration.onAppTiled(
+ task2,
+ desktopWindowDecoration,
+ DesktopTasksController.SnapPosition.LEFT,
+ BOUNDS,
+ )
+ tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+ tilingDecoration.rightTaskResizingHelper = tiledTaskHelper
+ tilingDecoration.onDividerHandleDragStart(motionEvent)
+ // Log start event for task1 and task2, but the tasks are the same in
+ // this test, so we verify the same log twice.
+ verify(desktopModeEventLogger, times(2)).logTaskResizingStarted(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ task1,
+ displayController,
+ )
+
+ tilingDecoration.onDividerHandleMoved(BOUNDS, transaction)
+ tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction, motionEvent)
+ // Log end event for task1 and task2, but the tasks are the same in
+ // this test, so we verify the same log twice.
+ verify(desktopModeEventLogger, times(2)).logTaskResizingEnded(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ task1,
+ BOUNDS.height(),
+ BOUNDS.width(),
+ displayController,
+ )
+ }
+
+ @Test
fun taskTiled_shouldBeRemoved_whenTileBroken() {
val task1 = createFreeformTask()
val stableBounds = STABLE_BOUNDS_MOCK
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt
index c96ce955f217..734815cdd915 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt
@@ -68,7 +68,7 @@ class TilingDividerViewTest : ShellTestCase() {
val downMotionEvent =
getMotionEvent(downTime, MotionEvent.ACTION_DOWN, x.toFloat(), y.toFloat())
tilingDividerView.handleMotionEvent(viewMock, downMotionEvent)
- verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any())
+ verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any(), any())
whenever(dividerMoveCallbackMock.onDividerMove(any())).thenReturn(true)
val motionEvent =
@@ -79,7 +79,7 @@ class TilingDividerViewTest : ShellTestCase() {
val upMotionEvent =
getMotionEvent(downTime, MotionEvent.ACTION_UP, x.toFloat(), y.toFloat())
tilingDividerView.handleMotionEvent(viewMock, upMotionEvent)
- verify(dividerMoveCallbackMock, times(1)).onDividerMovedEnd(any())
+ verify(dividerMoveCallbackMock, times(1)).onDividerMovedEnd(any(), any())
}
@Test
@@ -92,12 +92,12 @@ class TilingDividerViewTest : ShellTestCase() {
val downMotionEvent =
getMotionEvent(downTime, MotionEvent.ACTION_DOWN, x.toFloat(), y.toFloat())
tilingDividerView.handleMotionEvent(viewMock, downMotionEvent)
- verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any())
+ verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any(), any())
val upMotionEvent =
getMotionEvent(downTime, MotionEvent.ACTION_UP, x.toFloat(), y.toFloat())
tilingDividerView.handleMotionEvent(viewMock, upMotionEvent)
- verify(dividerMoveCallbackMock, never()).onDividerMovedEnd(any())
+ verify(dividerMoveCallbackMock, never()).onDividerMovedEnd(any(), any())
}
private fun getMotionEvent(eventTime: Long, action: Int, x: Float, y: Float): MotionEvent {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index b71abdc011c1..fcb7efc35c94 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -580,6 +580,7 @@ cc_defaults {
"utils/Color.cpp",
"utils/LinearAllocator.cpp",
"utils/StringUtils.cpp",
+ "utils/StatsUtils.cpp",
"utils/TypefaceUtils.cpp",
"utils/VectorDrawableUtils.cpp",
"AnimationContext.cpp",
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index e074a27db38f..a9a5db8181ba 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -27,8 +27,8 @@
#include <SkColorSpace.h>
#include <SkColorType.h>
#include <SkEncodedOrigin.h>
-#include <SkImageInfo.h>
#include <SkGainmapInfo.h>
+#include <SkImageInfo.h>
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkPngChunkReader.h>
@@ -43,6 +43,8 @@
#include <memory>
+#include "modules/skcms/src/skcms_public.h"
+
using namespace android;
sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const {
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 9cd6e253140e..e5fb75575ac3 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -49,6 +49,7 @@ minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
minikinPaint.fontStyle = resolvedFace->fStyle;
minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
minikinPaint.fontVariationSettings = paint->getFontVariationOverride();
+ minikinPaint.verticalText = paint->isVerticalText();
const std::optional<minikin::FamilyVariant>& familyVariant = paint->getFamilyVariant();
if (familyVariant.has_value()) {
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 7eb849fe6e3d..594ea31387ad 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -158,6 +158,7 @@ public:
SkSamplingOptions sampling() const {
return SkSamplingOptions(this->filterMode());
}
+ bool isVerticalText() const { return mVerticalText; }
void setVariationOverride(minikin::VariationSettings&& varSettings) {
mFontVariationOverride = std::move(varSettings);
@@ -202,6 +203,7 @@ private:
bool mUnderline = false;
bool mDevKern = false;
minikin::RunFlag mRunFlag = minikin::RunFlag::NONE;
+ bool mVerticalText = false;
};
} // namespace android
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 6dfcedc3d918..fa5325d90218 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -49,7 +49,8 @@ Paint::Paint(const Paint& paint)
, mStrikeThru(paint.mStrikeThru)
, mUnderline(paint.mUnderline)
, mDevKern(paint.mDevKern)
- , mRunFlag(paint.mRunFlag) {}
+ , mRunFlag(paint.mRunFlag)
+ , mVerticalText(paint.mVerticalText) {}
Paint::~Paint() {}
@@ -71,6 +72,7 @@ Paint& Paint::operator=(const Paint& other) {
mUnderline = other.mUnderline;
mDevKern = other.mDevKern;
mRunFlag = other.mRunFlag;
+ mVerticalText = other.mVerticalText;
return *this;
}
@@ -83,7 +85,8 @@ bool operator==(const Paint& a, const Paint& b) {
a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
a.mTypeface == b.mTypeface && a.mAlign == b.mAlign &&
a.mFilterBitmap == b.mFilterBitmap && a.mStrikeThru == b.mStrikeThru &&
- a.mUnderline == b.mUnderline && a.mDevKern == b.mDevKern && a.mRunFlag == b.mRunFlag;
+ a.mUnderline == b.mUnderline && a.mDevKern == b.mDevKern && a.mRunFlag == b.mRunFlag &&
+ a.mVerticalText == b.mVerticalText;
}
void Paint::reset() {
@@ -97,6 +100,7 @@ void Paint::reset() {
mStrikeThru = false;
mUnderline = false;
mDevKern = false;
+ mVerticalText = false;
mRunFlag = minikin::RunFlag::NONE;
}
@@ -135,6 +139,7 @@ static const uint32_t sForceAutoHinting = 0x800;
// flags related to minikin::Paint
static const uint32_t sUnderlineFlag = 0x08;
static const uint32_t sStrikeThruFlag = 0x10;
+static const uint32_t sVerticalTextFlag = 0x1000;
static const uint32_t sTextRunLeftEdge = 0x2000;
static const uint32_t sTextRunRightEdge = 0x4000;
// flags no longer supported on native side (but mirrored for compatibility)
@@ -190,6 +195,7 @@ uint32_t Paint::getJavaFlags() const {
flags |= -(int)mUnderline & sUnderlineFlag;
flags |= -(int)mDevKern & sDevKernFlag;
flags |= -(int)mFilterBitmap & sFilterBitmapFlag;
+ flags |= -(int)mVerticalText & sVerticalTextFlag;
if (mRunFlag & minikin::RunFlag::LEFT_EDGE) {
flags |= sTextRunLeftEdge;
}
@@ -206,6 +212,7 @@ void Paint::setJavaFlags(uint32_t flags) {
mUnderline = (flags & sUnderlineFlag) != 0;
mDevKern = (flags & sDevKernFlag) != 0;
mFilterBitmap = (flags & sFilterBitmapFlag) != 0;
+ mVerticalText = (flags & sVerticalTextFlag) != 0;
std::underlying_type<minikin::RunFlag>::type rawFlag = minikin::RunFlag::NONE;
if (flags & sTextRunLeftEdge) {
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index 49a7f73fb3a3..8b43f1db84af 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -10,6 +10,7 @@
#include <stdint.h>
#include <stdio.h>
#include <sys/stat.h>
+#include <utils/StatsUtils.h>
#include <memory>
@@ -630,6 +631,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
}
bitmap::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
outputBitmap.notifyPixelsChanged();
+ uirenderer::logBitmapDecode(*reuseBitmap);
// If a java bitmap was passed in for reuse, pass it back
return javaBitmap;
}
@@ -650,6 +652,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
}
}
+ uirenderer::logBitmapDecode(*hardwareBitmap);
return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags,
ninePatchChunk, ninePatchInsets, -1);
}
@@ -659,6 +662,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
heapBitmap->setGainmap(std::move(gainmap));
}
+ uirenderer::logBitmapDecode(*heapBitmap);
// now create the java bitmap
return bitmap::createBitmap(env, heapBitmap, bitmapCreateFlags, ninePatchChunk, ninePatchInsets,
-1);
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index f7e8e073a272..5ffd5b9016d8 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -19,6 +19,7 @@
#include <HardwareBitmapUploader.h>
#include <androidfw/Asset.h>
#include <sys/stat.h>
+#include <utils/StatsUtils.h>
#include <memory>
@@ -376,6 +377,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
recycledBitmap->setGainmap(std::move(gainmap));
}
bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
+ uirenderer::logBitmapDecode(*recycledBitmap);
return javaBitmap;
}
@@ -392,12 +394,14 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
hardwareBitmap->setGainmap(std::move(gm));
}
}
+ uirenderer::logBitmapDecode(*hardwareBitmap);
return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags);
}
Bitmap* heapBitmap = heapAlloc.getStorageObjAndReset();
if (hasGainmap && heapBitmap != nullptr) {
heapBitmap->setGainmap(std::move(gainmap));
}
+ uirenderer::logBitmapDecode(*heapBitmap);
return android::bitmap::createBitmap(env, heapBitmap, bitmapCreateFlags);
}
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index aebc4db37898..90fd3d87cba7 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -37,6 +37,7 @@
#include <hwui/Bitmap.h>
#include <hwui/ImageDecoder.h>
#include <sys/stat.h>
+#include <utils/StatsUtils.h>
#include "Bitmap.h"
#include "BitmapFactory.h"
@@ -485,6 +486,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
hwBitmap->setGainmap(std::move(gm));
}
}
+ uirenderer::logBitmapDecode(*hwBitmap);
return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
ninePatchChunk, ninePatchInsets);
}
@@ -498,6 +500,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
nativeBitmap->setImmutable();
}
+
+ uirenderer::logBitmapDecode(*nativeBitmap);
return bitmap::createBitmap(env, nativeBitmap.release(), bitmapCreateFlags, ninePatchChunk,
ninePatchInsets);
}
diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt
index 2414299321a9..b5591941453d 100644
--- a/libs/hwui/libhwui.map.txt
+++ b/libs/hwui/libhwui.map.txt
@@ -67,6 +67,7 @@ LIBHWUI_PLATFORM {
SkFILEStream::SkFILEStream*;
SkImageInfo::*;
SkMemoryStream::SkMemoryStream*;
+ android::uirenderer::logBitmapDecode*;
};
local:
*;
diff --git a/libs/hwui/utils/StatsUtils.cpp b/libs/hwui/utils/StatsUtils.cpp
new file mode 100644
index 000000000000..5c4027e1a846
--- /dev/null
+++ b/libs/hwui/utils/StatsUtils.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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.
+ */
+
+#ifdef __ANDROID__
+#include <dlfcn.h>
+#include <log/log.h>
+#include <statslog_hwui.h>
+#include <statssocket_lazy.h>
+#include <utils/Errors.h>
+
+#include <mutex>
+#endif
+
+#include <unistd.h>
+
+#include "StatsUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+#ifdef __ANDROID__
+
+namespace {
+
+int32_t toStatsColorSpaceTransfer(skcms_TFType transferType) {
+ switch (transferType) {
+ case skcms_TFType_sRGBish:
+ return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_SRGBISH;
+ case skcms_TFType_PQish:
+ return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_PQISH;
+ case skcms_TFType_HLGish:
+ return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_HLGISH;
+ default:
+ return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_UNKNOWN;
+ }
+}
+
+int32_t toStatsBitmapFormat(SkColorType type) {
+ switch (type) {
+ case kAlpha_8_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_A_8;
+ case kRGB_565_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_RGB_565;
+ case kN32_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_ARGB_8888;
+ case kRGBA_F16_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_RGBA_F16;
+ case kRGBA_1010102_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_RGBA_1010102;
+ default:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_UNKNOWN;
+ }
+}
+
+} // namespace
+
+#endif
+
+void logBitmapDecode(const SkImageInfo& info, bool hasGainmap) {
+#ifdef __ANDROID__
+
+ if (!statssocket::lazy::IsAvailable()) {
+ std::once_flag once;
+ std::call_once(once, []() { ALOGD("libstatssocket not available, dropping stats"); });
+ return;
+ }
+
+ skcms_TFType tfnType = skcms_TFType_Invalid;
+
+ if (info.colorSpace()) {
+ skcms_TransferFunction tfn;
+ info.colorSpace()->transferFn(&tfn);
+ tfnType = skcms_TransferFunction_getType(&tfn);
+ }
+
+ auto status =
+ stats::stats_write(uirenderer::stats::IMAGE_DECODED, static_cast<int32_t>(getuid()),
+ uirenderer::toStatsColorSpaceTransfer(tfnType), hasGainmap,
+ uirenderer::toStatsBitmapFormat(info.colorType()));
+ ALOGW_IF(status != OK, "Image decoding logging dropped!");
+#endif
+}
+
+void logBitmapDecode(const Bitmap& bitmap) {
+ logBitmapDecode(bitmap.info(), bitmap.hasGainmap());
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/StatsUtils.h b/libs/hwui/utils/StatsUtils.h
new file mode 100644
index 000000000000..0c247014a8eb
--- /dev/null
+++ b/libs/hwui/utils/StatsUtils.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <SkBitmap.h>
+#include <SkColorSpace.h>
+#include <SkColorType.h>
+#include <cutils/compiler.h>
+#include <hwui/Bitmap.h>
+
+namespace android {
+namespace uirenderer {
+
+ANDROID_API void logBitmapDecode(const SkImageInfo& info, bool hasGainmap);
+
+ANDROID_API void logBitmapDecode(const Bitmap& bitmap);
+
+} // namespace uirenderer
+} // namespace android
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 55c8ed5debf8..530d48d3e60b 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -40,6 +40,8 @@ import android.os.ParcelFileDescriptor;
import android.os.Trace;
import android.view.Surface;
+import com.android.internal.camera.flags.Flags;
+
import dalvik.system.VMRuntime;
import java.io.IOException;
@@ -1208,6 +1210,11 @@ public class ImageReader implements AutoCloseable {
default:
width = nativeGetWidth();
}
+ if (Flags.cameraHeifGainmap()) {
+ if (getFormat() == ImageFormat.HEIC_ULTRAHDR){
+ width = ImageReader.this.getWidth();
+ }
+ }
return width;
}
@@ -1227,6 +1234,11 @@ public class ImageReader implements AutoCloseable {
default:
height = nativeGetHeight();
}
+ if (Flags.cameraHeifGainmap()) {
+ if (getFormat() == ImageFormat.HEIC_ULTRAHDR){
+ height = ImageReader.this.getHeight();
+ }
+ }
return height;
}
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index f4caad727407..c7678067f0b5 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -23,6 +23,8 @@ import android.media.Image.Plane;
import android.util.Log;
import android.util.Size;
+import com.android.internal.camera.flags.Flags;
+
import libcore.io.Memory;
import java.nio.ByteBuffer;
@@ -44,6 +46,11 @@ class ImageUtils {
* are used.
*/
public static int getNumPlanesForFormat(int format) {
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ return 1;
+ }
+ }
switch (format) {
case ImageFormat.YV12:
case ImageFormat.YUV_420_888:
@@ -229,6 +236,11 @@ class ImageUtils {
public static int getEstimatedNativeAllocBytes(int width, int height, int format,
int numImages) {
double estimatedBytePerPixel;
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ estimatedBytePerPixel = 0.3;
+ }
+ }
switch (format) {
// 10x compression from RGB_888
case ImageFormat.JPEG:
@@ -283,6 +295,11 @@ class ImageUtils {
}
private static Size getEffectivePlaneSizeForImage(Image image, int planeIdx) {
+ if (Flags.cameraHeifGainmap()) {
+ if (image.getFormat() == ImageFormat.HEIC_ULTRAHDR){
+ return new Size(image.getWidth(), image.getHeight());
+ }
+ }
switch (image.getFormat()) {
case ImageFormat.YCBCR_P010:
case ImageFormat.YV12:
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index 5cbc81d5f92d..472d7985b304 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -21,11 +21,16 @@ import android.annotation.FlaggedApi;
import android.media.tv.flags.Flags;
/**
+ * The contract between the media quality service and applications. Contains definitions for the
+ * commonly used parameter names.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
public class MediaQualityContract {
+ /**
+ * @hide
+ */
public interface BaseParameters {
String PARAMETER_ID = "_id";
String PARAMETER_NAME = "_name";
@@ -33,13 +38,50 @@ public class MediaQualityContract {
String PARAMETER_INPUT_ID = "_input_id";
}
- public static final class PictureQuality implements BaseParameters {
+
+ /**
+ * Parameters picture quality.
+ * @hide
+ */
+ public static final class PictureQuality {
+ /**
+ * The brightness.
+ *
+ * <p>Type: INTEGER
+ */
public static final String PARAMETER_BRIGHTNESS = "brightness";
+
+ /**
+ * The contrast.
+ *
+ * <p>The ratio between the luminance of the brightest white and the darkest black.
+ * <p>Type: INTEGER
+ */
public static final String PARAMETER_CONTRAST = "contrast";
+
+ /**
+ * The sharpness.
+ *
+ * <p>Sharpness indicates the clarity of detail.
+ * <p>Type: INTEGER
+ */
public static final String PARAMETER_SHARPNESS = "sharpness";
+
+ /**
+ * The saturation.
+ *
+ * <p>Saturation indicates the intensity of the color.
+ * <p>Type: INTEGER
+ */
public static final String PARAMETER_SATURATION = "saturation";
+
+ private PictureQuality() {
+ }
}
+ /**
+ * @hide
+ */
public static final class SoundQuality implements BaseParameters {
public static final String PARAMETER_BALANCE = "balance";
public static final String PARAMETER_BASS = "bass";
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 1237d673162c..38a2025535f4 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -34,7 +34,8 @@ import java.util.List;
import java.util.concurrent.Executor;
/**
- * Expose TV setting APIs for the application to use
+ * Central system API to the overall media quality, which arbitrates interaction between
+ * applications and media quality service.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
@@ -177,6 +178,7 @@ public final class MediaQualityManager {
* Gets picture profile by given profile ID.
* @return the corresponding picture profile if available; {@code null} if the ID doesn't
* exist or the profile is not accessible to the caller.
+ * @hide
*/
public PictureProfile getPictureProfileById(long profileId) {
try {
@@ -187,7 +189,10 @@ public final class MediaQualityManager {
}
- /** @SystemApi gets profiles that available to the given package */
+ /**
+ * @SystemApi gets profiles that available to the given package
+ * @hide
+ */
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
try {
@@ -197,7 +202,10 @@ public final class MediaQualityManager {
}
}
- /** Gets profiles that available to the caller package */
+ /**
+ * Gets profiles that available to the caller.
+ */
+ @NonNull
public List<PictureProfile> getAvailablePictureProfiles() {
try {
return mService.getAvailablePictureProfiles();
@@ -206,7 +214,10 @@ public final class MediaQualityManager {
}
}
- /** @SystemApi all stored picture profiles of all packages */
+ /**
+ * @SystemApi all stored picture profiles of all packages
+ * @hide
+ */
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public List<PictureProfile> getAllPictureProfiles() {
try {
@@ -221,6 +232,7 @@ public final class MediaQualityManager {
* Creates a picture profile and store it in the system.
*
* @return the stored profile with an assigned profile ID.
+ * @hide
*/
public PictureProfile createPictureProfile(PictureProfile pp) {
try {
@@ -233,6 +245,7 @@ public final class MediaQualityManager {
/**
* Updates an existing picture profile and store it in the system.
+ * @hide
*/
public void updatePictureProfile(long profileId, PictureProfile pp) {
try {
@@ -245,6 +258,7 @@ public final class MediaQualityManager {
/**
* Removes a picture profile from the system.
+ * @hide
*/
public void removePictureProfile(long profileId) {
try {
@@ -291,6 +305,7 @@ public final class MediaQualityManager {
* Gets sound profile by given profile ID.
* @return the corresponding sound profile if available; {@code null} if the ID doesn't
* exist or the profile is not accessible to the caller.
+ * @hide
*/
public SoundProfile getSoundProfileById(long profileId) {
try {
@@ -301,7 +316,10 @@ public final class MediaQualityManager {
}
- /** @SystemApi gets profiles that available to the given package */
+ /**
+ * @SystemApi gets profiles that available to the given package
+ * @hide
+ */
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public List<SoundProfile> getSoundProfilesByPackage(String packageName) {
try {
@@ -311,7 +329,10 @@ public final class MediaQualityManager {
}
}
- /** Gets profiles that available to the caller package */
+ /**
+ * Gets profiles that available to the caller package
+ * @hide
+ */
public List<SoundProfile> getAvailableSoundProfiles() {
try {
return mService.getAvailableSoundProfiles();
@@ -320,7 +341,10 @@ public final class MediaQualityManager {
}
}
- /** @SystemApi all stored sound profiles of all packages */
+ /**
+ * @SystemApi all stored sound profiles of all packages
+ * @hide
+ */
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public List<SoundProfile> getAllSoundProfiles() {
try {
@@ -335,6 +359,7 @@ public final class MediaQualityManager {
* Creates a sound profile and store it in the system.
*
* @return the stored profile with an assigned profile ID.
+ * @hide
*/
public SoundProfile createSoundProfile(SoundProfile sp) {
try {
@@ -347,6 +372,7 @@ public final class MediaQualityManager {
/**
* Updates an existing sound profile and store it in the system.
+ * @hide
*/
public void updateSoundProfile(long profileId, SoundProfile sp) {
try {
@@ -359,6 +385,7 @@ public final class MediaQualityManager {
/**
* Removes a sound profile from the system.
+ * @hide
*/
public void removeSoundProfile(long profileId) {
try {
@@ -370,6 +397,7 @@ public final class MediaQualityManager {
/**
* Gets capability information of the given parameters.
+ * @hide
*/
public List<ParamCapability> getParamCapabilities(List<String> names) {
try {
@@ -396,6 +424,7 @@ public final class MediaQualityManager {
* different use cases.
*
* @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setAutoPictureQualityEnabled(boolean enabled) {
@@ -408,6 +437,7 @@ public final class MediaQualityManager {
/**
* Returns {@code true} if auto picture quality is enabled; {@code false} otherwise.
+ * @hide
*/
public boolean isAutoPictureQualityEnabled() {
try {
@@ -422,6 +452,7 @@ public final class MediaQualityManager {
* <p>Super resolution is a feature to improve resolution.
*
* @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setSuperResolutionEnabled(boolean enabled) {
@@ -434,6 +465,7 @@ public final class MediaQualityManager {
/**
* Returns {@code true} if super resolution is enabled; {@code false} otherwise.
+ * @hide
*/
public boolean isSuperResolutionEnabled() {
try {
@@ -449,6 +481,7 @@ public final class MediaQualityManager {
* different use cases.
*
* @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public void setAutoSoundQualityEnabled(boolean enabled) {
@@ -461,6 +494,7 @@ public final class MediaQualityManager {
/**
* Returns {@code true} if auto sound quality is enabled; {@code false} otherwise.
+ * @hide
*/
public boolean isAutoSoundQualityEnabled() {
try {
@@ -507,6 +541,7 @@ public final class MediaQualityManager {
* Set the ambient backlight settings.
*
* @param settings The settings to use for the backlight detector.
+ * @hide
*/
public void setAmbientBacklightSettings(
@NonNull AmbientBacklightSettings settings) {
@@ -522,6 +557,7 @@ public final class MediaQualityManager {
* Enables or disables the ambient backlight detection.
*
* @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
*/
public void setAmbientBacklightEnabled(boolean enabled) {
try {
@@ -700,6 +736,7 @@ public final class MediaQualityManager {
public abstract static class AmbientBacklightCallback {
/**
* Called when new ambient backlight event is emitted.
+ * @hide
*/
public void onAmbientBacklightEvent(AmbientBacklightEvent event) {
}
diff --git a/media/java/android/media/quality/PictureProfile.java b/media/java/android/media/quality/PictureProfile.java
index 36c49d80e13d..8fb57124d33e 100644
--- a/media/java/android/media/quality/PictureProfile.java
+++ b/media/java/android/media/quality/PictureProfile.java
@@ -17,6 +17,8 @@
package android.media.quality;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.media.tv.TvInputInfo;
import android.media.tv.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -24,30 +26,55 @@ import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
+ * Profile for picture quality.
* @hide
*/
-
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
-public class PictureProfile implements Parcelable {
+public final class PictureProfile implements Parcelable {
@Nullable
- private Long mId;
+ private String mId;
+ private final int mType;
@NonNull
private final String mName;
@Nullable
private final String mInputId;
- @Nullable
+ @NonNull
private final String mPackageName;
@NonNull
private final Bundle mParams;
- protected PictureProfile(Parcel in) {
- if (in.readByte() == 0) {
- mId = null;
- } else {
- mId = in.readLong();
- }
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "TYPE_", value = {
+ TYPE_SYSTEM,
+ TYPE_APPLICATION})
+ public @interface ProfileType {}
+
+ /**
+ * System profile type.
+ *
+ * <p>A profile of system type is managed by the system, and readable to the package define in
+ * {@link #getPackageName()}.
+ */
+ public static final int TYPE_SYSTEM = 1;
+ /**
+ * Application profile type.
+ *
+ * <p>A profile of application type is managed by the package define in
+ * {@link #getPackageName()}.
+ */
+ public static final int TYPE_APPLICATION = 2;
+
+
+ private PictureProfile(@NonNull Parcel in) {
+ mId = in.readString();
+ mType = in.readInt();
mName = in.readString();
mInputId = in.readString();
mPackageName = in.readString();
@@ -55,13 +82,9 @@ public class PictureProfile implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
- if (mId == null) {
- dest.writeByte((byte) 0);
- } else {
- dest.writeByte((byte) 1);
- dest.writeLong(mId);
- }
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeInt(mType);
dest.writeString(mName);
dest.writeString(mInputId);
dest.writeString(mPackageName);
@@ -73,6 +96,7 @@ public class PictureProfile implements Parcelable {
return 0;
}
+ @NonNull
public static final Creator<PictureProfile> CREATOR = new Creator<PictureProfile>() {
@Override
public PictureProfile createFromParcel(Parcel in) {
@@ -92,95 +116,166 @@ public class PictureProfile implements Parcelable {
* @hide
*/
public PictureProfile(
- @Nullable Long id,
+ @Nullable String id,
+ int type,
@NonNull String name,
@Nullable String inputId,
- @Nullable String packageName,
+ @NonNull String packageName,
@NonNull Bundle params) {
this.mId = id;
+ this.mType = type;
this.mName = name;
- com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
this.mInputId = inputId;
this.mPackageName = packageName;
this.mParams = params;
}
+ /**
+ * Gets profile ID.
+ *
+ * <p>A profile ID is a globally unique ID generated and assigned by the system. For profile
+ * objects retrieved from system (e.g {@link MediaQualityManager#getAvailablePictureProfiles()})
+ * this profile ID is non-null; For profiles built locally with {@link Builder}, it's
+ * {@code null}.
+ *
+ * @return the unique profile ID; {@code null} if the profile is built locally with
+ * {@link Builder}.
+ */
@Nullable
- public Long getProfileId() {
+ public String getProfileId() {
return mId;
}
+ /**
+ * Only used by system to assign the ID.
+ * @hide
+ */
+ public void setProfileId(String id) {
+ mId = id;
+ }
+
+ /**
+ * Gets profile type.
+ */
+ @ProfileType
+ public int getProfileType() {
+ return mType;
+ }
+
+ /**
+ * Gets the profile name.
+ */
@NonNull
public String getName() {
return mName;
}
+ /**
+ * Gets the input ID if the profile is for a TV input.
+ *
+ * @return the corresponding TV input ID; {@code null} if the profile is not associated with a
+ * TV input.
+ *
+ * @see TvInputInfo#getId()
+ */
@Nullable
public String getInputId() {
return mInputId;
}
+ /**
+ * Gets the package name of this profile.
+ *
+ * <p>The package name defines the user of a profile. Only this specific package and system app
+ * can access to this profile.
+ *
+ * @return the package name; {@code null} if the profile is built locally using
+ * {@link Builder} and the package is not set.
+ */
@Nullable
public String getPackageName() {
return mPackageName;
}
+
+ /**
+ * Gets the parameters of this profile.
+ *
+ * <p>The keys of commonly used parameters can be found in
+ * {@link MediaQualityContract.PictureQuality}.
+ */
@NonNull
public Bundle getParameters() {
return new Bundle(mParams);
}
/**
- * A builder for {@link PictureProfile}
+ * A builder for {@link PictureProfile}.
+ * @hide
*/
- public static class Builder {
+ public static final class Builder {
@Nullable
- private Long mId;
+ private String mId;
+ private int mType = TYPE_APPLICATION;
@NonNull
private String mName;
@Nullable
private String mInputId;
- @Nullable
+ @NonNull
private String mPackageName;
@NonNull
private Bundle mParams;
/**
* Creates a new Builder.
- *
- * @hide
*/
public Builder(@NonNull String name) {
mName = name;
- com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
}
/**
- * Copy constructor.
- *
- * @hide
+ * Copy constructor of builder.
*/
public Builder(@NonNull PictureProfile p) {
mId = null; // ID needs to be reset
+ mType = p.getProfileType();
mName = p.getName();
mPackageName = p.getPackageName();
mInputId = p.getInputId();
+ mParams = p.getParameters();
}
/* @hide using by MediaQualityService */
/**
- * Sets profile ID.
- * @hide using by MediaQualityService
+ * Only used by system to assign the ID.
+ * @hide
*/
@NonNull
- public Builder setProfileId(@Nullable Long id) {
+ public Builder setProfileId(@Nullable String id) {
mId = id;
return this;
}
/**
+ * Sets profile type.
+ *
+ * @hide @SystemApi
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ @NonNull
+ public Builder setProfileType(@ProfileType int value) {
+ mType = value;
+ return this;
+ }
+
+ /**
* Sets input ID.
+ *
+ * @see PictureProfile#getInputId()
+ *
+ * @hide @SystemApi
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
@NonNull
public Builder setInputId(@NonNull String value) {
mInputId = value;
@@ -189,7 +284,12 @@ public class PictureProfile implements Parcelable {
/**
* Sets package name of the profile.
+ *
+ * @see PictureProfile#getPackageName()
+ *
+ * @hide @SystemApi
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
@NonNull
public Builder setPackageName(@NonNull String value) {
mPackageName = value;
@@ -198,6 +298,8 @@ public class PictureProfile implements Parcelable {
/**
* Sets profile parameters.
+ *
+ * @see PictureProfile#getParameters()
*/
@NonNull
public Builder setParameters(@NonNull Bundle params) {
@@ -213,6 +315,7 @@ public class PictureProfile implements Parcelable {
PictureProfile o = new PictureProfile(
mId,
+ mType,
mName,
mInputId,
mPackageName,
diff --git a/media/java/android/media/tv/TvInputServiceExtensionManager.java b/media/java/android/media/tv/TvInputServiceExtensionManager.java
index 0e98488c93c0..c514f6ed04ef 100644
--- a/media/java/android/media/tv/TvInputServiceExtensionManager.java
+++ b/media/java/android/media/tv/TvInputServiceExtensionManager.java
@@ -17,13 +17,17 @@
package android.media.tv;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringDef;
import android.media.tv.flags.Flags;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -33,10 +37,14 @@ import java.util.Set;
/**
+ * This class provides a list of available standardized TvInputService extension interface names
+ * and a container storing IBinder objects that implement these interfaces created by SoC/OEMs.
+ * It also provides an API for SoC/OEMs to register implemented IBinder objects.
+ *
* @hide
*/
@FlaggedApi(Flags.FLAG_TIF_EXTENSION_STANDARDIZATION)
-public class TvInputServiceExtensionManager {
+public final class TvInputServiceExtensionManager {
private static final String TAG = "TvInputServiceExtensionManager";
private static final String SCAN_PACKAGE = "android.media.tv.extension.scan.";
private static final String OAD_PACKAGE = "android.media.tv.extension.oad.";
@@ -54,404 +62,608 @@ public class TvInputServiceExtensionManager {
private static final String ANALOG_PACKAGE = "android.media.tv.extension.analog.";
private static final String TUNE_PACKAGE = "android.media.tv.extension.tune.";
- /** Register binder returns success when it abides standardized interface structure */
+ /** @hide */
+ @IntDef(prefix = {"REGISTER_"}, value = {
+ REGISTER_SUCCESS,
+ REGISTER_FAIL_NAME_NOT_STANDARDIZED,
+ REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED,
+ REGISTER_FAIL_REMOTE_EXCEPTION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RegisterResult {}
+
+ /**
+ * Registering binder returns success when it abides standardized interface structure
+ */
public static final int REGISTER_SUCCESS = 0;
- /** Register binder returns fail when the extension name is not in the standardization list */
+ /**
+ * Registering binder returns failure when the extension name is not in the standardization
+ * list
+ */
public static final int REGISTER_FAIL_NAME_NOT_STANDARDIZED = 1;
- /** Register binder returns fail when the IBinder does not implement standardized interface */
+ /**
+ * Registering binder returns failure when the IBinder does not implement standardized interface
+ */
public static final int REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED = 2;
- /** Register binder returns fail when remote server not available */
+ /**
+ * Registering binder returns failure when remote server is not available
+ */
public static final int REGISTER_FAIL_REMOTE_EXCEPTION = 3;
+ /** @hide */
+ @StringDef({
+ ISCAN_INTERFACE,
+ ISCAN_SESSION,
+ ISCAN_LISTENER,
+ IHDPLUS_INFO,
+ IOPERATOR_DETECTION,
+ IOPERATOR_DETECTION_LISTENER,
+ IREGION_CHANNEL_LIST,
+ IREGION_CHANNEL_LIST_LISTENER,
+ ITARGET_REGION,
+ ITARGET_REGION_LISTENER,
+ ILCN_CONFLICT,
+ ILCN_CONFLICT_LISTENER,
+ ILCNV2_CHANNEL_LIST,
+ ILCNV2_CHANNEL_LIST_LISTENER,
+ IFAVORITE_NETWORK,
+ IFAVORITE_NETWORK_LISTENER,
+ ITKGS_INFO,
+ ITKGS_INFO_LISTENER,
+ ISCAN_SAT_SEARCH,
+ IOAD_UPDATE_INTERFACE,
+ ICAM_APP_INFO_SERVICE,
+ ICAM_APP_INFO_LISTENER,
+ ICAM_MONITORING_SERVICE,
+ ICAM_INFO_LISTENER,
+ ICI_OPERATOR_INTERFACE,
+ ICI_OPERATOR_LISTENER,
+ ICAM_PROFILE_INTERFACE,
+ ICONTENT_CONTROL_SERVICE,
+ ICAM_DRM_INFO_LISTENER,
+ ICAM_PIN_SERVICE,
+ ICAM_PIN_CAPABILITY_LISTENER,
+ ICAM_PIN_STATUS_LISTENER,
+ ICAM_HOST_CONTROL_SERVICE,
+ ICAM_HOST_CONTROL_ASK_RELEASE_REPLY_CALLBACK,
+ ICAM_HOST_CONTROL_INFO_LISTENER,
+ ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG,
+ ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG_LISTENER,
+ IMMI_INTERFACE,
+ IMMI_SESSION,
+ IMMI_STATUS_CALLBACK,
+ IENTER_MENU_ERROR_CALLBACK,
+ IDOWNLOADABLE_RATING_TABLE_MONITOR,
+ IRATING_INTERFACE,
+ IPMT_RATING_INTERFACE,
+ IPMT_RATING_LISTENER,
+ IVBI_RATING_INTERFACE,
+ IVBI_RATING_LISTENER,
+ IPROGRAM_INFO,
+ IPROGRAM_INFO_LISTENER,
+ IBROADCAST_TIME,
+ IDATA_SERVICE_SIGNAL_INFO,
+ IDATA_SERVICE_SIGNAL_INFO_LISTENER,
+ ITELETEXT_PAGE_SUB_CODE,
+ ISCAN_BACKGROUND_SERVICE_UPDATE,
+ ISCAN_BACKGROUND_SERVICE_UPDATE_LISTENER,
+ ICLIENT_TOKEN,
+ ISCREEN_MODE_SETTINGS,
+ IHDMI_SIGNAL_INTERFACE,
+ IHDMI_SIGNAL_INFO_LISTENER,
+ IAUDIO_SIGNAL_INFO,
+ IANALOG_AUDIO_INFO,
+ IAUDIO_SIGNAL_INFO_LISTENER,
+ IVIDEO_SIGNAL_INFO,
+ IVIDEO_SIGNAL_INFO_LISTENER,
+ ISERVICE_LIST_EDIT,
+ ISERVICE_LIST_EDIT_LISTENER,
+ ISERVICE_LIST,
+ ISERVICE_LIST_TRANSFER_INTERFACE,
+ ISERVICE_LIST_EXPORT_SESSION,
+ ISERVICE_LIST_EXPORT_LISTENER,
+ ISERVICE_LIST_IMPORT_SESSION,
+ ISERVICE_LIST_IMPORT_LISTENER,
+ ISERVICE_LIST_SET_CHANNEL_LIST_SESSION,
+ ISERVICE_LIST_SET_CHANNEL_LIST_LISTENER,
+ ICHANNEL_LIST_TRANSFER,
+ IRECORDED_CONTENTS,
+ IDELETE_RECORDED_CONTENTS_CALLBACK,
+ IGET_INFO_RECORDED_CONTENTS_CALLBACK,
+ IEVENT_MONITOR,
+ IEVENT_MONITOR_LISTENER,
+ IEVENT_DOWNLOAD,
+ IEVENT_DOWNLOAD_LISTENER,
+ IEVENT_DOWNLOAD_SESSION,
+ IANALOG_ATTRIBUTE_INTERFACE,
+ ICHANNEL_TUNED_INTERFACE,
+ ICHANNEL_TUNED_LISTENER,
+ ITUNER_FRONTEND_SIGNAL_INFO_INTERFACE,
+ ITUNER_FRONTEND_SIGNAL_INFO_LISTENER,
+ IMUX_TUNE_SESSION,
+ IMUX_TUNE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StandardizedExtensionName {}
/**
* Interface responsible for creating scan session and obtain parameters.
+ * @hide
*/
public static final String ISCAN_INTERFACE = SCAN_PACKAGE + "IScanInterface";
/**
* Interface that handles scan session and get/store related information.
+ * @hide
*/
public static final String ISCAN_SESSION = SCAN_PACKAGE + "IScanSession";
/**
* Interface that notifies changes related to scan session.
+ * @hide
*/
public static final String ISCAN_LISTENER = SCAN_PACKAGE + "IScanListener";
/**
* Interface for setting HDPlus information.
+ * @hide
*/
public static final String IHDPLUS_INFO = SCAN_PACKAGE + "IHDPlusInfo";
/**
* Interface for handling operator detection for scanning.
+ * @hide
*/
public static final String IOPERATOR_DETECTION = SCAN_PACKAGE + "IOperatorDetection";
/**
* Interface for changes related to operator detection searches.
+ * @hide
*/
public static final String IOPERATOR_DETECTION_LISTENER = SCAN_PACKAGE
+ "IOperatorDetectionListener";
/**
* Interface for handling region channel list for scanning.
+ * @hide
*/
public static final String IREGION_CHANNEL_LIST = SCAN_PACKAGE + "IRegionChannelList";
/**
* Interface for changes related to changes in region channel list search.
+ * @hide
*/
public static final String IREGION_CHANNEL_LIST_LISTENER = SCAN_PACKAGE
+ "IRegionChannelListListener";
/**
* Interface for handling target region information.
+ * @hide
*/
public static final String ITARGET_REGION = SCAN_PACKAGE + "ITargetRegion";
/**
* Interface for changes related to target regions during scanning.
+ * @hide
*/
public static final String ITARGET_REGION_LISTENER = SCAN_PACKAGE + "ITargetRegionListener";
/**
* Interface for handling LCN conflict groups.
+ * @hide
*/
public static final String ILCN_CONFLICT = SCAN_PACKAGE + "ILcnConflict";
/**
* Interface for detecting LCN conflicts during scanning.
+ * @hide
*/
public static final String ILCN_CONFLICT_LISTENER = SCAN_PACKAGE + "ILcnConflictListener";
/**
* Interface for handling LCN V2 channel list information.
+ * @hide
*/
public static final String ILCNV2_CHANNEL_LIST = SCAN_PACKAGE + "ILcnV2ChannelList";
/**
* Interface for detecting LCN V2 channel list during scanning.
+ * @hide
*/
public static final String ILCNV2_CHANNEL_LIST_LISTENER = SCAN_PACKAGE
+ "ILcnV2ChannelListListener";
/**
* Interface for handling favorite network related information.
+ * @hide
*/
public static final String IFAVORITE_NETWORK = SCAN_PACKAGE + "IFavoriteNetwork";
/**
* Interface for detecting favorite network during scanning.
+ * @hide
*/
public static final String IFAVORITE_NETWORK_LISTENER = SCAN_PACKAGE
+ "IFavoriteNetworkListener";
/**
* Interface for handling Turksat channel update system service.
+ * @hide
*/
public static final String ITKGS_INFO = SCAN_PACKAGE + "ITkgsInfo";
/**
* Interface for changes related to TKGS information.
+ * @hide
*/
public static final String ITKGS_INFO_LISTENER = SCAN_PACKAGE + "ITkgsInfoListener";
/**
* Interface for satellite search related to low noise block downconverter.
+ * @hide
*/
public static final String ISCAN_SAT_SEARCH = SCAN_PACKAGE + "IScanSatSearch";
/**
* Interface for Over-the-Air Download.
+ * @hide
*/
public static final String IOAD_UPDATE_INTERFACE = OAD_PACKAGE + "IOadUpdateInterface";
/**
* Interface for handling conditional access module app related information.
+ * @hide
*/
public static final String ICAM_APP_INFO_SERVICE = CAM_PACKAGE + "ICamAppInfoService";
/**
* Interface for changes on conditional access module app related information.
+ * @hide
*/
public static final String ICAM_APP_INFO_LISTENER = CAM_PACKAGE + "ICamAppInfoListener";
/**
* Interface for handling conditional access module related information.
+ * @hide
*/
public static final String ICAM_MONITORING_SERVICE = CAM_PACKAGE + "ICamMonitoringService";
/**
* Interface for changes on conditional access module related information.
+ * @hide
*/
public static final String ICAM_INFO_LISTENER = CAM_PACKAGE + "ICamInfoListener";
/**
* Interface for handling control of CI+ operations.
+ * @hide
*/
public static final String ICI_OPERATOR_INTERFACE = CAM_PACKAGE + "ICiOperatorInterface";
/**
* Interfaces for changes on CI+ operations.
+ * @hide
*/
public static final String ICI_OPERATOR_LISTENER = CAM_PACKAGE + "ICiOperatorListener";
/**
* Interface for handling conditional access module profile related information.
+ * @hide
*/
public static final String ICAM_PROFILE_INTERFACE = CAM_PACKAGE + "ICamProfileInterface";
/**
* Interface for handling conditional access module DRM related information.
+ * @hide
*/
public static final String ICONTENT_CONTROL_SERVICE = CAM_PACKAGE + "IContentControlService";
/**
* Interface for changes on DRM.
+ * @hide
*/
public static final String ICAM_DRM_INFO_LISTENER = CAM_PACKAGE + "ICamDrmInfoListener";
/**
* Interface for handling conditional access module pin related information.
+ * @hide
*/
public static final String ICAM_PIN_SERVICE = CAM_PACKAGE + "ICamPinService";
/**
* Interface for changes on conditional access module pin capability.
+ * @hide
*/
public static final String ICAM_PIN_CAPABILITY_LISTENER = CAM_PACKAGE
+ "ICamPinCapabilityListener";
/**
* Interface for changes on conditional access module pin status.
+ * @hide
*/
public static final String ICAM_PIN_STATUS_LISTENER = CAM_PACKAGE + "ICamPinStatusListener";
/**
* Interface for handling conditional access module host control service.
+ * @hide
*/
public static final String ICAM_HOST_CONTROL_SERVICE = CAM_PACKAGE + "ICamHostControlService";
/**
* Interface for handling conditional access module ask release reply.
+ * @hide
*/
public static final String ICAM_HOST_CONTROL_ASK_RELEASE_REPLY_CALLBACK = CAM_PACKAGE
+ "ICamHostControlAskReleaseReplyCallback";
/**
* Interface for changes on conditional access module host control service.
+ * @hide
*/
public static final String ICAM_HOST_CONTROL_INFO_LISTENER = CAM_PACKAGE
+ "ICamHostControlInfoListener";
/**
* Interface for handling conditional access module host control service tune_quietly_flag.
+ * @hide
*/
public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG = CAM_PACKAGE
+ "ICamHostControlTuneQuietlyFlag";
/**
* Interface for changes on conditional access module host control service tune_quietly_flag.
+ * @hide
*/
public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG_LISTENER = CAM_PACKAGE
+ "ICamHostControlTuneQuietlyFlagListener";
/**
* Interface for handling conditional access module multi media interface.
+ * @hide
*/
public static final String IMMI_INTERFACE = CAM_PACKAGE + "IMmiInterface";
/**
* Interface for controlling conditional access module multi media session.
+ * @hide
*/
public static final String IMMI_SESSION = CAM_PACKAGE + "IMmiSession";
/**
* Interface for changes on conditional access module multi media session status.
+ * @hide
*/
public static final String IMMI_STATUS_CALLBACK = CAM_PACKAGE + "IMmiStatusCallback";
/**
* Interface for changes on conditional access app info related to entering menu.
+ * @hide
*/
public static final String IENTER_MENU_ERROR_CALLBACK = CAM_PACKAGE + "IEnterMenuErrorCallback";
/**
* Interface for handling RRT downloadable rating data.
+ * @hide
*/
public static final String IDOWNLOADABLE_RATING_TABLE_MONITOR = RATING_PACKAGE
+ "IDownloadableRatingTableMonitor";
/**
* Interface for handling RRT rating related information.
+ * @hide
*/
public static final String IRATING_INTERFACE = RATING_PACKAGE + "IRatingInterface";
/**
* Interface for handling PMT rating related information.
+ * @hide
*/
public static final String IPMT_RATING_INTERFACE = RATING_PACKAGE + "IPmtRatingInterface";
/**
* Interface for changes on PMT rating related information.
+ * @hide
*/
public static final String IPMT_RATING_LISTENER = RATING_PACKAGE + "IPmtRatingListener";
/**
* Interface for handling IVBI rating related information.
+ * @hide
*/
public static final String IVBI_RATING_INTERFACE = RATING_PACKAGE + "IVbiRatingInterface";
/**
* Interface for changes on IVBI rating related information.
+ * @hide
*/
public static final String IVBI_RATING_LISTENER = RATING_PACKAGE + "IVbiRatingListener";
/**
* Interface for handling program rating related information.
+ * @hide
*/
public static final String IPROGRAM_INFO = RATING_PACKAGE + "IProgramInfo";
/**
* Interface for changes on program rating related information.
+ * @hide
*/
public static final String IPROGRAM_INFO_LISTENER = RATING_PACKAGE + "IProgramInfoListener";
/**
* Interface for getting broadcast time related information.
+ * @hide
*/
- public static final String BROADCAST_TIME = TIME_PACKAGE + "BroadcastTime";
+ public static final String IBROADCAST_TIME = TIME_PACKAGE + "BroadcastTime";
/**
* Interface for handling data service signal information on teletext.
+ * @hide
*/
public static final String IDATA_SERVICE_SIGNAL_INFO = TELETEXT_PACKAGE
+ "IDataServiceSignalInfo";
/**
* Interface for changes on data service signal information on teletext.
+ * @hide
*/
public static final String IDATA_SERVICE_SIGNAL_INFO_LISTENER = TELETEXT_PACKAGE
+ "IDataServiceSignalInfoListener";
/**
* Interface for handling teletext page information.
+ * @hide
*/
public static final String ITELETEXT_PAGE_SUB_CODE = TELETEXT_PACKAGE + "ITeletextPageSubCode";
/**
* Interface for handling scan background service update.
+ * @hide
*/
public static final String ISCAN_BACKGROUND_SERVICE_UPDATE = SCAN_BSU_PACKAGE
+ "IScanBackgroundServiceUpdate";
/**
* Interface for changes on background service update
+ * @hide
*/
public static final String ISCAN_BACKGROUND_SERVICE_UPDATE_LISTENER = SCAN_BSU_PACKAGE
+ "IScanBackgroundServiceUpdateListener";
/**
* Interface for generating client token.
+ * @hide
*/
public static final String ICLIENT_TOKEN = CLIENT_TOKEN_PACKAGE + "IClientToken";
/**
* Interfaces for handling screen mode information.
+ * @hide
*/
public static final String ISCREEN_MODE_SETTINGS = SCREEN_MODE_PACKAGE + "IScreenModeSettings";
/**
* Interfaces for handling HDMI signal information update.
+ * @hide
*/
public static final String IHDMI_SIGNAL_INTERFACE = SIGNAL_PACKAGE + "IHdmiSignalInterface";
/**
* Interfaces for changes on HDMI signal information update.
+ * @hide
*/
public static final String IHDMI_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ "IHdmiSignalInfoListener";
/**
* Interfaces for handling audio signal information update.
+ * @hide
*/
public static final String IAUDIO_SIGNAL_INFO = SIGNAL_PACKAGE + "IAudioSignalInfo";
/**
* Interfaces for handling analog audio signal information update.
+ * @hide
*/
public static final String IANALOG_AUDIO_INFO = SIGNAL_PACKAGE + "IAnalogAudioInfo";
/**
* Interfaces for change on audio signal information update.
+ * @hide
*/
public static final String IAUDIO_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ "IAudioSignalInfoListener";
/**
* Interfaces for handling video signal information update.
+ * @hide
*/
public static final String IVIDEO_SIGNAL_INFO = SIGNAL_PACKAGE + "IVideoSignalInfo";
/**
* Interfaces for changes on video signal information update.
+ * @hide
*/
public static final String IVIDEO_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ "IVideoSignalInfoListener";
/**
* Interfaces for handling service database updates.
+ * @hide
*/
public static final String ISERVICE_LIST_EDIT = SERVICE_DATABASE_PACKAGE + "IServiceListEdit";
/**
* Interfaces for changes on service database updates.
+ * @hide
*/
public static final String ISERVICE_LIST_EDIT_LISTENER = SERVICE_DATABASE_PACKAGE
+ "IServiceListEditListener";
/**
* Interfaces for getting service database related information.
+ * @hide
*/
public static final String ISERVICE_LIST = SERVICE_DATABASE_PACKAGE + "IServiceList";
/**
* Interfaces for transferring service database related information.
+ * @hide
*/
public static final String ISERVICE_LIST_TRANSFER_INTERFACE = SERVICE_DATABASE_PACKAGE
+ "IServiceListTransferInterface";
/**
* Interfaces for exporting service database session.
+ * @hide
*/
public static final String ISERVICE_LIST_EXPORT_SESSION = SERVICE_DATABASE_PACKAGE
+ "IServiceListExportSession";
/**
* Interfaces for changes on exporting service database session.
+ * @hide
*/
public static final String ISERVICE_LIST_EXPORT_LISTENER = SERVICE_DATABASE_PACKAGE
+ "IServiceListExportListener";
/**
* Interfaces for importing service database session.
+ * @hide
*/
public static final String ISERVICE_LIST_IMPORT_SESSION = SERVICE_DATABASE_PACKAGE
+ "IServiceListImportSession";
/**
* Interfaces for changes on importing service database session.
+ * @hide
*/
public static final String ISERVICE_LIST_IMPORT_LISTENER = SERVICE_DATABASE_PACKAGE
+ "IServiceListImportListener";
/**
* Interfaces for setting channel list resources.
+ * @hide
*/
public static final String ISERVICE_LIST_SET_CHANNEL_LIST_SESSION = SERVICE_DATABASE_PACKAGE
+ "IServiceListSetChannelListSession";
/**
* Interfaces for changes on setting channel list resources.
+ * @hide
*/
public static final String ISERVICE_LIST_SET_CHANNEL_LIST_LISTENER = SERVICE_DATABASE_PACKAGE
+ "IServiceListSetChannelListListener";
/**
* Interfaces for transferring channel list resources.
+ * @hide
*/
public static final String ICHANNEL_LIST_TRANSFER = SERVICE_DATABASE_PACKAGE
+ "IChannelListTransfer";
/**
* Interfaces for record contents updates.
+ * @hide
*/
public static final String IRECORDED_CONTENTS = PVR_PACKAGE + "IRecordedContents";
/**
* Interfaces for changes on deleting record contents.
+ * @hide
*/
public static final String IDELETE_RECORDED_CONTENTS_CALLBACK = PVR_PACKAGE
+ "IDeleteRecordedContentsCallback";
/**
* Interfaces for changes on getting record contents.
+ * @hide
*/
public static final String IGET_INFO_RECORDED_CONTENTS_CALLBACK = PVR_PACKAGE
+ "IGetInfoRecordedContentsCallback";
/**
* Interfaces for monitoring present event information.
+ * @hide
*/
public static final String IEVENT_MONITOR = EVENT_PACKAGE + "IEventMonitor";
/**
* Interfaces for changes on present event information.
+ * @hide
*/
public static final String IEVENT_MONITOR_LISTENER = EVENT_PACKAGE + "IEventMonitorListener";
/**
* Interfaces for handling download event information.
+ * @hide
*/
public static final String IEVENT_DOWNLOAD = EVENT_PACKAGE + "IEventDownload";
/**
* Interfaces for changes on downloading event information.
+ * @hide
*/
public static final String IEVENT_DOWNLOAD_LISTENER = EVENT_PACKAGE + "IEventDownloadListener";
/**
* Interfaces for handling download event information for DVB and DTMB.
+ * @hide
*/
public static final String IEVENT_DOWNLOAD_SESSION = EVENT_PACKAGE + "IEventDownloadSession";
/**
* Interfaces for handling analog color system.
+ * @hide
*/
public static final String IANALOG_ATTRIBUTE_INTERFACE = ANALOG_PACKAGE
+ "IAnalogAttributeInterface";
/**
* Interfaces for monitoring channel tuned information.
+ * @hide
*/
public static final String ICHANNEL_TUNED_INTERFACE = TUNE_PACKAGE + "IChannelTunedInterface";
/**
* Interfaces for changes on channel tuned information.
+ * @hide
*/
public static final String ICHANNEL_TUNED_LISTENER = TUNE_PACKAGE + "IChannelTunedListener";
/**
* Interfaces for handling tuner frontend signal info.
+ * @hide
*/
public static final String ITUNER_FRONTEND_SIGNAL_INFO_INTERFACE = SIGNAL_PACKAGE
+ "ITunerFrontendSignalInfoInterface";
/**
* Interfaces for changes on tuner frontend signal info.
+ * @hide
*/
public static final String ITUNER_FRONTEND_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ "ITunerFrontendSignalInfoListener";
/**
* Interfaces for handling mux tune operations.
+ * @hide
*/
public static final String IMUX_TUNE_SESSION = TUNE_PACKAGE + "IMuxTuneSession";
/**
* Interfaces for initing mux tune session.
+ * @hide
*/
public static final String IMUX_TUNE = TUNE_PACKAGE + "IMuxTune";
@@ -506,7 +718,7 @@ public class TvInputServiceExtensionManager {
IVBI_RATING_LISTENER,
IPROGRAM_INFO,
IPROGRAM_INFO_LISTENER,
- BROADCAST_TIME,
+ IBROADCAST_TIME,
IDATA_SERVICE_SIGNAL_INFO,
IDATA_SERVICE_SIGNAL_INFO_LISTENER,
ITELETEXT_PAGE_SUB_CODE,
@@ -586,7 +798,8 @@ public class TvInputServiceExtensionManager {
*
* @hide
*/
- public int registerExtensionIBinder(@NonNull String extensionName,
+ @RegisterResult
+ public int registerExtensionIBinder(@StandardizedExtensionName @NonNull String extensionName,
@NonNull IBinder binder) {
if (!checkIsStandardizedInterfaces(extensionName)) {
return REGISTER_FAIL_NAME_NOT_STANDARDIZED;
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 0fb3049f63d8..23dd9b7aee37 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -48,7 +48,9 @@ cc_library_shared {
"libhwui_internal_headers",
],
- static_libs: ["libarect"],
+ static_libs: [
+ "libarect",
+ ],
host_supported: true,
target: {
@@ -60,6 +62,11 @@ cc_library_shared {
shared_libs: [
"libandroid",
],
+ static_libs: [
+ "libstatslog_hwui",
+ "libstatspull_lazy",
+ "libstatssocket_lazy",
+ ],
version_script: "libjnigraphics.map.txt",
},
host: {
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index e18b4a9d2420..cb95bcf5d2b1 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -14,18 +14,9 @@
* limitations under the License.
*/
-#include "aassetstreamadaptor.h"
-
-#include <android/asset_manager.h>
-#include <android/bitmap.h>
-#include <android/data_space.h>
-#include <android/imagedecoder.h>
#include <MimeType.h>
-#include <android/rect.h>
-#include <hwui/ImageDecoder.h>
-#include <log/log.h>
-#include <SkAndroidCodec.h>
#include <SkAlphaType.h>
+#include <SkAndroidCodec.h>
#include <SkCodec.h>
#include <SkCodecAnimation.h>
#include <SkColorSpace.h>
@@ -35,14 +26,24 @@
#include <SkRefCnt.h>
#include <SkSize.h>
#include <SkStream.h>
-#include <utils/Color.h>
-
+#include <android/asset_manager.h>
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <android/imagedecoder.h>
+#include <android/rect.h>
#include <fcntl.h>
-#include <limits>
-#include <optional>
+#include <hwui/ImageDecoder.h>
+#include <log/log.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <utils/Color.h>
+#include <utils/StatsUtils.h>
+
+#include <limits>
+#include <optional>
+
+#include "aassetstreamadaptor.h"
using namespace android;
@@ -400,9 +401,7 @@ size_t AImageDecoder_getMinimumStride(AImageDecoder* decoder) {
return info.minRowBytes();
}
-int AImageDecoder_decodeImage(AImageDecoder* decoder,
- void* pixels, size_t stride,
- size_t size) {
+int AImageDecoder_decodeImage(AImageDecoder* decoder, void* pixels, size_t stride, size_t size) {
if (!decoder || !pixels || !stride) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
@@ -419,7 +418,13 @@ int AImageDecoder_decodeImage(AImageDecoder* decoder,
return ANDROID_IMAGE_DECODER_FINISHED;
}
- return ResultToErrorCode(imageDecoder->decode(pixels, stride));
+ const auto result = ResultToErrorCode(imageDecoder->decode(pixels, stride));
+
+ if (result == ANDROID_IMAGE_DECODER_SUCCESS) {
+ uirenderer::logBitmapDecode(imageDecoder->getOutputInfo(), false);
+ }
+
+ return result;
}
void AImageDecoder_delete(AImageDecoder* decoder) {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index f1103e19c90e..992f581a8a70 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -896,7 +896,8 @@ public class RescueParty {
int systemUserId = UserHandle.SYSTEM.getIdentifier();
int[] userIds = { systemUserId };
try {
- for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) {
+ for (File file : FileUtils.listFilesOrEmpty(
+ Environment.getDataSystemDeviceProtectedDirectory())) {
try {
final int userId = Integer.parseInt(file.getName());
if (userId != systemUserId) {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 8277e573e7c2..311def80f248 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -499,8 +499,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
// Check if the package is listed among the system modules or is an
// APK inside an updatable APEX.
try {
- final PackageInfo pkg = mContext.getPackageManager()
- .getPackageInfo(packageName, 0 /* flags */);
+ PackageManager pm = mContext.getPackageManager();
+ final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */);
String apexPackageName = pkg.getApexPackageName();
if (apexPackageName != null) {
packageName = apexPackageName;
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index 2acedd56e505..be339cdb75ab 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -300,6 +300,31 @@ public class PackageWatchdog {
sPackageWatchdog = this;
}
+ /**
+ * Creating this temp constructor to match module constructor.
+ * Note: To be only used in tests.
+ * Creates a PackageWatchdog that allows injecting dependencies,
+ * except for connectivity module connector.
+ */
+ @VisibleForTesting
+ PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler,
+ Handler longTaskHandler, ExplicitHealthCheckController controller,
+ SystemClock clock) {
+ mContext = context;
+ mPolicyFile = policyFile;
+ mShortTaskHandler = shortTaskHandler;
+ mLongTaskHandler = longTaskHandler;
+ mHealthCheckController = controller;
+ mConnectivityModuleConnector = ConnectivityModuleConnector.getInstance();
+ mSystemClock = clock;
+ mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
+ mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+ DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS);
+
+ loadFromFile();
+ sPackageWatchdog = this;
+ }
+
/** Creates or gets singleton instance of PackageWatchdog. */
public static @NonNull PackageWatchdog getInstance(@NonNull Context context) {
synchronized (sPackageWatchdogLock) {
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
index 72cf40387a25..49acc1d44144 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -115,7 +115,7 @@ interface PreferenceScreenCreator : PreferenceScreenMetadata, PreferenceScreenPr
fun isFlagEnabled(context: Context): Boolean = true
val preferenceBindingFactory: PreferenceBindingFactory
- get() = DefaultPreferenceBindingFactory
+ get() = PreferenceBindingFactory.defaultFactory
override fun createPreferenceScreen(factory: PreferenceScreenFactory) =
factory.getOrCreatePreferenceScreen().apply {
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
index 87c289f48ff0..51b9aac029a5 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
@@ -45,10 +45,15 @@ interface PreferenceBindingFactory {
/** Returns the [PreferenceBinding] associated with the [PreferenceMetadata]. */
fun getPreferenceBinding(metadata: PreferenceMetadata): PreferenceBinding?
+
+ companion object {
+ /** Default preference binding factory. */
+ @JvmStatic var defaultFactory: PreferenceBindingFactory = DefaultPreferenceBindingFactory()
+ }
}
/** Default [PreferenceBindingFactory]. */
-object DefaultPreferenceBindingFactory : PreferenceBindingFactory {
+open class DefaultPreferenceBindingFactory : PreferenceBindingFactory {
override fun getPreferenceBinding(metadata: PreferenceMetadata) =
metadata as? PreferenceBinding
@@ -66,5 +71,6 @@ class KeyedPreferenceBindingFactory(private val bindings: Map<String, Preference
PreferenceBindingFactory {
override fun getPreferenceBinding(metadata: PreferenceMetadata) =
- bindings[metadata.key] ?: DefaultPreferenceBindingFactory.getPreferenceBinding(metadata)
+ bindings[metadata.key]
+ ?: PreferenceBindingFactory.defaultFactory.getPreferenceBinding(metadata)
}
diff --git a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt
index f3142d031aa9..00bad5203f07 100644
--- a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt
+++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt
@@ -25,7 +25,7 @@ import com.android.settingslib.metadata.PreferenceMetadata
/** Creates [Preference] widget and binds with metadata. */
@VisibleForTesting
fun <P : Preference> PreferenceMetadata.createAndBindWidget(context: Context): P {
- val binding = DefaultPreferenceBindingFactory.getPreferenceBinding(this)
+ val binding = PreferenceBindingFactory.defaultFactory.getPreferenceBinding(this)!!
return (binding.createWidget(context) as P).also {
if (this is PersistentPreference<*>) {
storage(context)?.let { keyValueStore ->
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
index 6578eb7d50a6..c36ade979d47 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -22,14 +22,20 @@ import androidx.annotation.NonNull;
import androidx.preference.DropDownPreference;
import androidx.preference.PreferenceViewHolder;
-public class RestrictedDropDownPreference extends DropDownPreference {
- RestrictedPreferenceHelper mHelper;
+public class RestrictedDropDownPreference extends DropDownPreference implements
+ RestrictedPreferenceHelperProvider {
+ private final RestrictedPreferenceHelper mHelper;
public RestrictedDropDownPreference(@NonNull Context context) {
super(context);
mHelper = new RestrictedPreferenceHelper(context, this, null);
}
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedInterface.kt b/packages/SettingsLib/src/com/android/settingslib/RestrictedInterface.kt
deleted file mode 100644
index 14f9a19ee753..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedInterface.kt
+++ /dev/null
@@ -1,49 +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.settingslib
-
-import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
-
-interface RestrictedInterface {
- fun useAdminDisabledSummary(useSummary: Boolean)
-
- fun checkRestrictionAndSetDisabled(userRestriction: String)
-
- fun checkRestrictionAndSetDisabled(userRestriction: String, userId: Int)
-
- /**
- * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
- * package. Marks the preference as disabled if so.
- *
- * @param settingIdentifier The key identifying the setting
- * @param packageName the package to check the settingIdentifier for
- */
- fun checkEcmRestrictionAndSetDisabled(
- settingIdentifier: String,
- packageName: String
- )
-
- val isDisabledByAdmin: Boolean
-
- fun setDisabledByAdmin(admin: EnforcedAdmin?)
-
- val isDisabledByEcm: Boolean
-
- val uid: Int
-
- val packageName: String?
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 495410b0a8ae..332042a5c4f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -34,7 +34,9 @@ import com.android.settingslib.widget.TwoTargetPreference;
* Preference class that supports being disabled by a user restriction
* set by a device admin.
*/
-public class RestrictedPreference extends TwoTargetPreference {
+public class RestrictedPreference extends TwoTargetPreference implements
+ RestrictedPreferenceHelperProvider {
+
RestrictedPreferenceHelper mHelper;
public RestrictedPreference(Context context, AttributeSet attrs,
@@ -67,6 +69,11 @@ public class RestrictedPreference extends TwoTargetPreference {
}
@Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
+ @Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelperProvider.kt b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelperProvider.kt
new file mode 100644
index 000000000000..f2860845e3d3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelperProvider.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.settingslib
+
+/** Provider of [RestrictedPreferenceHelper]. */
+interface RestrictedPreferenceHelperProvider {
+
+ /** Returns the [RestrictedPreferenceHelper] applied to the preference. */
+ fun getRestrictedPreferenceHelper(): RestrictedPreferenceHelper
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
index c52c7ea7328b..573869db5073 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
@@ -32,8 +32,9 @@ import com.android.settingslib.widget.SelectorWithWidgetPreference;
/**
* Selector with widget preference that can be disabled by a device admin using a user restriction.
*/
-public class RestrictedSelectorWithWidgetPreference extends SelectorWithWidgetPreference {
- private RestrictedPreferenceHelper mHelper;
+public class RestrictedSelectorWithWidgetPreference extends SelectorWithWidgetPreference implements
+ RestrictedPreferenceHelperProvider {
+ private final RestrictedPreferenceHelper mHelper;
/**
* Perform inflation from XML and apply a class-specific base style.
@@ -82,8 +83,11 @@ public class RestrictedSelectorWithWidgetPreference extends SelectorWithWidgetPr
*/
public RestrictedSelectorWithWidgetPreference(@NonNull Context context) {
this(context, null);
- mHelper =
- new RestrictedPreferenceHelper(context, /* preference= */ this, /* attrs= */ null);
+ }
+
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java
index 1dc5281c266c..b690816a6fb6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java
@@ -19,7 +19,6 @@ package com.android.settingslib;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Process;
-import android.os.UserHandle;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
@@ -34,8 +33,10 @@ import com.android.settingslib.widget.SliderPreference;
* Slide Preference that supports being disabled by a user restriction
* set by a device admin.
*/
-public class RestrictedSliderPreference extends SliderPreference implements RestrictedInterface {
- RestrictedPreferenceHelper mHelper;
+public class RestrictedSliderPreference extends SliderPreference implements
+ RestrictedPreferenceHelperProvider {
+
+ private final RestrictedPreferenceHelper mHelper;
public RestrictedSliderPreference(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, @Nullable String packageName, int uid) {
@@ -66,6 +67,11 @@ public class RestrictedSliderPreference extends SliderPreference implements Rest
}
@Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
+ @Override
public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
@@ -81,12 +87,12 @@ public class RestrictedSliderPreference extends SliderPreference implements Rest
@Override
public void setEnabled(boolean enabled) {
- if (enabled && isDisabledByAdmin()) {
+ if (enabled && mHelper.isDisabledByAdmin()) {
mHelper.setDisabledByAdmin(null);
return;
}
- if (enabled && isDisabledByEcm()) {
+ if (enabled && mHelper.isDisabledByEcm()) {
mHelper.setDisabledByEcm(null);
return;
}
@@ -95,55 +101,6 @@ public class RestrictedSliderPreference extends SliderPreference implements Rest
}
@Override
- public void useAdminDisabledSummary(boolean useSummary) {
- mHelper.useAdminDisabledSummary(useSummary);
- }
-
- @Override
- public void checkRestrictionAndSetDisabled(@NonNull String userRestriction) {
- mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
- }
-
- @Override
- public void checkRestrictionAndSetDisabled(@NonNull String userRestriction, int userId) {
- mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
- }
-
- @Override
- public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
- @NonNull String packageName) {
- mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
- }
-
- @Override
- public void setDisabledByAdmin(@Nullable RestrictedLockUtils.EnforcedAdmin admin) {
- if (mHelper.setDisabledByAdmin(admin)) {
- notifyChanged();
- }
- }
-
- @Override
- public boolean isDisabledByAdmin() {
- return mHelper.isDisabledByAdmin();
- }
-
- @Override
- public boolean isDisabledByEcm() {
- return mHelper.isDisabledByEcm();
- }
-
- @Override
- public int getUid() {
- return mHelper != null ? mHelper.uid : Process.INVALID_UID;
- }
-
- @Override
- @Nullable
- public String getPackageName() {
- return mHelper != null ? mHelper.packageName : null;
- }
-
- @Override
protected void onAttachedToHierarchy(@NonNull PreferenceManager preferenceManager) {
mHelper.onAttachedToHierarchy();
super.onAttachedToHierarchy(preferenceManager);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index fffbb547c662..727dbe1019ae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -45,8 +45,9 @@ import androidx.preference.SwitchPreferenceCompat;
* Version of SwitchPreferenceCompat that can be disabled by a device admin
* using a user restriction.
*/
-public class RestrictedSwitchPreference extends SwitchPreferenceCompat {
- RestrictedPreferenceHelper mHelper;
+public class RestrictedSwitchPreference extends SwitchPreferenceCompat implements
+ RestrictedPreferenceHelperProvider {
+ private final RestrictedPreferenceHelper mHelper;
AppOpsManager mAppOpsManager;
boolean mUseAdditionalSummary = false;
CharSequence mRestrictedSwitchSummary;
@@ -98,6 +99,11 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat {
this(context, null);
}
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
@VisibleForTesting
public void setAppOps(AppOpsManager appOps) {
mAppOpsManager = appOps;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java
index 0096015aa875..34e4d8f7346f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java
@@ -22,14 +22,16 @@ import android.content.Context;
import android.os.UserHandle;
import android.util.AttributeSet;
+import androidx.annotation.NonNull;
import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
/** Top level preference that can be disabled by a device admin using a user restriction. */
-public class RestrictedTopLevelPreference extends Preference {
- private RestrictedPreferenceHelper mHelper;
+public class RestrictedTopLevelPreference extends Preference implements
+ RestrictedPreferenceHelperProvider {
+ private final RestrictedPreferenceHelper mHelper;
public RestrictedTopLevelPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
@@ -51,6 +53,11 @@ public class RestrictedTopLevelPreference extends Preference {
}
@Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
+ @Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
index 79dabf029c49..5d2a1669e24a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
@@ -41,7 +41,7 @@ class ZenIconKeys {
private static final ImmutableMap<Integer, ZenIcon.Key> TYPE_DEFAULTS = ImmutableMap.of(
AutomaticZenRule.TYPE_UNKNOWN,
- ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_unknown),
+ ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_special_dnd),
AutomaticZenRule.TYPE_OTHER,
ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_other),
AutomaticZenRule.TYPE_SCHEDULE_TIME,
@@ -61,7 +61,7 @@ class ZenIconKeys {
);
private static final ZenIcon.Key FOR_UNEXPECTED_TYPE =
- ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_unknown);
+ ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_special_dnd);
/** Default icon descriptors per mode {@link AutomaticZenRule.Type}. */
static ZenIcon.Key forType(@AutomaticZenRule.Type int ruleType) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1919572ff571..2c8c261fa8f8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -715,6 +715,9 @@
<uses-permission android:name="android.permission.UWB_PRIVILEGED" />
<uses-permission android:name="android.permission.UWB_RANGING" />
+ <!-- Permission required for CTS test - CtsRangingTestCases -->
+ <uses-permission android:name="android.permission.RANGING" />
+
<!-- Permission required for CTS test - CtsAlarmManagerTestCases -->
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f0e1b437ec51..e0117368515b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1013,6 +1013,7 @@
android:autoRemoveFromRecents="true"
android:launchMode="singleTop"
android:showForAllUsers="true"
+ android:turnScreenOn="true"
android:exported="false">
</activity>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 13a5248e193b..3bf3e24a2ba6 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -26,6 +26,16 @@ flag {
}
flag {
+ name: "user_encrypted_source"
+ namespace: "systemui"
+ description: "Get rid of the local cache and rely on UserManager.isUserUnlocked directly to determine whether user CE storage is encrypted."
+ bug: "333656491"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "modes_ui_dialog_paging"
namespace: "systemui"
description: "Add pagination to the Modes dialog in quick settings."
@@ -1578,6 +1588,13 @@ flag {
}
flag {
+ name: "show_clipboard_indication"
+ namespace: "systemui"
+ description: "Show indication text under the clipboard overlay when copied something"
+ bug: "361199935"
+}
+
+flag {
name: "media_projection_dialog_behind_lockscreen"
namespace: "systemui"
description: "Ensure MediaProjection Dialog appears behind the lockscreen"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index 0b15d230dee2..cbe11a3f2f60 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -268,7 +268,8 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner
// skip changes that we didn't wrap
if (!leashMap.containsKey(change.getLeash())) continue;
// Only make the update if we are closing Desktop tasks.
- if (change.getTaskInfo().isFreeform() && isClosingMode(change.getMode())) {
+ if (change.getTaskInfo() != null && change.getTaskInfo().isFreeform()
+ && isClosingMode(change.getMode())) {
startTransaction.setAlpha(leashMap.get(launcherChange.getLeash()), 0f);
return;
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index 4cf264253bf8..fdb4871423c3 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -20,6 +20,7 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
+import android.graphics.PointF
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.drawable.GradientDrawable
@@ -33,13 +34,13 @@ import android.view.ViewOverlay
import android.view.animation.Interpolator
import android.window.WindowAnimationState
import com.android.app.animation.Interpolators.LINEAR
-import com.android.app.animation.MathUtils.max
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.dynamicanimation.animation.SpringAnimation
import com.android.internal.dynamicanimation.animation.SpringForce
import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
import java.util.concurrent.Executor
import kotlin.math.abs
+import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
@@ -91,6 +92,14 @@ class TransitionAnimator(
)
}
+ /**
+ * Similar to [getProgress] above, bug the delay and duration are expressed as percentages
+ * of the animation duration (between 0f and 1f).
+ */
+ internal fun getProgress(linearProgress: Float, delay: Float, duration: Float): Float {
+ return getProgressInternal(totalDuration = 1f, linearProgress, delay, duration)
+ }
+
private fun getProgressInternal(
totalDuration: Float,
linearProgress: Float,
@@ -262,10 +271,10 @@ class TransitionAnimator(
var centerY: Float,
var scale: Float = 0f,
- // Cached values.
- var previousCenterX: Float = -1f,
- var previousCenterY: Float = -1f,
- var previousScale: Float = -1f,
+ // Update flags (used to decide whether it's time to update the transition state).
+ var isCenterXUpdated: Boolean = false,
+ var isCenterYUpdated: Boolean = false,
+ var isScaleUpdated: Boolean = false,
// Completion flags.
var isCenterXDone: Boolean = false,
@@ -286,6 +295,7 @@ class TransitionAnimator(
override fun setValue(state: SpringState, value: Float) {
state.centerX = value
+ state.isCenterXUpdated = true
}
},
CENTER_Y {
@@ -295,6 +305,7 @@ class TransitionAnimator(
override fun setValue(state: SpringState, value: Float) {
state.centerY = value
+ state.isCenterYUpdated = true
}
},
SCALE {
@@ -304,6 +315,7 @@ class TransitionAnimator(
override fun setValue(state: SpringState, value: Float) {
state.scale = value
+ state.isScaleUpdated = true
}
};
@@ -444,8 +456,8 @@ class TransitionAnimator(
* punching a hole in the [transition container][Controller.transitionContainer]) iff [drawHole]
* is true.
*
- * If [useSpring] is true, a multi-spring animation will be used instead of the default
- * interpolators.
+ * If [startVelocity] (expressed in pixels per second) is not null, a multi-spring animation
+ * using it for the initial momentum will be used instead of the default interpolators.
*/
fun startAnimation(
controller: Controller,
@@ -453,9 +465,9 @@ class TransitionAnimator(
windowBackgroundColor: Int,
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
- useSpring: Boolean = false,
+ startVelocity: PointF? = null,
): Animation {
- if (!controller.isLaunching || useSpring) checkReturnAnimationFrameworkFlag()
+ if (!controller.isLaunching || startVelocity != null) checkReturnAnimationFrameworkFlag()
// We add an extra layer with the same color as the dialog/app splash screen background
// color, which is usually the same color of the app background. We first fade in this layer
@@ -474,7 +486,7 @@ class TransitionAnimator(
windowBackgroundLayer,
fadeWindowBackgroundLayer,
drawHole,
- useSpring,
+ startVelocity,
)
.apply { start() }
}
@@ -487,7 +499,7 @@ class TransitionAnimator(
windowBackgroundLayer: GradientDrawable,
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
- useSpring: Boolean = false,
+ startVelocity: PointF? = null,
): Animation {
val transitionContainer = controller.transitionContainer
val transitionContainerOverlay = transitionContainer.overlay
@@ -504,11 +516,12 @@ class TransitionAnimator(
openingWindowSyncView != null &&
openingWindowSyncView.viewRootImpl != controller.transitionContainer.viewRootImpl
- return if (useSpring && springTimings != null && springInterpolators != null) {
+ return if (startVelocity != null && springTimings != null && springInterpolators != null) {
createSpringAnimation(
controller,
startState,
endState,
+ startVelocity,
windowBackgroundLayer,
transitionContainer,
transitionContainerOverlay,
@@ -693,6 +706,7 @@ class TransitionAnimator(
controller: Controller,
startState: State,
endState: State,
+ startVelocity: PointF,
windowBackgroundLayer: GradientDrawable,
transitionContainer: View,
transitionContainerOverlay: ViewGroupOverlay,
@@ -721,19 +735,20 @@ class TransitionAnimator(
fun updateProgress(state: SpringState) {
if (
- (!state.isCenterXDone && state.centerX == state.previousCenterX) ||
- (!state.isCenterYDone && state.centerY == state.previousCenterY) ||
- (!state.isScaleDone && state.scale == state.previousScale)
+ !(state.isCenterXUpdated || state.isCenterXDone) ||
+ !(state.isCenterYUpdated || state.isCenterYDone) ||
+ !(state.isScaleUpdated || state.isScaleDone)
) {
// Because all three springs use the same update method, we only actually update
- // when all values have changed, avoiding two redundant calls per frame.
+ // when all properties have received their new value (which could be unchanged from
+ // the previous one), avoiding two redundant calls per frame.
return
}
- // Update the latest values for the check above.
- state.previousCenterX = state.centerX
- state.previousCenterY = state.centerY
- state.previousScale = state.scale
+ // Reset the update flags.
+ state.isCenterXUpdated = false
+ state.isCenterYUpdated = false
+ state.isScaleUpdated = false
// Current scale-based values, that will be used to find the new animation bounds.
val width =
@@ -829,6 +844,7 @@ class TransitionAnimator(
}
setStartValue(startState.centerX)
+ setStartVelocity(startVelocity.x)
setMinValue(min(startState.centerX, endState.centerX))
setMaxValue(max(startState.centerX, endState.centerX))
@@ -850,6 +866,7 @@ class TransitionAnimator(
}
setStartValue(startState.centerY)
+ setStartVelocity(startVelocity.y)
setMinValue(min(startState.centerY, endState.centerY))
setMaxValue(max(startState.centerY, endState.centerY))
@@ -1057,15 +1074,13 @@ class TransitionAnimator(
interpolators = springInterpolators!!
val timings = springTimings!!
fadeInProgress =
- getProgressInternal(
- totalDuration = 1f,
+ getProgress(
linearProgress,
timings.contentBeforeFadeOutDelay,
timings.contentBeforeFadeOutDuration,
)
fadeOutProgress =
- getProgressInternal(
- totalDuration = 1f,
+ getProgress(
linearProgress,
timings.contentAfterFadeInDelay,
timings.contentAfterFadeInDuration,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 6d3039855077..87e9c427d695 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -42,7 +42,6 @@ import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
import com.android.systemui.communal.shared.model.CommunalBackgroundType
@@ -124,7 +123,7 @@ val sceneTransitions = transitions {
}
timestampRange(
startMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_DELAY_MS,
- endMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_END_MS
+ endMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_END_MS,
) {
fade(Communal.Elements.Grid)
}
@@ -187,14 +186,13 @@ fun CommunalContainer(
) {
scene(
CommunalScenes.Blank,
- userActions =
- mapOf(Swipe(SwipeDirection.Start, fromSource = Edge.End) to CommunalScenes.Communal)
+ userActions = mapOf(Swipe.Start(fromSource = Edge.End) to CommunalScenes.Communal),
) {
// This scene shows nothing only allowing for transitions to the communal scene.
Box(modifier = Modifier.fillMaxSize())
}
- val userActions = mapOf(Swipe(SwipeDirection.End) to CommunalScenes.Blank)
+ val userActions = mapOf(Swipe.End to CommunalScenes.Blank)
scene(CommunalScenes.Communal, userActions = userActions) {
CommunalScene(
@@ -257,13 +255,9 @@ fun SceneScope.CommunalScene(
/** Default background of the hub, a single color */
@Composable
-private fun BoxScope.DefaultBackground(
- colors: CommunalColors,
-) {
+private fun BoxScope.DefaultBackground(colors: CommunalColors) {
val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
- Box(
- modifier = Modifier.matchParentSize().background(Color(backgroundColor.toArgb())),
- )
+ Box(modifier = Modifier.matchParentSize().background(Color(backgroundColor.toArgb())))
}
/** Experimental hub background, static linear gradient */
@@ -273,7 +267,7 @@ private fun BoxScope.StaticLinearGradient() {
Box(
Modifier.matchParentSize()
.background(
- Brush.linearGradient(colors = listOf(colors.primary, colors.primaryContainer)),
+ Brush.linearGradient(colors = listOf(colors.primary, colors.primaryContainer))
)
)
BackgroundTopScrim()
@@ -288,7 +282,7 @@ private fun BoxScope.AnimatedLinearGradient() {
.background(colors.primary)
.animatedRadialGradientBackground(
toColor = colors.primary,
- fromColor = colors.primaryContainer.copy(alpha = 0.6f)
+ fromColor = colors.primaryContainer.copy(alpha = 0.6f),
)
)
BackgroundTopScrim()
@@ -324,9 +318,9 @@ fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color):
durationMillis = ANIMATION_DURATION_MS,
easing = CubicBezierEasing(0.33f, 0f, 0.67f, 1f),
),
- repeatMode = RepeatMode.Reverse
+ repeatMode = RepeatMode.Reverse,
),
- label = "radial gradient center fraction"
+ label = "radial gradient center fraction",
)
// Offset to place the center of the gradients offscreen. This is applied to both the
@@ -337,16 +331,9 @@ fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color):
val gradientRadius = (size.width / 2) + offsetPx
val totalHeight = size.height + 2 * offsetPx
- val leftCenter =
- Offset(
- x = -offsetPx,
- y = totalHeight * centerFraction - offsetPx,
- )
+ val leftCenter = Offset(x = -offsetPx, y = totalHeight * centerFraction - offsetPx)
val rightCenter =
- Offset(
- x = offsetPx + size.width,
- y = totalHeight * (1f - centerFraction) - offsetPx,
- )
+ Offset(x = offsetPx + size.width, y = totalHeight * (1f - centerFraction) - offsetPx)
// Right gradient
drawCircle(
@@ -354,7 +341,7 @@ fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color):
Brush.radialGradient(
colors = listOf(fromColor, toColor),
center = rightCenter,
- radius = gradientRadius
+ radius = gradientRadius,
),
center = rightCenter,
radius = gradientRadius,
@@ -367,7 +354,7 @@ fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color):
Brush.radialGradient(
colors = listOf(fromColor, toColor),
center = leftCenter,
- radius = gradientRadius
+ radius = gradientRadius,
),
center = leftCenter,
radius = gradientRadius,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 5b996704eb12..2af5ffaee7ed 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -18,23 +18,27 @@ package com.android.systemui.notifications.ui.composable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.layout.layoutId
import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
+import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.composable.Overlay
-import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
+import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.shade.ui.composable.SingleShadeMeasurePolicy
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
@@ -53,6 +57,8 @@ constructor(
private val statusBarIconController: StatusBarIconController,
private val shadeSession: SaveableSession,
private val stackScrollView: Lazy<NotificationScrollView>,
+ private val clockSection: DefaultClockSection,
+ private val clockInteractor: KeyguardClockInteractor,
) : Overlay {
override val key = Overlays.NotificationsShade
@@ -80,13 +86,28 @@ constructor(
OverlayShade(modifier = modifier, onScrimClicked = viewModel::onScrimClicked) {
Column {
- ExpandedShadeHeader(
- viewModelFactory = viewModel.shadeHeaderViewModelFactory,
- createTintedIconManager = tintedIconManagerFactory::create,
- createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
- statusBarIconController = statusBarIconController,
- modifier = Modifier.padding(horizontal = 16.dp),
- )
+ if (viewModel.showHeader) {
+ val burnIn = rememberBurnIn(clockInteractor)
+
+ CollapsedShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController =
+ batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ modifier =
+ Modifier.element(NotificationsShade.Elements.StatusBar)
+ .layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
+ )
+
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+ }
NotificationScrollingStack(
shadeSession = shadeSession,
@@ -110,3 +131,9 @@ constructor(
}
}
}
+
+object NotificationsShade {
+ object Elements {
+ val StatusBar = ElementKey("NotificationsShadeStatusBar")
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 67f412ed27ac..2cde6787f730 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -105,13 +105,17 @@ val SceneContainerTransitions = transitions {
// Overlay transitions
+ // TODO(b/376659778): Remove this transition once nested STLs are supported.
+ from(Scenes.Gone, to = Overlays.NotificationsShade) {
+ toNotificationsShadeTransition(translateClock = true)
+ }
to(Overlays.NotificationsShade) { toNotificationsShadeTransition() }
to(Overlays.QuickSettingsShade) { toQuickSettingsShadeTransition() }
from(Overlays.NotificationsShade, to = Overlays.QuickSettingsShade) {
notificationsShadeToQuickSettingsShadeTransition()
}
from(Scenes.Gone, to = Overlays.NotificationsShade, key = SlightlyFasterShadeCollapse) {
- toNotificationsShadeTransition(durationScale = 0.9)
+ toNotificationsShadeTransition(translateClock = true, durationScale = 0.9)
}
from(Scenes.Gone, to = Overlays.QuickSettingsShade, key = SlightlyFasterShadeCollapse) {
toQuickSettingsShadeTransition(durationScale = 0.9)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 23c4f12cb0ae..6bdb36331709 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -21,30 +21,42 @@ import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
import com.android.systemui.notifications.ui.composable.Notifications
+import com.android.systemui.notifications.ui.composable.NotificationsShade
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.composable.Shade
-import com.android.systemui.shade.ui.composable.ShadeHeader
import kotlin.time.Duration.Companion.milliseconds
-fun TransitionBuilder.toNotificationsShadeTransition(durationScale: Double = 1.0) {
+fun TransitionBuilder.toNotificationsShadeTransition(
+ translateClock: Boolean = false,
+ durationScale: Double = 1.0,
+) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
swipeSpec =
spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
)
+ // Ensure the clock isn't clipped by the shade outline during the transition from lockscreen.
+ sharedElement(
+ ClockElementKeys.smallClockElementKey,
+ elevateInContent = Overlays.NotificationsShade,
+ )
scaleSize(OverlayShade.Elements.Panel, height = 0f)
+ // TODO(b/376659778): This is a temporary hack to have a shared element transition with the
+ // lockscreen clock. Remove once nested STLs are supported.
+ if (!translateClock) {
+ translate(ClockElementKeys.smallClockElementKey)
+ }
+ // Avoid translating the status bar with the shade panel.
+ translate(NotificationsShade.Elements.StatusBar)
+ // Slide in the shade panel from the top edge.
translate(OverlayShade.Elements.Panel, Edge.Top)
fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
-
- fractionRange(start = .5f) {
- fade(ShadeHeader.Elements.Clock)
- fade(ShadeHeader.Elements.ExpandedContent)
- fade(ShadeHeader.Elements.PrivacyChip)
- fade(Notifications.Elements.NotificationScrim)
- }
+ fractionRange(start = .5f) { fade(Notifications.Elements.NotificationScrim) }
}
private val DefaultDuration = 300.milliseconds
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 63c5d7aed3e1..e7b66c5f0d2f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -52,6 +52,7 @@ import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.SharedElementTransformation
+import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.drawInContainer
import com.android.compose.ui.util.lerp
@@ -187,6 +188,7 @@ private fun Modifier.maybeElevateInContent(
state.transformationSpec
.transformations(key, content.key)
.shared
+ ?.transformation
?.elevateInContent == content.key &&
isSharedElement(stateByContent, state) &&
isSharedElementEnabled(key, state) &&
@@ -901,7 +903,7 @@ private fun shouldPlaceElement(
}
val sharedTransformation = sharedElementTransformation(element.key, transition)
- if (sharedTransformation?.enabled == false) {
+ if (sharedTransformation?.transformation?.enabled == false) {
return true
}
@@ -954,13 +956,13 @@ private fun isSharedElementEnabled(
element: ElementKey,
transition: TransitionState.Transition,
): Boolean {
- return sharedElementTransformation(element, transition)?.enabled ?: true
+ return sharedElementTransformation(element, transition)?.transformation?.enabled ?: true
}
internal fun sharedElementTransformation(
element: ElementKey,
transition: TransitionState.Transition,
-): SharedElementTransformation? {
+): TransformationWithRange<SharedElementTransformation>? {
val transformationSpec = transition.transformationSpec
val sharedInFromContent =
transformationSpec.transformations(element, transition.fromContent).shared
@@ -1244,7 +1246,7 @@ private inline fun <T> computeValue(
element: Element,
transition: TransitionState.Transition?,
contentValue: (Element.State) -> T,
- transformation: (ElementTransformations) -> PropertyTransformation<T>?,
+ transformation: (ElementTransformations) -> TransformationWithRange<PropertyTransformation<T>>?,
currentValue: () -> T,
isSpecified: (T) -> Boolean,
lerp: (T, T, Float) -> T,
@@ -1280,7 +1282,7 @@ private inline fun <T> computeValue(
checkNotNull(if (currentContent == toContent) toState else fromState)
val idleValue = contentValue(overscrollState)
val targetValue =
- with(propertySpec) {
+ with(propertySpec.transformation) {
layoutImpl.propertyTransformationScope.transform(
currentContent,
element.key,
@@ -1375,7 +1377,7 @@ private inline fun <T> computeValue(
val idleValue = contentValue(contentState)
val isEntering = content == toContent
val previewTargetValue =
- with(previewTransformation) {
+ with(previewTransformation.transformation) {
layoutImpl.propertyTransformationScope.transform(
content,
element.key,
@@ -1386,7 +1388,7 @@ private inline fun <T> computeValue(
val targetValueOrNull =
transformation?.let { transformation ->
- with(transformation) {
+ with(transformation.transformation) {
layoutImpl.propertyTransformationScope.transform(
content,
element.key,
@@ -1461,7 +1463,7 @@ private inline fun <T> computeValue(
val idleValue = contentValue(contentState)
val targetValue =
- with(transformation) {
+ with(transformation.transformation) {
layoutImpl.propertyTransformationScope.transform(
content,
element.key,
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 21d87e173728..dbf7d7b29834 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
@@ -405,7 +405,8 @@ data object Back : UserAction() {
}
/** The user swiped on the container. */
-data class Swipe(
+data class Swipe
+private constructor(
val direction: SwipeDirection,
val pointerCount: Int = 1,
val pointersType: PointerType? = null,
@@ -418,6 +419,42 @@ data class Swipe(
val Down = Swipe(SwipeDirection.Down)
val Start = Swipe(SwipeDirection.Start)
val End = Swipe(SwipeDirection.End)
+
+ fun Left(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Left, pointerCount, pointersType, fromSource)
+
+ fun Up(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Up, pointerCount, pointersType, fromSource)
+
+ fun Right(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Right, pointerCount, pointersType, fromSource)
+
+ fun Down(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Down, pointerCount, pointersType, fromSource)
+
+ fun Start(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Start, pointerCount, pointersType, fromSource)
+
+ fun End(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.End, pointerCount, pointersType, fromSource)
}
override fun resolve(layoutDirection: LayoutDirection): UserAction.Resolved {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index e1e2411da080..61332b61ed1b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -764,7 +764,8 @@ internal class MutableSceneTransitionLayoutStateImpl(
return@fastForEach
}
- state.transformationSpec.transformations.fastForEach { transformation ->
+ state.transformationSpec.transformations.fastForEach { transformationWithRange ->
+ val transformation = transformationWithRange.transformation
if (
transformation is SharedElementTransformation &&
transformation.elevateInContent != null
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index 8866fbfbf194..b083f79aebf5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -33,10 +33,10 @@ import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
import com.android.compose.animation.scene.transformation.OverscrollTranslate
import com.android.compose.animation.scene.transformation.PropertyTransformation
-import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
+import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.animation.scene.transformation.Translate
/** The transitions configuration of a [SceneTransitionLayout]. */
@@ -233,7 +233,7 @@ interface TransformationSpec {
val distance: UserActionDistance?
/** The list of [Transformation] applied to elements during this transition. */
- val transformations: List<Transformation>
+ val transformations: List<TransformationWithRange<*>>
companion object {
internal val Empty =
@@ -325,7 +325,7 @@ internal class TransformationSpecImpl(
override val progressSpec: AnimationSpec<Float>,
override val swipeSpec: SpringSpec<Float>?,
override val distance: UserActionDistance?,
- override val transformations: List<Transformation>,
+ override val transformations: List<TransformationWithRange<*>>,
) : TransformationSpec {
private val cache = mutableMapOf<ElementKey, MutableMap<ContentKey, ElementTransformations>>()
@@ -340,59 +340,65 @@ internal class TransformationSpecImpl(
element: ElementKey,
content: ContentKey,
): ElementTransformations {
- var shared: SharedElementTransformation? = null
- var offset: PropertyTransformation<Offset>? = null
- var size: PropertyTransformation<IntSize>? = null
- var drawScale: PropertyTransformation<Scale>? = null
- var alpha: PropertyTransformation<Float>? = null
-
- fun <T> onPropertyTransformation(
- root: PropertyTransformation<T>,
- current: PropertyTransformation<T> = root,
- ) {
- when (current) {
+ var shared: TransformationWithRange<SharedElementTransformation>? = null
+ var offset: TransformationWithRange<PropertyTransformation<Offset>>? = null
+ var size: TransformationWithRange<PropertyTransformation<IntSize>>? = null
+ var drawScale: TransformationWithRange<PropertyTransformation<Scale>>? = null
+ var alpha: TransformationWithRange<PropertyTransformation<Float>>? = null
+
+ transformations.fastForEach { transformationWithRange ->
+ val transformation = transformationWithRange.transformation
+ if (!transformation.matcher.matches(element, content)) {
+ return@fastForEach
+ }
+
+ when (transformation) {
+ is SharedElementTransformation -> {
+ throwIfNotNull(shared, element, name = "shared")
+ shared =
+ transformationWithRange
+ as TransformationWithRange<SharedElementTransformation>
+ }
is Translate,
is OverscrollTranslate,
is EdgeTranslate,
is AnchoredTranslate -> {
throwIfNotNull(offset, element, name = "offset")
- offset = root as PropertyTransformation<Offset>
+ offset =
+ transformationWithRange
+ as TransformationWithRange<PropertyTransformation<Offset>>
}
is ScaleSize,
is AnchoredSize -> {
throwIfNotNull(size, element, name = "size")
- size = root as PropertyTransformation<IntSize>
+ size =
+ transformationWithRange
+ as TransformationWithRange<PropertyTransformation<IntSize>>
}
is DrawScale -> {
throwIfNotNull(drawScale, element, name = "drawScale")
- drawScale = root as PropertyTransformation<Scale>
+ drawScale =
+ transformationWithRange
+ as TransformationWithRange<PropertyTransformation<Scale>>
}
is Fade -> {
throwIfNotNull(alpha, element, name = "alpha")
- alpha = root as PropertyTransformation<Float>
- }
- is RangedPropertyTransformation -> onPropertyTransformation(root, current.delegate)
- }
- }
-
- transformations.fastForEach { transformation ->
- if (!transformation.matcher.matches(element, content)) {
- return@fastForEach
- }
-
- when (transformation) {
- is SharedElementTransformation -> {
- throwIfNotNull(shared, element, name = "shared")
- shared = transformation
+ alpha =
+ transformationWithRange
+ as TransformationWithRange<PropertyTransformation<Float>>
}
- is PropertyTransformation<*> -> onPropertyTransformation(transformation)
+ else -> error("Unknown transformation: $transformation")
}
}
return ElementTransformations(shared, offset, size, drawScale, alpha)
}
- private fun throwIfNotNull(previous: Transformation?, element: ElementKey, name: String) {
+ private fun throwIfNotNull(
+ previous: TransformationWithRange<*>?,
+ element: ElementKey,
+ name: String,
+ ) {
if (previous != null) {
error("$element has multiple $name transformations")
}
@@ -401,9 +407,9 @@ internal class TransformationSpecImpl(
/** The transformations of an element during a transition. */
internal class ElementTransformations(
- val shared: SharedElementTransformation?,
- val offset: PropertyTransformation<Offset>?,
- val size: PropertyTransformation<IntSize>?,
- val drawScale: PropertyTransformation<Scale>?,
- val alpha: PropertyTransformation<Float>?,
+ val shared: TransformationWithRange<SharedElementTransformation>?,
+ val offset: TransformationWithRange<PropertyTransformation<Offset>>?,
+ val size: TransformationWithRange<PropertyTransformation<IntSize>>?,
+ val drawScale: TransformationWithRange<PropertyTransformation<Scale>>?,
+ val alpha: TransformationWithRange<PropertyTransformation<Float>>?,
)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 269d91b02e7d..e461f9ccc295 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -34,12 +34,11 @@ import com.android.compose.animation.scene.transformation.DrawScale
import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
import com.android.compose.animation.scene.transformation.OverscrollTranslate
-import com.android.compose.animation.scene.transformation.PropertyTransformation
-import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.TransformationRange
+import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.animation.scene.transformation.Translate
internal fun transitionsImpl(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions {
@@ -158,7 +157,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
}
internal abstract class BaseTransitionBuilderImpl : BaseTransitionBuilder {
- val transformations = mutableListOf<Transformation>()
+ val transformations = mutableListOf<TransformationWithRange<*>>()
private var range: TransformationRange? = null
protected var reversed = false
override var distance: UserActionDistance? = null
@@ -174,19 +173,13 @@ internal abstract class BaseTransitionBuilderImpl : BaseTransitionBuilder {
range = null
}
- protected fun transformation(transformation: PropertyTransformation<*>) {
- val transformation =
- if (range != null) {
- RangedPropertyTransformation(transformation, range!!)
- } else {
- transformation
- }
-
+ protected fun transformation(transformation: Transformation) {
+ val transformationWithRange = TransformationWithRange(transformation, range)
transformations.add(
if (reversed) {
- transformation.reversed()
+ transformationWithRange.reversed()
} else {
- transformation
+ transformationWithRange
}
)
}
@@ -264,7 +257,7 @@ internal class TransitionBuilderImpl(override val transition: TransitionState.Tr
"(${transition.toContent.debugName})"
}
- transformations.add(SharedElementTransformation(matcher, enabled, elevateInContent))
+ transformation(SharedElementTransformation(matcher, enabled, elevateInContent))
}
override fun timestampRange(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index 5936d2595465..0ddeb7c7445f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -33,7 +33,7 @@ internal class AnchoredSize(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: IntSize,
+ idleValue: IntSize,
): IntSize {
fun anchorSizeIn(content: ContentKey): IntSize {
val size =
@@ -45,8 +45,8 @@ internal class AnchoredSize(
)
return IntSize(
- width = if (anchorWidth) size.width else value.width,
- height = if (anchorHeight) size.height else value.height,
+ width = if (anchorWidth) size.width else idleValue.width,
+ height = if (anchorHeight) size.height else idleValue.height,
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 0a59dfe515fc..47508b41633c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -31,7 +31,7 @@ internal class AnchoredTranslate(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Offset,
+ idleValue: Offset,
): Offset {
fun throwException(content: ContentKey?): Nothing {
throwMissingAnchorException(
@@ -51,9 +51,9 @@ internal class AnchoredTranslate(
val offset = anchorToOffset - anchorFromOffset
return if (content == transition.toContent) {
- Offset(value.x - offset.x, value.y - offset.y)
+ Offset(idleValue.x - offset.x, idleValue.y - offset.y)
} else {
- Offset(value.x + offset.x, value.y + offset.y)
+ Offset(idleValue.x + offset.x, idleValue.y + offset.y)
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index 7223dad43a2e..8488ae5178b0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -33,12 +33,11 @@ internal class DrawScale(
private val scaleY: Float,
private val pivot: Offset = Offset.Unspecified,
) : PropertyTransformation<Scale> {
-
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Scale,
+ idleValue: Scale,
): Scale {
return Scale(scaleX, scaleY, pivot)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 4ae07c541011..884aae4b8b1a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -33,37 +33,37 @@ internal class EdgeTranslate(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Offset,
+ idleValue: Offset,
): Offset {
val sceneSize =
content.targetSize()
?: error("Content ${content.debugName} does not have a target size")
- val elementSize = element.targetSize(content) ?: return value
+ val elementSize = element.targetSize(content) ?: return idleValue
return when (edge.resolve(layoutDirection)) {
Edge.Resolved.Top ->
if (startsOutsideLayoutBounds) {
- Offset(value.x, -elementSize.height.toFloat())
+ Offset(idleValue.x, -elementSize.height.toFloat())
} else {
- Offset(value.x, 0f)
+ Offset(idleValue.x, 0f)
}
Edge.Resolved.Left ->
if (startsOutsideLayoutBounds) {
- Offset(-elementSize.width.toFloat(), value.y)
+ Offset(-elementSize.width.toFloat(), idleValue.y)
} else {
- Offset(0f, value.y)
+ Offset(0f, idleValue.y)
}
Edge.Resolved.Bottom ->
if (startsOutsideLayoutBounds) {
- Offset(value.x, sceneSize.height.toFloat())
+ Offset(idleValue.x, sceneSize.height.toFloat())
} else {
- Offset(value.x, (sceneSize.height - elementSize.height).toFloat())
+ Offset(idleValue.x, (sceneSize.height - elementSize.height).toFloat())
}
Edge.Resolved.Right ->
if (startsOutsideLayoutBounds) {
- Offset(sceneSize.width.toFloat(), value.y)
+ Offset(sceneSize.width.toFloat(), idleValue.y)
} else {
- Offset((sceneSize.width - elementSize.width).toFloat(), value.y)
+ Offset((sceneSize.width - elementSize.width).toFloat(), idleValue.y)
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index c11ec977fe2b..ef769e7d0c19 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -27,7 +27,7 @@ internal class Fade(override val matcher: ElementMatcher) : PropertyTransformati
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Float,
+ idleValue: Float,
): Float {
// Return the alpha value of [element] either when it starts fading in or when it finished
// fading out, which is `0` in both cases.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index a159a5b5b2bd..ef3654b65b0a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -36,11 +36,11 @@ internal class ScaleSize(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: IntSize,
+ idleValue: IntSize,
): IntSize {
return IntSize(
- width = (value.width * width).roundToInt(),
- height = (value.height * height).roundToInt(),
+ width = (idleValue.width * width).roundToInt(),
+ height = (idleValue.height * height).roundToInt(),
)
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index d38067d9af38..74a3ead3fbd7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -36,14 +36,6 @@ sealed interface Transformation {
*/
val matcher: ElementMatcher
- /**
- * The range during which the transformation is applied. If it is `null`, then the
- * transformation will be applied throughout the whole scene transition.
- */
- // TODO(b/240432457): Move this back to PropertyTransformation.
- val range: TransformationRange?
- get() = null
-
/*
* Reverse this transformation. This is called when we use Transition(from = A, to = B) when
* animating from B to A and there is no Transition(from = B, to = A) defined.
@@ -66,14 +58,14 @@ interface PropertyTransformation<T> : Transformation {
* - the value at progress = 100% for elements that are leaving the layout (i.e. elements in the
* content we are transitioning from).
*
- * The returned value will be interpolated using the [transition] progress and [value], the
+ * The returned value will be interpolated using the [transition] progress and [idleValue], the
* value of the property when we are idle.
*/
fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: T,
+ idleValue: T,
): T
}
@@ -82,20 +74,15 @@ interface PropertyTransformationScope : Density, ElementStateScope {
val layoutDirection: LayoutDirection
}
-/**
- * A [PropertyTransformation] associated to a range. This is a helper class so that normal
- * implementations of [PropertyTransformation] don't have to take care of reversing their range when
- * they are reversed.
- */
-internal class RangedPropertyTransformation<T>(
- val delegate: PropertyTransformation<T>,
- override val range: TransformationRange,
-) : PropertyTransformation<T> by delegate {
- override fun reversed(): Transformation {
- return RangedPropertyTransformation(
- delegate.reversed() as PropertyTransformation<T>,
- range.reversed(),
- )
+/** A pair consisting of a [transformation] and optional [range]. */
+class TransformationWithRange<out T : Transformation>(
+ val transformation: T,
+ val range: TransformationRange?,
+) {
+ fun reversed(): TransformationWithRange<T> {
+ if (range == null) return this
+
+ return TransformationWithRange(transformation = transformation, range = range.reversed())
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index af0a6edfa2fb..356ed9969458 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -35,9 +35,9 @@ internal class Translate(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Offset,
+ idleValue: Offset,
): Offset {
- return Offset(value.x + x.toPx(), value.y + y.toPx())
+ return Offset(idleValue.x + x.toPx(), idleValue.y + y.toPx())
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index e924ebfd2a8d..20a0b390a037 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -207,6 +207,9 @@ class PriorityNestedScrollConnection(
}
override suspend fun onPreFling(available: Velocity): Velocity {
+ // Note: This method may be called multiple times. Due to NestedScrollDispatcher, the order
+ // of method calls (pre/post scroll/fling) cannot be guaranteed.
+ if (isStopping) return Velocity.Zero
val controller = currentController ?: return Velocity.Zero
// If in priority mode and can stop on pre-fling phase, stop the scroll.
@@ -219,6 +222,9 @@ class PriorityNestedScrollConnection(
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+ // Note: This method may be called multiple times. Due to NestedScrollDispatcher, the order
+ // of method calls (pre/post scroll/fling) cannot be guaranteed.
+ if (isStopping) return Velocity.Zero
val availableFloat = available.toFloat()
val controller = currentController
@@ -315,6 +321,7 @@ class PriorityNestedScrollConnection(
* @return The consumed velocity.
*/
suspend fun stop(velocity: Float): Velocity {
+ if (isStopping) return Velocity.Zero
val controller = requireController(isStopping = false)
return coroutineScope {
try {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 5dad0d75cfc5..098673e5d186 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -101,10 +101,7 @@ class DraggableHandlerTest {
scene(
key = SceneC,
userActions =
- mapOf(
- Swipe.Up to SceneB,
- Swipe(SwipeDirection.Up, fromSource = Edge.Bottom) to SceneA,
- ),
+ mapOf(Swipe.Up to SceneB, Swipe.Up(fromSource = Edge.Bottom) to SceneA),
) {
Text("SceneC")
}
@@ -1231,7 +1228,7 @@ class DraggableHandlerTest {
assertTransition(
currentScene = SceneC,
fromScene = SceneC,
- // userAction: Swipe(SwipeDirection.Up, fromSource = Edge.Bottom) to SceneA
+ // userAction: Swipe.Up(fromSource = Edge.Bottom) to SceneA
toScene = SceneA,
progress = 0.1f,
)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index ee807e6a7ede..4a9051598ddc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -869,10 +869,7 @@ class ElementTest {
state = state,
modifier = Modifier.size(layoutWidth, layoutHeight),
) {
- scene(
- SceneA,
- userActions = mapOf(Swipe(SwipeDirection.Down, pointerCount = 2) to SceneB),
- ) {
+ scene(SceneA, userActions = mapOf(Swipe.Down(pointerCount = 2) to SceneB)) {
Box(
Modifier
// A scrollable that does not consume the scroll gesture
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index aaeaba93304d..3b2ee98a2a93 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -128,10 +128,10 @@ class SwipeToSceneTest {
if (swipesEnabled())
mapOf(
Swipe.Down to SceneA,
- Swipe(SwipeDirection.Down, pointerCount = 2) to SceneB,
- Swipe(SwipeDirection.Down, pointersType = PointerType.Mouse) to SceneD,
- Swipe(SwipeDirection.Right, fromSource = Edge.Left) to SceneB,
- Swipe(SwipeDirection.Down, fromSource = Edge.Top) to SceneB,
+ Swipe.Down(pointerCount = 2) to SceneB,
+ Swipe.Down(pointersType = PointerType.Mouse) to SceneD,
+ Swipe.Down(fromSource = Edge.Top) to SceneB,
+ Swipe.Right(fromSource = Edge.Left) to SceneB,
)
else emptyMap(),
) {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index d66d6b3ab219..d31711496ff0 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -28,8 +28,8 @@ import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.OverscrollTranslate
-import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.TransformationRange
+import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.test.transition
import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
@@ -310,7 +310,8 @@ class TransitionDslTest {
}
val overscrollSpec = transitions.overscrollSpecs.single()
- val transformation = overscrollSpec.transformationSpec.transformations.single()
+ val transformation =
+ overscrollSpec.transformationSpec.transformations.single().transformation
assertThat(transformation).isInstanceOf(OverscrollTranslate::class.java)
}
@@ -344,7 +345,7 @@ class TransitionDslTest {
companion object {
private val TRANSFORMATION_RANGE =
- Correspondence.transforming<Transformation, TransformationRange?>(
+ Correspondence.transforming<TransformationWithRange<*>, TransformationRange?>(
{ it?.range },
"has range equal to",
)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 54428404bd0c..91079b89a56c 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -29,6 +29,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -262,4 +264,16 @@ class PriorityNestedScrollConnectionTest {
scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
assertThat(isStarted).isEqualTo(true)
}
+
+ @Test
+ fun handleMultipleOnPreFlingCalls() = runTest {
+ startPriorityModePostScroll()
+
+ coroutineScope {
+ launch { scrollConnection.onPreFling(available = Velocity.Zero) }
+ launch { scrollConnection.onPreFling(available = Velocity.Zero) }
+ }
+
+ assertThat(lastStop).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index 0b55a6e3ffa1..d86c0d664590 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -39,7 +39,7 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
val digitLeftTopMap = mutableMapOf<Int, Point>()
var maxSingleDigitSize = Point(-1, -1)
val lockscreenTranslate = Point(0, 0)
- val aodTranslate = Point(0, 0)
+ var aodTranslate = Point(0, 0)
init {
setWillNotDraw(false)
@@ -64,8 +64,7 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
maxSingleDigitSize.y = max(maxSingleDigitSize.y, textView.measuredHeight)
}
val textView = digitalClockTextViewMap[R.id.HOUR_FIRST_DIGIT]!!
- aodTranslate.x = -(maxSingleDigitSize.x * AOD_HORIZONTAL_TRANSLATE_RATIO).toInt()
- aodTranslate.y = (maxSingleDigitSize.y * AOD_VERTICAL_TRANSLATE_RATIO).toInt()
+ aodTranslate = Point(0, 0)
return Point(
((maxSingleDigitSize.x + abs(aodTranslate.x)) * 2),
((maxSingleDigitSize.y + abs(aodTranslate.y)) * 2),
@@ -162,9 +161,6 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
val AOD_TRANSITION_DURATION = 750L
val CHARGING_TRANSITION_DURATION = 300L
- val AOD_HORIZONTAL_TRANSLATE_RATIO = 0.15F
- val AOD_VERTICAL_TRANSLATE_RATIO = 0.075F
-
// Use the sign of targetTranslation to control the direction of digit translation
fun updateDirectionalTargetTranslate(id: Int, targetTranslation: Point): Point {
val outPoint = Point(targetTranslation)
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 c0899e3006a6..5c84f2d04ccc 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
@@ -148,7 +148,11 @@ open class SimpleDigitalClockTextView(
lsFontVariation = ClockFontAxisSetting.toFVar(axes + OPTICAL_SIZE_AXIS)
lockScreenPaint.typeface = typefaceCache.getTypefaceForVariant(lsFontVariation)
typeface = lockScreenPaint.typeface
- textAnimator.setTextStyle(fvar = lsFontVariation, animate = true)
+
+ lockScreenPaint.getTextBounds(text, 0, text.length, textBounds)
+ targetTextBounds.set(textBounds)
+
+ textAnimator.setTextStyle(fvar = lsFontVariation, animate = false)
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
recomputeMaxSingleDigitSizes()
requestLayout()
@@ -201,7 +205,7 @@ open class SimpleDigitalClockTextView(
} else {
textBounds.height() + 2 * lockScreenPaint.strokeWidth.toInt()
},
- MeasureSpec.getMode(measuredHeight),
+ MeasureSpec.getMode(measuredHeightAndState),
)
}
@@ -215,10 +219,10 @@ open class SimpleDigitalClockTextView(
} else {
max(
textBounds.width() + 2 * lockScreenPaint.strokeWidth.toInt(),
- MeasureSpec.getSize(measuredWidth),
+ MeasureSpec.getSize(measuredWidthAndState),
)
},
- MeasureSpec.getMode(measuredWidth),
+ MeasureSpec.getMode(measuredWidthAndState),
)
}
diff --git a/packages/SystemUI/docs/demo_mode.md b/packages/SystemUI/docs/demo_mode.md
index ade5171d6415..c18d1f143b85 100644
--- a/packages/SystemUI/docs/demo_mode.md
+++ b/packages/SystemUI/docs/demo_mode.md
@@ -35,6 +35,7 @@ Commands are sent as string extras with key ```command``` (required). Possible v
| | ```fully``` | | Sets MCS state to fully connected (```true```, ```false```)
| | ```wifi``` | | ```show``` to show icon, any other value to hide
| | | ```level``` | Sets wifi level (null or 0-4)
+| | | ```hotspot``` | Sets the wifi to be from an Instant Hotspot. Values: ```none```, ```unknown```, ```phone```, ```tablet```, ```laptop```, ```watch```, ```auto```. (See `DemoModeWifiDataSource.kt`.)
| | ```mobile``` | | ```show``` to show icon, any other value to hide
| | | ```datatype``` | Values: ```1x```, ```3g```, ```4g```, ```e```, ```g```, ```h```, ```lte```, ```roam```, any other value to hide
| | | ```level``` | Sets mobile signal strength level (null or 0-4)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index c74d340ee325..b087ecf1a488 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -19,7 +19,6 @@ package com.android.systemui.accessibility;
import static com.android.systemui.accessibility.MagnificationImpl.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -190,8 +189,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
@Test
public void showMagnificationButton_delayedShowButton() throws RemoteException {
- // magnification settings panel should not be showing
- assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
+ when(mMagnificationSettingsController.isMagnificationSettingsShowing()).thenReturn(false);
mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
@@ -237,8 +235,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
@Test
public void removeMagnificationButton_delayingShowButton_doNotShowButtonAfterTimeout()
throws RemoteException {
- // magnification settings panel should not be showing
- assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
+ when(mMagnificationSettingsController.isMagnificationSettingsShowing()).thenReturn(false);
mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS
new file mode 100644
index 000000000000..a2001e66e55b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/accessibility/OWNERS \ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 25696bffdd66..f41d5c8eeb23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -20,7 +20,6 @@ import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.view.Choreographer.FrameCallback;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.systemGestures;
@@ -28,14 +27,8 @@ import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityActi
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.hasItems;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.AdditionalAnswers.returnsSecondArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -45,13 +38,17 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.util.Arrays.asList;
+
import android.animation.ValueAnimator;
import android.annotation.IdRes;
import android.annotation.Nullable;
@@ -63,38 +60,36 @@ import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.RegionIterator;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.TestableLooper;
import android.testing.TestableResources;
-import android.text.TextUtils;
import android.util.Size;
+import android.view.AttachedSurfaceControl;
import android.view.Display;
-import android.view.IWindowSession;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.widget.FrameLayout;
+import android.window.InputTransferToken;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
@@ -109,8 +104,6 @@ import com.android.systemui.utils.os.FakeHandler;
import com.google.common.util.concurrent.AtomicDouble;
-import kotlin.Lazy;
-
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
@@ -118,31 +111,27 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
@LargeTest
@TestableLooper.RunWithLooper
@RunWith(AndroidJUnit4.class)
-@RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
public class WindowMagnificationControllerTest extends SysuiTestCase {
@Rule
// NOTE: pass 'null' to allow this test advances time on the main thread.
- public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(null);
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(/* test= */ null);
private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
@Mock
- private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
- @Mock
private MirrorWindowControl mMirrorWindowControl;
@Mock
private WindowMagnifierCallback mWindowMagnifierCallback;
@@ -150,12 +139,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock
IRemoteMagnificationAnimationCallback mAnimationCallback2;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
+ private SurfaceControl.Transaction mTransaction;
@Mock
private SecureSettings mSecureSettings;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
private long mWaitAnimationDuration;
private long mWaitBounceEffectDuration;
@@ -170,11 +157,17 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
- private IWindowSession mWindowSessionSpy;
-
private View mSpyView;
private View.OnTouchListener mTouchListener;
+
private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+
+ // This list contains all SurfaceControlViewHosts created during a given test. If the
+ // magnification window is recreated during a test, the list will contain more than a single
+ // element.
+ private List<SurfaceControlViewHost> mSurfaceControlViewHosts = new ArrayList<>();
+ // The most recently created SurfaceControlViewHost.
+ private SurfaceControlViewHost mSurfaceControlViewHost;
private KosmosJavaAdapter mKosmos;
private FakeSharedPreferences mSharedPreferences;
@@ -196,15 +189,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
- mWindowSessionSpy = spy(WindowManagerGlobal.getWindowSession());
-
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
- doAnswer(invocation -> {
- FrameCallback callback = invocation.getArgument(0);
- callback.doFrame(0);
- return null;
- }).when(mSfVsyncFrameProvider).postFrameCallback(
- any(FrameCallback.class));
mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin());
mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
@@ -228,13 +213,20 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
mContext, mValueAnimator);
+ Supplier<SurfaceControlViewHost> scvhSupplier = () -> {
+ mSurfaceControlViewHost = spy(new SurfaceControlViewHost(
+ mContext, mContext.getDisplay(), new InputTransferToken(),
+ "WindowMagnification"));
+ ViewRootImpl viewRoot = mock(ViewRootImpl.class);
+ when(mSurfaceControlViewHost.getRootSurfaceControl()).thenReturn(viewRoot);
+ mSurfaceControlViewHosts.add(mSurfaceControlViewHost);
+ return mSurfaceControlViewHost;
+ };
+ mTransaction = spy(new SurfaceControl.Transaction());
mSharedPreferences = new FakeSharedPreferences();
when(mContext.getSharedPreferences(
eq("window_magnification_preferences"), anyInt()))
.thenReturn(mSharedPreferences);
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager = new
- ViewCaptureAwareWindowManager(mWindowManager, mLazyViewCapture,
- /* isViewCaptureEnabled= */ false);
mWindowMagnificationController =
new WindowMagnificationController(
mContext,
@@ -245,10 +237,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnifierCallback,
mSysUiState,
mSecureSettings,
- /* scvhSupplier= */ () -> null,
- mSfVsyncFrameProvider,
- /* globalWindowSessionSupplier= */ () -> mWindowSessionSpy,
- viewCaptureAwareWindowManager);
+ scvhSupplier);
verify(mMirrorWindowControl).setWindowDelegate(
any(MirrorWindowControl.MirrorWindowDelegate.class));
@@ -277,7 +266,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
verify(mSecureSettings).getIntForUser(
eq(Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING),
/* def */ eq(1), /* userHandle= */ anyInt());
- assertTrue(mWindowMagnificationController.isDiagonalScrollingEnabled());
+ assertThat(mWindowMagnificationController.isDiagonalScrollingEnabled()).isTrue();
}
@Test
@@ -325,7 +314,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
advanceTimeBy(LAYOUT_CHANGE_TIMEOUT_MS);
- verify(mSfVsyncFrameProvider, atLeast(2)).postFrameCallback(any());
+ verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(),
+ eq(Surface.ROTATION_0));
}
@Test
@@ -342,10 +332,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
(eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertEquals(mWindowMagnificationController.getCenterX(),
- sourceBoundsCaptor.getValue().exactCenterX(), 0);
- assertEquals(mWindowMagnificationController.getCenterY(),
- sourceBoundsCaptor.getValue().exactCenterY(), 0);
+ assertThat(mWindowMagnificationController.getCenterX())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
+ assertThat(mWindowMagnificationController.getCenterY())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
}
@Test
@@ -357,8 +347,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
// Wait for Rects updated.
waitForIdleSync();
- List<Rect> rects = mWindowManager.getAttachedView().getSystemGestureExclusionRects();
- assertFalse(rects.isEmpty());
+ List<Rect> rects = mSurfaceControlViewHost.getView().getSystemGestureExclusionRects();
+ assertThat(rects).isNotEmpty();
}
@Ignore("The default window size should be constrained after fixing b/288056772")
@@ -373,11 +363,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
final int halfScreenSize = screenSize / 2;
- WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+ ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
// The frame size should be the half of smaller value of window height/width unless it
//exceed the max frame size.
- assertTrue(params.width < halfScreenSize);
- assertTrue(params.height < halfScreenSize);
+ assertThat(params.width).isLessThan(halfScreenSize);
+ assertThat(params.height).isLessThan(halfScreenSize);
}
@Test
@@ -411,7 +401,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
verify(mMirrorWindowControl).destroyControl();
- assertFalse(hasMagnificationOverlapFlag());
+ assertThat(hasMagnificationOverlapFlag()).isFalse();
}
@Test
@@ -435,10 +425,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
- mWindowMagnificationController.moveWindowMagnifier(100f, 100f);
});
- verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any());
+ waitForIdleSync();
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.moveWindowMagnifier(100f, 100f));
+
+ verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(),
+ eq(Surface.ROTATION_0));
}
@Test
@@ -455,6 +449,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10;
final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10;
+ reset(mWindowMagnifierCallback);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.moveWindowMagnifierToPosition(
targetCenterX, targetCenterY, mAnimationCallback);
@@ -465,12 +460,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
verify(mAnimationCallback, never()).onResult(eq(false));
verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
.onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertEquals(mWindowMagnificationController.getCenterX(),
- sourceBoundsCaptor.getValue().exactCenterX(), 0);
- assertEquals(mWindowMagnificationController.getCenterY(),
- sourceBoundsCaptor.getValue().exactCenterY(), 0);
- assertEquals(mWindowMagnificationController.getCenterX(), targetCenterX, 0);
- assertEquals(mWindowMagnificationController.getCenterY(), targetCenterY, 0);
+ assertThat(mWindowMagnificationController.getCenterX())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
+ assertThat(mWindowMagnificationController.getCenterY())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
+ assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(targetCenterX);
+ assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(targetCenterY);
}
@Test
@@ -487,6 +482,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final float centerX = sourceBoundsCaptor.getValue().exactCenterX();
final float centerY = sourceBoundsCaptor.getValue().exactCenterY();
+ reset(mWindowMagnifierCallback);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.moveWindowMagnifierToPosition(
centerX + 10, centerY + 10, mAnimationCallback);
@@ -505,12 +501,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
verify(mAnimationCallback, times(3)).onResult(eq(false));
verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
.onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertEquals(mWindowMagnificationController.getCenterX(),
- sourceBoundsCaptor.getValue().exactCenterX(), 0);
- assertEquals(mWindowMagnificationController.getCenterY(),
- sourceBoundsCaptor.getValue().exactCenterY(), 0);
- assertEquals(mWindowMagnificationController.getCenterX(), centerX + 40, 0);
- assertEquals(mWindowMagnificationController.getCenterY(), centerY + 40, 0);
+ assertThat(mWindowMagnificationController.getCenterX())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
+ assertThat(mWindowMagnificationController.getCenterY())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
+ assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(centerX + 40);
+ assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(centerY + 40);
}
@Test
@@ -521,10 +517,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
- assertEquals(3.0f, mWindowMagnificationController.getScale(), 0);
- final View mirrorView = mWindowManager.getAttachedView();
- assertNotNull(mirrorView);
- assertThat(mirrorView.getStateDescription().toString(), containsString("300"));
+ assertThat(mWindowMagnificationController.getScale()).isEqualTo(3.0f);
+ final View mirrorView = mSurfaceControlViewHost.getView();
+ assertThat(mirrorView).isNotNull();
+ assertThat(mirrorView.getStateDescription().toString()).contains("300");
}
@Test
@@ -563,12 +559,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
ActivityInfo.CONFIG_ORIENTATION));
- assertEquals(newRotation, mWindowMagnificationController.mRotation);
+ assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
final PointF expectedCenter = new PointF(magnifiedCenter.y,
displayWidth - magnifiedCenter.x);
final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
mWindowMagnificationController.getCenterY());
- assertEquals(expectedCenter, actualCenter);
+ assertThat(actualCenter).isEqualTo(expectedCenter);
}
@Test
@@ -583,7 +579,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
ActivityInfo.CONFIG_ORIENTATION));
- assertEquals(newRotation, mWindowMagnificationController.mRotation);
+ assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
}
@Test
@@ -610,14 +606,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
// The ratio of center to window size should be the same.
- assertEquals(expectedRatio,
- mWindowMagnificationController.getCenterX() / testWindowBounds.width(),
- 0);
- assertEquals(expectedRatio,
- mWindowMagnificationController.getCenterY() / testWindowBounds.height(),
- 0);
+ assertThat(mWindowMagnificationController.getCenterX() / testWindowBounds.width())
+ .isEqualTo(expectedRatio);
+ assertThat(mWindowMagnificationController.getCenterY() / testWindowBounds.height())
+ .isEqualTo(expectedRatio);
}
+ @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
@Test
public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierWindow() {
int newSmallestScreenWidthDp =
@@ -635,7 +630,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- // Change screen density and size to trigger restoring the preferred window size
+ // Screen density and size change
mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
final Rect testWindowBounds = new Rect(
mWindowManager.getCurrentWindowMetrics().getBounds());
@@ -648,12 +643,56 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
// wait for rect update
waitForIdleSync();
- WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+ ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
R.dimen.magnification_mirror_surface_margin);
// The width and height of the view include the magnification frame and the margins.
- assertTrue(params.width == (windowFrameSize + 2 * mirrorSurfaceMargin));
- assertTrue(params.height == (windowFrameSize + 2 * mirrorSurfaceMargin));
+ assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
+ assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
+ }
+
+ @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
+ @Test
+ public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierIndexAndWindow() {
+ int newSmallestScreenWidthDp =
+ mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
+ int windowFrameSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize);
+ mSharedPreferences
+ .edit()
+ .putString(String.valueOf(newSmallestScreenWidthDp),
+ WindowMagnificationFrameSpec.serialize(
+ WindowMagnificationSettings.MagnificationSize.CUSTOM,
+ preferredWindowSize))
+ .commit();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+
+ // Screen density and size change
+ mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
+ final Rect testWindowBounds = new Rect(
+ mWindowManager.getCurrentWindowMetrics().getBounds());
+ testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
+ testWindowBounds.right + 100, testWindowBounds.bottom + 100);
+ mWindowManager.setWindowBounds(testWindowBounds);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+ });
+
+ // wait for rect update
+ waitForIdleSync();
+ verify(mWindowMagnifierCallback).onWindowMagnifierBoundsRestored(
+ eq(mContext.getDisplayId()),
+ eq(WindowMagnificationSettings.MagnificationSize.CUSTOM));
+ ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
+ final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+ R.dimen.magnification_mirror_surface_margin);
+ // The width and height of the view include the magnification frame and the margins.
+ assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
+ assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
}
@Test
@@ -675,10 +714,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final int defaultWindowSize =
mWindowMagnificationController.getMagnificationWindowSizeFromIndex(
WindowMagnificationSettings.MagnificationSize.MEDIUM);
- WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+ ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
- assertTrue(params.width == defaultWindowSize);
- assertTrue(params.height == defaultWindowSize);
+ assertThat(params.width).isEqualTo(defaultWindowSize);
+ assertThat(params.height).isEqualTo(defaultWindowSize);
}
@Test
@@ -695,9 +734,9 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
- verify(mWindowManager).removeView(any());
+ verify(mSurfaceControlViewHosts.get(0)).release();
verify(mMirrorWindowControl).destroyControl();
- verify(mWindowManager).addView(any(), any());
+ verify(mSurfaceControlViewHosts.get(1)).setView(any(), any());
verify(mMirrorWindowControl).showControl();
}
@@ -716,21 +755,30 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
Float.NaN);
});
- final View mirrorView = mWindowManager.getAttachedView();
- assertNotNull(mirrorView);
+ final View mirrorView = mSurfaceControlViewHost.getView();
+ assertThat(mirrorView).isNotNull();
final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
mirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
- assertNotNull(nodeInfo.getContentDescription());
- assertThat(nodeInfo.getStateDescription().toString(), containsString("250"));
- assertThat(nodeInfo.getActionList(),
- hasItems(new AccessibilityAction(R.id.accessibility_action_zoom_in, null),
- new AccessibilityAction(R.id.accessibility_action_zoom_out, null),
- new AccessibilityAction(R.id.accessibility_action_move_right, null),
- new AccessibilityAction(R.id.accessibility_action_move_left, null),
- new AccessibilityAction(R.id.accessibility_action_move_down, null),
- new AccessibilityAction(R.id.accessibility_action_move_up, null)));
+ assertThat(nodeInfo.getContentDescription()).isNotNull();
+ assertThat(nodeInfo.getStateDescription().toString()).contains("250");
+ assertThat(nodeInfo.getActionList()).containsExactlyElementsIn(asList(
+ new AccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
+ mContext.getResources().getString(
+ R.string.magnification_open_settings_click_label)),
+ new AccessibilityAction(R.id.accessibility_action_zoom_in,
+ mContext.getString(R.string.accessibility_control_zoom_in)),
+ new AccessibilityAction(R.id.accessibility_action_zoom_out,
+ mContext.getString(R.string.accessibility_control_zoom_out)),
+ new AccessibilityAction(R.id.accessibility_action_move_right,
+ mContext.getString(R.string.accessibility_control_move_right)),
+ new AccessibilityAction(R.id.accessibility_action_move_left,
+ mContext.getString(R.string.accessibility_control_move_left)),
+ new AccessibilityAction(R.id.accessibility_action_move_down,
+ mContext.getString(R.string.accessibility_control_move_down)),
+ new AccessibilityAction(R.id.accessibility_action_move_up,
+ mContext.getString(R.string.accessibility_control_move_up))));
}
@Test
@@ -741,29 +789,34 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- final View mirrorView = mWindowManager.getAttachedView();
- assertTrue(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null));
+ final View mirrorView = mSurfaceControlViewHost.getView();
+ assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null))
+ .isTrue();
// Minimum scale is 1.0.
verify(mWindowMagnifierCallback).onPerformScaleAction(
eq(displayId), /* scale= */ eq(1.0f), /* updatePersistence= */ eq(true));
- assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null));
+ assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null))
+ .isTrue();
verify(mWindowMagnifierCallback).onPerformScaleAction(
eq(displayId), /* scale= */ eq(2.5f), /* updatePersistence= */ eq(true));
// TODO: Verify the final state when the mirror surface is visible.
- assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null));
- assertTrue(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null));
- assertTrue(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null));
- assertTrue(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
+ assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null))
+ .isTrue();
+ assertThat(
+ mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null))
+ .isTrue();
+ assertThat(
+ mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null))
+ .isTrue();
+ assertThat(
+ mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null))
+ .isTrue();
verify(mWindowMagnifierCallback, times(4)).onMove(eq(displayId));
- assertTrue(mirrorView.performAccessibilityAction(
- AccessibilityAction.ACTION_CLICK.getId(), null));
+ assertThat(mirrorView.performAccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.getId(), null)).isTrue();
verify(mWindowMagnifierCallback).onClickSettingsButton(eq(displayId));
}
@@ -775,7 +828,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId));
@@ -795,20 +848,22 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
View topRightCorner = getInternalView(R.id.top_right_corner);
View topLeftCorner = getInternalView(R.id.top_left_corner);
- assertEquals(View.VISIBLE, closeButton.getVisibility());
- assertEquals(View.VISIBLE, bottomRightCorner.getVisibility());
- assertEquals(View.VISIBLE, bottomLeftCorner.getVisibility());
- assertEquals(View.VISIBLE, topRightCorner.getVisibility());
- assertEquals(View.VISIBLE, topLeftCorner.getVisibility());
+ assertThat(closeButton.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(topRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(topLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
- final View mirrorView = mWindowManager.getAttachedView();
- mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(), null);
+ final View mirrorView = mSurfaceControlViewHost.getView();
+ mInstrumentation.runOnMainSync(() ->
+ mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
+ null));
- assertEquals(View.GONE, closeButton.getVisibility());
- assertEquals(View.GONE, bottomRightCorner.getVisibility());
- assertEquals(View.GONE, bottomLeftCorner.getVisibility());
- assertEquals(View.GONE, topRightCorner.getVisibility());
- assertEquals(View.GONE, topLeftCorner.getVisibility());
+ assertThat(closeButton.getVisibility()).isEqualTo(View.GONE);
+ assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.GONE);
+ assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.GONE);
+ assertThat(topRightCorner.getVisibility()).isEqualTo(View.GONE);
+ assertThat(topLeftCorner.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -828,7 +883,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicInteger actualWindowHeight = new AtomicInteger();
final AtomicInteger actualWindowWidth = new AtomicInteger();
@@ -836,8 +891,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mirrorView.performAccessibilityAction(
R.id.accessibility_action_increase_window_width, null);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
@@ -847,8 +904,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
int newWindowWidth =
(int) ((startingWidth - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
+ 2 * mirrorSurfaceMargin;
- assertEquals(newWindowWidth, actualWindowWidth.get());
- assertEquals(startingHeight, actualWindowHeight.get());
+ assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
+ assertThat(actualWindowHeight.get()).isEqualTo(startingHeight);
}
@Test
@@ -868,7 +925,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicInteger actualWindowHeight = new AtomicInteger();
final AtomicInteger actualWindowWidth = new AtomicInteger();
@@ -876,8 +933,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mirrorView.performAccessibilityAction(
R.id.accessibility_action_increase_window_height, null);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
@@ -887,8 +946,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
int newWindowHeight =
(int) ((startingHeight - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
+ 2 * mirrorSurfaceMargin;
- assertEquals(startingWidth, actualWindowWidth.get());
- assertEquals(newWindowHeight, actualWindowHeight.get());
+ assertThat(actualWindowWidth.get()).isEqualTo(startingWidth);
+ assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
}
@Test
@@ -904,11 +963,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AccessibilityNodeInfo accessibilityNodeInfo =
mirrorView.createAccessibilityNodeInfo();
- assertFalse(accessibilityNodeInfo.getActionList().contains(
- new AccessibilityAction(R.id.accessibility_action_increase_window_width, null)));
+ assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+ new AccessibilityAction(
+ R.id.accessibility_action_increase_window_width,
+ mContext.getString(
+ R.string.accessibility_control_increase_window_width)));
}
@Test
@@ -924,11 +986,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AccessibilityNodeInfo accessibilityNodeInfo =
mirrorView.createAccessibilityNodeInfo();
- assertFalse(accessibilityNodeInfo.getActionList().contains(
- new AccessibilityAction(R.id.accessibility_action_increase_window_height, null)));
+ assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+ new AccessibilityAction(
+ R.id.accessibility_action_increase_window_height, null));
}
@Test
@@ -947,7 +1010,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicInteger actualWindowHeight = new AtomicInteger();
final AtomicInteger actualWindowWidth = new AtomicInteger();
@@ -955,8 +1018,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mirrorView.performAccessibilityAction(
R.id.accessibility_action_decrease_window_width, null);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
@@ -966,8 +1031,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
int newWindowWidth =
(int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
+ 2 * mirrorSurfaceMargin;
- assertEquals(newWindowWidth, actualWindowWidth.get());
- assertEquals(startingSize, actualWindowHeight.get());
+ assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
+ assertThat(actualWindowHeight.get()).isEqualTo(startingSize);
}
@Test
@@ -987,7 +1052,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicInteger actualWindowHeight = new AtomicInteger();
final AtomicInteger actualWindowWidth = new AtomicInteger();
@@ -995,8 +1060,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mirrorView.performAccessibilityAction(
R.id.accessibility_action_decrease_window_height, null);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
@@ -1006,8 +1073,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
int newWindowHeight =
(int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
+ 2 * mirrorSurfaceMargin;
- assertEquals(startingSize, actualWindowWidth.get());
- assertEquals(newWindowHeight, actualWindowHeight.get());
+ assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
+ assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
}
@Test
@@ -1023,15 +1090,16 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AccessibilityNodeInfo accessibilityNodeInfo =
mirrorView.createAccessibilityNodeInfo();
- assertFalse(accessibilityNodeInfo.getActionList().contains(
- new AccessibilityAction(R.id.accessibility_action_decrease_window_width, null)));
+ assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+ new AccessibilityAction(
+ R.id.accessibility_action_decrease_window_width, null));
}
@Test
- public void windowHeightIsMin_noDecreaseWindowHeightA11yAcyion() {
+ public void windowHeightIsMin_noDecreaseWindowHeightA11yAction() {
int mMinWindowSize = mResources.getDimensionPixelSize(
com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
final int startingSize = mMinWindowSize;
@@ -1043,11 +1111,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AccessibilityNodeInfo accessibilityNodeInfo =
mirrorView.createAccessibilityNodeInfo();
- assertFalse(accessibilityNodeInfo.getActionList().contains(
- new AccessibilityAction(R.id.accessibility_action_decrease_window_height, null)));
+ assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+ new AccessibilityAction(
+ R.id.accessibility_action_decrease_window_height, null));
}
@Test
@@ -1057,8 +1126,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- assertEquals(getContext().getResources().getString(
- com.android.internal.R.string.android_system_label), getAccessibilityWindowTitle());
+ assertThat(getAccessibilityWindowTitle()).isEqualTo(getContext().getResources().getString(
+ com.android.internal.R.string.android_system_label));
}
@Test
@@ -1073,14 +1142,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- assertEquals(Float.NaN, mWindowMagnificationController.getScale(), 0);
+ assertThat(mWindowMagnificationController.getScale()).isEqualTo(Float.NaN);
}
@Test
public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
// the config orientation should not be undefined, since it would cause config.diff
// returning 0 and thus the orientation changed would not be detected
- assertNotEquals(ORIENTATION_UNDEFINED, mResources.getConfiguration().orientation);
+ assertThat(mResources.getConfiguration().orientation).isNotEqualTo(ORIENTATION_UNDEFINED);
final Configuration config = mResources.getConfiguration();
config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
@@ -1091,7 +1160,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
Float.NaN, Float.NaN));
- assertEquals(newRotation, mWindowMagnificationController.mRotation);
+ assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
}
@Test
@@ -1119,7 +1188,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
});
- assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle()));
+ assertThat(getAccessibilityWindowTitle()).isEqualTo(newA11yWindowTitle);
}
@Ignore("it's flaky in presubmit but works in abtd, filter for now. b/305654925")
@@ -1134,7 +1203,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.onSingleTap(mSpyView);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicDouble maxScaleX = new AtomicDouble();
advanceTimeBy(mWaitBounceEffectDuration, /* runnableOnEachRefresh= */ () -> {
@@ -1142,10 +1211,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
// maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
final double oldMax = maxScaleX.get();
final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
- assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+ assertThat(maxScaleX.compareAndSet(oldMax, newMax)).isTrue();
});
- assertTrue(maxScaleX.get() > 1.0);
+ assertThat(maxScaleX.get()).isGreaterThan(1.0);
}
@Test
@@ -1174,30 +1243,23 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.updateWindowMagnificationInternal(
Float.NaN, Float.NaN, Float.NaN);
});
+ // Wait for Region updated.
+ waitForIdleSync();
mInstrumentation.runOnMainSync(
() -> {
mWindowMagnificationController.moveWindowMagnifier(bounds.width(), 0);
});
-
// Wait for Region updated.
waitForIdleSync();
- final ArgumentCaptor<Region> tapExcludeRegionCapturer =
- ArgumentCaptor.forClass(Region.class);
- verify(mWindowSessionSpy, times(2))
- .updateTapExcludeRegion(any(), tapExcludeRegionCapturer.capture());
- Region tapExcludeRegion = tapExcludeRegionCapturer.getValue();
- RegionIterator iterator = new RegionIterator(tapExcludeRegion);
+ AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl();
+ // Verifying two times in: (1) enable window magnification (2) reposition drag handle
+ verify(viewRoot, times(2)).setTouchableRegion(any());
- final Rect topRect = new Rect();
- final Rect bottomRect = new Rect();
- assertTrue(iterator.next(topRect));
- assertTrue(iterator.next(bottomRect));
- assertFalse(iterator.next(new Rect()));
-
- assertEquals(topRect.right, bottomRect.right);
- assertNotEquals(topRect.left, bottomRect.left);
+ View dragButton = getInternalView(R.id.drag_handle);
+ FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
+ assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.LEFT);
}
@Test
@@ -1210,29 +1272,23 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.updateWindowMagnificationInternal(
Float.NaN, Float.NaN, Float.NaN);
});
+ // Wait for Region updated.
+ waitForIdleSync();
mInstrumentation.runOnMainSync(
() -> {
mWindowMagnificationController.moveWindowMagnifier(-bounds.width(), 0);
});
-
// Wait for Region updated.
waitForIdleSync();
- final ArgumentCaptor<Region> tapExcludeRegionCapturer =
- ArgumentCaptor.forClass(Region.class);
- verify(mWindowSessionSpy).updateTapExcludeRegion(any(), tapExcludeRegionCapturer.capture());
- Region tapExcludeRegion = tapExcludeRegionCapturer.getValue();
- RegionIterator iterator = new RegionIterator(tapExcludeRegion);
-
- final Rect topRect = new Rect();
- final Rect bottomRect = new Rect();
- assertTrue(iterator.next(topRect));
- assertTrue(iterator.next(bottomRect));
- assertFalse(iterator.next(new Rect()));
+ AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl();
+ // Verifying one times in: (1) enable window magnification
+ verify(viewRoot).setTouchableRegion(any());
- assertEquals(topRect.left, bottomRect.left);
- assertNotEquals(topRect.right, bottomRect.right);
+ View dragButton = getInternalView(R.id.drag_handle);
+ FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
+ assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.RIGHT);
}
@Test
@@ -1249,13 +1305,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final AtomicInteger actualWindowWidth = new AtomicInteger();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(expectedWindowHeight, actualWindowHeight.get());
- assertEquals(expectedWindowWidth, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
+ assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
}
@Test
@@ -1271,12 +1327,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
Float.NaN, Float.NaN);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(expectedWindowHeight, actualWindowHeight.get());
- assertEquals(expectedWindowWidth, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
+ assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
}
@Test
@@ -1292,12 +1348,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.setWindowSize(minimumWindowSize - 10,
minimumWindowSize - 10);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(minimumWindowSize, actualWindowHeight.get());
- assertEquals(minimumWindowSize, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(minimumWindowSize);
+ assertThat(actualWindowWidth.get()).isEqualTo(minimumWindowSize);
}
@Test
@@ -1311,12 +1367,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final AtomicInteger actualWindowWidth = new AtomicInteger();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(bounds.height(), actualWindowHeight.get());
- assertEquals(bounds.width(), actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(bounds.height());
+ assertThat(actualWindowWidth.get()).isEqualTo(bounds.width());
}
@Test
@@ -1342,12 +1398,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mWindowMagnificationController.changeMagnificationSize(
WindowMagnificationSettings.MagnificationSize.LARGE);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(expectedWindowHeight, actualWindowHeight.get());
- assertEquals(expectedWindowWidth, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
+ assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
}
@Test
@@ -1376,12 +1434,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mWindowMagnificationController
.onDrag(getInternalView(R.id.bottom_right_corner), 2f, 1f);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(startingSize + 1, actualWindowHeight.get());
- assertEquals(startingSize + 2, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
+ assertThat(actualWindowWidth.get()).isEqualTo(startingSize + 2);
}
@Test
@@ -1404,11 +1464,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
mWindowMagnificationController
.onDrag(getInternalView(R.id.bottom_handle), 2f, 1f);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(startingSize + 1, actualWindowHeight.get());
- assertEquals(startingSize, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
+ assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
}
@Test
@@ -1430,8 +1492,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
});
- assertTrue(magnificationCenterX.get() < bounds.right);
- assertTrue(magnificationCenterY.get() < bounds.bottom);
+ assertThat(magnificationCenterX.get()).isLessThan(bounds.right);
+ assertThat(magnificationCenterY.get()).isLessThan(bounds.bottom);
}
@Test
@@ -1451,13 +1513,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
dragButton.dispatchTouchEvent(
obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100));
- verify(mWindowManager).addView(any(View.class), any());
+ verify(mSurfaceControlViewHost).setView(any(View.class), any());
}
private <T extends View> T getInternalView(@IdRes int idRes) {
- View mirrorView = mWindowManager.getAttachedView();
+ View mirrorView = mSurfaceControlViewHost.getView();
T view = mirrorView.findViewById(idRes);
- assertNotNull(view);
+ assertThat(view).isNotNull();
return view;
}
@@ -1466,14 +1528,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y);
}
- private CharSequence getAccessibilityWindowTitle() {
- final View mirrorView = mWindowManager.getAttachedView();
+ private String getAccessibilityWindowTitle() {
+ final View mirrorView = mSurfaceControlViewHost.getView();
if (mirrorView == null) {
return null;
}
WindowManager.LayoutParams layoutParams =
(WindowManager.LayoutParams) mirrorView.getLayoutParams();
- return layoutParams.accessibilityTitle;
+ return layoutParams.accessibilityTitle.toString();
}
private boolean hasMagnificationOverlapFlag() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
index f58bbc3cf0cf..d3715926932c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
@@ -20,7 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -71,7 +70,7 @@ class BouncerUserActionsViewModelTest : SysuiTestCase() {
assertThat(actions)
.containsEntriesExactly(
Back to UserActionResult(Scenes.QuickSettings),
- Swipe(SwipeDirection.Down) to UserActionResult(Scenes.QuickSettings),
+ Swipe.Down to UserActionResult(Scenes.QuickSettings),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index 3388a785a26a..20cd8608d204 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -28,11 +28,13 @@ import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.Serializable
@@ -68,6 +70,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
@Mock private lateinit var systemProperties: SystemPropertiesHelper
@Mock private lateinit var resources: Resources
@Mock private lateinit var restarter: Restarter
+ private lateinit var fakeExecutor: FakeExecutor
private lateinit var userTracker: FakeUserTracker
private val flagMap = mutableMapOf<String, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
@@ -83,6 +86,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
flagMap.put(releasedFlagB.name, releasedFlagB)
+ fakeExecutor = FakeExecutor(FakeSystemClock())
userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockContext })
mFeatureFlagsClassicDebug =
@@ -95,7 +99,8 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
serverFlagReader,
flagMap,
restarter,
- userTracker
+ userTracker,
+ fakeExecutor,
)
mFeatureFlagsClassicDebug.init()
verify(flagManager).onSettingsChangedAction = any()
@@ -325,14 +330,14 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
// trying to erase an id not in the map does nothing
broadcastReceiver.onReceive(
mockContext,
- Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "")
+ Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, ""),
)
verifyNoMoreInteractions(flagManager, globalSettings)
// valid id with no value puts empty string in the setting
broadcastReceiver.onReceive(
mockContext,
- Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "1")
+ Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "1"),
)
verifyPutData("1", "", numReads = 0)
}
@@ -415,7 +420,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
serverFlagReader.setFlagValue(
teamfoodableFlagA.namespace,
teamfoodableFlagA.name,
- !teamfoodableFlagA.default
+ !teamfoodableFlagA.default,
)
verify(restarter, never()).restartSystemUI(anyString())
}
@@ -428,7 +433,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
serverFlagReader.setFlagValue(
teamfoodableFlagA.namespace,
teamfoodableFlagA.name,
- !teamfoodableFlagA.default
+ !teamfoodableFlagA.default,
)
verify(restarter).restartSystemUI(anyString())
}
@@ -441,7 +446,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
serverFlagReader.setFlagValue(
teamfoodableFlagA.namespace,
teamfoodableFlagA.name,
- teamfoodableFlagA.default
+ teamfoodableFlagA.default,
)
verify(restarter, never()).restartSystemUI(anyString())
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 7f313564e35a..2c12f8782ddc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -193,7 +193,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
@Test
@EnableSceneContainer
- fun surfaceBehindVisibility_fromLockscreenToGone_noUserInput_trueThroughout() =
+ fun surfaceBehindVisibility_fromLockscreenToGone_dependsOnDeviceEntry() =
testScope.runTest {
val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
@@ -208,7 +208,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
SuccessFingerprintAuthenticationStatus(0, true)
)
- // Start the transition to Gone, the surface should become immediately visible.
+ // Start the transition to Gone, the surface should remain invisible.
kosmos.setSceneTransition(
ObservableTransitionState.Transition(
fromScene = Scenes.Lockscreen,
@@ -220,9 +220,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
)
)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- assertThat(isSurfaceBehindVisible).isTrue()
+ assertThat(isSurfaceBehindVisible).isFalse()
- // Towards the end of the transition, the surface should continue to be visible.
+ // Towards the end of the transition, the surface should continue to remain invisible.
kosmos.setSceneTransition(
ObservableTransitionState.Transition(
fromScene = Scenes.Lockscreen,
@@ -234,7 +234,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
)
)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- assertThat(isSurfaceBehindVisible).isTrue()
+ assertThat(isSurfaceBehindVisible).isFalse()
// After the transition, settles on Gone. Surface behind should stay visible now.
kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
@@ -245,43 +245,6 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
@Test
@EnableSceneContainer
- fun surfaceBehindVisibility_fromLockscreenToGone_withUserInput_falseUntilInputStops() =
- testScope.runTest {
- val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
- val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
-
- // Before the transition, we start on Lockscreen so the surface should start invisible.
- kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen))
- assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- assertThat(isSurfaceBehindVisible).isFalse()
-
- // Unlocked with fingerprint.
- kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
-
- // Start the transition to Gone, the surface should not be visible while
- // isUserInputOngoing is true
- val isUserInputOngoing = MutableStateFlow(true)
- kosmos.setSceneTransition(
- ObservableTransitionState.Transition(
- fromScene = Scenes.Lockscreen,
- toScene = Scenes.Gone,
- isInitiatedByUserInput = true,
- isUserInputOngoing = isUserInputOngoing,
- progress = flowOf(0.51f),
- currentScene = flowOf(Scenes.Gone),
- )
- )
- assertThat(isSurfaceBehindVisible).isFalse()
-
- // When isUserInputOngoing becomes false, then the surface should become visible.
- isUserInputOngoing.value = false
- assertThat(isSurfaceBehindVisible).isTrue()
- }
-
- @Test
- @EnableSceneContainer
fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() =
testScope.runTest {
val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 7f0937059494..0e3b03f74c02 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -44,6 +44,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Ignore
@@ -107,6 +108,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
fun translationAndScale_whenNotDozing() =
testScope.runTest {
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to not dozing (on lockscreen)
keyguardTransitionRepository.sendTransitionStep(
@@ -180,6 +182,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
testScope.runTest {
underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100))
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -221,6 +224,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
testScope.runTest {
underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -263,6 +267,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
testScope.runTest {
underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -305,6 +310,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
whenever(clockController.config.useAlternateSmartspaceAODTransition).thenReturn(true)
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -423,6 +429,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
.thenReturn(if (isWeatherClock) true else false)
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -434,6 +441,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
),
validateStep = false,
)
+ runCurrent()
// Trigger a change to the burn-in model
burnInFlow.value = BurnInModel(translationX = 20, translationY = 30, scale = 0.5f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
index 6397979d9627..5c4b7432e18d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
@@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.ShowOverlay
@@ -201,8 +200,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() {
val userActions by collectLastValue(underTest.actions)
val downDestination =
userActions?.get(
- Swipe(
- SwipeDirection.Down,
+ Swipe.Down(
fromSource = Edge.Top.takeIf { downFromEdge },
pointerCount = if (downWithTwoPointers) 2 else 1,
)
@@ -292,8 +290,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() {
val downDestination =
userActions?.get(
- Swipe(
- SwipeDirection.Down,
+ Swipe.Down(
fromSource = Edge.Top.takeIf { downFromEdge },
pointerCount = if (downWithTwoPointers) 2 else 1,
)
@@ -310,8 +307,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() {
val downFromTopRightDestination =
userActions?.get(
- Swipe(
- SwipeDirection.Down,
+ Swipe.Down(
fromSource = SceneContainerEdge.TopRight,
pointerCount = if (downWithTwoPointers) 2 else 1,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt
index 9e3fdf377b83..8e67e602abd9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt
@@ -20,7 +20,6 @@ import android.media.session.MediaSession
import android.os.Bundle
import android.os.Handler
import android.os.looper
-import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.media.utils.MediaConstants
import androidx.media3.common.Player
@@ -69,10 +68,9 @@ class Media3ActionFactoryTest : SysuiTestCase() {
private val testScope = kosmos.testScope
private val controllerFactory = kosmos.fakeMediaControllerFactory
private val tokenFactory = kosmos.fakeSessionTokenFactory
- private lateinit var testableLooper: TestableLooper
- private var commandCaptor = argumentCaptor<SessionCommand>()
- private var runnableCaptor = argumentCaptor<Runnable>()
+ private val commandCaptor = argumentCaptor<SessionCommand>()
+ private val runnableCaptor = argumentCaptor<Runnable>()
private val legacyToken = MediaSession.Token(1, null)
private val token = mock<SessionToken>()
@@ -97,8 +95,6 @@ class Media3ActionFactoryTest : SysuiTestCase() {
@Before
fun setup() {
- testableLooper = TestableLooper.get(this)
-
underTest =
Media3ActionFactory(
context,
@@ -246,7 +242,6 @@ class Media3ActionFactoryTest : SysuiTestCase() {
assertThat(actions.custom0!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 2")
actions.custom0!!.action!!.run()
runCurrent()
- testableLooper.processAllMessages()
verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 2")
verify(media3Controller).release()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
index 1a7265b09aae..cf503bbf1310 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
@@ -82,7 +82,7 @@ class MediaDataLoaderTest : SysuiTestCase() {
private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
private val mediaFlags = kosmos.mediaFlags
private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
- private val media3ActionFactory = kosmos.media3ActionFactory
+ private lateinit var media3ActionFactory: Media3ActionFactory
private val session = MediaSession(context, "MediaDataLoaderTestSession")
private val metadataBuilder =
MediaMetadata.Builder().apply {
@@ -94,6 +94,7 @@ class MediaDataLoaderTest : SysuiTestCase() {
@Before
fun setUp() {
+ media3ActionFactory = kosmos.media3ActionFactory
mediaControllerFactory.setControllerForToken(session.sessionToken, mediaController)
whenever(mediaController.sessionToken).thenReturn(session.sessionToken)
whenever(mediaController.metadata).then { metadataBuilder.build() }
@@ -311,7 +312,6 @@ class MediaDataLoaderTest : SysuiTestCase() {
}
build()
}
-
val result = underTest.loadMediaData(KEY, mediaNotification)
assertThat(result).isNotNull()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index e12c67b24893..104aa517fa4f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -16,7 +16,7 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.wm.shell.recents.RecentTasks
-import com.android.wm.shell.shared.GroupedRecentTaskInfo
+import com.android.wm.shell.shared.GroupedTaskInfo
import com.android.wm.shell.shared.split.SplitBounds
import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50
import com.google.common.truth.Truth.assertThat
@@ -219,9 +219,9 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
}
@Suppress("UNCHECKED_CAST")
- private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
+ private fun givenRecentTasks(vararg tasks: GroupedTaskInfo) {
whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
- val consumer = it.arguments.last() as Consumer<List<GroupedRecentTaskInfo>>
+ val consumer = it.arguments.last() as Consumer<List<GroupedTaskInfo>>
consumer.accept(tasks.toList())
}
}
@@ -247,7 +247,7 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
userId: Int = 0,
isVisible: Boolean = false,
userType: RecentTask.UserType = STANDARD,
- ): GroupedRecentTaskInfo {
+ ): GroupedTaskInfo {
val userInfo =
mock<UserInfo> {
whenever(isCloneProfile).thenReturn(userType == CLONED)
@@ -255,7 +255,7 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
whenever(isPrivateProfile).thenReturn(userType == PRIVATE)
}
whenever(userManager.getUserInfo(userId)).thenReturn(userInfo)
- return GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId, userId, isVisible))
+ return GroupedTaskInfo.forFullscreenTasks(createTaskInfo(taskId, userId, isVisible))
}
private fun createTaskPair(
@@ -263,9 +263,9 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
userId1: Int = 0,
taskId2: Int,
userId2: Int = 0,
- isVisible: Boolean = false
- ): GroupedRecentTaskInfo =
- GroupedRecentTaskInfo.forSplitTasks(
+ isVisible: Boolean = false,
+ ): GroupedTaskInfo =
+ GroupedTaskInfo.forSplitTasks(
createTaskInfo(taskId1, userId1, isVisible),
createTaskInfo(taskId2, userId2, isVisible),
SplitBounds(Rect(), Rect(), taskId1, taskId2, SNAP_TO_2_50_50)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
index 642d9a0b1e9d..855931c32671 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
@@ -21,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -76,12 +75,8 @@ class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() {
underTest.activateIn(this)
assertThat(
- (actions?.get(
- Swipe(
- direction = SwipeDirection.Down,
- fromSource = SceneContainerEdge.TopRight,
- )
- ) as? UserActionResult.ReplaceByOverlay)
+ (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopRight))
+ as? UserActionResult.ReplaceByOverlay)
?.overlay
)
.isEqualTo(Overlays.QuickSettingsShade)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
index ada2138d4d52..b30313e07012 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
@@ -35,9 +35,12 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -121,6 +124,38 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade)
}
+ @Test
+ fun showHeader_showsOnNarrowScreen() =
+ testScope.runTest {
+ kosmos.shadeRepository.setShadeLayoutWide(false)
+
+ // Shown when notifications are present.
+ kosmos.activeNotificationListRepository.setActiveNotifs(1)
+ runCurrent()
+ assertThat(underTest.showHeader).isTrue()
+
+ // Hidden when notifications are not present.
+ kosmos.activeNotificationListRepository.setActiveNotifs(0)
+ runCurrent()
+ assertThat(underTest.showHeader).isFalse()
+ }
+
+ @Test
+ fun showHeader_hidesOnWideScreen() =
+ testScope.runTest {
+ kosmos.shadeRepository.setShadeLayoutWide(true)
+
+ // Hidden when notifications are present.
+ kosmos.activeNotificationListRepository.setActiveNotifs(1)
+ runCurrent()
+ assertThat(underTest.showHeader).isFalse()
+
+ // Hidden when notifications are not present.
+ kosmos.activeNotificationListRepository.setActiveNotifs(0)
+ runCurrent()
+ assertThat(underTest.showHeader).isFalse()
+ }
+
private fun TestScope.lockDevice() {
val currentScene by collectLastValue(sceneInteractor.currentScene)
kosmos.powerInteractor.setAsleepForTest()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index 9fe9ed2bf47a..5cba325450e6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -36,6 +36,7 @@ import com.android.systemui.qs.composefragment.viewmodel.MediaState.ANY_MEDIA
import com.android.systemui.qs.composefragment.viewmodel.MediaState.NO_MEDIA
import com.android.systemui.qs.fgsManagerController
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
+import com.android.systemui.qs.panels.ui.viewmodel.setConfigurationForMediaInRow
import com.android.systemui.res.R
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.StatusBarState
@@ -269,6 +270,54 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest()
}
}
+ @Test
+ fun mediaNotInRow() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setConfigurationForMediaInRow(mediaInRow = false)
+ setMediaState(ACTIVE_MEDIA)
+
+ assertThat(underTest.qqsMediaInRow).isFalse()
+ assertThat(underTest.qsMediaInRow).isFalse()
+ }
+ }
+
+ @Test
+ fun mediaInRow_mediaActive_bothInRow() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setConfigurationForMediaInRow(mediaInRow = true)
+ setMediaState(ACTIVE_MEDIA)
+
+ assertThat(underTest.qqsMediaInRow).isTrue()
+ assertThat(underTest.qsMediaInRow).isTrue()
+ }
+ }
+
+ @Test
+ fun mediaInRow_mediaRecommendation_onlyQSInRow() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setConfigurationForMediaInRow(mediaInRow = true)
+ setMediaState(ANY_MEDIA)
+
+ assertThat(underTest.qqsMediaInRow).isFalse()
+ assertThat(underTest.qsMediaInRow).isTrue()
+ }
+ }
+
+ @Test
+ fun mediaInRow_correctConfig_noMediaVisible_noMediaInRow() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setConfigurationForMediaInRow(mediaInRow = true)
+ setMediaState(NO_MEDIA)
+
+ assertThat(underTest.qqsMediaInRow).isFalse()
+ assertThat(underTest.qsMediaInRow).isFalse()
+ }
+ }
+
private fun TestScope.setMediaState(state: MediaState) {
with(kosmos) {
val activeMedia = state == ACTIVE_MEDIA
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt
new file mode 100644
index 000000000000..ab78029684d4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.getBoundsInRoot
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpRect
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.width
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.composefragment.QuickQuickSettingsLayout
+import com.android.systemui.qs.composefragment.QuickSettingsLayout
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class QSFragmentComposeTest : SysuiTestCase() {
+
+ @get:Rule val composeTestRule = createComposeRule()
+
+ @Test
+ fun portraitLayout_qqs() {
+ composeTestRule.setContent {
+ QuickQuickSettingsLayout(
+ tiles = { Tiles(TILES_HEIGHT_PORTRAIT) },
+ media = { Media() },
+ mediaInRow = false,
+ )
+ }
+
+ composeTestRule.waitForIdle()
+
+ val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot()
+ val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot()
+
+ // All nodes aligned in a column
+ assertThat(tilesBounds.left).isEqualTo(mediaBounds.left)
+ assertThat(tilesBounds.right).isEqualTo(mediaBounds.right)
+ assertThat(tilesBounds.bottom).isLessThan(mediaBounds.top)
+ }
+
+ @Test
+ fun landscapeLayout_qqs() {
+ composeTestRule.setContent {
+ QuickQuickSettingsLayout(
+ tiles = { Tiles(TILES_HEIGHT_LANDSCAPE) },
+ media = { Media() },
+ mediaInRow = true,
+ )
+ }
+
+ composeTestRule.waitForIdle()
+
+ val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot()
+ val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot()
+
+ // Media to the right of tiles
+ assertThat(tilesBounds.right).isLessThan(mediaBounds.left)
+ // "Same" width
+ assertThat((tilesBounds.width - mediaBounds.width).abs()).isAtMost(1.dp)
+ // Vertically centered
+ assertThat((tilesBounds.centerY - mediaBounds.centerY).abs()).isAtMost(1.dp)
+ }
+
+ @Test
+ fun portraitLayout_qs() {
+ composeTestRule.setContent {
+ QuickSettingsLayout(
+ brightness = { Brightness() },
+ tiles = { Tiles(TILES_HEIGHT_PORTRAIT) },
+ media = { Media() },
+ mediaInRow = false,
+ )
+ }
+
+ composeTestRule.waitForIdle()
+
+ val brightnessBounds = composeTestRule.onNodeWithTag(BRIGHTNESS).getBoundsInRoot()
+ val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot()
+ val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot()
+
+ assertThat(brightnessBounds.left).isEqualTo(tilesBounds.left)
+ assertThat(tilesBounds.left).isEqualTo(mediaBounds.left)
+
+ assertThat(brightnessBounds.right).isEqualTo(tilesBounds.right)
+ assertThat(tilesBounds.right).isEqualTo(mediaBounds.right)
+
+ assertThat(brightnessBounds.bottom).isLessThan(tilesBounds.top)
+ assertThat(tilesBounds.bottom).isLessThan(mediaBounds.top)
+ }
+
+ @Test
+ fun landscapeLayout_qs() {
+ composeTestRule.setContent {
+ QuickSettingsLayout(
+ brightness = { Brightness() },
+ tiles = { Tiles(TILES_HEIGHT_PORTRAIT) },
+ media = { Media() },
+ mediaInRow = true,
+ )
+ }
+
+ composeTestRule.waitForIdle()
+
+ val brightnessBounds = composeTestRule.onNodeWithTag(BRIGHTNESS).getBoundsInRoot()
+ val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot()
+ val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot()
+
+ // Brightness takes full width, with left end aligned with tiles and right end aligned with
+ // media
+ assertThat(brightnessBounds.left).isEqualTo(tilesBounds.left)
+ assertThat(brightnessBounds.right).isEqualTo(mediaBounds.right)
+
+ // Brightness above tiles and media
+ assertThat(brightnessBounds.bottom).isLessThan(tilesBounds.top)
+ assertThat(brightnessBounds.bottom).isLessThan(mediaBounds.top)
+
+ // Media to the right of tiles
+ assertThat(tilesBounds.right).isLessThan(mediaBounds.left)
+ // "Same" width
+ assertThat((tilesBounds.width - mediaBounds.width).abs()).isAtMost(1.dp)
+ // Vertically centered
+ assertThat((tilesBounds.centerY - mediaBounds.centerY).abs()).isAtMost(1.dp)
+ }
+
+ private companion object {
+ const val BRIGHTNESS = "brightness"
+ const val TILES = "tiles"
+ const val MEDIA = "media"
+ val TILES_HEIGHT_PORTRAIT = 300.dp
+ val TILES_HEIGHT_LANDSCAPE = 150.dp
+ val MEDIA_HEIGHT = 100.dp
+ val BRIGHTNESS_HEIGHT = 64.dp
+
+ @Composable
+ fun Brightness() {
+ Box(modifier = Modifier.testTag(BRIGHTNESS).height(BRIGHTNESS_HEIGHT).fillMaxWidth())
+ }
+
+ @Composable
+ fun Tiles(height: Dp) {
+ Box(modifier = Modifier.testTag(TILES).height(height).fillMaxWidth())
+ }
+
+ @Composable
+ fun Media() {
+ Box(modifier = Modifier.testTag(MEDIA).height(MEDIA_HEIGHT).fillMaxWidth())
+ }
+
+ val DpRect.centerY: Dp
+ get() = (top + bottom) / 2
+
+ fun Dp.abs() = if (this > 0.dp) this else -this
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt
new file mode 100644
index 000000000000..635badac04f5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.content.res.Configuration
+import android.content.res.mainResources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.flags.setFlagValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.ui.controller.MediaLocation
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(ParameterizedAndroidJunit4::class)
+@SmallTest
+class MediaInRowInLandscapeViewModelTest(private val testData: TestData) : SysuiTestCase() {
+
+ private val kosmos = testKosmos().apply { usingMediaInComposeFragment = testData.usingMedia }
+
+ private val underTest by lazy {
+ kosmos.mediaInRowInLandscapeViewModelFactory.create(TESTED_MEDIA_LOCATION)
+ }
+
+ @Before
+ fun setUp() {
+ mSetFlagsRule.setFlagValue(DualShade.FLAG_NAME, testData.shadeMode == ShadeMode.Dual)
+ }
+
+ @Test
+ fun shouldMediaShowInRow() =
+ with(kosmos) {
+ testScope.runTest {
+ underTest.activateIn(testScope)
+
+ shadeRepository.setShadeLayoutWide(testData.shadeMode != ShadeMode.Single)
+ val config =
+ Configuration(mainResources.configuration).apply {
+ orientation = testData.orientation
+ screenLayout = testData.screenLayoutLong
+ }
+ fakeConfigurationRepository.onConfigurationChange(config)
+ mainResources.configuration.updateFrom(config)
+ mediaHostStatesManager.updateHostState(
+ testData.mediaLocation,
+ MediaHost.MediaHostStateHolder().apply { visible = testData.mediaVisible },
+ )
+ runCurrent()
+
+ assertThat(underTest.shouldMediaShowInRow).isEqualTo(testData.mediaInRowExpected)
+ }
+ }
+
+ data class TestData(
+ val usingMedia: Boolean,
+ val shadeMode: ShadeMode,
+ val orientation: Int,
+ val screenLayoutLong: Int,
+ val mediaVisible: Boolean,
+ @MediaLocation val mediaLocation: Int,
+ ) {
+ val mediaInRowExpected: Boolean
+ get() =
+ usingMedia &&
+ shadeMode == ShadeMode.Single &&
+ orientation == Configuration.ORIENTATION_LANDSCAPE &&
+ screenLayoutLong == Configuration.SCREENLAYOUT_LONG_YES &&
+ mediaVisible &&
+ mediaLocation == TESTED_MEDIA_LOCATION
+ }
+
+ companion object {
+ private const val TESTED_MEDIA_LOCATION = LOCATION_QS
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun data(): Collection<TestData> {
+ val usingMediaValues = setOf(true, false)
+ val shadeModeValues = setOf(ShadeMode.Single, ShadeMode.Split, ShadeMode.Dual)
+ val orientationValues =
+ setOf(Configuration.ORIENTATION_LANDSCAPE, Configuration.ORIENTATION_PORTRAIT)
+ val screenLayoutLongValues =
+ setOf(Configuration.SCREENLAYOUT_LONG_YES, Configuration.SCREENLAYOUT_LONG_NO)
+ val mediaVisibleValues = setOf(true, false)
+ val mediaLocationsValues = setOf(LOCATION_QS, LOCATION_QQS)
+
+ return usingMediaValues.flatMap { usingMedia ->
+ shadeModeValues.flatMap { shadeMode ->
+ orientationValues.flatMap { orientation ->
+ screenLayoutLongValues.flatMap { screenLayoutLong ->
+ mediaVisibleValues.flatMap { mediaVisible ->
+ mediaLocationsValues.map { mediaLocation ->
+ TestData(
+ usingMedia,
+ shadeMode,
+ orientation,
+ screenLayoutLong,
+ mediaVisible,
+ mediaLocation,
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt
new file mode 100644
index 000000000000..4ae8589de87b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.content.res.mainResources
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.ui.controller.MediaLocation
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
+import com.android.systemui.qs.panels.data.repository.QSColumnsRepository
+import com.android.systemui.qs.panels.data.repository.qsColumnsRepository
+import com.android.systemui.res.R
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class QSColumnsViewModelTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ usingMediaInComposeFragment = true
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_settings_infinite_grid_num_columns,
+ SINGLE_SPLIT_SHADE_COLUMNS,
+ )
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_settings_dual_shade_num_columns,
+ DUAL_SHADE_COLUMNS,
+ )
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_settings_split_shade_num_columns,
+ SINGLE_SPLIT_SHADE_COLUMNS,
+ )
+ qsColumnsRepository = QSColumnsRepository(mainResources, configurationRepository)
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationNull_singleOrSplit_alwaysSingleShadeColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(null)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ makeMediaVisible(LOCATION_QS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+ }
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationNull_dualShade_alwaysDualShadeColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(null)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ makeMediaVisible(LOCATION_QS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+ }
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationQS_dualShade_alwaysDualShadeColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(LOCATION_QS)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+
+ makeMediaVisible(LOCATION_QS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+ }
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationQQS_dualShade_alwaysDualShadeColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+ }
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationQS_singleOrSplit_halfColumnsOnCorrectConfigurationAndVisible() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(LOCATION_QS)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ makeMediaVisible(LOCATION_QS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS / 2)
+ }
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationQQS_singleOrSplit_halfColumnsOnCorrectConfigurationAndVisible() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS / 2)
+ }
+ }
+
+ companion object {
+ private const val SINGLE_SPLIT_SHADE_COLUMNS = 4
+ private const val DUAL_SHADE_COLUMNS = 2
+
+ private fun Kosmos.makeMediaVisible(@MediaLocation location: Int, visible: Boolean) {
+ mediaHostStatesManager.updateHostState(
+ location,
+ MediaHost.MediaHostStateHolder().apply { this.visible = visible },
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
index ab5a049c91f1..3d1265aec45e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
@@ -24,6 +24,11 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
+import com.android.systemui.media.controls.ui.controller.MediaLocation
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
import com.android.systemui.qs.panels.domain.interactor.qsPreferencesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -67,6 +72,7 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
4,
)
fakeConfigurationRepository.onConfigurationChange()
+ usingMediaInComposeFragment = true
}
private val underTest =
@@ -145,6 +151,33 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
}
}
+ @Test
+ fun mediaVisibleInLandscape_doubleRows_halfColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ setRows(1)
+ assertThat(underTest.columns).isEqualTo(4)
+ // All tiles in 4 columns (but we only show the first 3 tiles)
+ // [1] [2] [3 3]
+ // [4] [5 5]
+ // [6 6] [7] [8]
+ // [9 9]
+
+ runCurrent()
+ assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(3))
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(2)
+ // Tiles in 4 columns
+ // [1] [2]
+ // [3 3]
+ assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(3))
+ }
+ }
+
private fun Kosmos.setTiles(tiles: List<TileSpec>) {
currentTilesInteractor.setTiles(tiles)
}
@@ -163,5 +196,12 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
private companion object {
const val PREFIX_SMALL = "small"
const val PREFIX_LARGE = "large"
+
+ private fun Kosmos.makeMediaVisible(@MediaLocation location: Int, visible: Boolean) {
+ mediaHostStatesManager.updateHostState(
+ location,
+ MediaHost.MediaHostStateHolder().apply { this.visible = visible },
+ )
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 0d12483bad0a..53708fd417e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -23,7 +23,6 @@ import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -160,9 +159,7 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
ArgumentCaptor<Runnable> onStartRecordingClicked = ArgumentCaptor.forClass(Runnable.class);
- verify(mController).createScreenRecordDialog(any(), eq(mFeatureFlags),
- eq(mDialogTransitionAnimator), eq(mActivityStarter),
- onStartRecordingClicked.capture());
+ verify(mController).createScreenRecordDialog(onStartRecordingClicked.capture());
// When starting the recording, we collapse the shade and disable the dialog animation.
assertNotNull(onStartRecordingClicked.getValue());
@@ -298,14 +295,13 @@ public class ScreenRecordTileTest extends SysuiTestCase {
public void showingDialogPrompt_logsMediaProjectionPermissionRequested() {
when(mController.isStarting()).thenReturn(false);
when(mController.isRecording()).thenReturn(false);
- when(mController.createScreenRecordDialog(any(), any(), any(), any(), any()))
+ when(mController.createScreenRecordDialog(any()))
.thenReturn(mPermissionDialogPrompt);
mTile.handleClick(null /* view */);
mTestableLooper.processAllMessages();
- verify(mController).createScreenRecordDialog(any(), eq(mFeatureFlags),
- eq(mDialogTransitionAnimator), eq(mActivityStarter), any());
+ verify(mController).createScreenRecordDialog(any());
var onDismissAction = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction.class);
verify(mKeyguardDismissUtil).executeWhenUnlocked(
onDismissAction.capture(), anyBoolean(), anyBoolean());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
new file mode 100644
index 000000000000..2ac3e081b8f4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain
+
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Button
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.impl.notes.qsNotesTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotesTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val qsTileConfig = kosmos.qsNotesTileConfig
+
+ private val mapper by lazy {
+ NotesTileMapper(
+ context.orCreateTestableResources
+ .apply { addOverride(R.drawable.ic_qs_notes, TestStubDrawable()) }
+ .resources,
+ context.theme,
+ )
+ }
+
+ @Test
+ fun mappedStateMatchesModel() {
+ val inputModel = NotesTileModel
+
+ val outputState = mapper.map(qsTileConfig, inputModel)
+
+ val expectedState = createNotesTileState()
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ private fun createNotesTileState(): QSTileState =
+ QSTileState(
+ Icon.Loaded(context.getDrawable(R.drawable.ic_qs_notes)!!, null),
+ R.drawable.ic_qs_notes,
+ context.getString(R.string.quick_settings_notes_label),
+ QSTileState.ActivationState.INACTIVE,
+ /* secondaryLabel= */ null,
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+ context.getString(R.string.quick_settings_notes_label),
+ /* stateDescription= */ null,
+ QSTileState.SideViewIcon.Chevron,
+ QSTileState.EnabledState.ENABLED,
+ Button::class.qualifiedName,
+ )
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
new file mode 100644
index 000000000000..35d6d5adc3f4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.interactor
+
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toCollection
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotesTileDataInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val testUser = UserHandle.of(1)
+ private lateinit var underTest: NotesTileDataInteractor
+
+
+ @EnableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
+ @Test
+ fun availability_qsFlagEnabled_notesRoleEnabled_returnTrue() =
+ testScope.runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
+
+ val availability = underTest.availability(testUser).toCollection(mutableListOf())
+
+ assertThat(availability).containsExactly(true)
+ }
+
+ @DisableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
+ @Test
+ fun availability_qsFlagDisabled_notesRoleEnabled_returnFalse() =
+ testScope.runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
+
+ val availability = underTest.availability(testUser).toCollection(mutableListOf())
+
+ assertThat(availability).containsExactly(false)
+ }
+
+ @EnableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
+ @Test
+ fun availability_qsFlagEnabled_notesRoleDisabled_returnFalse() =
+ testScope.runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = false)
+
+ val availability = underTest.availability(testUser).toCollection(mutableListOf())
+
+ assertThat(availability).containsExactly(false)
+ }
+
+ @DisableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
+ @Test
+ fun availability_qsFlagDisabled_notesRoleDisabled_returnFalse() =
+ testScope.runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = false)
+
+ val availability = underTest.availability(testUser).toCollection(mutableListOf())
+
+ assertThat(availability).containsExactly(false)
+ }
+
+ @Test
+ fun tileData_notEmpty() = runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
+ val flowValue by
+ collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
+
+ runCurrent()
+
+ assertThat(flowValue).isNotNull()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
new file mode 100644
index 000000000000..54911e612291
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.interactor
+
+import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotesTileUserActionInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val inputHandler = FakeQSTileIntentUserInputHandler()
+ private val panelInteractor = mock<PanelInteractor>()
+ private val noteTaskController = mock<NoteTaskController>()
+
+ private lateinit var underTest: NotesTileUserActionInteractor
+
+ @Before
+ fun setUp() {
+ underTest = NotesTileUserActionInteractor(inputHandler, panelInteractor, noteTaskController)
+ }
+
+ @Test
+ fun handleClick_launchDefaultNotesApp() =
+ testScope.runTest {
+ underTest.handleInput(QSTileInputTestKtx.click(NotesTileModel))
+
+ verify(noteTaskController).showNoteTask(NoteTaskEntryPoint.QS_NOTES_TILE)
+ }
+
+ @Test
+ fun handleLongClick_launchSettings() =
+ testScope.runTest {
+ underTest.handleInput(QSTileInputTestKtx.longClick(NotesTileModel))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ assertThat(it.intent.action).isEqualTo(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index 899122d4dd45..0b56d7b64aab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -23,29 +23,27 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
-import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
-import com.android.systemui.plugins.activityStarter
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -54,24 +52,11 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
private val testScope = kosmos.testScope
private val keyguardInteractor = kosmos.keyguardInteractor
private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
- private val featureFlags = kosmos.featureFlagsClassic
- private val activityStarter = kosmos.activityStarter
private val keyguardDismissUtil = mock<KeyguardDismissUtil>()
private val panelInteractor = mock<PanelInteractor>()
private val dialog = mock<Dialog>()
private val recordingController =
- mock<RecordingController> {
- whenever(
- createScreenRecordDialog(
- eq(context),
- eq(featureFlags),
- eq(dialogTransitionAnimator),
- eq(activityStarter),
- any()
- )
- )
- .thenReturn(dialog)
- }
+ mock<RecordingController> { on { createScreenRecordDialog(any()) } doReturn dialog }
private val screenRecordRepository =
ScreenRecordRepositoryImpl(
@@ -81,7 +66,6 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
private val underTest =
ScreenRecordTileUserActionInteractor(
- context,
testScope.testScheduler,
testScope.testScheduler,
screenRecordRepository,
@@ -91,8 +75,6 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
dialogTransitionAnimator,
panelInteractor,
mock<MediaProjectionMetricsLogger>(),
- featureFlags,
- activityStarter,
)
@Test
@@ -120,22 +102,16 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
underTest.handleInput(QSTileInputTestKtx.click(recordingModel))
val onStartRecordingClickedCaptor = argumentCaptor<Runnable>()
verify(recordingController)
- .createScreenRecordDialog(
- eq(context),
- eq(featureFlags),
- eq(dialogTransitionAnimator),
- eq(activityStarter),
- onStartRecordingClickedCaptor.capture()
- )
+ .createScreenRecordDialog(onStartRecordingClickedCaptor.capture())
val onDismissActionCaptor = argumentCaptor<OnDismissAction>()
verify(keyguardDismissUtil)
.executeWhenUnlocked(onDismissActionCaptor.capture(), eq(false), eq(true))
- onDismissActionCaptor.value.onDismiss()
+ onDismissActionCaptor.lastValue.onDismiss()
verify(dialog).show() // because the view was null
// When starting the recording, we collapse the shade and disable the dialog animation.
- onStartRecordingClickedCaptor.value.run()
+ onStartRecordingClickedCaptor.lastValue.run()
verify(dialogTransitionAnimator).disableAllCurrentDialogsExitAnimations()
verify(panelInteractor).collapsePanels()
}
@@ -145,9 +121,9 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
*/
@Test
fun handleClickFromView_whenDoingNothing_whenKeyguardNotShowing_showDialogFromView() = runTest {
- val expandable = mock<Expandable>()
val controller = mock<DialogTransitionAnimator.Controller>()
- whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
+ val expandable =
+ mock<Expandable> { on { dialogTransitionController(any()) } doReturn controller }
kosmos.fakeKeyguardRepository.setKeyguardShowing(false)
@@ -158,18 +134,12 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
)
val onStartRecordingClickedCaptor = argumentCaptor<Runnable>()
verify(recordingController)
- .createScreenRecordDialog(
- eq(context),
- eq(featureFlags),
- eq(dialogTransitionAnimator),
- eq(activityStarter),
- onStartRecordingClickedCaptor.capture()
- )
+ .createScreenRecordDialog(onStartRecordingClickedCaptor.capture())
val onDismissActionCaptor = argumentCaptor<OnDismissAction>()
verify(keyguardDismissUtil)
.executeWhenUnlocked(onDismissActionCaptor.capture(), eq(false), eq(true))
- onDismissActionCaptor.value.onDismiss()
+ onDismissActionCaptor.lastValue.onDismiss()
verify(dialogTransitionAnimator).show(eq(dialog), eq(controller), eq(true))
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
index c3a777cc3e44..939644594d31 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -21,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -89,12 +88,8 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
underTest.activateIn(this)
assertThat(
- (actions?.get(
- Swipe(
- direction = SwipeDirection.Down,
- fromSource = SceneContainerEdge.TopLeft,
- )
- ) as? UserActionResult.ReplaceByOverlay)
+ (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopLeft))
+ as? UserActionResult.ReplaceByOverlay)
?.overlay
)
.isEqualTo(Overlays.NotificationsShade)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
index f32894dfd383..24f6b6d8566b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Overlays
@@ -55,7 +56,10 @@ import org.junit.runner.RunWith
@EnableFlags(DualShade.FLAG_NAME)
class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos =
+ testKosmos().apply {
+ usingMediaInComposeFragment = false // This is not for the compose fragment
+ }
private val testScope = kosmos.testScope
private val sceneInteractor = kosmos.sceneInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
index 62b6391ca54c..d5fbe49a153b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
@@ -22,7 +22,6 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -98,9 +97,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Shade),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Gone)
@@ -125,9 +123,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Lockscreen),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Lockscreen),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Lockscreen),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
@@ -154,9 +151,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Shade),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Gone)
@@ -178,9 +174,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Shade),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
@@ -214,9 +209,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Shade),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Gone)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 2c8f7cf47723..55f88cc5b7a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -2119,40 +2119,6 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
@Test
- fun switchToGone_whenSurfaceBehindLockscreenVisibleMidTransition() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.currentScene)
- val transitionStateFlow =
- prepareState(authenticationMethod = AuthenticationMethodModel.None)
- underTest.start()
- assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- // Swipe to Gone, more than halfway
- transitionStateFlow.value =
- ObservableTransitionState.Transition(
- fromScene = Scenes.Lockscreen,
- toScene = Scenes.Gone,
- currentScene = flowOf(Scenes.Gone),
- progress = flowOf(0.51f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(true),
- )
- runCurrent()
- // Lift finger
- transitionStateFlow.value =
- ObservableTransitionState.Transition(
- fromScene = Scenes.Lockscreen,
- toScene = Scenes.Gone,
- currentScene = flowOf(Scenes.Gone),
- progress = flowOf(0.51f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
- )
- runCurrent()
-
- assertThat(currentScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
fun switchToGone_extendUnlock() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
index 47fae9f0629f..bb2e9417ecef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
@@ -23,7 +23,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
@@ -71,8 +70,7 @@ class GoneUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(true)
runCurrent()
- assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey)
- .isEqualTo(ToSplitShade)
+ assertThat(userActions?.get(Swipe.Down)?.transitionKey).isEqualTo(ToSplitShade)
}
@Test
@@ -83,7 +81,7 @@ class GoneUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(false)
runCurrent()
- assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey).isNull()
+ assertThat(userActions?.get(Swipe.Down)?.transitionKey).isNull()
}
@Test
@@ -94,7 +92,7 @@ class GoneUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(true)
runCurrent()
- assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey).isNull()
+ assertThat(userActions?.get(Swipe.Down)?.transitionKey).isNull()
}
@Test
@@ -132,6 +130,6 @@ class GoneUserActionsViewModelTest : SysuiTestCase() {
}
private fun swipeDownFromTopWithTwoFingers(): UserAction {
- return Swipe(direction = SwipeDirection.Down, pointerCount = 2, fromSource = Edge.Top)
+ return Swipe.Down(pointerCount = 2, fromSource = Edge.Top)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
index 972afb58352d..d5f57da40e5a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
@@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
@@ -77,7 +76,7 @@ class UserActionsViewModelTest : SysuiTestCase() {
val expected1 =
mapOf(
Back to UserActionResult(toScene = Scenes.Gone),
- Swipe(SwipeDirection.Up) to UserActionResult(toScene = Scenes.Shade)
+ Swipe.Up to UserActionResult(toScene = Scenes.Shade),
)
underTest.upstream.value = expected1
runCurrent()
@@ -86,7 +85,7 @@ class UserActionsViewModelTest : SysuiTestCase() {
val expected2 =
mapOf(
Back to UserActionResult(toScene = Scenes.Lockscreen),
- Swipe(SwipeDirection.Down) to UserActionResult(toScene = Scenes.Shade)
+ Swipe.Down to UserActionResult(toScene = Scenes.Shade),
)
underTest.upstream.value = expected2
runCurrent()
@@ -104,7 +103,7 @@ class UserActionsViewModelTest : SysuiTestCase() {
val expected =
mapOf(
Back to UserActionResult(toScene = Scenes.Lockscreen),
- Swipe(SwipeDirection.Down) to UserActionResult(toScene = Scenes.Shade)
+ Swipe.Down to UserActionResult(toScene = Scenes.Shade),
)
underTest.upstream.value = expected
runCurrent()
@@ -120,7 +119,7 @@ class UserActionsViewModelTest : SysuiTestCase() {
val upstream = MutableStateFlow<Map<UserAction, UserActionResult>>(emptyMap())
override suspend fun hydrateActions(
- setActions: (Map<UserAction, UserActionResult>) -> Unit,
+ setActions: (Map<UserAction, UserActionResult>) -> Unit
) {
upstream.collect { setActions(it) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
index fcb366bfd8ee..bbfc66a8d8e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
@@ -92,10 +92,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
AuthenticationMethodModel.Pin
)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
}
@@ -110,10 +107,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
)
setDeviceEntered(true)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Gone)
}
@@ -128,10 +122,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
)
kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Gone)
}
@@ -147,10 +138,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
)
sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
}
@@ -167,10 +155,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
runCurrent()
sceneInteractor.changeScene(Scenes.Gone, "reason")
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Gone)
}
@@ -182,8 +167,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(true)
runCurrent()
- assertThat(actions?.get(Swipe(SwipeDirection.Up))?.transitionKey)
- .isEqualTo(ToSplitShade)
+ assertThat(actions?.get(Swipe.Up)?.transitionKey).isEqualTo(ToSplitShade)
}
@Test
@@ -193,7 +177,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(false)
runCurrent()
- assertThat(actions?.get(Swipe(SwipeDirection.Up))?.transitionKey).isNull()
+ assertThat(actions?.get(Swipe.Up)?.transitionKey).isNull()
}
@Test
@@ -202,10 +186,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
overrideResource(R.bool.config_use_split_notification_shade, true)
kosmos.shadeStartable.start()
val actions by collectLastValue(underTest.actions)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Down)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
.isNull()
}
@@ -215,10 +196,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
overrideResource(R.bool.config_use_split_notification_shade, false)
kosmos.shadeStartable.start()
val actions by collectLastValue(underTest.actions)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Down)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(Scenes.QuickSettings)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
index d6b3b919913f..72a2ce50ed9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
@@ -85,7 +85,6 @@ class OperatorNameViewControllerTest : SysuiTestCase() {
underTest =
OperatorNameViewController.Factory(
- darkIconDispatcher,
tunerService,
telephonyManager,
keyguardUpdateMonitor,
@@ -94,7 +93,7 @@ class OperatorNameViewControllerTest : SysuiTestCase() {
subscriptionManagerProxy,
javaAdapter,
)
- .create(view)
+ .create(view, darkIconDispatcher)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 60a185537b0d..fb7252b24295 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static android.app.Notification.CATEGORY_CALL;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -197,6 +199,19 @@ public class StatusBarIconViewTest extends SysuiTestCase {
}
@Test
+ public void testContentDescForNotification_noNotifContent() {
+ Notification n = new Notification.Builder(mContext, "test")
+ .setSmallIcon(0)
+ .setContentTitle("hello")
+ .setCategory(CATEGORY_CALL)
+ .build();
+ assertThat(NotificationContentDescription.contentDescForNotification(mContext, n)
+ .toString()).startsWith("com.android.systemui.tests notification");
+ assertThat(NotificationContentDescription.contentDescForNotification(mContext, n)
+ .toString()).doesNotContain("hello");
+ }
+
+ @Test
@EnableFlags({Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS})
public void setIcon_withPreloaded_usesPreloaded() {
Icon mockIcon = mock(Icon.class);
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 32f4164d509f..1b4132910555 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
@@ -97,7 +97,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
assertThat(latest).hasSize(1)
val chip = latest!![0]
- assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
}
@@ -168,8 +168,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
companion object {
fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
- assertThat(latest)
- .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
assertThat((latest as OngoingActivityChipModel.Shown).icon)
.isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
index 5be5fb4aa9ba..c06da4bc5080 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
@@ -23,9 +23,11 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.data.repository.fakeLightBarControllerStore
import com.android.systemui.statusbar.data.repository.fakePrivacyDotWindowControllerStore
import com.android.systemui.testKosmos
import com.google.common.truth.Expect
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -48,6 +50,7 @@ class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
private val fakeOrchestratorFactory = kosmos.fakeStatusBarOrchestratorFactory
private val fakeInitializerStore = kosmos.fakeStatusBarInitializerStore
private val fakePrivacyDotStore = kosmos.fakePrivacyDotWindowControllerStore
+ private val fakeLightBarStore = kosmos.fakeLightBarControllerStore
// Lazy, so that @EnableFlags is set before initializer is instantiated.
private val underTest by lazy { kosmos.multiDisplayStatusBarStarter }
@@ -95,6 +98,31 @@ class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
}
@Test
+ fun start_doesNotStartLightBarControllerForCurrentDisplays() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = 1)
+ fakeDisplayRepository.addDisplay(displayId = 2)
+
+ underTest.start()
+ runCurrent()
+
+ verify(fakeLightBarStore.forDisplay(displayId = 1), never()).start()
+ verify(fakeLightBarStore.forDisplay(displayId = 2), never()).start()
+ }
+
+ @Test
+ fun start_createsLightBarControllerForCurrentDisplays() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = 1)
+ fakeDisplayRepository.addDisplay(displayId = 2)
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(1, 2)
+ }
+
+ @Test
fun start_doesNotStartPrivacyDotForDefaultDisplay() =
testScope.runTest {
fakeDisplayRepository.addDisplay(displayId = Display.DEFAULT_DISPLAY)
@@ -145,6 +173,30 @@ class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
}
@Test
+ fun displayAdded_lightBarForNewDisplayIsCreated() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(3)
+ }
+
+ @Test
+ fun displayAdded_lightBarForNewDisplayIsNotStarted() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ verify(fakeLightBarStore.forDisplay(displayId = 3), never()).start()
+ }
+
+ @Test
fun displayAddedDuringStart_initializerForNewDisplayIsStarted() =
testScope.runTest {
underTest.start()
@@ -178,4 +230,26 @@ class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
verify(fakePrivacyDotStore.forDisplay(displayId = 3)).start()
}
+
+ @Test
+ fun displayAddedDuringStart_lightBarForNewDisplayIsCreated() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(3)
+ }
+
+ @Test
+ fun displayAddedDuringStart_lightBarForNewDisplayIsNotStarted() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ verify(fakeLightBarStore.forDisplay(displayId = 3), never()).start()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt
new file mode 100644
index 000000000000..a2c3c66f4448
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.statusbar.data.repository
+
+import android.platform.test.annotations.EnableFlags
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplayDarkIconDispatcherStoreTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val fakeDisplayRepository = kosmos.displayRepository
+
+ // Lazy so that @EnableFlags has time to run before underTest is instantiated.
+ private val underTest by lazy { kosmos.multiDisplayDarkIconDispatcherStore }
+
+ @Before
+ fun start() {
+ underTest.start()
+ }
+
+ @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) }
+
+ @Test
+ fun beforeDisplayRemoved_doesNotStopInstances() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ verify(instance, never()).stop()
+ }
+
+ @Test
+ fun displayRemoved_stopsInstance() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
+
+ verify(instance).stop()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
index 12473cb46793..896f940f8a60 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
@@ -34,31 +34,9 @@ class NotificationContentDescriptionTest : SysuiTestCase() {
private val TICKER = "this is a ticker"
@Test
- fun notificationWithAllDifferentFields_descriptionIsTitle() {
+ fun notificationWithAllDifferentFields_descriptionIsAppName() {
val n = createNotification(TITLE, TEXT, TICKER)
val description = contentDescForNotification(context, n)
- assertThat(description).isEqualTo(createDescriptionText(n, TITLE))
- }
-
- @Test
- fun notificationWithAllDifferentFields_titleMatchesAppName_descriptionIsText() {
- val n = createNotification(getTestAppName(), TEXT, TICKER)
- val description = contentDescForNotification(context, n)
- assertThat(description).isEqualTo(createDescriptionText(n, TEXT))
- }
-
- @Test
- fun notificationWithAllDifferentFields_titleMatchesAppNameNoText_descriptionIsTicker() {
- val n = createNotification(getTestAppName(), null, TICKER)
- val description = contentDescForNotification(context, n)
- assertThat(description).isEqualTo(createDescriptionText(n, TICKER))
- }
-
- @Test
- fun notificationWithAllDifferentFields_titleMatchesAppNameNoTextNoTicker_descriptionEmpty() {
- val appName = getTestAppName()
- val n = createNotification(appName, null, null)
- val description = contentDescForNotification(context, n)
assertThat(description).isEqualTo(createDescriptionText(n, ""))
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index 9367a93a2890..46c360aecd48 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -110,14 +110,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
lastSleepReason = WakeSleepReason.OTHER,
)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(transitionState = TransitionState.STARTED)
)
keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(
- to = DozeStateModel.DOZE_AOD,
- )
+ DozeTransitionModel(to = DozeStateModel.DOZE_AOD)
)
val animationsEnabled by collectLastValue(underTest.animationsEnabled)
runCurrent()
@@ -133,14 +129,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
lastSleepReason = WakeSleepReason.OTHER,
)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(transitionState = TransitionState.STARTED)
)
keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(
- to = DozeStateModel.DOZE_PULSING,
- )
+ DozeTransitionModel(to = DozeStateModel.DOZE_PULSING)
)
val animationsEnabled by collectLastValue(underTest.animationsEnabled)
runCurrent()
@@ -201,9 +193,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
lastSleepReason = WakeSleepReason.OTHER,
)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(transitionState = TransitionState.STARTED)
)
val animationsEnabled by collectLastValue(underTest.animationsEnabled)
runCurrent()
@@ -216,9 +206,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
val animationsEnabled by collectLastValue(underTest.animationsEnabled)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(transitionState = TransitionState.STARTED)
)
keyguardRepository.setKeyguardShowing(true)
runCurrent()
@@ -234,13 +222,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
@Test
fun iconColors_testsDarkBounds() =
testScope.runTest {
- darkIconRepository.darkState.value =
- SysuiDarkIconDispatcher.DarkChange(
- emptyList(),
- 0f,
- 0xAABBCC,
- )
- val iconColorsLookup by collectLastValue(underTest.iconColors)
+ val displayId = 123
+ darkIconRepository.darkState(displayId).value =
+ SysuiDarkIconDispatcher.DarkChange(emptyList(), 0f, 0xAABBCC)
+ val iconColorsLookup by collectLastValue(underTest.iconColors(displayId))
assertThat(iconColorsLookup).isNotNull()
val iconColors = iconColorsLookup?.iconColors(Rect())
@@ -257,13 +242,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
@Test
fun iconColors_staticDrawableColor_notInDarkTintArea() =
testScope.runTest {
- darkIconRepository.darkState.value =
- SysuiDarkIconDispatcher.DarkChange(
- listOf(Rect(0, 0, 5, 5)),
- 0f,
- 0xAABBCC,
- )
- val iconColorsLookup by collectLastValue(underTest.iconColors)
+ val displayId = 321
+ darkIconRepository.darkState(displayId).value =
+ SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
+ val iconColorsLookup by collectLastValue(underTest.iconColors(displayId))
val iconColors = iconColorsLookup?.iconColors(Rect(1, 1, 4, 4))
val staticDrawableColor = iconColors?.staticDrawableColor(Rect(6, 6, 7, 7))
assertThat(staticDrawableColor).isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT)
@@ -272,13 +254,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
@Test
fun iconColors_notInDarkTintArea() =
testScope.runTest {
- darkIconRepository.darkState.value =
- SysuiDarkIconDispatcher.DarkChange(
- listOf(Rect(0, 0, 5, 5)),
- 0f,
- 0xAABBCC,
- )
- val iconColorsLookup by collectLastValue(underTest.iconColors)
+ val displayId = 987
+ darkIconRepository.darkState(displayId).value =
+ SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
+ val iconColorsLookup by collectLastValue(underTest.iconColors(displayId))
val iconColors = iconColorsLookup?.iconColors(Rect(6, 6, 7, 7))
assertThat(iconColors).isNull()
}
@@ -295,7 +274,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
activeNotificationModel(
key = "notif1",
groupKey = "group",
- statusBarIcon = icon
+ statusBarIcon = icon,
)
)
}
@@ -322,7 +301,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
activeNotificationModel(
key = "notif1",
groupKey = "group",
- statusBarIcon = icon
+ statusBarIcon = icon,
)
)
}
@@ -354,7 +333,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
activeNotificationModel(
key = "notif1",
groupKey = "group",
- statusBarIcon = icon
+ statusBarIcon = icon,
)
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
new file mode 100644
index 000000000000..a1b63b159277
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -0,0 +1,648 @@
+/*
+ * 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
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.row
+
+import android.R
+import android.app.AppOpsManager
+import android.app.INotificationManager
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Intent
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.content.pm.ShortcutManager
+import android.graphics.Color
+import android.os.Binder
+import android.os.UserManager
+import android.os.fakeExecutorHandler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.provider.Settings
+import android.service.notification.NotificationListenerService
+import android.testing.TestableLooper.RunWithLooper
+import android.util.ArraySet
+import android.view.View
+import android.view.accessibility.AccessibilityManager
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.domain.interactor.PowerInteractorFactory.create
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.statusbar.NotificationEntryHelper
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.AssistantFeedbackController
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.testKosmos
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import kotlin.test.assertNotNull
+import kotlin.test.fail
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+/** Tests for [NotificationGutsManager]. */
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@RunWithLooper
+class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase() {
+ private val testNotificationChannel =
+ NotificationChannel(
+ TEST_CHANNEL_ID,
+ TEST_CHANNEL_ID,
+ NotificationManager.IMPORTANCE_DEFAULT,
+ )
+
+ private val kosmos = testKosmos()
+
+ private val testScope = kosmos.testScope
+ private val javaAdapter = JavaAdapter(testScope.backgroundScope)
+ private val executor = kosmos.fakeExecutor
+ private val handler = kosmos.fakeExecutorHandler
+ private lateinit var helper: NotificationTestHelper
+ private lateinit var gutsManager: NotificationGutsManager
+
+ @get:Rule val rule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var onUserInteractionCallback: OnUserInteractionCallback
+ @Mock private lateinit var presenter: NotificationPresenter
+ @Mock private lateinit var notificationActivityStarter: NotificationActivityStarter
+ @Mock private lateinit var notificationListContainer: NotificationListContainer
+ @Mock
+ private lateinit var onSettingsClickListener: NotificationGutsManager.OnSettingsClickListener
+ @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var accessibilityManager: AccessibilityManager
+ @Mock private lateinit var highPriorityProvider: HighPriorityProvider
+ @Mock private lateinit var iNotificationManager: INotificationManager
+ @Mock private lateinit var barService: IStatusBarService
+ @Mock private lateinit var launcherApps: LauncherApps
+ @Mock private lateinit var shortcutManager: ShortcutManager
+ @Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+ @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
+ @Mock private lateinit var contextTracker: UserContextProvider
+ @Mock private lateinit var bubblesManager: BubblesManager
+ @Mock private lateinit var shadeController: ShadeController
+ @Mock private lateinit var peopleSpaceWidgetManager: PeopleSpaceWidgetManager
+ @Mock private lateinit var assistantFeedbackController: AssistantFeedbackController
+ @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var headsUpManager: HeadsUpManager
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var userManager: UserManager
+
+ private lateinit var windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
+
+ companion object {
+ private const val TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ helper = NotificationTestHelper(mContext, mDependency)
+ whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(false)
+
+ windowRootViewVisibilityInteractor =
+ WindowRootViewVisibilityInteractor(
+ testScope.backgroundScope,
+ WindowRootViewVisibilityRepository(barService, executor),
+ FakeKeyguardRepository(),
+ headsUpManager,
+ create().powerInteractor,
+ kosmos.activeNotificationsInteractor,
+ ) {
+ kosmos.sceneInteractor
+ }
+
+ gutsManager =
+ NotificationGutsManager(
+ mContext,
+ handler,
+ handler,
+ javaAdapter,
+ accessibilityManager,
+ highPriorityProvider,
+ iNotificationManager,
+ userManager,
+ peopleSpaceWidgetManager,
+ launcherApps,
+ shortcutManager,
+ channelEditorDialogController,
+ contextTracker,
+ assistantFeedbackController,
+ Optional.of(bubblesManager),
+ UiEventLoggerFake(),
+ onUserInteractionCallback,
+ shadeController,
+ windowRootViewVisibilityInteractor,
+ notificationLockscreenUserManager,
+ statusBarStateController,
+ barService,
+ deviceProvisionedController,
+ metricsLogger,
+ headsUpManager,
+ activityStarter,
+ )
+ gutsManager.setUpWithPresenter(
+ presenter,
+ notificationListContainer,
+ onSettingsClickListener,
+ )
+ gutsManager.setNotificationActivityStarter(notificationActivityStarter)
+ gutsManager.start()
+ }
+
+ @Test
+ fun testOpenAndCloseGuts() {
+ val guts = spy(NotificationGuts(mContext))
+ whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
+ handler.post(((invocation.arguments[0] as Runnable)))
+ null
+ }
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any())
+
+ val realRow = createTestNotificationRow()
+ val menuItem = createTestMenuItem(realRow)
+
+ val row = spy(realRow)
+ whenever(row.windowToken).thenReturn(Binder())
+ whenever(row.guts).thenReturn(guts)
+
+ assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
+ assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong())
+ executor.runAllReady()
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
+ verify(headsUpManager).setGutsShown(realRow.entry, true)
+
+ assertEquals(View.VISIBLE.toLong(), guts.visibility.toLong())
+ gutsManager.closeAndSaveGuts(false, false, true, 0, 0, false)
+
+ verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
+ verify(row, times(1)).setGutsView(any<MenuItem>())
+ executor.runAllReady()
+ verify(headsUpManager).setGutsShown(realRow.entry, false)
+ }
+
+ @Test
+ fun testLockscreenShadeVisible_visible_gutsNotClosed() =
+ testScope.runTest {
+ // First, start out lockscreen or shade as not visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false)
+ runCurrent()
+
+ val guts: NotificationGuts = mock()
+ gutsManager.exposedGuts = guts
+
+ // WHEN the lockscreen or shade becomes visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ runCurrent()
+
+ // THEN the guts are not closed
+ verify(guts, never()).removeCallbacks(any())
+ verify(guts, never())
+ .closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun testLockscreenShadeVisible_notVisible_gutsClosed() =
+ testScope.runTest {
+ // First, start out lockscreen or shade as visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ runCurrent()
+
+ val guts: NotificationGuts = mock()
+ gutsManager.exposedGuts = guts
+
+ // WHEN the lockscreen or shade is no longer visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false)
+ runCurrent()
+
+ // THEN the guts are closed
+ verify(guts).removeCallbacks(null)
+ verify(guts)
+ .closeControls(
+ /* leavebehinds = */ eq(true),
+ /* controls = */ eq(true),
+ /* x = */ anyInt(),
+ /* y = */ anyInt(),
+ /* force = */ eq(true),
+ )
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testShadeVisible_notVisible_gutsClosed() =
+ testScope.runTest {
+ // First, start with shade as visible
+ kosmos.setSceneTransition(Idle(Scenes.Shade))
+ runCurrent()
+
+ val guts: NotificationGuts = mock()
+ gutsManager.exposedGuts = guts
+
+ // WHEN the shade is no longer visible
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+ runCurrent()
+
+ // THEN the guts are closed
+ verify(guts).removeCallbacks(null)
+ verify(guts)
+ .closeControls(
+ /* leavebehinds = */ eq(true),
+ /* controls = */ eq(true),
+ /* x = */ anyInt(),
+ /* y = */ anyInt(),
+ /* force = */ eq(true),
+ )
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun testLockscreenShadeVisible_notVisible_listContainerReset() =
+ testScope.runTest {
+ // First, start out lockscreen or shade as visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ runCurrent()
+ clearInvocations(notificationListContainer)
+
+ // WHEN the lockscreen or shade is no longer visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false)
+ runCurrent()
+
+ // THEN the list container is reset
+ verify(notificationListContainer).resetExposedMenuView(anyBoolean(), anyBoolean())
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testShadeVisible_notVisible_listContainerReset() =
+ testScope.runTest {
+ // First, start with shade as visible
+ kosmos.setSceneTransition(Idle(Scenes.Shade))
+ runCurrent()
+ clearInvocations(notificationListContainer)
+
+ // WHEN the shade is no longer visible
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+ runCurrent()
+
+ // THEN the list container is reset
+ verify(notificationListContainer).resetExposedMenuView(anyBoolean(), anyBoolean())
+ }
+
+ @Test
+ fun testChangeDensityOrFontScale() {
+ val guts = spy(NotificationGuts(mContext))
+ whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
+ handler.post(((invocation.arguments[0] as Runnable)))
+ null
+ }
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
+
+ val realRow = createTestNotificationRow()
+ val menuItem = createTestMenuItem(realRow)
+
+ val row = spy(realRow)
+
+ whenever(row.windowToken).thenReturn(Binder())
+ whenever(row.guts).thenReturn(guts)
+ doNothing().whenever(row).ensureGutsInflated()
+
+ val realEntry = realRow.entry
+ val entry = spy(realEntry)
+
+ whenever(entry.row).thenReturn(row)
+ whenever(entry.guts).thenReturn(guts)
+
+ assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
+ executor.runAllReady()
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
+
+ // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
+ verify(row).setGutsView(any<MenuItem>())
+
+ row.onDensityOrFontScaleChanged()
+ gutsManager.onDensityOrFontScaleChanged(entry)
+
+ executor.runAllReady()
+
+ gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
+
+ verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
+
+ // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
+ verify(row, times(2)).setGutsView(any<MenuItem>())
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_camera() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_CAMERA)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_mic() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_RECORD_AUDIO)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_camera_mic() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_CAMERA)
+ ops.add(AppOpsManager.OP_RECORD_AUDIO)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_overlay() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_camera_mic_overlay() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_CAMERA)
+ ops.add(AppOpsManager.OP_RECORD_AUDIO)
+ ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_camera_overlay() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_CAMERA)
+ ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_mic_overlay() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_RECORD_AUDIO)
+ ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testInitializeNotificationInfoView_highPriority() {
+ val notificationInfoView: NotificationInfo = mock()
+ val row = spy(helper.createRow())
+ val entry = row.entry
+ NotificationEntryHelper.modifyRanking(entry)
+ .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
+ .setImportance(NotificationManager.IMPORTANCE_HIGH)
+ .build()
+
+ whenever(row.isNonblockable).thenReturn(false)
+ whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
+ val statusBarNotification = entry.sbn
+ gutsManager.initializeNotificationInfo(row, notificationInfoView)
+
+ verify(notificationInfoView)
+ .bindNotification(
+ any<PackageManager>(),
+ any<INotificationManager>(),
+ eq(onUserInteractionCallback),
+ eq(channelEditorDialogController),
+ eq(statusBarNotification.packageName),
+ any<NotificationChannel>(),
+ eq(entry),
+ any<NotificationInfo.OnSettingsClickListener>(),
+ any<NotificationInfo.OnAppSettingsClickListener>(),
+ any<UiEventLogger>(),
+ /* isDeviceProvisioned = */ eq(false),
+ /* isNonblockable = */ eq(false),
+ /* wasShownHighPriority = */ eq(true),
+ eq(assistantFeedbackController),
+ eq(metricsLogger),
+ )
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testInitializeNotificationInfoView_PassesAlongProvisionedState() {
+ val notificationInfoView: NotificationInfo = mock()
+ val row = spy(helper.createRow())
+ NotificationEntryHelper.modifyRanking(row.entry)
+ .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
+ .build()
+ whenever(row.isNonblockable).thenReturn(false)
+ val statusBarNotification = row.entry.sbn
+ val entry = row.entry
+
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+
+ gutsManager.initializeNotificationInfo(row, notificationInfoView)
+
+ verify(notificationInfoView)
+ .bindNotification(
+ any<PackageManager>(),
+ any<INotificationManager>(),
+ eq(onUserInteractionCallback),
+ eq(channelEditorDialogController),
+ eq(statusBarNotification.packageName),
+ any<NotificationChannel>(),
+ eq(entry),
+ any<NotificationInfo.OnSettingsClickListener>(),
+ any<NotificationInfo.OnAppSettingsClickListener>(),
+ any<UiEventLogger>(),
+ /* isDeviceProvisioned = */ eq(true),
+ /* isNonblockable = */ eq(false),
+ /* wasShownHighPriority = */ eq(false),
+ eq(assistantFeedbackController),
+ eq(metricsLogger),
+ )
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testInitializeNotificationInfoView_withInitialAction() {
+ val notificationInfoView: NotificationInfo = mock()
+ val row = spy(helper.createRow())
+ NotificationEntryHelper.modifyRanking(row.entry)
+ .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
+ .build()
+ whenever(row.isNonblockable).thenReturn(false)
+ val statusBarNotification = row.entry.sbn
+ val entry = row.entry
+
+ gutsManager.initializeNotificationInfo(row, notificationInfoView)
+
+ verify(notificationInfoView)
+ .bindNotification(
+ any<PackageManager>(),
+ any<INotificationManager>(),
+ eq(onUserInteractionCallback),
+ eq(channelEditorDialogController),
+ eq(statusBarNotification.packageName),
+ any<NotificationChannel>(),
+ eq(entry),
+ any<NotificationInfo.OnSettingsClickListener>(),
+ any<NotificationInfo.OnAppSettingsClickListener>(),
+ any<UiEventLogger>(),
+ /* isDeviceProvisioned = */ eq(false),
+ /* isNonblockable = */ eq(false),
+ /* wasShownHighPriority = */ eq(false),
+ eq(assistantFeedbackController),
+ eq(metricsLogger),
+ )
+ }
+
+ private fun createTestNotificationRow(): ExpandableNotificationRow {
+ val nb =
+ Notification.Builder(mContext, testNotificationChannel.id)
+ .setContentTitle("foo")
+ .setColorized(true)
+ .setColor(Color.RED)
+ .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+ .setSmallIcon(R.drawable.sym_def_app_icon)
+
+ try {
+ val row = helper.createRow(nb.build())
+ NotificationEntryHelper.modifyRanking(row.entry)
+ .setChannel(testNotificationChannel)
+ .build()
+ return row
+ } catch (e: Exception) {
+ fail()
+ }
+ }
+
+ private fun createTestMenuItem(
+ row: ExpandableNotificationRow
+ ): NotificationMenuRowPlugin.MenuItem {
+ val menuRow: NotificationMenuRowPlugin =
+ NotificationMenuRow(mContext, peopleNotificationIdentifier)
+ menuRow.createMenu(row, row.entry.sbn)
+
+ val menuItem = menuRow.getLongpressMenuItem(mContext)
+ assertNotNull(menuItem)
+ return menuItem
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
index 11dd587a04ad..cae990769444 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
@@ -31,10 +31,13 @@ import android.widget.LinearLayout
import androidx.annotation.ColorInt
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.statusBarConfigurationControllerStore
+import com.android.systemui.statusbar.data.repository.sysUiDarkIconDispatcherStore
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -50,16 +53,18 @@ import org.mockito.Mockito.verify
@SmallTest
class StatusOverlayHoverListenerTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private val viewOverlay = mock<ViewGroupOverlay>()
private val overlayCaptor = argumentCaptor<Drawable>()
- private val darkDispatcher = mock<SysuiDarkIconDispatcher>()
private val darkChange: MutableStateFlow<DarkChange> = MutableStateFlow(DarkChange.EMPTY)
+ private val darkDispatcher = kosmos.sysUiDarkIconDispatcherStore.forDisplay(context.displayId)
private val factory =
StatusOverlayHoverListenerFactory(
context.resources,
FakeConfigurationController(),
- darkDispatcher
+ kosmos.sysUiDarkIconDispatcherStore,
+ kosmos.statusBarConfigurationControllerStore,
)
private val view = TestableStatusContainer(context, viewOverlay)
@@ -186,7 +191,7 @@ class StatusOverlayHoverListenerTest : SysuiTestCase() {
/* action= */ action,
/* x= */ 0f,
/* y= */ 0f,
- /* metaState= */ 0
+ /* metaState= */ 0,
)
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
index 403c7c500426..43185fd08613 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
@@ -36,6 +36,9 @@ import java.util.Collection;
public interface DarkIconDispatcher {
int VERSION = 2;
+ /** Called when work should stop and resources should be cleaned up. */
+ default void stop() {}
+
/**
* Sets the dark area so {@link #applyDark} only affects the icons in the specified area.
*
diff --git a/packages/SystemUI/res/drawable/ic_qs_notes.xml b/packages/SystemUI/res/drawable/ic_qs_notes.xml
new file mode 100644
index 000000000000..6c1d2e49369c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_notes.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the Lice/packages/SystemUI/res/drawable/ic_qs_notes.xmlnse.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/white" android:pathData="M499,673L834,338Q834,338 834,338Q834,338 834,338L782,286Q782,286 782,286Q782,286 782,286L447,621L499,673ZM238,760Q138,755 89,718Q40,681 40,611Q40,546 93.5,505.5Q147,465 242,457Q281,454 300.5,444.5Q320,435 320,418Q320,392 290.5,379Q261,366 193,360L200,280Q303,288 351.5,321.5Q400,355 400,418Q400,471 361.5,501Q323,531 248,537Q184,542 152,560.5Q120,579 120,611Q120,646 148,661.5Q176,677 242,680L238,760ZM518,767L353,602L735,220Q755,200 782.5,200Q810,200 830,220L900,290Q920,310 920,337.5Q920,365 900,385L518,767ZM359,800Q342,804 329,791Q316,778 320,761L353,602L518,767L359,800Z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 65005f840598..572f063c20c4 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -32,6 +32,34 @@
android:id="@+id/min_edge_guideline"
app:layout_constraintGuide_begin="@dimen/overlay_action_container_minimum_edge_spacing"
android:orientation="vertical"/>
+ <!-- This toast-like indication layout was forked from text_toast.xml and will have the same
+ appearance as system toast. -->
+ <FrameLayout
+ android:id="@+id/indication_container"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:maxWidth="@*android:dimen/toast_width"
+ android:background="@android:drawable/toast_frame"
+ android:elevation="@*android:dimen/toast_elevation"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent">
+ <TextView
+ android:id="@+id/indication_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:textAppearance="@*android:style/TextAppearance.Toast"/>
+ </FrameLayout>
<!-- Negative horizontal margin because this container background must render beyond the thing
it's constrained by (the actions themselves). -->
<FrameLayout
@@ -47,7 +75,7 @@
app:layout_constraintStart_toStartOf="@id/min_edge_guideline"
app:layout_constraintTop_toTopOf="@id/actions_container"
app:layout_constraintEnd_toEndOf="@id/actions_container"
- app:layout_constraintBottom_toBottomOf="parent"/>
+ app:layout_constraintBottom_toTopOf="@id/indication_container"/>
<HorizontalScrollView
android:id="@+id/actions_container"
android:layout_width="0dp"
@@ -144,7 +172,7 @@
android:visibility="gone"
android:elevation="7dp"
android:padding="8dp"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/indication_container"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
deleted file mode 100644
index dc560bf2fab7..000000000000
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <!-- Scrollview is necessary to fit everything in landscape layout -->
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/dialog_side_padding"
- android:paddingEnd="@dimen/dialog_side_padding"
- android:paddingTop="@dimen/dialog_top_padding"
- android:paddingBottom="@dimen/dialog_bottom_padding"
- android:orientation="vertical">
-
- <!-- Header -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center">
- <ImageView
- android:layout_width="@dimen/screenrecord_logo_size"
- android:layout_height="@dimen/screenrecord_logo_size"
- android:src="@drawable/ic_screenrecord"
- android:tint="@color/screenrecord_icon_color"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Dialog.Title"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:text="@string/screenrecord_permission_dialog_title"
- android:layout_marginTop="22dp"
- android:layout_marginBottom="15dp"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
- android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
- android:gravity="center"
- android:layout_marginBottom="20dp"/>
-
- <!-- Options -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <ImageView
- android:layout_width="@dimen/screenrecord_option_icon_size"
- android:layout_height="@dimen/screenrecord_option_icon_size"
- android:src="@drawable/ic_mic_26dp"
- android:tint="?android:attr/textColorSecondary"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:layout_marginEnd="@dimen/screenrecord_option_padding"/>
- <Spinner
- android:id="@+id/screen_recording_options"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:layout_weight="1"
- android:popupBackground="@drawable/screenrecord_spinner_background"
- android:textColor="?androidprv:attr/materialColorOnSurface"
- android:dropDownWidth="274dp"
- android:prompt="@string/screenrecord_audio_label"/>
- <Switch
- android:layout_width="wrap_content"
- android:minWidth="48dp"
- android:layout_height="48dp"
- android:layout_weight="0"
- android:layout_gravity="end"
- android:contentDescription="@string/screenrecord_audio_label"
- android:id="@+id/screenrecord_audio_switch"
- style="@style/ScreenRecord.Switch"/>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="@dimen/screenrecord_option_padding">
- <ImageView
- android:layout_width="@dimen/screenrecord_option_icon_size"
- android:layout_height="@dimen/screenrecord_option_icon_size"
- android:layout_weight="0"
- android:src="@drawable/ic_touch"
- android:tint="?android:attr/textColorSecondary"
- android:layout_gravity="center"
- android:layout_marginEnd="@dimen/screenrecord_option_padding"/>
- <TextView
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:layout_weight="1"
- android:layout_gravity="fill_vertical"
- android:gravity="center_vertical"
- android:text="@string/screenrecord_taps_label"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?androidprv:attr/materialColorOnSurface"
- android:importantForAccessibility="no"/>
- <Switch
- android:layout_width="wrap_content"
- android:minWidth="48dp"
- android:layout_height="48dp"
- android:layout_weight="0"
- android:id="@+id/screenrecord_taps_switch"
- android:contentDescription="@string/screenrecord_taps_label"
- style="@style/ScreenRecord.Switch"/>
- </LinearLayout>
- </LinearLayout>
-
- <!-- Buttons -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="36dp">
- <TextView
- android:id="@+id/button_cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:layout_gravity="start"
- android:text="@string/cancel"
- style="@style/Widget.Dialog.Button.BorderButton" />
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
-
- <TextView
- android:id="@+id/button_start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:layout_gravity="end"
- android:text="@string/screenrecord_continue"
- style="@style/Widget.Dialog.Button" />
- </LinearLayout>
- </LinearLayout>
- </ScrollView>
-</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index 9b3af52e9704..7c266e60b503 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -65,7 +65,7 @@
android:background="@drawable/volume_drawer_selection_bg"
android:contentDescription="@string/volume_ringer_change"
android:gravity="center"
- android:padding="10dp"
+ android:padding="@dimen/volume_dialog_ringer_horizontal_padding"
android:src="@drawable/ic_volume_media"
android:tint="?androidprv:attr/materialColorOnPrimary" />
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c8ef093c40db..82c8c44f1efe 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -116,7 +116,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices,notes
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1766cdf8c804..af6eacbe499f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1004,6 +1004,9 @@
<!-- QuickSettings: Tool name for hearing devices dialog related tools [CHAR LIMIT=40] [BACKUP_MESSAGE_ID=8916875614623730005]-->
<string name="quick_settings_hearing_devices_live_caption_title">Live Caption</string>
+ <!-- QuickSettings: Notes tile. The label of a quick settings tile for launching the default notes taking app. [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_notes_label">Note</string>
+
<!--- Title of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=150] -->
<string name="sensor_privacy_start_use_mic_dialog_title">Unblock device microphone?</string>
<!--- Title of dialog triggered if the camera is disabled but an app tried to access it. [CHAR LIMIT=150] -->
@@ -1518,7 +1521,7 @@
<string name="no_unseen_notif_text">No new notifications</string>
<!-- Title of heads up notification for adaptive notifications user education. [CHAR LIMIT=60] -->
- <string name="adaptive_notification_edu_hun_title">Notification cooldown is on</string>
+ <string name="adaptive_notification_edu_hun_title">Notification cooldown is now on</string>
<!-- Text of heads up notification for adaptive notifications user education. [CHAR LIMIT=100] -->
<string name="adaptive_notification_edu_hun_text">Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once.</string>
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index ad09b466dd3e..d885e00fbe82 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -348,4 +348,14 @@
<item>Off</item>
<item>On</item>
</string-array>
+
+ <!-- State names for notes tile: unavailable, off, on.
+ This subtitle is shown when the tile is in that particular state but does not set its own
+ subtitle, so some of these may never appear on screen. They should still be translated as
+ if they could appear. [CHAR LIMIT=32] -->
+ <string-array name="tile_states_notes">
+ <item>Unavailable</item>
+ <item>Off</item>
+ <item>On</item>
+ </string-array>
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 283e4556d05c..83ca496dbef2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -115,23 +115,23 @@ oneway interface IOverviewProxy {
/**
* Sent when {@link TaskbarDelegate#checkNavBarModes} is called.
*/
- void checkNavBarModes() = 30;
+ void checkNavBarModes(int displayId) = 30;
/**
* Sent when {@link TaskbarDelegate#finishBarAnimations} is called.
*/
- void finishBarAnimations() = 31;
+ void finishBarAnimations(int displayId) = 31;
/**
* Sent when {@link TaskbarDelegate#touchAutoDim} is called. {@param reset} is true, when auto
* dim is reset after a timeout.
*/
- void touchAutoDim(boolean reset) = 32;
+ void touchAutoDim(int displayid, boolean reset) = 32;
/**
* Sent when {@link TaskbarDelegate#transitionTo} is called.
*/
- void transitionTo(int barMode, boolean animate) = 33;
+ void transitionTo(int displayId, int barMode, boolean animate) = 33;
/**
* Sent when {@link TaskbarDelegate#appTransitionPending} is called.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 7ec977a8d6aa..9e8cabf141ed 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -243,10 +243,6 @@ public class Task {
public Rect appBounds;
- // Last snapshot data, only used for recent tasks
- public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
- new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData();
-
@ViewDebug.ExportedProperty(category="recents")
public boolean isVisible;
@@ -283,7 +279,6 @@ public class Task {
public Task(Task other) {
this(other.key, other.colorPrimary, other.colorBackground, other.isDockable,
other.isLocked, other.taskDescription, other.topActivity);
- lastSnapshotData.set(other.lastSnapshotData);
positionInParent = other.positionInParent;
appBounds = other.appBounds;
isVisible = other.isVisible;
@@ -315,33 +310,10 @@ public class Task {
: key.baseIntent.getComponent();
}
- public void setLastSnapshotData(ActivityManager.RecentTaskInfo rawTask) {
- lastSnapshotData.set(rawTask.lastSnapshotData);
- }
-
public TaskKey getKey() {
return key;
}
- /**
- * Returns the visible width to height ratio. Returns 0f if snapshot data is not available.
- */
- public float getVisibleThumbnailRatio(boolean clipInsets) {
- if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) {
- return 0f;
- }
-
- float availableWidth = lastSnapshotData.taskSize.x;
- float availableHeight = lastSnapshotData.taskSize.y;
- if (clipInsets) {
- availableWidth -=
- (lastSnapshotData.contentInsets.left + lastSnapshotData.contentInsets.right);
- availableHeight -=
- (lastSnapshotData.contentInsets.top + lastSnapshotData.contentInsets.bottom);
- }
- return availableWidth / availableHeight;
- }
-
@Override
public boolean equals(Object o) {
if (o == this) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8b593701540b..f98890ec9c5d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -121,6 +121,7 @@ import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -473,6 +474,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
+ @Deprecated
private final SparseBooleanArray mUserIsUnlocked = new SparseBooleanArray();
private final SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
private final SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
@@ -2688,7 +2690,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* @see Intent#ACTION_USER_UNLOCKED
*/
public boolean isUserUnlocked(int userId) {
- return mUserIsUnlocked.get(userId);
+ if (Flags.userEncryptedSource()) {
+ return mUserManager.isUserUnlocked(userId);
+ } else {
+ return mUserIsUnlocked.get(userId);
+ }
}
/**
@@ -4213,7 +4219,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println("ActiveUnlockRunning="
+ mTrustManager.isActiveUnlockRunning(mSelectedUserInteractor.getSelectedUserId()));
- pw.println("userUnlockedCache[userid=" + userId + "]=" + isUserUnlocked(userId));
+ pw.println("userUnlockedCache[userid=" + userId + "]=" + mUserIsUnlocked.get(userId));
pw.println("actualUserUnlocked[userid=" + userId + "]="
+ mUserManager.isUserUnlocked(userId));
new DumpsysTableLogger(
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index bebfd859f9ed..cd19aaac6831 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -116,7 +116,7 @@ constructor(
fun logUpdateLockScreenUserLockedMsg(
userId: Int,
- userUnlocked: Boolean,
+ userStorageUnlocked: Boolean,
encryptedOrLockdown: Boolean,
) {
buffer.log(
@@ -124,12 +124,12 @@ constructor(
LogLevel.DEBUG,
{
int1 = userId
- bool1 = userUnlocked
+ bool1 = userStorageUnlocked
bool2 = encryptedOrLockdown
},
{
"updateLockScreenUserLockedMsg userId=$int1 " +
- "userUnlocked:$bool1 encryptedOrLockdown:$bool2"
+ "userStorageUnlocked:$bool1 encryptedOrLockdown:$bool2"
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index f8b445ba069e..3cf400aa5c16 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -38,7 +38,6 @@ import android.view.IWindowManager;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
@@ -138,10 +137,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
mWindowMagnifierCallback,
mSysUiState,
mSecureSettings,
- scvhSupplier,
- new SfVsyncFrameCallbackProvider(),
- WindowManagerGlobal::getWindowSession,
- mViewCaptureAwareWindowManager);
+ scvhSupplier);
}
}
@@ -407,7 +403,8 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
}
}
- boolean isMagnificationSettingsPanelShowing(int displayId) {
+ @MainThread
+ private boolean isMagnificationSettingsPanelShowing(int displayId) {
final MagnificationSettingsController magnificationSettingsController =
mMagnificationSettingsSupplier.get(displayId);
if (magnificationSettingsController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 0883a0611d15..7d5cf232bcb9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -54,11 +54,8 @@ import android.util.Range;
import android.util.Size;
import android.util.SparseArray;
import android.util.TypedValue;
-import android.view.Choreographer;
import android.view.Display;
import android.view.Gravity;
-import android.view.IWindow;
-import android.view.IWindowSession;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
@@ -80,10 +77,8 @@ import android.widget.ImageView;
import androidx.annotation.UiThread;
import androidx.core.math.MathUtils;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.accessibility.common.MagnificationConstants;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.Flags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.res.R;
@@ -127,7 +122,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private final SurfaceControl.Transaction mTransaction;
private final WindowManager mWm;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private float mScale;
private int mSettingsButtonIndex = MagnificationSize.DEFAULT;
@@ -219,11 +213,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private int mMinWindowSize;
private final WindowMagnificationAnimationController mAnimationController;
- private final Supplier<IWindowSession> mGlobalWindowSessionSupplier;
- private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
private final MagnificationGestureDetector mGestureDetector;
private int mBounceEffectDuration;
- private final Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback;
private Locale mLocale;
private NumberFormat mPercentFormat;
private float mBounceEffectAnimationScale;
@@ -258,18 +249,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
@NonNull WindowMagnifierCallback callback,
SysUiState sysUiState,
SecureSettings secureSettings,
- Supplier<SurfaceControlViewHost> scvhSupplier,
- SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
- Supplier<IWindowSession> globalWindowSessionSupplier,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ Supplier<SurfaceControlViewHost> scvhSupplier) {
mContext = context;
mHandler = handler;
mAnimationController = animationController;
- mAnimationController.setOnAnimationEndRunnable(() -> {
- if (Flags.createWindowlessWindowMagnifier()) {
- notifySourceBoundsChanged();
- }
- });
+ mAnimationController.setOnAnimationEndRunnable(this::notifySourceBoundsChanged);
mAnimationController.setWindowMagnificationController(this);
mWindowMagnifierCallback = callback;
mSysUiState = sysUiState;
@@ -283,7 +267,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mWm = context.getSystemService(WindowManager.class);
mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mResources = mContext.getResources();
mScale = secureSettings.getFloatForUser(
@@ -313,76 +296,31 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mGestureDetector =
new MagnificationGestureDetector(mContext, handler, this);
mWindowInsetChangeRunnable = this::onWindowInsetChanged;
- mGlobalWindowSessionSupplier = globalWindowSessionSupplier;
- mSfVsyncFrameProvider = sfVsyncFrameProvider;
// Initialize listeners.
- if (Flags.createWindowlessWindowMagnifier()) {
- mMirrorViewRunnable = new Runnable() {
- final Rect mPreviousBounds = new Rect();
-
- @Override
- public void run() {
- if (mMirrorView != null) {
- if (mPreviousBounds.width() != mMirrorViewBounds.width()
- || mPreviousBounds.height() != mMirrorViewBounds.height()) {
- mMirrorView.setSystemGestureExclusionRects(Collections.singletonList(
- new Rect(0, 0, mMirrorViewBounds.width(),
- mMirrorViewBounds.height())));
- mPreviousBounds.set(mMirrorViewBounds);
- }
- updateSystemUIStateIfNeeded();
- mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(
- mDisplayId, mMirrorViewBounds);
- }
- }
- };
-
- mMirrorSurfaceViewLayoutChangeListener =
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
- mMirrorView.post(this::applyTapExcludeRegion);
+ mMirrorViewRunnable = new Runnable() {
+ final Rect mPreviousBounds = new Rect();
- mMirrorViewGeometryVsyncCallback = null;
- } else {
- mMirrorViewRunnable = () -> {
+ @Override
+ public void run() {
if (mMirrorView != null) {
- final Rect oldViewBounds = new Rect(mMirrorViewBounds);
- mMirrorView.getBoundsOnScreen(mMirrorViewBounds);
- if (oldViewBounds.width() != mMirrorViewBounds.width()
- || oldViewBounds.height() != mMirrorViewBounds.height()) {
+ if (mPreviousBounds.width() != mMirrorViewBounds.width()
+ || mPreviousBounds.height() != mMirrorViewBounds.height()) {
mMirrorView.setSystemGestureExclusionRects(Collections.singletonList(
- new Rect(0, 0,
- mMirrorViewBounds.width(), mMirrorViewBounds.height())));
+ new Rect(0, 0, mMirrorViewBounds.width(),
+ mMirrorViewBounds.height())));
+ mPreviousBounds.set(mMirrorViewBounds);
}
updateSystemUIStateIfNeeded();
mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(
mDisplayId, mMirrorViewBounds);
}
- };
-
- mMirrorSurfaceViewLayoutChangeListener =
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
- mMirrorView.post(this::applyTapExcludeRegion);
-
- mMirrorViewGeometryVsyncCallback =
- l -> {
- if (isActivated() && mMirrorSurface != null && calculateSourceBounds(
- mMagnificationFrame, mScale)) {
- // The final destination for the magnification surface should be at 0,0
- // since the ViewRootImpl's position will change
- mTmpRect.set(0, 0, mMagnificationFrame.width(),
- mMagnificationFrame.height());
- mTransaction.setGeometry(mMirrorSurface, mSourceBounds, mTmpRect,
- Surface.ROTATION_0).apply();
-
- // Notify source bounds change when the magnifier is not animating.
- if (!mAnimationController.isAnimating()) {
- mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId,
- mSourceBounds);
- }
- }
- };
- }
+ }
+ };
+
+ mMirrorSurfaceViewLayoutChangeListener =
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ mMirrorView.post(this::applyTouchableRegion);
mMirrorViewLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@@ -463,7 +401,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (isActivated()) {
updateDimensions();
- applyTapExcludeRegion();
+ applyTouchableRegion();
}
if (!enable) {
@@ -513,9 +451,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (mMirrorView != null) {
mHandler.removeCallbacks(mMirrorViewRunnable);
mMirrorView.removeOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
- if (!Flags.createWindowlessWindowMagnifier()) {
- mViewCaptureAwareWindowManager.removeView(mMirrorView);
- }
mMirrorView = null;
}
@@ -624,11 +559,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (!isActivated()) return;
LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams();
params.accessibilityTitle = getAccessibilityWindowTitle();
- if (Flags.createWindowlessWindowMagnifier()) {
- mSurfaceControlViewHost.relayout(params);
- } else {
- mWm.updateViewLayout(mMirrorView, params);
- }
+ mSurfaceControlViewHost.relayout(params);
}
/**
@@ -678,62 +609,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
return (oldRotation - newRotation + 4) % 4 * 90;
}
- private void createMirrorWindow() {
- if (Flags.createWindowlessWindowMagnifier()) {
- createWindowlessMirrorWindow();
- return;
- }
-
- // The window should be the size the mirrored surface will be but also add room for the
- // border and the drag handle.
- int windowWidth = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
- int windowHeight = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin;
-
- LayoutParams params = new LayoutParams(
- windowWidth, windowHeight,
- LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
- LayoutParams.FLAG_NOT_TOUCH_MODAL
- | LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSPARENT);
- params.gravity = Gravity.TOP | Gravity.LEFT;
- params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
- params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
- params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- params.receiveInsetsIgnoringZOrder = true;
- params.setTitle(mContext.getString(R.string.magnification_window_title));
- params.accessibilityTitle = getAccessibilityWindowTitle();
-
- mMirrorView = LayoutInflater.from(mContext).inflate(R.layout.window_magnifier_view, null);
- mMirrorSurfaceView = mMirrorView.findViewById(R.id.surface_view);
-
- mMirrorBorderView = mMirrorView.findViewById(R.id.magnification_inner_border);
-
- // Allow taps to go through to the mirror SurfaceView below.
- mMirrorSurfaceView.addOnLayoutChangeListener(mMirrorSurfaceViewLayoutChangeListener);
-
- mMirrorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
- mMirrorView.addOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
- mMirrorView.setAccessibilityDelegate(new MirrorWindowA11yDelegate());
- mMirrorView.setOnApplyWindowInsetsListener((v, insets) -> {
- if (!mHandler.hasCallbacks(mWindowInsetChangeRunnable)) {
- mHandler.post(mWindowInsetChangeRunnable);
- }
- return v.onApplyWindowInsets(insets);
- });
-
- mViewCaptureAwareWindowManager.addView(mMirrorView, params);
-
- SurfaceHolder holder = mMirrorSurfaceView.getHolder();
- holder.addCallback(this);
- holder.setFormat(PixelFormat.RGBA_8888);
- addDragTouchListeners();
- }
-
private void createWindowlessMirrorWindow() {
// The window should be the size the mirrored surface will be but also add room for the
// border and the drag handle.
@@ -802,62 +677,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
}
- private void applyTapExcludeRegion() {
- if (Flags.createWindowlessWindowMagnifier()) {
- applyTouchableRegion();
- return;
- }
-
- // Sometimes this can get posted and run after deleteWindowMagnification() is called.
- if (mMirrorView == null) return;
-
- final Region tapExcludeRegion = calculateTapExclude();
- final IWindow window = IWindow.Stub.asInterface(mMirrorView.getWindowToken());
- try {
- IWindowSession session = mGlobalWindowSessionSupplier.get();
- session.updateTapExcludeRegion(window, tapExcludeRegion);
- } catch (RemoteException e) {
- }
- }
-
- private Region calculateTapExclude() {
- Region regionInsideDragBorder = new Region(mBorderDragSize, mBorderDragSize,
- mMirrorView.getWidth() - mBorderDragSize,
- mMirrorView.getHeight() - mBorderDragSize);
-
- Region tapExcludeRegion = new Region();
-
- Rect dragArea = new Rect();
- mDragView.getHitRect(dragArea);
-
- Rect topLeftArea = new Rect();
- mTopLeftCornerView.getHitRect(topLeftArea);
-
- Rect topRightArea = new Rect();
- mTopRightCornerView.getHitRect(topRightArea);
-
- Rect bottomLeftArea = new Rect();
- mBottomLeftCornerView.getHitRect(bottomLeftArea);
-
- Rect bottomRightArea = new Rect();
- mBottomRightCornerView.getHitRect(bottomRightArea);
-
- Rect closeArea = new Rect();
- mCloseView.getHitRect(closeArea);
-
- // add tapExcludeRegion for Drag or close
- tapExcludeRegion.op(dragArea, Region.Op.UNION);
- tapExcludeRegion.op(topLeftArea, Region.Op.UNION);
- tapExcludeRegion.op(topRightArea, Region.Op.UNION);
- tapExcludeRegion.op(bottomLeftArea, Region.Op.UNION);
- tapExcludeRegion.op(bottomRightArea, Region.Op.UNION);
- tapExcludeRegion.op(closeArea, Region.Op.UNION);
-
- regionInsideDragBorder.op(tapExcludeRegion, Region.Op.DIFFERENCE);
-
- return regionInsideDragBorder;
- }
-
private void applyTouchableRegion() {
// Sometimes this can get posted and run after deleteWindowMagnification() is called.
if (mMirrorView == null) return;
@@ -1085,13 +904,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
* {@link #mMagnificationFrame}.
*/
private void modifyWindowMagnification(boolean computeWindowSize) {
- if (Flags.createWindowlessWindowMagnifier()) {
- updateMirrorSurfaceGeometry();
- updateWindowlessMirrorViewLayout(computeWindowSize);
- } else {
- mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback);
- updateMirrorViewLayout(computeWindowSize);
- }
+ updateMirrorSurfaceGeometry();
+ updateWindowlessMirrorViewLayout(computeWindowSize);
}
/**
@@ -1169,58 +983,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mMirrorViewRunnable.run();
}
- /**
- * Updates the layout params of MirrorView based on the size of {@link #mMagnificationFrame}
- * and translates MirrorView position when the view is moved close to the screen edges;
- *
- * @param computeWindowSize set to {@code true} to compute window size with
- * {@link #mMagnificationFrame}.
- */
- private void updateMirrorViewLayout(boolean computeWindowSize) {
- if (!isActivated()) {
- return;
- }
- final int maxMirrorViewX = mWindowBounds.width() - mMirrorView.getWidth();
- final int maxMirrorViewY = mWindowBounds.height() - mMirrorView.getHeight();
-
- LayoutParams params =
- (LayoutParams) mMirrorView.getLayoutParams();
- params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
- params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
- if (computeWindowSize) {
- params.width = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
- params.height = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin;
- }
-
- // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
- // able to move close to the screen edges.
- final float translationX;
- final float translationY;
- if (params.x < 0) {
- translationX = Math.max(params.x, -mOuterBorderSize);
- } else if (params.x > maxMirrorViewX) {
- translationX = Math.min(params.x - maxMirrorViewX, mOuterBorderSize);
- } else {
- translationX = 0;
- }
- if (params.y < 0) {
- translationY = Math.max(params.y, -mOuterBorderSize);
- } else if (params.y > maxMirrorViewY) {
- translationY = Math.min(params.y - maxMirrorViewY, mOuterBorderSize);
- } else {
- translationY = 0;
- }
- mMirrorView.setTranslationX(translationX);
- mMirrorView.setTranslationY(translationY);
- mWm.updateViewLayout(mMirrorView, params);
-
- // If they are not dragging the handle, we can move the drag handle immediately without
- // disruption. But if they are dragging it, we avoid moving until the end of the drag.
- if (!mIsDragging) {
- mMirrorView.post(this::maybeRepositionButton);
- }
- }
-
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v == mDragView
@@ -1474,7 +1236,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
calculateMagnificationFrameBoundary();
updateMagnificationFramePosition((int) offsetX, (int) offsetY);
if (!isActivated()) {
- createMirrorWindow();
+ createWindowlessMirrorWindow();
showControls();
applyResourcesValues();
} else {
@@ -1766,7 +1528,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (newGravity != layoutParams.gravity) {
layoutParams.gravity = newGravity;
mDragView.setLayoutParams(layoutParams);
- mDragView.post(this::applyTapExcludeRegion);
+ mDragView.post(this::applyTouchableRegion);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
index b8ff3bb43203..178e1112fa6f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
@@ -24,6 +24,7 @@ import com.android.systemui.biometrics.shared.model.SensorLocation
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -42,7 +43,7 @@ constructor(
@Application private val applicationScope: CoroutineScope,
@Application private val context: Context,
repository: FingerprintPropertyRepository,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
displayStateInteractor: DisplayStateInteractor,
udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
@@ -73,15 +74,12 @@ constructor(
* - device's natural orientation
*/
private val unscaledSensorLocation: Flow<SensorLocationInternal> =
- combine(
- repository.sensorLocations,
- uniqueDisplayId,
- ) { locations, displayId ->
+ combine(repository.sensorLocations, uniqueDisplayId) { locations, displayId ->
// Devices without multiple physical displays do not use the display id as the key;
// instead, the key is an empty string.
locations.getOrDefault(
displayId,
- locations.getOrDefault("", SensorLocationInternal.DEFAULT)
+ locations.getOrDefault("", SensorLocationInternal.DEFAULT),
)
}
@@ -92,16 +90,15 @@ constructor(
* - device's natural orientation
*/
val sensorLocation: Flow<SensorLocation> =
- combine(
+ combine(unscaledSensorLocation, configurationInteractor.scaleForResolution) {
unscaledSensorLocation,
- configurationInteractor.scaleForResolution,
- ) { unscaledSensorLocation, scale ->
+ scale ->
val sensorLocation =
SensorLocation(
naturalCenterX = unscaledSensorLocation.sensorLocationX,
naturalCenterY = unscaledSensorLocation.sensorLocationY,
naturalRadius = unscaledSensorLocation.sensorRadius,
- scale = scale
+ scale = scale,
)
sensorLocation
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index e178c093b746..7039d5e7d9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.bouncer.domain.interactor
import android.app.StatusBarManager.SESSION_KEYGUARD
+import com.android.app.tracing.coroutines.asyncTraced as async
import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.UiEventLogger
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
@@ -39,9 +40,9 @@ import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.asyncTraced as async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
@@ -65,7 +66,7 @@ constructor(
private val uiEventLogger: UiEventLogger,
private val sessionTracker: SessionTracker,
sceneBackInteractor: SceneBackInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
) {
private val _onIncorrectBouncerInput = MutableSharedFlow<Unit>()
val onIncorrectBouncerInput: SharedFlow<Unit> = _onIncorrectBouncerInput
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
index 4fe6fc69e8be..f50a2ab1d803 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.bouncer.ui.viewmodel
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
@@ -33,16 +32,14 @@ import kotlinx.coroutines.flow.map
*/
class BouncerUserActionsViewModel
@AssistedInject
-constructor(
- private val bouncerInteractor: BouncerInteractor,
-) : UserActionsViewModel() {
+constructor(private val bouncerInteractor: BouncerInteractor) : UserActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
bouncerInteractor.dismissDestination
.map { prevScene ->
mapOf(
Back to UserActionResult(prevScene),
- Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
+ Swipe.Down to UserActionResult(prevScene),
)
}
.collect { actions -> setActions(actions) }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt
new file mode 100644
index 000000000000..ddd6bc9ef16b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.clipboardoverlay
+
+/** Interface for listening to indication text changed from [ClipboardIndicationProvider]. */
+interface ClipboardIndicationCallback {
+
+ /** Notifies the indication text changed. */
+ fun onIndicationTextChanged(text: CharSequence)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt
new file mode 100644
index 000000000000..be3272369d46
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.clipboardoverlay
+
+/** Interface to provide the clipboard indication to be shown under the overlay. */
+interface ClipboardIndicationProvider {
+
+ /**
+ * Gets the indication text async.
+ *
+ * @param callback callback for getting the indication text.
+ */
+ fun getIndicationText(callback: ClipboardIndicationCallback)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProviderImpl.kt
index 03bc9350674b..da94d5b518b7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProviderImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer
+package com.android.systemui.clipboardoverlay
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import javax.inject.Inject
-/** Flags related to media tap-to-transfer. */
@SysUISingleton
-class MediaTttFlags @Inject constructor(private val featureFlags: FeatureFlags) {
- /** */
- fun isMediaTttEnabled(): Boolean = featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER)
+open class ClipboardIndicationProviderImpl @Inject constructor() : ClipboardIndicationProvider {
+
+ override fun getIndicationText(callback: ClipboardIndicationCallback) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 65c01ed9eecd..ac747845267c 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -21,6 +21,7 @@ import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS;
import static com.android.systemui.Flags.clipboardImageTimeout;
import static com.android.systemui.Flags.clipboardSharedTransitions;
+import static com.android.systemui.Flags.showClipboardIndication;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISSED_OTHER;
@@ -99,6 +100,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
private final ClipboardTransitionExecutor mTransitionExecutor;
private final ClipboardOverlayView mView;
+ private final ClipboardIndicationProvider mClipboardIndicationProvider;
private Runnable mOnSessionCompleteListener;
private Runnable mOnRemoteCopyTapped;
@@ -173,6 +175,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
}
};
+ private ClipboardIndicationCallback mIndicationCallback = new ClipboardIndicationCallback() {
+ @Override
+ public void onIndicationTextChanged(@NonNull CharSequence text) {
+ mView.setIndicationText(text);
+ }
+ };
+
@Inject
public ClipboardOverlayController(@OverlayWindowContext Context context,
ClipboardOverlayView clipboardOverlayView,
@@ -185,11 +194,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
@Background Executor bgExecutor,
ClipboardImageLoader clipboardImageLoader,
ClipboardTransitionExecutor transitionExecutor,
+ ClipboardIndicationProvider clipboardIndicationProvider,
UiEventLogger uiEventLogger) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mClipboardImageLoader = clipboardImageLoader;
mTransitionExecutor = transitionExecutor;
+ mClipboardIndicationProvider = clipboardIndicationProvider;
mClipboardLogger = new ClipboardLogger(uiEventLogger);
@@ -288,6 +299,9 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
boolean shouldAnimate = !model.dataMatches(mClipboardModel) || wasExiting;
mClipboardModel = model;
mClipboardLogger.setClipSource(mClipboardModel.getSource());
+ if (showClipboardIndication()) {
+ mClipboardIndicationProvider.getIndicationText(mIndicationCallback);
+ }
if (clipboardImageTimeout()) {
if (shouldAnimate) {
reset();
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 1762d82b3237..7e4d76280909 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -18,6 +18,8 @@ package com.android.systemui.clipboardoverlay;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.systemui.Flags.showClipboardIndication;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -53,6 +55,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -103,6 +106,8 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
private View mShareChip;
private View mRemoteCopyChip;
private View mActionContainerBackground;
+ private View mIndicationContainer;
+ private TextView mIndicationText;
private View mDismissButton;
private LinearLayout mActionContainer;
private ClipboardOverlayCallbacks mClipboardCallbacks;
@@ -136,6 +141,8 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
mShareChip = requireViewById(R.id.share_chip);
mRemoteCopyChip = requireViewById(R.id.remote_copy_chip);
mDismissButton = requireViewById(R.id.dismiss_button);
+ mIndicationContainer = requireViewById(R.id.indication_container);
+ mIndicationText = mIndicationContainer.findViewById(R.id.indication_text);
bindDefaultActionChips();
@@ -208,6 +215,14 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
}
}
+ void setIndicationText(CharSequence text) {
+ mIndicationText.setText(text);
+
+ // Set the visibility of clipboard indication based on the text is empty or not.
+ int visibility = text.isEmpty() ? View.GONE : View.VISIBLE;
+ mIndicationContainer.setVisibility(visibility);
+ }
+
void setMinimized(boolean minimized) {
if (minimized) {
mMinimizedPreview.setVisibility(View.VISIBLE);
@@ -221,6 +236,18 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
mPreviewBorder.setVisibility(View.VISIBLE);
mActionContainer.setVisibility(View.VISIBLE);
}
+
+ if (showClipboardIndication()) {
+ // Adjust the margin of clipboard indication based on the minimized state.
+ int marginStart = minimized ? getResources().getDimensionPixelSize(
+ R.dimen.overlay_action_container_margin_horizontal)
+ : getResources().getDimensionPixelSize(
+ R.dimen.overlay_action_container_minimum_edge_spacing);
+ ConstraintLayout.LayoutParams params =
+ (ConstraintLayout.LayoutParams) mIndicationContainer.getLayoutParams();
+ params.setMarginStart(marginStart);
+ mIndicationContainer.setLayoutParams(params);
+ }
}
void setInsets(WindowInsets insets, int orientation) {
@@ -313,6 +340,7 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
setTranslationX(0);
setAlpha(0);
mActionContainerBackground.setVisibility(View.GONE);
+ mIndicationContainer.setVisibility(View.GONE);
mDismissButton.setVisibility(View.GONE);
mShareChip.setVisibility(View.GONE);
mRemoteCopyChip.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayOverrideModule.kt
index 527819c73e2f..c81f0d98f3ad 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayOverrideModule.kt
@@ -15,18 +15,26 @@
*/
package com.android.systemui.clipboardoverlay.dagger
+import com.android.systemui.clipboardoverlay.ClipboardIndicationProvider
+import com.android.systemui.clipboardoverlay.ClipboardIndicationProviderImpl
import com.android.systemui.clipboardoverlay.ClipboardOverlaySuppressionController
import com.android.systemui.clipboardoverlay.ClipboardOverlaySuppressionControllerImpl
import dagger.Binds
import dagger.Module
-/** Dagger Module for code in the clipboard overlay package. */
+/** Dagger Module to provide default implementations which could be overridden. */
@Module
-interface ClipboardOverlaySuppressionModule {
+interface ClipboardOverlayOverrideModule {
/** Provides implementation for [ClipboardOverlaySuppressionController]. */
@Binds
fun provideClipboardOverlaySuppressionController(
clipboardOverlaySuppressionControllerImpl: ClipboardOverlaySuppressionControllerImpl
): ClipboardOverlaySuppressionController
+
+ /** Provides implementation for [ClipboardIndicationProvider]. */
+ @Binds
+ fun provideClipboardIndicationProvider(
+ clipboardIndicationProviderImpl: ClipboardIndicationProviderImpl
+ ): ClipboardIndicationProvider
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index ca4bbc0ae3bd..00c62092421d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -33,6 +33,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
import com.android.internal.logging.UiEventLogger
import com.android.systemui.Flags.communalEditWidgetsActivityFinishFix
@@ -44,6 +45,7 @@ import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
@@ -52,13 +54,13 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
import kotlinx.coroutines.flow.first
-import com.android.app.tracing.coroutines.launchTraced as launch
/** An Activity for editing the widgets that appear in hub mode. */
class EditWidgetsActivity
@Inject
constructor(
private val communalViewModel: CommunalEditModeViewModel,
+ private val keyguardInteractor: KeyguardInteractor,
private var windowManagerService: IWindowManager? = null,
private val uiEventLogger: UiEventLogger,
private val widgetConfiguratorFactory: WidgetConfigurationController.Factory,
@@ -223,6 +225,9 @@ constructor(
communalViewModel.currentScene.first { it == CommunalScenes.Blank }
}
+ // Wait for dream to exit, if we were previously dreaming.
+ keyguardInteractor.isDreaming.first { !it }
+
communalViewModel.setEditModeState(EditModeState.SHOWING)
// Inform the ActivityController that we are now fully visible.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
index d727a70de377..769d7faad2b0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.dagger;
+
import com.android.systemui.classifier.FalsingManagerProxy;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.globalactions.GlobalActionsImpl;
@@ -26,18 +27,20 @@ import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore;
+import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore;
import com.android.systemui.statusbar.phone.ActivityStarterImpl;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher;
import com.android.systemui.volume.VolumeDialogControllerImpl;
import dagger.Binds;
import dagger.Module;
+import dagger.Provides;
/**
* Module for binding Plugin implementations.
*
- * TODO(b/166258224): Many of these should be moved closer to their implementations.
+ * <p>TODO(b/166258224): Many of these should be moved closer to their implementations.
*/
@Module
public abstract class PluginModule {
@@ -47,12 +50,18 @@ public abstract class PluginModule {
abstract ActivityStarter provideActivityStarter(ActivityStarterImpl activityStarterImpl);
/** */
- @Binds
- abstract DarkIconDispatcher provideDarkIconDispatcher(DarkIconDispatcherImpl controllerImpl);
+ @Provides
+ @SysUISingleton
+ static DarkIconDispatcher provideDarkIconDispatcher(DarkIconDispatcherStore store) {
+ return store.getDefaultDisplay();
+ }
- @Binds
- abstract SysuiDarkIconDispatcher provideSysuiDarkIconDispatcher(
- DarkIconDispatcherImpl controllerImpl);
+ @Provides
+ @SysUISingleton
+ static SysuiDarkIconDispatcher provideSysuiDarkIconDispatcher(
+ SysuiDarkIconDispatcherStore store) {
+ return store.getDefaultDisplay();
+ }
/** */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 609b7330b600..2e323d40edcd 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -29,7 +29,7 @@ import com.android.systemui.accessibility.AccessibilityModule;
import com.android.systemui.accessibility.SystemActionsModule;
import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.battery.BatterySaverModule;
-import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlaySuppressionModule;
+import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayOverrideModule;
import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
@@ -125,7 +125,7 @@ import javax.inject.Named;
AospPolicyModule.class,
BatterySaverModule.class,
CentralSurfacesModule.class,
- ClipboardOverlaySuppressionModule.class,
+ ClipboardOverlayOverrideModule.class,
CollapsedStatusBarFragmentStartableModule.class,
ConnectingDisplayViewModel.StartableModule.class,
DefaultBlueprintModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 303f91688575..911331b8bff1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -42,7 +42,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.GlobalSettings;
import java.io.PrintWriter;
@@ -51,6 +51,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -74,7 +75,6 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
static final String TAG = "SysUIFlags";
private final FlagManager mFlagManager;
- private final Context mContext;
private final GlobalSettings mGlobalSettings;
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
@@ -84,6 +84,8 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
private final Map<String, String> mStringFlagCache = new ConcurrentHashMap<>();
private final Map<String, Integer> mIntFlagCache = new ConcurrentHashMap<>();
private final Restarter mRestarter;
+ private final UserTracker mUserTracker;
+ private final Executor mMainExecutor;
private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
new ServerFlagReader.ChangeListener() {
@@ -118,6 +120,18 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
}
};
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ mContext.unregisterReceiver(mReceiver);
+ mContext = userContext;
+ registerReceiver();
+ }
+ };
+
+ private Context mContext;
+
@Inject
public FeatureFlagsClassicDebug(
FlagManager flagManager,
@@ -128,10 +142,11 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
Restarter restarter,
- UserContextProvider userContextProvider) {
+ UserTracker userTracker,
+ @Main Executor executor) {
mFlagManager = flagManager;
if (classicFlagsMultiUser()) {
- mContext = userContextProvider.createCurrentUserContext(context);
+ mContext = userTracker.createCurrentUserContext(context);
} else {
mContext = context;
}
@@ -141,19 +156,28 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
mRestarter = restarter;
+ mUserTracker = userTracker;
+ mMainExecutor = executor;
}
/** Call after construction to setup listeners. */
void init() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_SET_FLAG);
- filter.addAction(ACTION_GET_FLAGS);
mFlagManager.setOnSettingsChangedAction(
suppressRestart -> restartSystemUI(suppressRestart, "Settings changed"));
mFlagManager.setClearCacheAction(this::removeFromCache);
+ registerReceiver();
+ if (classicFlagsMultiUser()) {
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
+ }
+ mServerFlagReader.listenForChanges(mAllFlags.values(), mOnPropertiesChanged);
+ }
+
+ void registerReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SET_FLAG);
+ filter.addAction(ACTION_GET_FLAGS);
mContext.registerReceiver(mReceiver, filter, null, null,
Context.RECEIVER_EXPORTED_UNAUDITED);
- mServerFlagReader.listenForChanges(mAllFlags.values(), mOnPropertiesChanged);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b431636b0e8b..c62593063020 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -15,7 +15,6 @@
*/
package com.android.systemui.flags
-import android.provider.DeviceConfig
import com.android.internal.annotations.Keep
import com.android.systemui.flags.FlagsFactory.releasedFlag
import com.android.systemui.flags.FlagsFactory.resourceBooleanFlag
@@ -220,10 +219,6 @@ object Flags {
// TODO(b/293380347): Tracking Bug
@JvmField val COLOR_FIDELITY = unreleasedFlag("color_fidelity")
- // 900 - media
- // TODO(b/254512697): Tracking Bug
- val MEDIA_TAP_TO_TRANSFER = releasedFlag("media_tap_to_transfer")
-
// TODO(b/254512654): Tracking Bug
@JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag("dream_media_complication")
@@ -255,15 +250,6 @@ object Flags {
val WM_ENABLE_SHELL_TRANSITIONS =
sysPropBooleanFlag("persist.wm.debug.shell_transit", default = true)
- // TODO(b/254513207): Tracking Bug to delete
- @Keep
- @JvmField
- val WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES =
- releasedFlag(
- name = "enable_screen_record_enterprise_policies",
- namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- )
-
// TODO(b/293252410) : Tracking Bug
@JvmField val LOCKSCREEN_ENABLE_LANDSCAPE = unreleasedFlag("lockscreen.enable_landscape")
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
index f0b2b86d67a0..38fc2a80ad02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
@@ -19,17 +19,18 @@ package com.android.systemui.keyboard.docking.ui.viewmodel
import android.content.Context
import android.view.Surface
import android.view.WindowManager
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.Utils
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.docking.domain.interactor.KeyboardDockingIndicationInteractor
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.surfaceeffects.glowboxeffect.GlowBoxConfig
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class KeyboardDockingIndicationViewModel
@@ -38,7 +39,7 @@ constructor(
private val windowManager: WindowManager,
private val context: Context,
keyboardDockingIndicationInteractor: KeyboardDockingIndicationInteractor,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
@Background private val backgroundScope: CoroutineScope,
) {
@@ -128,7 +129,7 @@ constructor(
blurAmount = BLUR_AMOUNT,
duration = DURATION,
easeInDuration = EASE_DURATION,
- easeOutDuration = EASE_DURATION
+ easeOutDuration = EASE_DURATION,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index e79f5902575f..2d056001b669 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -60,6 +60,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.LightRevealScrim
@@ -93,7 +94,7 @@ constructor(
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
private val shadeInteractor: ShadeInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index ca862896efaa..73a4cc3ad9df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -25,6 +25,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.doze.util.BurnInHelperWrapper
import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -47,7 +48,7 @@ constructor(
private val context: Context,
private val burnInHelperWrapper: BurnInHelperWrapper,
@Application private val scope: CoroutineScope,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
) {
val deviceEntryIconXOffset: StateFlow<Int> =
@@ -62,7 +63,7 @@ constructor(
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- burnInHelperWrapper.burnInProgressOffset()
+ burnInHelperWrapper.burnInProgressOffset(),
)
/** Given the max x,y dimens, determine the current translation shifts. */
@@ -71,7 +72,7 @@ constructor(
burnInOffset(xDimenResourceId, isXAxis = true),
burnInOffset(yDimenResourceId, isXAxis = false).map {
it * 2 - context.resources.getDimensionPixelSize(yDimenResourceId)
- }
+ },
) { translationX, translationY ->
BurnInModel(translationX, translationY, burnInHelperWrapper.burnInScale())
}
@@ -117,7 +118,7 @@ constructor(
private fun calculateOffset(
maxBurnInOffsetPixels: Int,
isXAxis: Boolean,
- scale: Float = 1f
+ scale: Float = 1f,
): Int {
return (burnInHelperWrapper.burnInOffset(maxBurnInOffsetPixels, isXAxis) * scale).toInt()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 6ac0a3f8443f..021cce6d1e23 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -20,6 +20,7 @@ import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.app.DreamManager
import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -41,7 +42,6 @@ import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.debounce
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class FromDozingTransitionInteractor
@@ -135,11 +135,22 @@ constructor(
if (!deviceEntryInteractor.isLockscreenEnabled()) {
if (!SceneContainerFlag.isEnabled) {
- startTransitionTo(KeyguardState.GONE)
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason = "lockscreen not enabled",
+ )
}
} else if (canDismissLockscreen() || isKeyguardGoingAway) {
if (!SceneContainerFlag.isEnabled) {
- startTransitionTo(KeyguardState.GONE)
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason =
+ if (canDismissLockscreen()) {
+ "canDismissLockscreen()"
+ } else {
+ "isKeyguardGoingAway"
+ },
+ )
}
} else if (primaryBouncerShowing) {
if (!SceneContainerFlag.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 6385b3cffbd4..2aaec8797cbe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -19,6 +19,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
@@ -32,6 +33,7 @@ import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.Intra
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -39,7 +41,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class KeyguardBlueprintInteractor
@@ -48,7 +49,7 @@ constructor(
private val keyguardBlueprintRepository: KeyguardBlueprintRepository,
@Application private val applicationScope: CoroutineScope,
shadeInteractor: ShadeInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
private val fingerprintPropertyInteractor: FingerprintPropertyInteractor,
private val smartspaceSection: SmartspaceSection,
) : CoreStartable {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 2e0a160bfd16..26c286df01d7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -49,6 +49,7 @@ import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.sample
@@ -85,7 +86,7 @@ constructor(
private val repository: KeyguardRepository,
powerInteractor: PowerInteractor,
bouncerRepository: KeyguardBouncerRepository,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
shadeRepository: ShadeRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
sceneInteractorProvider: Provider<SceneInteractor>,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 0dae17c594c8..cd62d5f3b6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -16,9 +16,11 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.logging.KeyguardLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.log.core.LogLevel.VERBOSE
@@ -29,7 +31,6 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNoti
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.debounce
-import com.android.app.tracing.coroutines.launchTraced as launch
private val TAG = KeyguardTransitionAuditLogger::class.simpleName!!
@@ -48,6 +49,7 @@ constructor(
private val aodBurnInViewModel: AodBurnInViewModel,
private val shadeInteractor: ShadeInteractor,
private val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
) {
fun start() {
@@ -84,6 +86,18 @@ constructor(
}
scope.launch {
+ deviceEntryInteractor.isUnlocked.collect {
+ logger.log(TAG, VERBOSE, "DeviceEntry isUnlocked", it)
+ }
+ }
+
+ scope.launch {
+ deviceEntryInteractor.isLockscreenEnabled.collect {
+ logger.log(TAG, VERBOSE, "DeviceEntry isLockscreenEnabled", it)
+ }
+ }
+
+ scope.launch {
keyguardInteractor.primaryBouncerShowing.collect {
logger.log(TAG, VERBOSE, "Primary bouncer showing", it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index abd7f90bbf22..7d4d377c768a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import android.util.Log
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -33,7 +34,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Each TransitionInteractor is responsible for determining under which conditions to notify
@@ -201,9 +201,18 @@ sealed class TransitionInteractor(
scope.launch {
keyguardInteractor.onCameraLaunchDetected.filterRelevantKeyguardState().collect {
if (!maybeHandleInsecurePowerGesture()) {
+ val lastStep = transitionInteractor.transitionState.value
+ val modeOnCanceled =
+ if (lastStep.to == KeyguardState.AOD) {
+ // Enabled smooth transition when double-tap camera cancels
+ // transition to AOD
+ TransitionModeOnCanceled.REVERSE
+ } else {
+ TransitionModeOnCanceled.RESET
+ }
startTransitionTo(
toState = KeyguardState.OCCLUDED,
- modeOnCanceled = TransitionModeOnCanceled.RESET,
+ modeOnCanceled = modeOnCanceled,
ownerReason = "keyguardInteractor.onCameraLaunchDetected",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 2c9884a63990..f473a82138e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -126,14 +126,7 @@ constructor(
sceneInteractor.get().transitionState.flatMapLatestConflated { state ->
when {
state.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Gone) ->
- (state as Transition).isUserInputOngoing.flatMapLatestConflated {
- isUserInputOngoing ->
- if (isUserInputOngoing) {
- isDeviceEntered
- } else {
- flowOf(true)
- }
- }
+ isDeviceEntered
state.isTransitioning(from = Scenes.Bouncer, to = Scenes.Gone) ->
(state as Transition).progress.map { progress ->
progress >
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 36ef78e08c4f..faa497833e7b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -33,6 +33,7 @@ import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
@@ -47,7 +48,7 @@ class AodNotificationIconsSection
@Inject
constructor(
private val context: Context,
- private val configurationState: ConfigurationState,
+ @ShadeDisplayAware private val configurationState: ConfigurationState,
private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
@@ -70,7 +71,7 @@ constructor(
resources.getDimensionPixelSize(R.dimen.below_clock_padding_start_icons),
0,
0,
- 0
+ 0,
)
setVisibility(View.INVISIBLE)
}
@@ -113,18 +114,18 @@ constructor(
START,
PARENT_ID,
START,
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
)
connect(
nicId,
END,
PARENT_ID,
END,
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
)
constrainHeight(
nicId,
- context.resources.getDimensionPixelSize(R.dimen.notification_shelf_height)
+ context.resources.getDimensionPixelSize(R.dimen.notification_shelf_height),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index 4908dbdec61e..56e3125f7078 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -24,6 +24,7 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.recents.utilities.Utilities.clamp
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -41,7 +42,7 @@ class AlternateBouncerUdfpsIconViewModel
@Inject
constructor(
val context: Context,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
deviceEntryBackgroundViewModel: DeviceEntryBackgroundViewModel,
fingerprintPropertyInteractor: FingerprintPropertyInteractor,
@@ -85,10 +86,7 @@ constructor(
}
private val fgIconPadding: Flow<Int> = udfpsOverlayInteractor.iconPadding
val fgViewModel: Flow<DeviceEntryForegroundViewModel.ForegroundIconViewModel> =
- combine(
- fgIconColor,
- fgIconPadding,
- ) { color, padding ->
+ combine(fgIconColor, fgIconPadding) { color, padding ->
DeviceEntryForegroundViewModel.ForegroundIconViewModel(
type = DeviceEntryIconView.IconType.FINGERPRINT,
useAodVariant = false,
@@ -100,12 +98,7 @@ constructor(
val bgColor: Flow<Int> = deviceEntryBackgroundViewModel.color
val bgAlpha: Flow<Float> = flowOf(1f)
- data class IconLocation(
- val left: Int,
- val top: Int,
- val right: Int,
- val bottom: Int,
- ) {
+ data class IconLocation(val left: Int, val top: Int, val right: Int, val bottom: Int) {
val width = right - left
val height = bottom - top
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index c78e0c9f5266..1c897237fe89 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -30,9 +30,11 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.ClockSize
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.math.max
import kotlinx.coroutines.CoroutineScope
@@ -42,8 +44,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
@@ -57,7 +61,7 @@ class AodBurnInViewModel
constructor(
@Application private val applicationScope: CoroutineScope,
private val burnInInteractor: BurnInInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
@@ -164,9 +168,17 @@ constructor(
private fun burnIn(params: BurnInParameters): Flow<BurnInModel> {
return combine(
- keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).map {
- Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it)
- },
+ merge(
+ keyguardTransitionInteractor.transition(Edge.create(to = KeyguardState.AOD)),
+ keyguardTransitionInteractor
+ .transition(Edge.create(from = KeyguardState.AOD))
+ .map { it.copy(value = 1f - it.value) },
+ keyguardTransitionInteractor
+ .transition(Edge.create(to = KeyguardState.LOCKSCREEN))
+ .filter { it.from != KeyguardState.AOD }
+ .map { it.copy(value = 0f) },
+ )
+ .map { Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value) },
burnInInteractor.burnIn(
xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
yDimenResourceId = R.dimen.burn_in_prevention_offset_y,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 4c667c1c702d..12f9467c0f96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -23,6 +23,7 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -42,7 +43,7 @@ constructor(
val context: Context,
val deviceEntryIconViewModel: DeviceEntryIconViewModel,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
alternateBouncerToDozingTransitionViewModel: AlternateBouncerToDozingTransitionViewModel,
aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 87c32a54438e..749f19315409 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -27,6 +27,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,7 +46,7 @@ class DeviceEntryForegroundViewModel
@Inject
constructor(
val context: Context,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
transitionInteractor: KeyguardTransitionInteractor,
deviceEntryIconViewModel: DeviceEntryIconViewModel,
@@ -106,12 +107,11 @@ constructor(
}
val viewModel: Flow<ForegroundIconViewModel> =
- combine(
- deviceEntryIconViewModel.iconType,
- useAodIconVariant,
+ combine(deviceEntryIconViewModel.iconType, useAodIconVariant, color, padding) {
+ iconType,
+ useAodVariant,
color,
- padding,
- ) { iconType, useAodVariant, color, padding ->
+ padding ->
ForegroundIconViewModel(
type = iconType,
useAodVariant = useAodVariant,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
index 11ed52ac35b7..c9fdf7a31458 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
@@ -27,6 +27,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -41,7 +42,7 @@ class DreamingToGlanceableHubTransitionViewModel
@Inject
constructor(
animationFlow: KeyguardTransitionAnimationFlow,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow
@@ -49,15 +50,13 @@ constructor(
duration = TO_GLANCEABLE_HUB_DURATION,
edge = Edge.create(from = DREAMING, to = Scenes.Communal),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = DREAMING, to = GLANCEABLE_HUB),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = DREAMING, to = GLANCEABLE_HUB))
val dreamOverlayTranslationX: Flow<Float> =
configurationInteractor
.directionalDimensionPixelSize(
LayoutDirection.LTR,
- R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x
+ R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x,
)
.flatMapLatest { translatePx ->
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
index f69f9969ea37..723fba6d976e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
@@ -27,6 +27,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -41,7 +42,7 @@ class GlanceableHubToDreamingTransitionViewModel
@Inject
constructor(
animationFlow: KeyguardTransitionAnimationFlow,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
) : DeviceEntryIconTransition {
private val transitionAnimation =
@@ -50,9 +51,7 @@ constructor(
duration = FROM_GLANCEABLE_HUB_DURATION,
edge = Edge.create(from = Scenes.Communal, to = DREAMING),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = GLANCEABLE_HUB, to = DREAMING),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = GLANCEABLE_HUB, to = DREAMING))
val dreamOverlayAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
@@ -66,7 +65,7 @@ constructor(
configurationInteractor
.directionalDimensionPixelSize(
LayoutDirection.LTR,
- R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x
+ R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x,
)
.flatMapLatest { translatePx: Int ->
transitionAnimation.sharedFlow(
@@ -74,7 +73,7 @@ constructor(
onStep = { value -> -translatePx + value * translatePx },
interpolator = Interpolators.EMPHASIZED,
onCancel = { -translatePx.toFloat() },
- name = "GLANCEABLE_HUB->LOCKSCREEN: dreamOverlayTranslationX"
+ name = "GLANCEABLE_HUB->LOCKSCREEN: dreamOverlayTranslationX",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index 7f3ef61d02c0..5a4d0689d209 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -28,6 +28,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,7 +46,7 @@ import kotlinx.coroutines.flow.map
class GlanceableHubToLockscreenTransitionViewModel
@Inject
constructor(
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 36f684ee4759..5c79c0b5c1bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -29,6 +29,7 @@ import com.android.systemui.keyguard.shared.model.ClockSize
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.ui.SystemBarUtilsProxy
@@ -50,7 +51,8 @@ constructor(
aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
@get:VisibleForTesting val shadeInteractor: ShadeInteractor,
private val systemBarUtils: SystemBarUtilsProxy,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
+ // TODO: b/374267505 - Use ShadeDisplayAware resources here.
@Main private val resources: Resources,
) {
var burnInLayer: Layer? = null
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index ceae1b5e9038..bc3ef02a0ec5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -31,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import javax.inject.Inject
import javax.inject.Named
@@ -53,7 +54,7 @@ constructor(
burnInInteractor: BurnInInteractor,
@Named(KeyguardQuickAffordancesCombinedViewModelModule.Companion.LOCKSCREEN_INSTANCE)
shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
communalSceneInteractor: CommunalSceneInteractor,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@@ -70,7 +71,7 @@ constructor(
val visible: Flow<Boolean> =
anyOf(
keyguardInteractor.statusBarState.map { state -> state == StatusBarState.KEYGUARD },
- communalSceneInteractor.isCommunalVisible
+ communalSceneInteractor.isCommunalVisible,
)
/** An observable for whether the indication area should be padded. */
@@ -85,7 +86,7 @@ constructor(
} else {
combine(
keyguardBottomAreaViewModel.startButton,
- keyguardBottomAreaViewModel.endButton
+ keyguardBottomAreaViewModel.endButton,
) { startButtonModel, endButtonModel ->
startButtonModel.isVisible || endButtonModel.isVisible
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index dd8ff8c4a052..acaa9b918da8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -28,6 +28,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,7 +46,7 @@ import kotlinx.coroutines.flow.map
class LockscreenToGlanceableHubTransitionViewModel
@Inject
constructor(
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index 88e8968501dd..6565e31c2c6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -26,6 +26,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -40,7 +41,7 @@ class LockscreenToOccludedTransitionViewModel
@Inject
constructor(
shadeDependentFlows: ShadeDependentFlows,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 737bd7ac218d..d10970f28995 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -29,6 +29,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -49,7 +50,7 @@ class OccludedToLockscreenTransitionViewModel
@Inject
constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
keyguardInteractor: KeyguardInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
@@ -104,7 +105,7 @@ constructor(
!isOccluded &&
keyguardTransitionInteractor.getCurrentState() == OCCLUDED
}
- .map { 0f }
+ .map { 0f },
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
index a33685b61237..d38e507082b9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
@@ -28,6 +28,7 @@ import androidx.annotation.WorkerThread
import androidx.media.utils.MediaConstants
import androidx.media3.common.Player
import androidx.media3.session.CommandButton
+import androidx.media3.session.MediaController as Media3Controller
import androidx.media3.session.SessionCommand
import androidx.media3.session.SessionToken
import com.android.systemui.dagger.SysUISingleton
@@ -82,10 +83,19 @@ constructor(
// Build button info
val buttons = suspendCancellableCoroutine { continuation ->
// Media3Controller methods must always be called from a specific looper
- handler.post {
- val result = getMedia3Actions(packageName, m3controller, token)
- m3controller.release()
- continuation.resumeWith(Result.success(result))
+ val runnable = Runnable {
+ try {
+ val result = getMedia3Actions(packageName, m3controller, token)
+ continuation.resumeWith(Result.success(result))
+ } finally {
+ m3controller.release()
+ }
+ }
+ handler.post(runnable)
+ continuation.invokeOnCancellation {
+ // Ensure controller is released, even if loading was cancelled partway through
+ handler.post(m3controller::release)
+ handler.removeCallbacks(runnable)
}
}
return buttons
@@ -95,7 +105,7 @@ constructor(
@WorkerThread
private fun getMedia3Actions(
packageName: String,
- m3controller: androidx.media3.session.MediaController,
+ m3controller: Media3Controller,
token: SessionToken,
): MediaButton? {
Assert.isNotMainThread()
@@ -197,7 +207,7 @@ constructor(
* @return A [MediaAction] representing the first supported command, or null if not supported
*/
private fun getStandardAction(
- controller: androidx.media3.session.MediaController,
+ controller: Media3Controller,
token: SessionToken,
vararg commands: @Player.Command Int,
): MediaAction? {
@@ -304,37 +314,40 @@ constructor(
bgScope.launch {
val controller = controllerFactory.create(token, looper)
handler.post {
- when (command) {
- Player.COMMAND_PLAY_PAUSE -> {
- if (controller.isPlaying) controller.pause() else controller.play()
- }
+ try {
+ when (command) {
+ Player.COMMAND_PLAY_PAUSE -> {
+ if (controller.isPlaying) controller.pause() else controller.play()
+ }
- Player.COMMAND_SEEK_TO_PREVIOUS -> controller.seekToPrevious()
- Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM ->
- controller.seekToPreviousMediaItem()
+ Player.COMMAND_SEEK_TO_PREVIOUS -> controller.seekToPrevious()
+ Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM ->
+ controller.seekToPreviousMediaItem()
- Player.COMMAND_SEEK_TO_NEXT -> controller.seekToNext()
- Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM -> controller.seekToNextMediaItem()
- Player.COMMAND_INVALID -> {
- if (
- customAction != null &&
- customAction!!.sessionCommand != null &&
- controller.isSessionCommandAvailable(
- customAction!!.sessionCommand!!
- )
- ) {
- controller.sendCustomCommand(
- customAction!!.sessionCommand!!,
- customAction!!.extras,
- )
- } else {
- logger.logMedia3UnsupportedCommand("$command, action $customAction")
+ Player.COMMAND_SEEK_TO_NEXT -> controller.seekToNext()
+ Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM -> controller.seekToNextMediaItem()
+ Player.COMMAND_INVALID -> {
+ if (customAction?.sessionCommand != null) {
+ val sessionCommand = customAction.sessionCommand!!
+ if (controller.isSessionCommandAvailable(sessionCommand)) {
+ controller.sendCustomCommand(
+ sessionCommand,
+ customAction.extras,
+ )
+ } else {
+ logger.logMedia3UnsupportedCommand(
+ "$sessionCommand, action $customAction"
+ )
+ }
+ } else {
+ logger.logMedia3UnsupportedCommand("$command, action $customAction")
+ }
}
+ else -> logger.logMedia3UnsupportedCommand(command.toString())
}
-
- else -> logger.logMedia3UnsupportedCommand(command.toString())
+ } finally {
+ controller.release()
}
- controller.release()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt
index 741f52998782..d815852b790f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt
@@ -35,7 +35,12 @@ open class MediaControllerFactory @Inject constructor(private val context: Conte
return MediaController(context, token)
}
- /** Creates a new [Media3Controller] from a [SessionToken] */
+ /**
+ * Creates a new [Media3Controller] from the media3 [SessionToken].
+ *
+ * @param token The token for the session
+ * @param looper The looper that will be used for this controller's operations
+ */
open suspend fun create(token: SessionToken, looper: Looper): Media3Controller {
return Media3Controller.Builder(context, token)
.setApplicationLooper(looper)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 36a9fb3eb753..45a3a8ce60c4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -27,17 +27,12 @@ import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager;
import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
-import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
-import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer;
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogBuffer;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-import java.util.Optional;
-
import javax.inject.Named;
/** Dagger module for the media package. */
@@ -132,16 +127,4 @@ public interface MediaModule {
static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
return factory.create("MediaTttReceiver", 20);
}
-
- /** */
- @Provides
- @SysUISingleton
- static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
- MediaTttFlags mediaTttFlags,
- Lazy<MediaTttCommandLineHelper> helperLazy) {
- if (!mediaTttFlags.isMediaTttEnabled()) {
- return Optional.empty();
- }
- return Optional.of(helperLazy.get());
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 92db804d2730..1204cde19c76 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -43,7 +43,6 @@ import com.android.systemui.common.ui.binder.TintedIconViewBinder
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttIcon
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.res.R
@@ -68,25 +67,27 @@ import javax.inject.Inject
* TODO(b/245610654): Re-name this to be MediaTttReceiverCoordinator.
*/
@SysUISingleton
-open class MediaTttChipControllerReceiver @Inject constructor(
- private val commandQueue: CommandQueue,
- context: Context,
- logger: MediaTttReceiverLogger,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
- @Main mainExecutor: DelayableExecutor,
- accessibilityManager: AccessibilityManager,
- configurationController: ConfigurationController,
- dumpManager: DumpManager,
- powerManager: PowerManager,
- @Main private val mainHandler: Handler,
- private val mediaTttFlags: MediaTttFlags,
- private val uiEventLogger: MediaTttReceiverUiEventLogger,
- private val viewUtil: ViewUtil,
- wakeLockBuilder: WakeLock.Builder,
- systemClock: SystemClock,
- private val rippleController: MediaTttReceiverRippleController,
- private val temporaryViewUiEventLogger: TemporaryViewUiEventLogger,
-) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
+open class MediaTttChipControllerReceiver
+@Inject
+constructor(
+ private val commandQueue: CommandQueue,
+ context: Context,
+ logger: MediaTttReceiverLogger,
+ viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ @Main mainExecutor: DelayableExecutor,
+ accessibilityManager: AccessibilityManager,
+ configurationController: ConfigurationController,
+ dumpManager: DumpManager,
+ powerManager: PowerManager,
+ @Main private val mainHandler: Handler,
+ private val uiEventLogger: MediaTttReceiverUiEventLogger,
+ private val viewUtil: ViewUtil,
+ wakeLockBuilder: WakeLock.Builder,
+ systemClock: SystemClock,
+ private val rippleController: MediaTttReceiverRippleController,
+ private val temporaryViewUiEventLogger: TemporaryViewUiEventLogger,
+) :
+ TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
context,
logger,
viewCaptureAwareWindowManager,
@@ -99,36 +100,43 @@ open class MediaTttChipControllerReceiver @Inject constructor(
wakeLockBuilder,
systemClock,
temporaryViewUiEventLogger,
-) {
+ ) {
@SuppressLint("WrongConstant") // We're allowed to use LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- override val windowLayoutParams = commonWindowLayoutParams.apply {
- gravity = Gravity.BOTTOM.or(Gravity.CENTER_HORIZONTAL)
- // Params below are needed for the ripple to work correctly
- width = WindowManager.LayoutParams.MATCH_PARENT
- height = WindowManager.LayoutParams.MATCH_PARENT
- layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- fitInsetsTypes = 0 // Ignore insets from all system bars
- }
+ override val windowLayoutParams =
+ commonWindowLayoutParams.apply {
+ gravity = Gravity.BOTTOM.or(Gravity.CENTER_HORIZONTAL)
+ // Params below are needed for the ripple to work correctly
+ width = WindowManager.LayoutParams.MATCH_PARENT
+ height = WindowManager.LayoutParams.MATCH_PARENT
+ layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ fitInsetsTypes = 0 // Ignore insets from all system bars
+ }
// Value animator that controls the bouncing animation of views.
- private val bounceAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
- repeatCount = ValueAnimator.INFINITE
- repeatMode = ValueAnimator.REVERSE
- duration = ICON_BOUNCE_ANIM_DURATION
- }
+ private val bounceAnimator =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ repeatCount = ValueAnimator.INFINITE
+ repeatMode = ValueAnimator.REVERSE
+ duration = ICON_BOUNCE_ANIM_DURATION
+ }
- private val commandQueueCallbacks = object : CommandQueue.Callbacks {
- override fun updateMediaTapToTransferReceiverDisplay(
- @StatusBarManager.MediaTransferReceiverState displayState: Int,
- routeInfo: MediaRoute2Info,
- appIcon: Icon?,
- appName: CharSequence?
- ) {
- this@MediaTttChipControllerReceiver.updateMediaTapToTransferReceiverDisplay(
- displayState, routeInfo, appIcon, appName
- )
+ private val commandQueueCallbacks =
+ object : CommandQueue.Callbacks {
+ override fun updateMediaTapToTransferReceiverDisplay(
+ @StatusBarManager.MediaTransferReceiverState displayState: Int,
+ routeInfo: MediaRoute2Info,
+ appIcon: Icon?,
+ appName: CharSequence?,
+ ) {
+ this@MediaTttChipControllerReceiver.updateMediaTapToTransferReceiverDisplay(
+ displayState,
+ routeInfo,
+ appIcon,
+ appName,
+ )
+ }
}
- }
// A map to store instance id per route info id.
private var instanceMap: MutableMap<String, InstanceId> = mutableMapOf()
@@ -139,7 +147,7 @@ open class MediaTttChipControllerReceiver @Inject constructor(
@StatusBarManager.MediaTransferReceiverState displayState: Int,
routeInfo: MediaRoute2Info,
appIcon: Icon?,
- appName: CharSequence?
+ appName: CharSequence?,
) {
val chipState: ChipStateReceiver? = ChipStateReceiver.getReceiverStateFromId(displayState)
val stateName = chipState?.name ?: "Invalid"
@@ -150,8 +158,8 @@ open class MediaTttChipControllerReceiver @Inject constructor(
return
}
- val instanceId: InstanceId = instanceMap[routeInfo.id]
- ?: temporaryViewUiEventLogger.getNewInstanceId()
+ val instanceId: InstanceId =
+ instanceMap[routeInfo.id] ?: temporaryViewUiEventLogger.getNewInstanceId()
uiEventLogger.logReceiverStateChange(chipState, instanceId)
if (chipState != ChipStateReceiver.CLOSE_TO_SENDER) {
@@ -175,53 +183,51 @@ open class MediaTttChipControllerReceiver @Inject constructor(
}
appIcon.loadDrawableAsync(
- context,
- Icon.OnDrawableLoadedListener { drawable ->
- displayView(
- ChipReceiverInfo(
- routeInfo,
- drawable,
- appName,
- id = routeInfo.id,
- instanceId = instanceId,
- )
+ context,
+ Icon.OnDrawableLoadedListener { drawable ->
+ displayView(
+ ChipReceiverInfo(
+ routeInfo,
+ drawable,
+ appName,
+ id = routeInfo.id,
+ instanceId = instanceId,
)
- },
- // Notify the listener on the main handler since the listener will update
- // the UI.
- mainHandler
+ )
+ },
+ // Notify the listener on the main handler since the listener will update
+ // the UI.
+ mainHandler,
)
}
override fun start() {
super.start()
- if (mediaTttFlags.isMediaTttEnabled()) {
- commandQueue.addCallback(commandQueueCallbacks)
- }
+ commandQueue.addCallback(commandQueueCallbacks)
registerListener(displayListener)
}
override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
val packageName: String? = newInfo.routeInfo.clientPackageName
- var iconInfo = MediaTttUtils.getIconInfoFromPackageName(
- context,
- packageName,
- isReceiver = true,
- ) {
- packageName?.let { logger.logPackageNotFound(it) }
- }
+ var iconInfo =
+ MediaTttUtils.getIconInfoFromPackageName(context, packageName, isReceiver = true) {
+ packageName?.let { logger.logPackageNotFound(it) }
+ }
if (newInfo.appNameOverride != null) {
- iconInfo = iconInfo.copy(
- contentDescription = ContentDescription.Loaded(newInfo.appNameOverride.toString())
- )
+ iconInfo =
+ iconInfo.copy(
+ contentDescription =
+ ContentDescription.Loaded(newInfo.appNameOverride.toString())
+ )
}
if (newInfo.appIconDrawableOverride != null) {
- iconInfo = iconInfo.copy(
- icon = MediaTttIcon.Loaded(newInfo.appIconDrawableOverride),
- isAppIcon = true,
- )
+ iconInfo =
+ iconInfo.copy(
+ icon = MediaTttIcon.Loaded(newInfo.appIconDrawableOverride),
+ isAppIcon = true,
+ )
}
val iconPadding =
@@ -298,16 +304,14 @@ open class MediaTttChipControllerReceiver @Inject constructor(
alphaDuration: Long = ICON_ALPHA_ANIM_DURATION,
onAnimationEnd: Runnable? = null,
) {
- view.animate()
+ view
+ .animate()
.translationYBy(translationYBy)
.setInterpolator(interpolator)
.setDuration(translationDuration)
.withEndAction { onAnimationEnd?.run() }
.start()
- view.animate()
- .alpha(alphaEndValue)
- .setDuration(alphaDuration)
- .start()
+ view.animate().alpha(alphaEndValue).setDuration(alphaDuration).start()
}
/** Returns the amount that the chip will be translated by in its intro animation. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 3e6d46c00df9..6ca04710d74c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -28,7 +28,6 @@ import com.android.systemui.Dumpable
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
@@ -53,7 +52,6 @@ constructor(
private val context: Context,
private val dumpManager: DumpManager,
private val logger: MediaTttSenderLogger,
- private val mediaTttFlags: MediaTttFlags,
private val uiEventLogger: MediaTttSenderUiEventLogger,
) : CoreStartable, Dumpable {
@@ -68,27 +66,25 @@ constructor(
override fun updateMediaTapToTransferSenderDisplay(
@StatusBarManager.MediaTransferSenderState displayState: Int,
routeInfo: MediaRoute2Info,
- undoCallback: IUndoMediaTransferCallback?
+ undoCallback: IUndoMediaTransferCallback?,
) {
this@MediaTttSenderCoordinator.updateMediaTapToTransferSenderDisplay(
displayState,
routeInfo,
- undoCallback
+ undoCallback,
)
}
}
override fun start() {
- if (mediaTttFlags.isMediaTttEnabled()) {
- commandQueue.addCallback(commandQueueCallbacks)
- dumpManager.registerNormalDumpable(this)
- }
+ commandQueue.addCallback(commandQueueCallbacks)
+ dumpManager.registerNormalDumpable(this)
}
private fun updateMediaTapToTransferSenderDisplay(
@StatusBarManager.MediaTransferSenderState displayState: Int,
routeInfo: MediaRoute2Info,
- undoCallback: IUndoMediaTransferCallback?
+ undoCallback: IUndoMediaTransferCallback?,
) {
val chipState: ChipStateSender? = ChipStateSender.getSenderStateFromId(displayState)
val stateName = chipState?.name ?: "Invalid"
@@ -107,7 +103,7 @@ constructor(
// ChipStateSender.FAR_FROM_RECEIVER is the default state when there is no state.
logger.logInvalidStateTransitionError(
currentState = currentStateForId?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
- chipState.name
+ chipState.name,
)
return
}
@@ -126,7 +122,7 @@ constructor(
// still be able to see the status of the transfer.
logger.logRemovalBypass(
removalReason,
- bypassReason = "transferStatus=${currentStateForId.transferStatus.name}"
+ bypassReason = "transferStatus=${currentStateForId.transferStatus.name}",
)
return
}
@@ -139,14 +135,7 @@ constructor(
logger.logStateMap(stateMap)
chipbarCoordinator.registerListener(displayListener)
chipbarCoordinator.displayView(
- createChipbarInfo(
- chipState,
- routeInfo,
- undoCallback,
- context,
- logger,
- instanceId,
- )
+ createChipbarInfo(chipState, routeInfo, undoCallback, context, logger, instanceId)
)
}
}
@@ -245,10 +234,7 @@ constructor(
)
}
- return ChipbarEndItem.Button(
- Text.Resource(R.string.media_transfer_undo),
- onClickListener,
- )
+ return ChipbarEndItem.Button(Text.Resource(R.string.media_transfer_undo), onClickListener)
}
private val displayListener =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index bf2aa7efc0c4..56885c3eea9f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -18,7 +18,7 @@ package com.android.systemui.mediaprojection.appselector.data
import android.annotation.ColorInt
import android.annotation.UserIdInt
-import android.app.ActivityManager.RecentTaskInfo
+import android.app.TaskInfo
import android.content.ComponentName
import com.android.wm.shell.shared.split.SplitBounds
@@ -34,7 +34,7 @@ data class RecentTask(
val splitBounds: SplitBounds?,
) {
constructor(
- taskInfo: RecentTaskInfo,
+ taskInfo: TaskInfo,
isForegroundTask: Boolean,
userType: UserType,
splitBounds: SplitBounds? = null
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 82e58cc7f1d9..d94424c59376 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -23,7 +23,7 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.kotlin.getOrNull
import com.android.wm.shell.recents.RecentTasks
-import com.android.wm.shell.shared.GroupedRecentTaskInfo
+import com.android.wm.shell.shared.GroupedTaskInfo
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -51,7 +51,7 @@ constructor(
override suspend fun loadRecentTasks(): List<RecentTask> =
withContext(coroutineDispatcher) {
- val groupedTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
+ val groupedTasks: List<GroupedTaskInfo> = recents?.getTasks() ?: emptyList()
// Note: the returned task list is from the most-recent to least-recent order.
// When opening the app selector in full screen, index 0 will be just the app selector
// activity and a null second task, so the foreground task will be index 1, but when
@@ -86,7 +86,7 @@ constructor(
}
}
- private suspend fun RecentTasks.getTasks(): List<GroupedRecentTaskInfo> =
+ private suspend fun RecentTasks.getTasks(): List<GroupedTaskInfo> =
suspendCoroutine { continuation ->
getRecentTasks(
Integer.MAX_VALUE,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 47dacae6e0a0..2fda2013d6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -57,7 +57,6 @@ import android.view.Display;
import android.view.Window;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
import com.android.systemui.mediaprojection.MediaProjectionUtils;
@@ -187,11 +186,9 @@ public class MediaProjectionPermissionActivity extends Activity {
return;
}
- if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) {
- if (showScreenCaptureDisabledDialogIfNeeded()) {
- finishAsCancelled();
- return;
- }
+ if (showScreenCaptureDisabledDialogIfNeeded()) {
+ finishAsCancelled();
+ return;
}
final String appName = extractAppName(aInfo, packageManager);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index b019c136b6ca..a3b7590117c1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -406,7 +406,7 @@ public class NavigationBarControllerImpl implements
if (navBar != null) {
navBar.checkNavBarModes();
} else {
- mTaskbarDelegate.checkNavBarModes();
+ mTaskbarDelegate.checkNavBarModes(displayId);
}
}
@@ -416,7 +416,7 @@ public class NavigationBarControllerImpl implements
if (navBar != null) {
navBar.finishBarAnimations();
} else {
- mTaskbarDelegate.finishBarAnimations();
+ mTaskbarDelegate.finishBarAnimations(displayId);
}
}
@@ -426,7 +426,7 @@ public class NavigationBarControllerImpl implements
if (navBar != null) {
navBar.touchAutoDim();
} else {
- mTaskbarDelegate.touchAutoDim();
+ mTaskbarDelegate.touchAutoDim(displayId);
}
}
@@ -436,7 +436,7 @@ public class NavigationBarControllerImpl implements
if (navBar != null) {
navBar.transitionTo(barMode, animate);
} else {
- mTaskbarDelegate.transitionTo(barMode, animate);
+ mTaskbarDelegate.transitionTo(displayId, barMode, animate);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 1216a8879751..2a3aeae2a550 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -159,7 +159,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() {
@Override
public void synchronizeState() {
- checkNavBarModes();
+ checkNavBarModes(mDisplayId);
}
@Override
@@ -220,6 +220,16 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mEdgeBackGestureHandler = navBarHelper.getEdgeBackGestureHandler();
}
+ @Override
+ public void onDisplayReady(int displayId) {
+ CommandQueue.Callbacks.super.onDisplayReady(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ CommandQueue.Callbacks.super.onDisplayRemoved(displayId);
+ }
+
// Separated into a method to keep setDependencies() clean/readable.
private LightBarTransitionsController createLightBarTransitionsController() {
@@ -349,31 +359,31 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
}
- void checkNavBarModes() {
+ void checkNavBarModes(int displayId) {
if (mOverviewProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().checkNavBarModes();
+ mOverviewProxyService.getProxy().checkNavBarModes(displayId);
} catch (RemoteException e) {
Log.e(TAG, "checkNavBarModes() failed", e);
}
}
- void finishBarAnimations() {
+ void finishBarAnimations(int displayId) {
if (mOverviewProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().finishBarAnimations();
+ mOverviewProxyService.getProxy().finishBarAnimations(displayId);
} catch (RemoteException e) {
Log.e(TAG, "finishBarAnimations() failed", e);
}
}
- void touchAutoDim() {
+ void touchAutoDim(int displayId) {
if (mOverviewProxyService.getProxy() == null) {
return;
}
@@ -382,19 +392,19 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
int state = mStatusBarStateController.getState();
boolean shouldReset =
state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED;
- mOverviewProxyService.getProxy().touchAutoDim(shouldReset);
+ mOverviewProxyService.getProxy().touchAutoDim(displayId, shouldReset);
} catch (RemoteException e) {
Log.e(TAG, "touchAutoDim() failed", e);
}
}
- void transitionTo(@BarTransitions.TransitionMode int barMode, boolean animate) {
+ void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, boolean animate) {
if (mOverviewProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().transitionTo(barMode, animate);
+ mOverviewProxyService.getProxy().transitionTo(displayId, barMode, animate);
} catch (RemoteException e) {
Log.e(TAG, "transitionTo() failed, barMode: " + barMode, e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index d0f6f7961889..1fa5baaa21ae 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -118,9 +118,7 @@ constructor(
getUserForHandlingNotesTaking(entryPoint)
}
activityContext.startActivityAsUser(
- Intent(Intent.ACTION_MANAGE_DEFAULT_APP).apply {
- putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
- },
+ createNotesRoleHolderSettingsIntent(),
user
)
}
@@ -399,6 +397,10 @@ constructor(
* @see com.android.launcher3.icons.IconCache.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
*/
const val EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE = "extra_shortcut_badge_override_package"
+
+ /** Returns notes role holder settings intent. */
+ fun createNotesRoleHolderSettingsIntent() = Intent(Intent.ACTION_MANAGE_DEFAULT_APP).
+ putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
index 442000281862..2d62c1067401 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
@@ -44,4 +44,7 @@ enum class NoteTaskEntryPoint {
/** @see [NoteTaskInitializer.callbacks] */
KEYBOARD_SHORTCUT,
+
+ /** @see [NotesTile] */
+ QS_NOTES_TILE,
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
index a79057e5464b..f152b01360df 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
@@ -19,6 +19,7 @@ import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS
import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT
+import com.android.systemui.notetask.NoteTaskEntryPoint.QS_NOTES_TILE
import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
@@ -43,45 +44,47 @@ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEvent
/** Logs a [NoteTaskInfo] as an **open** [NoteTaskUiEvent], including package name and uid. */
fun logNoteTaskOpened(info: NoteTaskInfo) {
val event =
- when (info.entryPoint) {
- TAIL_BUTTON -> {
- if (info.isKeyguardLocked) {
- NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
- } else {
- NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
- }
+ when (info.entryPoint) {
+ TAIL_BUTTON -> {
+ if (info.isKeyguardLocked) {
+ NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+ } else {
+ NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
}
+ }
- WIDGET_PICKER_SHORTCUT,
- WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE -> NOTE_OPENED_VIA_SHORTCUT
+ WIDGET_PICKER_SHORTCUT,
+ WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE -> NOTE_OPENED_VIA_SHORTCUT
- QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
- APP_CLIPS,
- KEYBOARD_SHORTCUT,
- null -> return
- }
+ QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
+ APP_CLIPS,
+ KEYBOARD_SHORTCUT,
+ QS_NOTES_TILE, // TODO(b/376640872): Add logging for QS Tile entry point.
+ null -> return
+ }
uiEventLogger.log(event, info.uid, info.packageName)
}
/** Logs a [NoteTaskInfo] as a **closed** [NoteTaskUiEvent], including package name and uid. */
fun logNoteTaskClosed(info: NoteTaskInfo) {
val event =
- when (info.entryPoint) {
- TAIL_BUTTON -> {
- if (info.isKeyguardLocked) {
- NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED
- } else {
- NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON
- }
+ when (info.entryPoint) {
+ TAIL_BUTTON -> {
+ if (info.isKeyguardLocked) {
+ NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+ } else {
+ NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON
}
-
- WIDGET_PICKER_SHORTCUT,
- WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE,
- QUICK_AFFORDANCE,
- APP_CLIPS,
- KEYBOARD_SHORTCUT,
- null -> return
}
+
+ WIDGET_PICKER_SHORTCUT,
+ WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE,
+ QUICK_AFFORDANCE,
+ APP_CLIPS,
+ KEYBOARD_SHORTCUT,
+ QS_NOTES_TILE,
+ null -> return
+ }
uiEventLogger.log(event, info.uid, info.packageName)
}
@@ -90,19 +93,20 @@ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEvent
@UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.")
NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294),
-
- @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was unlocked.") // ktlint-disable max-line-length
+ @UiEvent(
+ doc =
+ "User opened a note by pressing the stylus tail button while the screen was unlocked."
+ )
NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295),
-
- @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was locked.") // ktlint-disable max-line-length
+ @UiEvent(
+ doc =
+ "User opened a note by pressing the stylus tail button while the screen was locked."
+ )
NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296),
-
@UiEvent(doc = "User opened a note by tapping on an app shortcut.")
NOTE_OPENED_VIA_SHORTCUT(1297),
-
@UiEvent(doc = "Note closed via a tail button while device is unlocked")
NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON(1311),
-
@UiEvent(doc = "Note closed via a tail button while device is locked")
NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1312);
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index c7aae3ca788e..a1c5c9c682c3 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -27,11 +27,27 @@ import com.android.systemui.notetask.NoteTaskBubblesController.NoteTaskBubblesSe
import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceModule
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.NotesTile
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.notes.domain.NotesTileMapper
+import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileDataInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
/** Compose all dependencies required by Note Task feature. */
@Module(includes = [NoteTaskQuickAffordanceModule::class])
@@ -54,8 +70,22 @@ interface NoteTaskModule {
@[Binds IntoMap ClassKey(CreateNoteTaskShortcutActivity::class)]
fun bindNoteTaskShortcutActivity(activity: CreateNoteTaskShortcutActivity): Activity
+ @Binds
+ @IntoMap
+ @StringKey(NOTES_TILE_SPEC)
+ fun provideNotesAvailabilityInteractor(
+ impl: NotesTileDataInteractor
+ ): QSTileAvailabilityInteractor
+
+ @Binds
+ @IntoMap
+ @StringKey(NotesTile.TILE_SPEC)
+ fun bindNotesTile(notesTile: NotesTile): QSTileImpl<*>
+
companion object {
+ const val NOTES_TILE_SPEC = "notes"
+
@[Provides NoteTaskEnabledKey]
fun provideIsNoteTaskEnabled(
featureFlags: FeatureFlags,
@@ -65,5 +95,37 @@ interface NoteTaskModule {
val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
return isRoleAvailable && isFeatureEnabled
}
+
+ /** Inject NotesTile into tileViewModelMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(NOTES_TILE_SPEC)
+ fun provideNotesTileViewModel(
+ factory: QSTileViewModelFactory.Static<NotesTileModel>,
+ mapper: NotesTileMapper,
+ stateInteractor: NotesTileDataInteractor,
+ userActionInteractor: NotesTileUserActionInteractor,
+ ): QSTileViewModel =
+ factory.create(
+ TileSpec.create(NOTES_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+
+ @Provides
+ @IntoMap
+ @StringKey(NOTES_TILE_SPEC)
+ fun provideNotesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(NOTES_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.ic_qs_notes,
+ labelRes = R.string.quick_settings_notes_label,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
index 63bfbd1dc1ba..195b0cebe2eb 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.notifications.ui.viewmodel
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
@@ -38,7 +37,7 @@ class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() :
mapOf(
Swipe.Up to HideOverlay(Overlays.NotificationsShade),
Back to HideOverlay(Overlays.NotificationsShade),
- Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to
+ Swipe.Down(fromSource = SceneContainerEdge.TopRight) to
ReplaceByOverlay(Overlays.QuickSettingsShade),
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
index 7178d095d230..4fe63379aed4 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
@@ -16,19 +16,23 @@
package com.android.systemui.notifications.ui.viewmodel
+import androidx.compose.runtime.getValue
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Models UI state used to render the content of the notifications shade overlay.
@@ -43,10 +47,32 @@ constructor(
val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
val sceneInteractor: SceneInteractor,
private val shadeInteractor: ShadeInteractor,
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
) : ExclusiveActivatable() {
+ private val hydrator = Hydrator("NotificationsShadeOverlayContentViewModel.hydrator")
+
+ val showHeader: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "showHeader",
+ initialValue =
+ shouldShowHeader(
+ isShadeLayoutWide = shadeInteractor.isShadeLayoutWide.value,
+ areAnyNotificationsPresent =
+ activeNotificationsInteractor.areAnyNotificationsPresentValue,
+ ),
+ source =
+ combine(
+ shadeInteractor.isShadeLayoutWide,
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ this::shouldShowHeader,
+ ),
+ )
+
override suspend fun onActivated(): Nothing {
coroutineScope {
+ launch { hydrator.activate() }
+
launch {
sceneInteractor.currentScene.collect { currentScene ->
when (currentScene) {
@@ -77,6 +103,13 @@ constructor(
shadeInteractor.collapseNotificationsShade(loggingReason = "shade scrim clicked")
}
+ private fun shouldShowHeader(
+ isShadeLayoutWide: Boolean,
+ areAnyNotificationsPresent: Boolean,
+ ): Boolean {
+ return !isShadeLayoutWide && areAnyNotificationsPresent
+ }
+
@AssistedFactory
interface Factory {
fun create(): NotificationsShadeOverlayContentViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
index 11854d9317c9..398ace4b67f4 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.notifications.ui.viewmodel
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
@@ -40,7 +39,7 @@ class NotificationsShadeUserActionsViewModel @AssistedInject constructor() :
mapOf(
Back to SceneFamilies.Home,
Swipe.Up to SceneFamilies.Home,
- Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to
+ Swipe.Down(fromSource = SceneContainerEdge.TopRight) to
ReplaceByOverlay(Overlays.QuickSettingsShade),
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index bacff99fe048..51ff598c580b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -31,6 +31,7 @@ import android.widget.FrameLayout
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.annotation.VisibleForTesting
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
@@ -41,14 +42,14 @@ import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
-import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -59,6 +60,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.PointerEventPass
@@ -75,7 +77,6 @@ import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastRoundToInt
import androidx.compose.ui.viewinterop.AndroidView
@@ -97,6 +98,7 @@ import com.android.compose.modifiers.padding
import com.android.compose.modifiers.thenIf
import com.android.compose.theme.PlatformTheme
import com.android.systemui.Dumpable
+import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dump.DumpManager
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -107,6 +109,7 @@ import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.qs.composefragment.SceneKeys.QuickQuickSettings
import com.android.systemui.qs.composefragment.SceneKeys.QuickSettings
import com.android.systemui.qs.composefragment.SceneKeys.toIdleSceneKey
+import com.android.systemui.qs.composefragment.ui.GridAnchor
import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams
import com.android.systemui.qs.composefragment.ui.notificationScrimClip
import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings
@@ -115,8 +118,8 @@ import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.qs.footer.ui.compose.FooterActions
import com.android.systemui.qs.panels.ui.compose.EditMode
import com.android.systemui.qs.panels.ui.compose.QuickQuickSettings
+import com.android.systemui.qs.panels.ui.compose.TileGrid
import com.android.systemui.qs.shared.ui.ElementKeys
-import com.android.systemui.qs.ui.composable.QuickSettingsLayout
import com.android.systemui.qs.ui.composable.QuickSettingsShade
import com.android.systemui.qs.ui.composable.QuickSettingsTheme
import com.android.systemui.res.R
@@ -195,6 +198,7 @@ constructor(
val context = inflater.context
val composeView =
ComposeView(context).apply {
+ id = R.id.quick_settings_container
repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
setViewTreeOnBackPressedDispatcherOwner(
@@ -239,7 +243,6 @@ constructor(
visible = viewModel.isQsVisible,
modifier =
Modifier.graphicsLayer { alpha = viewModel.viewAlpha }
- .windowInsetsPadding(WindowInsets.navigationBars)
// Clipping before translation to match QSContainerImpl.onDraw
.offset {
IntOffset(x = 0, y = viewModel.viewTranslationY.fastRoundToInt())
@@ -299,7 +302,7 @@ constructor(
transitions =
transitions {
from(QuickQuickSettings, QuickSettings) {
- quickQuickSettingsToQuickSettings(viewModel::inFirstPage::get)
+ quickQuickSettingsToQuickSettings(viewModel::animateTilesExpansion::get)
}
},
)
@@ -596,8 +599,21 @@ constructor(
}
.padding(top = { qqsPadding }, bottom = { bottomPadding })
) {
+ val Tiles =
+ @Composable {
+ QuickQuickSettings(
+ viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel
+ )
+ }
+ val Media =
+ @Composable {
+ if (viewModel.qqsMediaVisible) {
+ MediaObject(mediaHost = viewModel.qqsMediaHost)
+ }
+ }
+
if (viewModel.isQsEnabled) {
- Column(
+ Box(
modifier =
Modifier.collapseExpandSemanticAction(
stringResource(
@@ -608,16 +624,13 @@ constructor(
horizontal = {
QuickSettingsShade.Dimensions.Padding.roundToPx()
}
- ),
- verticalArrangement =
- spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
+ )
) {
- QuickQuickSettings(
- viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel
+ QuickQuickSettingsLayout(
+ tiles = Tiles,
+ media = Media,
+ mediaInRow = viewModel.qqsMediaInRow,
)
- if (viewModel.qqsMediaVisible) {
- MediaObject(mediaHost = viewModel.qqsMediaHost)
- }
}
}
}
@@ -657,23 +670,58 @@ constructor(
.verticalScroll(scrollState)
.sysuiResTag(ResIdTags.qsScroll)
) {
+ val containerViewModel = viewModel.containerViewModel
Spacer(
modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() }
)
- QuickSettingsLayout(
- viewModel = viewModel.containerViewModel,
- modifier = Modifier.sysuiResTag(ResIdTags.quickSettingsPanel),
- )
- Spacer(modifier = Modifier.height(8.dp))
- if (viewModel.qsMediaVisible) {
- MediaObject(
- mediaHost = viewModel.qsMediaHost,
- modifier =
- Modifier.padding(
- horizontal = {
- QuickSettingsShade.Dimensions.Padding.roundToPx()
- }
- ),
+ val BrightnessSlider =
+ @Composable {
+ BrightnessSliderContainer(
+ viewModel = containerViewModel.brightnessSliderViewModel,
+ modifier =
+ Modifier.fillMaxWidth()
+ .height(
+ QuickSettingsShade.Dimensions.BrightnessSliderHeight
+ ),
+ )
+ }
+ val TileGrid =
+ @Composable {
+ Box {
+ GridAnchor()
+ TileGrid(
+ viewModel = containerViewModel.tileGridViewModel,
+ modifier =
+ Modifier.fillMaxWidth()
+ .heightIn(
+ max =
+ QuickSettingsShade.Dimensions.GridMaxHeight
+ ),
+ containerViewModel.editModeViewModel::startEditing,
+ )
+ }
+ }
+ val Media =
+ @Composable {
+ if (viewModel.qsMediaVisible) {
+ MediaObject(mediaHost = viewModel.qsMediaHost)
+ }
+ }
+ Box(
+ modifier =
+ Modifier.fillMaxWidth()
+ .sysuiResTag(ResIdTags.quickSettingsPanel)
+ .padding(
+ top = QuickSettingsShade.Dimensions.Padding,
+ start = QuickSettingsShade.Dimensions.Padding,
+ end = QuickSettingsShade.Dimensions.Padding,
+ )
+ ) {
+ QuickSettingsLayout(
+ brightness = BrightnessSlider,
+ tiles = TileGrid,
+ media = Media,
+ mediaInRow = viewModel.qsMediaInRow,
)
}
}
@@ -957,6 +1005,63 @@ private fun MediaObject(mediaHost: MediaHost, modifier: Modifier = Modifier) {
}
}
+@Composable
+@VisibleForTesting
+fun QuickQuickSettingsLayout(
+ tiles: @Composable () -> Unit,
+ media: @Composable () -> Unit,
+ mediaInRow: Boolean,
+) {
+ if (mediaInRow) {
+ Row(
+ horizontalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(modifier = Modifier.weight(1f)) { tiles() }
+ Box(modifier = Modifier.weight(1f)) { media() }
+ }
+ } else {
+ Column(verticalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical))) {
+ tiles()
+ media()
+ }
+ }
+}
+
+@Composable
+@VisibleForTesting
+fun QuickSettingsLayout(
+ brightness: @Composable () -> Unit,
+ tiles: @Composable () -> Unit,
+ media: @Composable () -> Unit,
+ mediaInRow: Boolean,
+) {
+ if (mediaInRow) {
+ Column(
+ verticalArrangement = spacedBy(QuickSettingsShade.Dimensions.Padding),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ brightness()
+ Row(
+ horizontalArrangement = spacedBy(QuickSettingsShade.Dimensions.Padding),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(modifier = Modifier.weight(1f)) { tiles() }
+ Box(modifier = Modifier.weight(1f)) { media() }
+ }
+ }
+ } else {
+ Column(
+ verticalArrangement = spacedBy(QuickSettingsShade.Dimensions.Padding),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ brightness()
+ tiles()
+ media()
+ }
+ }
+}
+
private object ResIdTags {
const val quickSettingsPanel = "quick_settings_panel"
const val quickQsPanel = "quick_qs_panel"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
index 512732090036..676f6a426264 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
@@ -19,6 +19,7 @@ package com.android.systemui.qs.composefragment.dagger
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.util.Utils
import dagger.Module
import dagger.Provides
@@ -34,7 +35,7 @@ interface QSFragmentComposeModule {
@SysUISingleton
@Named(QS_USING_MEDIA_PLAYER)
fun providesUsingMedia(@Application context: Context): Boolean {
- return Utils.useQsMediaPlayer(context)
+ return QSComposeFragment.isEnabled && Utils.useQsMediaPlayer(context)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt
index 9e3945ecba57..c1a417411975 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt
@@ -20,7 +20,9 @@ import com.android.compose.animation.scene.TransitionBuilder
import com.android.systemui.qs.composefragment.SceneKeys
import com.android.systemui.qs.shared.ui.ElementKeys
-fun TransitionBuilder.quickQuickSettingsToQuickSettings(inFirstPage: () -> Boolean = { true }) {
+fun TransitionBuilder.quickQuickSettingsToQuickSettings(
+ animateTilesExpansion: () -> Boolean = { true }
+) {
fractionRange(start = 0.5f) { fade(ElementKeys.QuickSettingsContent) }
@@ -28,7 +30,7 @@ fun TransitionBuilder.quickQuickSettingsToQuickSettings(inFirstPage: () -> Boole
anchoredTranslate(ElementKeys.QuickSettingsContent, ElementKeys.GridAnchor)
- sharedElement(ElementKeys.TileElementMatcher, enabled = inFirstPage())
+ sharedElement(ElementKeys.TileElementMatcher, enabled = animateTilesExpansion())
// This will animate between 0f (QQS) and 0.6, fading in the QQS tiles when coming back
// from non first page QS. The QS content ends fading out at 0.5f, so there's a brief
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index 0ca621d7d2e2..624cf306a3b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -39,6 +39,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule.QS_PANEL
@@ -49,10 +51,12 @@ import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.panels.domain.interactor.TileSquishinessInteractor
import com.android.systemui.qs.panels.ui.viewmodel.InFirstPageViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.MediaInRowInLandscapeViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -90,10 +94,11 @@ constructor(
disableFlagsRepository: DisableFlagsRepository,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val largeScreenShadeInterpolator: LargeScreenShadeInterpolator,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val squishinessInteractor: TileSquishinessInteractor,
private val inFirstPageViewModel: InFirstPageViewModel,
+ mediaInRowInLandscapeViewModelFactory: MediaInRowInLandscapeViewModel.Factory,
@Named(QUICK_QS_PANEL) val qqsMediaHost: MediaHost,
@Named(QS_PANEL) val qsMediaHost: MediaHost,
@Named(QSFragmentComposeModule.QS_USING_MEDIA_PLAYER) private val usingMedia: Boolean,
@@ -101,6 +106,8 @@ constructor(
) : Dumpable, ExclusiveActivatable() {
val containerViewModel = containerViewModelFactory.create(true)
+ private val qqsMediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QQS)
+ private val qsMediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QS)
private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator")
@@ -195,7 +202,7 @@ constructor(
}
}
- val isQsFullyExpanded by derivedStateOf { expansionState.progress >= 1f }
+ val isQsFullyExpanded by derivedStateOf { expansionState.progress >= 1f && isQsExpanded }
/**
* Accessibility action for collapsing/expanding QS. The provided runnable is responsible for
@@ -203,9 +210,6 @@ constructor(
*/
var collapseExpandAccessibilityAction: Runnable? = null
- val inFirstPage: Boolean
- get() = inFirstPageViewModel.inFirstPage
-
var overScrollAmount by mutableStateOf(0)
val viewTranslationY by derivedStateOf {
@@ -252,6 +256,9 @@ constructor(
},
)
+ val qqsMediaInRow: Boolean
+ get() = qqsMediaInRowViewModel.shouldMediaShowInRow
+
val qsMediaVisible by
hydrator.hydratedStateOf(
traceName = "qsMediaVisible",
@@ -259,6 +266,18 @@ constructor(
source = if (usingMedia) mediaHostVisible(qsMediaHost) else flowOf(false),
)
+ val qsMediaInRow: Boolean
+ get() = qsMediaInRowViewModel.shouldMediaShowInRow
+
+ val animateTilesExpansion: Boolean
+ get() = inFirstPage && !mediaSuddenlyAppearingInLandscape
+
+ private val inFirstPage: Boolean
+ get() = inFirstPageViewModel.inFirstPage
+
+ private val mediaSuddenlyAppearingInLandscape: Boolean
+ get() = !qqsMediaInRow && qsMediaInRow
+
private var qsBounds by mutableStateOf(Rect())
private val constrainedSquishinessFraction: Float
@@ -362,6 +381,8 @@ constructor(
launch { hydrateSquishinessInteractor() }
launch { hydrator.activate() }
launch { containerViewModel.activate() }
+ launch { qqsMediaInRowViewModel.activate() }
+ launch { qsMediaInRowViewModel.activate() }
awaitCancellation()
}
}
@@ -391,6 +412,7 @@ constructor(
println("isQSVisible", isQsVisible)
println("isQSEnabled", isQsEnabled)
println("isCustomizing", containerViewModel.editModeViewModel.isEditing.value)
+ println("inFirstPage", inFirstPage)
}
printSection("Expansion state") {
println("qsExpansion", qsExpansion)
@@ -423,7 +445,9 @@ constructor(
}
printSection("Media") {
println("qqsMediaVisible", qqsMediaVisible)
+ println("qqsMediaInRow", qqsMediaInRow)
println("qsMediaVisible", qsMediaVisible)
+ println("qsMediaInRow", qsMediaInRow)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 43fd0f5feec7..1f55ac777de5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -35,8 +35,6 @@ import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout
import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModelImpl
-import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsSizeViewModelImpl
-import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -58,8 +56,6 @@ interface PanelsModule {
@Binds fun bindIconTilesViewModel(impl: IconTilesViewModelImpl): IconTilesViewModel
- @Binds fun bindQSColumnsViewModel(impl: QSColumnsSizeViewModelImpl): QSColumnsViewModel
-
@Binds
@PaginatedBaseLayoutType
fun bindPaginatedBaseGridLayout(impl: InfiniteGridLayout): PaginatableGridLayout
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 6cc2cbc63d09..2efe500912cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -73,7 +73,7 @@ constructor(
tiles.forEach { it.startListening(token) }
onDispose { tiles.forEach { it.stopListening(token) } }
}
- val columns by viewModel.columns
+ val columns = viewModel.columns
val rows = viewModel.rows
val pages =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 19ab29e6c796..5ac2ad02d671 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -30,6 +30,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
import com.android.systemui.qs.panels.ui.compose.bounceableInfo
@@ -72,7 +73,12 @@ constructor(
rememberViewModel(traceName = "InfiniteGridLayout.TileGrid") {
viewModel.dynamicIconTilesViewModelFactory.create()
}
- val columns by viewModel.gridSizeViewModel.columns
+ val columnsWithMediaViewModel =
+ rememberViewModel(traceName = "InfiniteGridLAyout.TileGrid") {
+ viewModel.columnsWithMediaViewModelFactory.create(LOCATION_QS)
+ }
+
+ val columns = columnsWithMediaViewModel.columns
val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
val bounceables =
remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
@@ -118,7 +124,11 @@ constructor(
rememberViewModel(traceName = "InfiniteGridLayout.EditTileGrid") {
viewModel.dynamicIconTilesViewModelFactory.create()
}
- val columns by viewModel.gridSizeViewModel.columns
+ val columnsViewModel =
+ rememberViewModel(traceName = "InfiniteGridLayout.EditTileGrid") {
+ viewModel.columnsWithMediaViewModelFactory.createWithoutMediaTracking()
+ }
+ val columns = columnsViewModel.columns
val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle()
// Non-current tiles should always be displayed as icon tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 7fe856b871bd..4e34e73654fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -30,6 +30,7 @@ import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import javax.inject.Named
@@ -54,7 +55,7 @@ constructor(
private val currentTilesInteractor: CurrentTilesInteractor,
private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
private val minTilesInteractor: MinimumTilesInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
@Application private val applicationContext: Context,
@Named("Default") private val defaultGridLayout: GridLayout,
@Application private val applicationScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
index d68710048e13..3327141d2bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.panels.ui.viewmodel
-import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.panels.ui.dialog.QSResetDialogDelegate
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -25,19 +24,15 @@ class InfiniteGridViewModel
@AssistedInject
constructor(
val dynamicIconTilesViewModelFactory: DynamicIconTilesViewModel.Factory,
- val gridSizeViewModel: QSColumnsViewModel,
+ val columnsWithMediaViewModelFactory: QSColumnsViewModel.Factory,
val squishinessViewModel: TileSquishinessViewModel,
private val resetDialogDelegate: QSResetDialogDelegate,
-) : ExclusiveActivatable() {
+) {
fun showResetDialog() {
resetDialogDelegate.showDialog()
}
- override suspend fun onActivated(): Nothing {
- gridSizeViewModel.activate()
- }
-
@AssistedFactory
interface Factory {
fun create(): InfiniteGridViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModel.kt
new file mode 100644
index 000000000000..2ed8fd20df8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModel.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.content.res.Configuration
+import android.content.res.Resources
+import androidx.compose.runtime.getValue
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import javax.inject.Named
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Indicates whether a particular UMO in [LOCATION_QQS] or [LOCATION_QS] should currently show in a
+ * row with the tiles, based on its visibility and device configuration. If the player is not
+ * visible, it will never indicate that media should show in row.
+ */
+class MediaInRowInLandscapeViewModel
+@AssistedInject
+constructor(
+ @Main resources: Resources,
+ configurationInteractor: ConfigurationInteractor,
+ shadeModeInteractor: ShadeModeInteractor,
+ private val mediaHostStatesManager: MediaHostStatesManager,
+ @Named(QSFragmentComposeModule.QS_USING_MEDIA_PLAYER) private val usingMedia: Boolean,
+ @Assisted @MediaLocation private val inLocation: Int,
+) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("MediaInRowInLanscapeViewModel - $inLocation")
+
+ val shouldMediaShowInRow: Boolean
+ get() = usingMedia && inSingleShade && isLandscapeAndLong && isMediaVisible
+
+ private val inSingleShade: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "inSingleShade",
+ initialValue = shadeModeInteractor.shadeMode.value == ShadeMode.Single,
+ source = shadeModeInteractor.shadeMode.map { it == ShadeMode.Single },
+ )
+
+ private val isLandscapeAndLong: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isLandscapeAndLong",
+ initialValue = resources.configuration.isLandscapeAndLong,
+ source = configurationInteractor.configurationValues.map { it.isLandscapeAndLong },
+ )
+
+ private val isMediaVisible by
+ hydrator.hydratedStateOf(
+ traceName = "isMediaVisible",
+ initialValue = false,
+ source =
+ conflatedCallbackFlow {
+ val callback =
+ object : MediaHostStatesManager.Callback {
+ override fun onHostStateChanged(
+ location: Int,
+ mediaHostState: MediaHostState,
+ ) {
+ if (location == inLocation) {
+ trySend(mediaHostState.visible)
+ }
+ }
+ }
+ mediaHostStatesManager.addCallback(callback)
+
+ awaitClose { mediaHostStatesManager.removeCallback(callback) }
+ }
+ .onStart {
+ emit(
+ mediaHostStatesManager.mediaHostStates.get(inLocation)?.visible ?: false
+ )
+ },
+ )
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(@MediaLocation inLocation: Int): MediaInRowInLandscapeViewModel
+ }
+}
+
+private val Configuration.isLandscapeAndLong: Boolean
+ get() =
+ orientation == Configuration.ORIENTATION_LANDSCAPE &&
+ (screenLayout and Configuration.SCREENLAYOUT_LONG_MASK) ==
+ Configuration.SCREENLAYOUT_LONG_YES
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
index 8bd9ed05c12c..e5607eb6e620 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -16,10 +16,10 @@
package com.android.systemui.qs.panels.ui.viewmodel
-import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.qs.panels.domain.interactor.PaginatedGridInteractor
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -31,12 +31,13 @@ class PaginatedGridViewModel
@AssistedInject
constructor(
iconTilesViewModel: IconTilesViewModel,
- private val gridSizeViewModel: QSColumnsViewModel,
+ columnsWithMediaViewModelFactory: QSColumnsViewModel.Factory,
paginatedGridInteractor: PaginatedGridInteractor,
inFirstPageViewModel: InFirstPageViewModel,
) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() {
private val hydrator = Hydrator("PaginatedGridViewModel")
+ private val columnsWithMediaViewModel = columnsWithMediaViewModelFactory.create(LOCATION_QS)
val rows by
hydrator.hydratedStateOf(
@@ -47,13 +48,13 @@ constructor(
var inFirstPage by inFirstPageViewModel::inFirstPage
- val columns: State<Int>
- get() = gridSizeViewModel.columns
+ val columns: Int
+ get() = columnsWithMediaViewModel.columns
override suspend fun onActivated(): Nothing {
coroutineScope {
launch { hydrator.activate() }
- launch { gridSizeViewModel.activate() }
+ launch { columnsWithMediaViewModel.activate() }
awaitCancellation()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
index 8926d2ff107e..85b7831fb270 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
@@ -16,25 +16,61 @@
package com.android.systemui.qs.panels.ui.viewmodel
-import androidx.compose.runtime.State
-import com.android.systemui.lifecycle.Activatable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.qs.panels.domain.interactor.QSColumnsInteractor
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
-interface QSColumnsViewModel : Activatable {
- val columns: State<Int>
-}
+/**
+ * View model for the number of columns that should be shown in a QS grid.
+ * * Create it with a [MediaLocation] to halve the number of columns when media should show in a row
+ * with the tiles.
+ * * Create it with a `null` [MediaLocation] to ignore media visibility (useful for edit mode).
+ */
+class QSColumnsViewModel
+@AssistedInject
+constructor(
+ interactor: QSColumnsInteractor,
+ mediaInRowInLandscapeViewModelFactory: MediaInRowInLandscapeViewModel.Factory,
+ @Assisted @MediaLocation mediaLocation: Int?,
+) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("QSColumnsViewModelWithMedia")
+
+ val columns by derivedStateOf {
+ if (mediaInRowInLandscapeViewModel?.shouldMediaShowInRow == true) {
+ columnsWithoutMedia / 2
+ } else {
+ columnsWithoutMedia
+ }
+ }
-class QSColumnsSizeViewModelImpl @Inject constructor(interactor: QSColumnsInteractor) :
- QSColumnsViewModel, ExclusiveActivatable() {
- private val hydrator = Hydrator("QSColumnsSizeViewModelImpl")
+ private val mediaInRowInLandscapeViewModel =
+ mediaLocation?.let { mediaInRowInLandscapeViewModelFactory.create(it) }
- override val columns =
- hydrator.hydratedStateOf(traceName = "columns", source = interactor.columns)
+ private val columnsWithoutMedia by
+ hydrator.hydratedStateOf(traceName = "columnsWithoutMedia", source = interactor.columns)
override suspend fun onActivated(): Nothing {
- hydrator.activate()
+ coroutineScope {
+ launch { hydrator.activate() }
+ launch { mediaInRowInLandscapeViewModel?.activate() }
+ awaitCancellation()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(mediaLocation: Int?): QSColumnsViewModel
+
+ fun createWithoutMediaTracking() = create(null)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
index 0859c86d74e1..33ce5519b68c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
@@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue
import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
import com.android.systemui.qs.panels.domain.interactor.QuickQuickSettingsRowInteractor
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
@@ -36,23 +37,35 @@ class QuickQuickSettingsViewModel
@AssistedInject
constructor(
tilesInteractor: CurrentTilesInteractor,
- private val qsColumnsViewModel: QSColumnsViewModel,
+ qsColumnsViewModelFactory: QSColumnsViewModel.Factory,
quickQuickSettingsRowInteractor: QuickQuickSettingsRowInteractor,
+ mediaInRowInLandscapeViewModelFactory: MediaInRowInLandscapeViewModel.Factory,
val squishinessViewModel: TileSquishinessViewModel,
iconTilesViewModel: IconTilesViewModel,
val tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider,
) : ExclusiveActivatable() {
private val hydrator = Hydrator("QuickQuickSettingsViewModel")
+ private val qsColumnsViewModel = qsColumnsViewModelFactory.create(LOCATION_QQS)
+ private val mediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QQS)
- val columns by qsColumnsViewModel.columns
+ val columns: Int
+ get() = qsColumnsViewModel.columns
private val largeTiles by
hydrator.hydratedStateOf(traceName = "largeTiles", source = iconTilesViewModel.largeTiles)
- private val rows by
+ private val rows: Int
+ get() =
+ if (mediaInRowViewModel.shouldMediaShowInRow) {
+ rowsWithoutMedia * 2
+ } else {
+ rowsWithoutMedia
+ }
+
+ private val rowsWithoutMedia by
hydrator.hydratedStateOf(
- traceName = "rows",
+ traceName = "rowsWithoutMedia",
initialValue = quickQuickSettingsRowInteractor.defaultRows,
source = quickQuickSettingsRowInteractor.rows,
)
@@ -73,6 +86,7 @@ constructor(
coroutineScope {
launch { hydrator.activate() }
launch { qsColumnsViewModel.activate() }
+ launch { mediaInRowViewModel.activate() }
awaitCancellation()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
index f702da46717a..c9a0635021da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
@@ -54,6 +54,7 @@ object SubtitleArrayMapping {
subtitleIdsMap["dream"] = R.array.tile_states_dream
subtitleIdsMap["font_scaling"] = R.array.tile_states_font_scaling
subtitleIdsMap["hearing_devices"] = R.array.tile_states_hearing_devices
+ subtitleIdsMap["notes"] = R.array.tile_states_notes
}
/** Get the subtitle resource id of the given tile */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
new file mode 100644
index 000000000000..69df0961bf66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.service.quicksettings.Tile
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.animation.Expandable
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.impl.notes.domain.NotesTileMapper
+import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileDataInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.runBlocking
+
+/** Quick settings tile: Notes */
+class NotesTile
+@Inject constructor(
+ private val host: QSHost,
+ private val uiEventLogger: QsEventLogger,
+ @Background private val backgroundLooper: Looper,
+ @Main private val mainHandler: Handler,
+ private val falsingManager: FalsingManager,
+ private val metricsLogger: MetricsLogger,
+ private val statusBarStateController: StatusBarStateController,
+ private val activityStarter: ActivityStarter,
+ private val qsLogger: QSLogger,
+ private val qsTileConfigProvider: QSTileConfigProvider,
+ private val dataInteractor: NotesTileDataInteractor,
+ private val tileMapper: NotesTileMapper,
+ private val userActionInteractor: NotesTileUserActionInteractor,
+) :
+ QSTileImpl<QSTile.State?>(
+ host,
+ uiEventLogger,
+ backgroundLooper,
+ mainHandler,
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ ) {
+
+ private lateinit var tileState: QSTileState
+ private val config = qsTileConfigProvider.getConfig(TILE_SPEC)
+
+ override fun getTileLabel(): CharSequence =
+ mContext.getString(config.uiConfig.labelRes)
+
+ override fun newTileState(): QSTile.State? {
+ return QSTile.State().apply { state = Tile.STATE_INACTIVE }
+ }
+
+ override fun handleClick(expandable: Expandable?) {
+ userActionInteractor.handleClick()
+ }
+
+ override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
+
+ override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
+ val model =
+ if (arg is NotesTileModel) arg else dataInteractor.getCurrentTileModel()
+ tileState = tileMapper.map(config, model)
+
+ state?.apply {
+ this.state = tileState.activationState.legacyState
+ icon = ResourceIcon.get(tileState.iconRes ?: R.drawable.ic_qs_notes)
+ label = tileState.label
+ contentDescription = tileState.contentDescription
+ expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
+ }
+ }
+
+ override fun isAvailable(): Boolean {
+ return dataInteractor.isAvailable()
+ }
+
+ companion object {
+ const val TILE_SPEC = "notes"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 284239ab1a78..f3be340f4951 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -191,8 +191,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
mPanelInteractor.collapsePanels();
};
- final Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
- mDialogTransitionAnimator, mActivityStarter, onStartRecordingClicked);
+ final Dialog dialog = mController.createScreenRecordDialog(onStartRecordingClicked);
ActivityStarter.OnDismissAction dismissAction = () -> {
if (shouldAnimateFromExpandable) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
new file mode 100644
index 000000000000..ee1b9e5171b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain
+
+import android.content.res.Resources
+import android.widget.Button
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class NotesTileMapper
+@Inject
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<NotesTileModel> {
+ override fun map(config: QSTileConfig, data: NotesTileModel): QSTileState =
+ QSTileState.build(resources, theme, config.uiConfig) {
+ iconRes = R.drawable.ic_qs_notes
+ icon =
+ Icon.Loaded(
+ resources.getDrawable(
+ iconRes!!,
+ theme),
+ contentDescription = null
+ )
+ contentDescription = label
+ activationState = QSTileState.ActivationState.INACTIVE
+ sideViewIcon = QSTileState.SideViewIcon.Chevron
+ supportedActions =
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ expandedAccessibilityClass = Button::class
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
new file mode 100644
index 000000000000..a501b8561330
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.Flags
+import com.android.systemui.notetask.NoteTaskEnabledKey
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+class NotesTileDataInteractor
+@Inject
+constructor(@NoteTaskEnabledKey private val isNoteTaskEnabled: Boolean) :
+ QSTileDataInteractor<NotesTileModel> {
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>,
+ ): Flow<NotesTileModel> = flowOf(NotesTileModel)
+
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(isAvailable())
+
+ fun isAvailable(): Boolean {
+ return Flags.notesRoleQsTile() && isNoteTaskEnabled
+ }
+
+ fun getCurrentTileModel(): NotesTileModel {
+ return NotesTileModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
new file mode 100644
index 000000000000..df01d99df0df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.interactor
+
+import com.android.systemui.animation.Expandable
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+
+class NotesTileUserActionInteractor
+@Inject constructor(
+ private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
+ private val panelInteractor: PanelInteractor,
+ private val noteTaskController: NoteTaskController,
+) : QSTileUserActionInteractor<NotesTileModel> {
+ val longClickIntent = NoteTaskController.createNotesRoleHolderSettingsIntent()
+
+ override suspend fun handleInput(input: QSTileInput<NotesTileModel>) {
+ when (input.action) {
+ is QSTileUserAction.Click -> handleClick()
+ is QSTileUserAction.LongClick -> handleLongClick(input.action.expandable)
+ is QSTileUserAction.ToggleClick -> {}
+ }
+ }
+
+ fun handleClick() {
+ noteTaskController.showNoteTask(NoteTaskEntryPoint.QS_NOTES_TILE)
+ panelInteractor.collapsePanels()
+ }
+
+ fun handleLongClick(expandable: Expandable?) {
+ qsTileIntentUserInputHandler.handle(expandable, longClickIntent)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/model/NotesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/model/NotesTileModel.kt
new file mode 100644
index 000000000000..b72aa60e16f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/model/NotesTileModel.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.model
+
+/** NotesTileModel tile model. */
+data object NotesTileModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
index 48b39ed25750..85aa6745e438 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -16,16 +16,13 @@
package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
-import android.content.Context
import android.util.Log
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.plugins.ActivityStarter
@@ -45,7 +42,6 @@ import kotlinx.coroutines.withContext
class ScreenRecordTileUserActionInteractor
@Inject
constructor(
- @Application private val context: Context,
@Main private val mainContext: CoroutineContext,
@Background private val backgroundContext: CoroutineContext,
private val screenRecordRepository: ScreenRecordRepository,
@@ -55,8 +51,6 @@ constructor(
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val panelInteractor: PanelInteractor,
private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
- private val featureFlags: FeatureFlagsClassic,
- private val activityStarter: ActivityStarter,
) : QSTileUserActionInteractor<ScreenRecordModel> {
override suspend fun handleInput(input: QSTileInput<ScreenRecordModel>): Unit =
with(input) {
@@ -89,14 +83,7 @@ constructor(
panelInteractor.collapsePanels()
}
- val dialog =
- recordingController.createScreenRecordDialog(
- context,
- featureFlags,
- dialogTransitionAnimator,
- activityStarter,
- onStartRecordingClicked
- )
+ val dialog = recordingController.createScreenRecordDialog(onStartRecordingClicked)
if (dialog == null) {
Log.w(TAG, "showPrompt: dialog was null")
@@ -115,7 +102,7 @@ constructor(
expandable?.dialogTransitionController(
DialogCuj(
InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
- INTERACTION_JANK_TAG
+ INTERACTION_JANK_TAG,
)
)
controller?.let {
@@ -135,7 +122,7 @@ constructor(
keyguardDismissUtil.executeWhenUnlocked(
dismissAction,
false /* requiresShadeOpen */,
- true /* afterKeyguardDone */
+ true, /* afterKeyguardDone */
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
index 9a416d1d3b3b..000f7f8a7d31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.qs.ui.viewmodel
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
@@ -47,7 +46,7 @@ constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewM
put(Back, HideOverlay(Overlays.QuickSettingsShade))
}
put(
- Swipe(SwipeDirection.Down, fromSource = SceneContainerEdge.TopLeft),
+ Swipe.Down(fromSource = SceneContainerEdge.TopLeft),
ReplaceByOverlay(Overlays.NotificationsShade),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
index 54e5caca107c..f59541545de4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
@@ -20,7 +20,6 @@ import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
@@ -44,10 +43,8 @@ import kotlinx.coroutines.flow.map
*/
class QuickSettingsUserActionsViewModel
@AssistedInject
-constructor(
- private val qsSceneAdapter: QSSceneAdapter,
- sceneBackInteractor: SceneBackInteractor,
-) : UserActionsViewModel() {
+constructor(private val qsSceneAdapter: QSSceneAdapter, sceneBackInteractor: SceneBackInteractor) :
+ UserActionsViewModel() {
private val backScene: Flow<SceneKey> =
sceneBackInteractor.backScene
@@ -55,10 +52,7 @@ constructor(
.map { it ?: Scenes.Shade }
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
- combine(
- qsSceneAdapter.isCustomizerShowing,
- backScene,
- ) { isCustomizing, backScene ->
+ combine(qsSceneAdapter.isCustomizerShowing, backScene) { isCustomizing, backScene ->
buildMap<UserAction, UserActionResult> {
if (isCustomizing) {
// TODO(b/332749288) Empty map so there are no back handlers and back can
@@ -69,9 +63,9 @@ constructor(
// while customizing
} else {
put(Back, UserActionResult(backScene))
- put(Swipe(SwipeDirection.Up), UserActionResult(backScene))
+ put(Swipe.Up, UserActionResult(backScene))
put(
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up),
+ Swipe.Up(fromSource = Edge.Bottom),
UserActionResult(SceneFamilies.Home),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 6758c3ba0767..02b2bb1585bd 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -35,7 +35,6 @@ import androidx.annotation.WorkerThread
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.SessionCreationSource
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
@@ -132,10 +131,9 @@ constructor(
@WorkerThread
private fun onScreenRecordSwitchClicked() {
if (
- flags.isEnabled(WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES) &&
- devicePolicyResolver
- .get()
- .isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
+ devicePolicyResolver
+ .get()
+ .isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
) {
mainExecutor.execute {
screenCaptureDisabledDialogDelegate.createSysUIDialog().show()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 1fbe8e2f21e5..580a51a3dc0a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -46,7 +46,6 @@ import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
import com.android.systemui.model.SceneContainerPlugin
import com.android.systemui.model.SysUiState
import com.android.systemui.model.updateFlags
@@ -97,7 +96,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -138,7 +136,6 @@ constructor(
private val uiEventLogger: UiEventLogger,
private val sceneBackInteractor: SceneBackInteractor,
private val shadeSessionStorage: SessionStorage,
- private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor,
private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
private val dismissCallbackRegistry: DismissCallbackRegistry,
private val statusBarStateController: SysuiStatusBarStateController,
@@ -270,27 +267,6 @@ constructor(
handleDeviceUnlockStatus()
handlePowerState()
handleShadeTouchability()
- handleSurfaceBehindKeyguardVisibility()
- }
-
- private fun handleSurfaceBehindKeyguardVisibility() {
- applicationScope.launch {
- sceneInteractor.currentScene.collectLatest { currentScene ->
- if (currentScene == Scenes.Lockscreen) {
- // Wait for the screen to be on
- powerInteractor.isAwake.first { it }
- // Wait for surface to become visible
- windowMgrLockscreenVisInteractor.surfaceBehindVisibility.first { it }
- // Make sure the device is actually unlocked before force-changing the scene
- deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked }
- // Override the current transition, if any, by forcing the scene to Gone
- sceneInteractor.changeScene(
- toScene = Scenes.Gone,
- loggingReason = "surface behind keyguard is visible",
- )
- }
- }
- }
}
private fun handleBouncerImeVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index a8a78a91097c..d7463f8f0c36 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -32,17 +32,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -66,12 +62,10 @@ public class RecordingController
private CountDownTimer mCountDownTimer = null;
private final Executor mMainExecutor;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final FeatureFlags mFlags;
private final UserTracker mUserTracker;
private final RecordingControllerLogger mRecordingControllerLogger;
private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
private final ScreenCaptureDisabledDialogDelegate mScreenCaptureDisabledDialogDelegate;
- private final ScreenRecordDialogDelegate.Factory mScreenRecordDialogFactory;
private final ScreenRecordPermissionDialogDelegate.Factory
mScreenRecordPermissionDialogDelegateFactory;
@@ -116,24 +110,20 @@ public class RecordingController
public RecordingController(
@Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher,
- FeatureFlags flags,
Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
UserTracker userTracker,
RecordingControllerLogger recordingControllerLogger,
MediaProjectionMetricsLogger mediaProjectionMetricsLogger,
ScreenCaptureDisabledDialogDelegate screenCaptureDisabledDialogDelegate,
- ScreenRecordDialogDelegate.Factory screenRecordDialogFactory,
ScreenRecordPermissionDialogDelegate.Factory
screenRecordPermissionDialogDelegateFactory) {
mMainExecutor = mainExecutor;
- mFlags = flags;
mDevicePolicyResolver = devicePolicyResolver;
mBroadcastDispatcher = broadcastDispatcher;
mUserTracker = userTracker;
mRecordingControllerLogger = recordingControllerLogger;
mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
mScreenCaptureDisabledDialogDelegate = screenCaptureDisabledDialogDelegate;
- mScreenRecordDialogFactory = screenRecordDialogFactory;
mScreenRecordPermissionDialogDelegateFactory = screenRecordPermissionDialogDelegateFactory;
BroadcastOptions options = BroadcastOptions.makeBasic();
@@ -158,12 +148,8 @@ public class RecordingController
/** Create a dialog to show screen recording options to the user.
* If screen capturing is currently not allowed it will return a dialog
* that warns users about it. */
- public Dialog createScreenRecordDialog(Context context, FeatureFlags flags,
- DialogTransitionAnimator dialogTransitionAnimator,
- ActivityStarter activityStarter,
- @Nullable Runnable onStartRecordingClicked) {
- if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)
- && mDevicePolicyResolver.get()
+ public Dialog createScreenRecordDialog(@Nullable Runnable onStartRecordingClicked) {
+ if (mDevicePolicyResolver.get()
.isScreenCaptureCompletelyDisabled(getHostUserHandle())) {
return mScreenCaptureDisabledDialogDelegate.createSysUIDialog();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java
deleted file mode 100644
index 9f1447b1f509..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2018 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.screenrecord;
-
-import static android.app.Activity.RESULT_OK;
-
-import static com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.NONE;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.ResultReceiver;
-import android.view.Gravity;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
-import com.android.systemui.res.R;
-import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
-
-import dagger.assisted.Assisted;
-import dagger.assisted.AssistedFactory;
-import dagger.assisted.AssistedInject;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Dialog to select screen recording options
- */
-public class ScreenRecordDialogDelegate implements SystemUIDialog.Delegate {
- private static final List<ScreenRecordingAudioSource> MODES = Arrays.asList(INTERNAL, MIC,
- MIC_AND_INTERNAL);
- private static final long DELAY_MS = 3000;
- private static final long INTERVAL_MS = 1000;
-
- private final SystemUIDialog.Factory mSystemUIDialogFactory;
- private final UserContextProvider mUserContextProvider;
- private final RecordingController mController;
- private final Runnable mOnStartRecordingClicked;
- private Switch mTapsSwitch;
- private Switch mAudioSwitch;
- private Spinner mOptions;
-
- @AssistedFactory
- public interface Factory {
- ScreenRecordDialogDelegate create(
- RecordingController recordingController,
- @Nullable Runnable onStartRecordingClicked
- );
- }
-
- @AssistedInject
- public ScreenRecordDialogDelegate(
- SystemUIDialog.Factory systemUIDialogFactory,
- UserContextProvider userContextProvider,
- @Assisted RecordingController controller,
- @Assisted @Nullable Runnable onStartRecordingClicked) {
- mSystemUIDialogFactory = systemUIDialogFactory;
- mUserContextProvider = userContextProvider;
- mController = controller;
- mOnStartRecordingClicked = onStartRecordingClicked;
- }
-
- @Override
- public SystemUIDialog createDialog() {
- return mSystemUIDialogFactory.create(this);
- }
-
- @Override
- public void onCreate(SystemUIDialog dialog, Bundle savedInstanceState) {
- Window window = dialog.getWindow();
-
- window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
-
- window.setGravity(Gravity.CENTER);
- dialog.setTitle(R.string.screenrecord_title);
-
- dialog.setContentView(R.layout.screen_record_dialog);
-
- TextView cancelBtn = dialog.findViewById(R.id.button_cancel);
- cancelBtn.setOnClickListener(v -> dialog.dismiss());
- TextView startBtn = dialog.findViewById(R.id.button_start);
- startBtn.setOnClickListener(v -> {
- if (mOnStartRecordingClicked != null) {
- // Note that it is important to run this callback before dismissing, so that the
- // callback can disable the dialog exit animation if it wants to.
- mOnStartRecordingClicked.run();
- }
-
- // Start full-screen recording
- requestScreenCapture(/* captureTarget= */ null);
- dialog.dismiss();
- });
-
- mAudioSwitch = dialog.findViewById(R.id.screenrecord_audio_switch);
- mTapsSwitch = dialog.findViewById(R.id.screenrecord_taps_switch);
- mOptions = dialog.findViewById(R.id.screen_recording_options);
- ArrayAdapter a = new ScreenRecordingAdapter(dialog.getContext().getApplicationContext(),
- android.R.layout.simple_spinner_dropdown_item,
- MODES);
- a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mOptions.setAdapter(a);
- mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
- mAudioSwitch.setChecked(true);
- });
-
- // disable redundant Touch & Hold accessibility action for Switch Access
- mOptions.setAccessibilityDelegate(new View.AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(@NonNull View host,
- @NonNull AccessibilityNodeInfo info) {
- info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
- super.onInitializeAccessibilityNodeInfo(host, info);
- }
- });
- mOptions.setLongClickable(false);
- }
-
- /**
- * Starts screen capture after some countdown
- * @param captureTarget target to capture (could be e.g. a task) or
- * null to record the whole screen
- */
- private void requestScreenCapture(@Nullable MediaProjectionCaptureTarget captureTarget) {
- Context userContext = mUserContextProvider.getUserContext();
- boolean showTaps = mTapsSwitch.isChecked();
- ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
- ? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
- : NONE;
- PendingIntent startIntent = PendingIntent.getForegroundService(userContext,
- RecordingService.REQUEST_CODE,
- RecordingService.getStartIntent(
- userContext, Activity.RESULT_OK,
- audioMode.ordinal(), showTaps, captureTarget),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- PendingIntent stopIntent = PendingIntent.getService(userContext,
- RecordingService.REQUEST_CODE,
- RecordingService.getStopIntent(userContext),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent);
- }
-
- private class CaptureTargetResultReceiver extends ResultReceiver {
-
- CaptureTargetResultReceiver() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (resultCode == RESULT_OK) {
- MediaProjectionCaptureTarget captureTarget = resultData
- .getParcelable(KEY_CAPTURE_TARGET, MediaProjectionCaptureTarget.class);
-
- // Start recording of the selected target
- requestScreenCapture(captureTarget);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
index e5f684635ac7..b0777c9e20b9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
@@ -18,7 +18,6 @@ package com.android.systemui.shade.ui.viewmodel
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.scene.shared.model.Overlays
@@ -35,7 +34,7 @@ fun singleShadeActions(
return arrayOf(
// Swiping down, not from the edge, always goes to shade.
Swipe.Down to shadeUserActionResult,
- swipeDown(pointerCount = 2) to shadeUserActionResult,
+ Swipe.Down(pointerCount = 2) to shadeUserActionResult,
// Swiping down from the top edge.
swipeDownFromTop(pointerCount = 1) to
@@ -54,7 +53,7 @@ fun splitShadeActions(): Array<Pair<UserAction, UserActionResult>> {
return arrayOf(
// Swiping down, not from the edge, always goes to shade.
Swipe.Down to shadeUserActionResult,
- swipeDown(pointerCount = 2) to shadeUserActionResult,
+ Swipe.Down(pointerCount = 2) to shadeUserActionResult,
// Swiping down from the top edge goes to QS.
swipeDownFromTop(pointerCount = 1) to shadeUserActionResult,
swipeDownFromTop(pointerCount = 2) to shadeUserActionResult,
@@ -69,15 +68,10 @@ fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> {
UserActionResult.ShowOverlay(Overlays.QuickSettingsShade, isIrreversible = true)
return arrayOf(
Swipe.Down to notifShadeUserActionResult,
- Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to
- qsShadeuserActionResult,
+ Swipe.Down(fromSource = SceneContainerEdge.TopRight) to qsShadeuserActionResult,
)
}
private fun swipeDownFromTop(pointerCount: Int): Swipe {
- return Swipe(SwipeDirection.Down, fromSource = Edge.Top, pointerCount = pointerCount)
-}
-
-private fun swipeDown(pointerCount: Int): Swipe {
- return Swipe(SwipeDirection.Down, pointerCount = pointerCount)
+ return Swipe.Down(fromSource = Edge.Top, pointerCount = pointerCount)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
index 4bdd36773655..7d6b1a3126dc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade.ui.viewmodel
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
@@ -58,7 +57,7 @@ constructor(
buildMap<UserAction, UserActionResult> {
if (!isCustomizerShowing) {
set(
- Swipe(SwipeDirection.Up),
+ Swipe.Up,
UserActionResult(
backScene,
ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
@@ -69,7 +68,7 @@ constructor(
// TODO(b/330200163) Add an else to be able to collapse the shade while
// customizing
if (shadeMode is ShadeMode.Single) {
- set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings))
+ set(Swipe.Down, UserActionResult(Scenes.QuickSettings))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 520cbf9d80d9..8c5a711d6a75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -619,10 +619,11 @@ public class KeyguardIndicationController {
}
private void updateLockScreenUserLockedMsg(int userId) {
- boolean userUnlocked = mKeyguardUpdateMonitor.isUserUnlocked(userId);
+ boolean userStorageUnlocked = mKeyguardUpdateMonitor.isUserUnlocked(userId);
boolean encryptedOrLockdown = mKeyguardUpdateMonitor.isEncryptedOrLockdown(userId);
- mKeyguardLogger.logUpdateLockScreenUserLockedMsg(userId, userUnlocked, encryptedOrLockdown);
- if (!userUnlocked || encryptedOrLockdown) {
+ mKeyguardLogger.logUpdateLockScreenUserLockedMsg(userId, userStorageUnlocked,
+ encryptedOrLockdown);
+ if (!userStorageUnlocked || encryptedOrLockdown) {
mRotateTextViewController.updateIndication(
INDICATION_TYPE_USER_LOCKED,
new KeyguardIndication.Builder()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
index 0d789c703d48..f5d443443838 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
@@ -119,7 +119,6 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
/** Factory for constructing an {@link OperatorNameViewController}. */
public static class Factory {
- private final DarkIconDispatcher mDarkIconDispatcher;
private final TunerService mTunerService;
private final TelephonyManager mTelephonyManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -129,7 +128,7 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
private final JavaAdapter mJavaAdapter;
@Inject
- public Factory(DarkIconDispatcher darkIconDispatcher,
+ public Factory(
TunerService tunerService,
TelephonyManager telephonyManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -137,7 +136,6 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
AirplaneModeInteractor airplaneModeInteractor,
SubscriptionManagerProxy subscriptionManagerProxy,
JavaAdapter javaAdapter) {
- mDarkIconDispatcher = darkIconDispatcher;
mTunerService = tunerService;
mTelephonyManager = telephonyManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -148,9 +146,11 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
}
/** Create an {@link OperatorNameViewController}. */
- public OperatorNameViewController create(OperatorNameView view) {
- return new OperatorNameViewController(view,
- mDarkIconDispatcher,
+ public OperatorNameViewController create(
+ OperatorNameView view, DarkIconDispatcher darkIconDispatcher) {
+ return new OperatorNameViewController(
+ view,
+ darkIconDispatcher,
mTunerService,
mTelephonyManager,
mKeyguardUpdateMonitor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index c8d3f339b3e9..752674854e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -68,13 +68,8 @@ constructor(
notifChipsInteractor.onPromotedNotificationChipTapped(this@toChipModel.key)
}
}
- return OngoingActivityChipModel.Shown.ShortTimeDelta(
- icon,
- colors,
- time = this.whenTime,
- onClickListener,
- )
- // TODO(b/364653005): If Notification.showWhen = false, don't show the time delta.
+ return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+ // TODO(b/364653005): Use Notification.showWhen to determine if we should show the time.
// TODO(b/364653005): If Notification.whenTime is in the past, show "ago" in the text.
// TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`.
// TODO(b/364653005): If the app that posted the notification is in the foreground, don't
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
index 9b3513e8a363..84c7ab200fad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
@@ -23,6 +23,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.display.data.repository.DisplayScopeRepository
+import com.android.systemui.statusbar.data.repository.LightBarControllerStore
import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStore
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
@@ -50,6 +51,7 @@ constructor(
private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
private val statusBarInitializerStore: StatusBarInitializerStore,
private val privacyDotWindowControllerStore: PrivacyDotWindowControllerStore,
+ private val lightBarControllerStore: LightBarControllerStore,
) : CoreStartable {
init {
@@ -74,6 +76,14 @@ constructor(
createAndStartOrchestratorForDisplay(displayId)
createAndStartInitializerForDisplay(displayId)
startPrivacyDotForDisplay(displayId)
+ createLightBarControllerForDisplay(displayId)
+ }
+
+ private fun createLightBarControllerForDisplay(displayId: Int) {
+ // Explicitly not calling `start()`, because the store is already calling `start()`.
+ // This is to maintain the legacy behavior with NavigationBar, that was already expecting
+ // LightBarController to start at construction time.
+ lightBarControllerStore.forDisplay(displayId)
}
private fun createAndStartOrchestratorForDisplay(displayId: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
index 39de28e7cb49..27d815190d85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.statusbar.data
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStoreModule
import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule
import com.android.systemui.statusbar.data.repository.LightBarControllerStoreModule
import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule
@@ -28,6 +29,7 @@ import dagger.Module
@Module(
includes =
[
+ DarkIconDispatcherStoreModule::class,
KeyguardStatusBarRepositoryModule::class,
LightBarControllerStoreModule::class,
RemoteInputRepositoryModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt
new file mode 100644
index 000000000000..8183a487cee2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.statusbar.data.repository
+
+import android.content.Context
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.display.data.repository.SingleDisplayStore
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl
+import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher
+import dagger.Binds
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Provides per display instances of [DarkIconDispatcher]. */
+interface DarkIconDispatcherStore : PerDisplayStore<DarkIconDispatcher>
+
+/** Provides per display instances of [SysuiDarkIconDispatcher]. */
+interface SysuiDarkIconDispatcherStore : PerDisplayStore<SysuiDarkIconDispatcher>
+
+/**
+ * Multi display implementation that should be used when the [StatusBarConnectedDisplays] flag is
+ * enabled.
+ */
+@SysUISingleton
+class MultiDisplayDarkIconDispatcherStore
+@Inject
+constructor(
+ @Background backgroundApplicationScope: CoroutineScope,
+ displayRepository: DisplayRepository,
+ private val factory: DarkIconDispatcherImpl.Factory,
+ private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
+) :
+ SysuiDarkIconDispatcherStore,
+ PerDisplayStoreImpl<SysuiDarkIconDispatcher>(backgroundApplicationScope, displayRepository) {
+
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
+ }
+
+ override fun createInstanceForDisplay(displayId: Int): SysuiDarkIconDispatcher {
+ val properties = displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR)
+ return factory.create(displayId, properties.context)
+ }
+
+ override suspend fun onDisplayRemovalAction(instance: SysuiDarkIconDispatcher) {
+ instance.stop()
+ }
+
+ override val instanceClass = SysuiDarkIconDispatcher::class.java
+}
+
+/**
+ * Single display implementation that should be used when the [StatusBarConnectedDisplays] flag is
+ * disabled.
+ */
+@SysUISingleton
+class SingleDisplayDarkIconDispatcherStore
+@Inject
+constructor(factory: DarkIconDispatcherImpl.Factory, context: Context) :
+ SysuiDarkIconDispatcherStore,
+ PerDisplayStore<SysuiDarkIconDispatcher> by SingleDisplayStore(
+ defaultInstance = factory.create(context.displayId, context)
+ ) {
+
+ init {
+ StatusBarConnectedDisplays.assertInLegacyMode()
+ }
+}
+
+/** Extra implementation that simply implements the [DarkIconDispatcherStore] interface. */
+@SysUISingleton
+class DarkIconDispatcherStoreImpl
+@Inject
+constructor(private val store: SysuiDarkIconDispatcherStore) : DarkIconDispatcherStore {
+ override val defaultDisplay: DarkIconDispatcher
+ get() = store.defaultDisplay
+
+ override fun forDisplay(displayId: Int): DarkIconDispatcher = store.forDisplay(displayId)
+}
+
+@Module
+interface DarkIconDispatcherStoreModule {
+
+ @Binds fun store(impl: DarkIconDispatcherStoreImpl): DarkIconDispatcherStore
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun sysUiStore(
+ singleDisplayLazy: Lazy<SingleDisplayDarkIconDispatcherStore>,
+ multiDisplayLazy: Lazy<MultiDisplayDarkIconDispatcherStore>,
+ ): SysuiDarkIconDispatcherStore {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayLazy.get()
+ } else {
+ singleDisplayLazy.get()
+ }
+ }
+
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(DarkIconDispatcherStore::class)
+ fun storeAsCoreStartable(
+ multiDisplayLazy: Lazy<MultiDisplayDarkIconDispatcherStore>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayLazy.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
index ff50e3100672..e4987555833b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
@@ -44,6 +44,7 @@ constructor(
private val factory: LightBarControllerImpl.Factory,
private val displayScopeRepository: DisplayScopeRepository,
private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+ private val darkIconDispatcherStore: DarkIconDispatcherStore,
) :
LightBarControllerStore,
PerDisplayStoreImpl<LightBarController>(backgroundApplicationScope, displayRepository) {
@@ -53,6 +54,7 @@ constructor(
.create(
displayId,
displayScopeRepository.scopeForDisplay(displayId),
+ darkIconDispatcherStore.forDisplay(displayId),
statusBarModeRepositoryStore.forDisplay(displayId),
)
.also { it.start() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
index bdd9fd032800..6b6920a3621a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
@@ -28,14 +28,5 @@ import com.android.systemui.res.R
@MainThread
fun contentDescForNotification(c: Context, n: Notification): CharSequence {
val appName = n.loadHeaderAppName(c) ?: ""
- val title = n.extras?.getCharSequence(Notification.EXTRA_TITLE)
- val text = n.extras?.getCharSequence(Notification.EXTRA_TEXT)
- val ticker = n.tickerText
-
- // Some apps just put the app name into the title
- val titleOrText = if (TextUtils.equals(title, appName)) text else title
- val desc =
- if (!TextUtils.isEmpty(titleOrText)) titleOrText
- else if (!TextUtils.isEmpty(ticker)) ticker else ""
- return c.getString(R.string.accessibility_desc_notification_icon, appName, desc)
+ return c.getString(R.string.accessibility_desc_notification_icon, appName, "")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
index 663588c8f8c8..fc432ba973ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
@@ -17,11 +17,13 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
@@ -30,7 +32,6 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Binds a [NotificationIconContainer] to a [NotificationIconContainerAlwaysOnDisplayViewModel]. */
class NotificationIconContainerAlwaysOnDisplayViewBinder
@@ -38,7 +39,7 @@ class NotificationIconContainerAlwaysOnDisplayViewBinder
constructor(
private val viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val failureTracker: StatusBarIconViewBindingFailureTracker,
private val screenOffAnimationController: ScreenOffAnimationController,
private val systemBarUtilsState: SystemBarUtilsState,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
index 4e40888ee88a..5432f1448902 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.bindIcons
@@ -30,7 +31,7 @@ class NotificationIconContainerShelfViewBinder
@Inject
constructor(
private val viewModel: NotificationIconContainerShelfViewModel,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val systemBarUtilsState: SystemBarUtilsState,
private val failureTracker: StatusBarIconViewBindingFailureTracker,
private val viewStore: ShelfNotificationIconViewStore,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
index f0f529e2c615..a21dabb821d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
@@ -17,9 +17,11 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
@@ -27,23 +29,23 @@ import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Binds a [NotificationIconContainer] to a [NotificationIconContainerStatusBarViewModel]. */
class NotificationIconContainerStatusBarViewBinder
@Inject
constructor(
private val viewModel: NotificationIconContainerStatusBarViewModel,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val systemBarUtilsState: SystemBarUtilsState,
private val failureTracker: StatusBarIconViewBindingFailureTracker,
private val viewStore: StatusBarNotificationIconViewStore,
) {
- fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
+ fun bindWhileAttached(view: NotificationIconContainer, displayId: Int): DisposableHandle {
return traceSection("NICStatusBar#bindWhileAttached") {
view.repeatWhenAttached {
lifecycleScope.launch {
NotificationIconContainerViewBinder.bind(
+ displayId = displayId,
view = view,
viewModel = viewModel,
configuration = configuration,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 063fe45cd199..6dbb71463602 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -24,6 +24,7 @@ import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.internal.R as RInternal
import com.android.internal.statusbar.StatusBarIcon
@@ -54,12 +55,12 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Binds a view-model to a [NotificationIconContainer]. */
object NotificationIconContainerViewBinder {
suspend fun bind(
+ displayId: Int,
view: NotificationIconContainer,
viewModel: NotificationIconContainerStatusBarViewModel,
configuration: ConfigurationState,
@@ -70,7 +71,10 @@ object NotificationIconContainerViewBinder {
launch {
val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
val iconColors: StateFlow<NotificationIconColors> =
- viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }.stateIn(this)
+ viewModel
+ .iconColors(displayId)
+ .mapNotNull { it.iconColors(view.viewBounds) }
+ .stateIn(this)
viewModel.icons.bindIcons(
logTag = "statusbar",
view = view,
@@ -79,11 +83,7 @@ object NotificationIconContainerViewBinder {
notifyBindingFailures = { failureTracker.statusBarFailures = it },
viewStore = viewStore,
) { _, sbiv ->
- StatusBarIconViewBinder.bindIconColors(
- sbiv,
- iconColors,
- contrastColorUtil,
- )
+ StatusBarIconViewBinder.bindIconColors(sbiv, iconColors, contrastColorUtil)
}
}
launch { viewModel.bindIsolatedIcon(view, viewStore) }
@@ -194,8 +194,7 @@ object NotificationIconContainerViewBinder {
combine(iconSizeFlow, iconHorizontalPaddingFlow, systemBarUtilsState.statusBarHeight) {
iconSize,
iconHPadding,
- statusBarHeight,
- ->
+ statusBarHeight ->
FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
}
.stateIn(this)
@@ -251,10 +250,7 @@ object NotificationIconContainerViewBinder {
traceSection("addIcon") {
(sbiv.parent as? ViewGroup)?.run {
if (this !== view) {
- Log.wtf(
- TAG,
- "[$logTag] SBIV($notifKey) has an unexpected parent",
- )
+ Log.wtf(TAG, "[$logTag] SBIV($notifKey) has an unexpected parent")
}
// If the container was re-inflated and re-bound, then SBIVs might still
// be attached to the prior view.
@@ -271,7 +267,7 @@ object NotificationIconContainerViewBinder {
launch {
launch {
layoutParams.collectTracingEach(
- tag = { "[$logTag] SBIV#bindLayoutParams" },
+ tag = { "[$logTag] SBIV#bindLayoutParams" }
) {
if (it != sbiv.layoutParams) {
sbiv.layoutParams = it
@@ -344,7 +340,7 @@ object NotificationIconContainerViewBinder {
// a single SBIV instance for the group. Then this whole concept can go away.
private inline fun <R> NotificationIconContainer.withIconReplacements(
replacements: ArrayMap<String, StatusBarIcon>,
- block: () -> R
+ block: () -> R,
): R {
setReplacingIcons(replacements)
return block().also { setReplacingIcons(null) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index a64f888cb238..f0b03065e637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -45,8 +45,8 @@ import kotlinx.coroutines.flow.map
class NotificationIconContainerStatusBarViewModel
@Inject
constructor(
- @Background bgContext: CoroutineContext,
- darkIconInteractor: DarkIconInteractor,
+ @Background private val bgContext: CoroutineContext,
+ private val darkIconInteractor: DarkIconInteractor,
iconsInteractor: StatusBarNotificationIconsInteractor,
headsUpIconInteractor: HeadsUpNotificationIconInteractor,
keyguardInteractor: KeyguardInteractor,
@@ -58,10 +58,9 @@ constructor(
/** Are changes to the icon container animated? */
val animationsEnabled: Flow<Boolean> =
- combine(
- shadeInteractor.isShadeTouchable,
- keyguardInteractor.isKeyguardShowing,
- ) { panelTouchesEnabled, isKeyguardShowing ->
+ combine(shadeInteractor.isShadeTouchable, keyguardInteractor.isKeyguardShowing) {
+ panelTouchesEnabled,
+ isKeyguardShowing ->
panelTouchesEnabled && !isKeyguardShowing
}
.flowOn(bgContext)
@@ -69,8 +68,9 @@ constructor(
.distinctUntilChanged()
/** The colors with which to display the notification icons. */
- val iconColors: Flow<NotificationIconColorLookup> =
- darkIconInteractor.darkState
+ fun iconColors(displayId: Int): Flow<NotificationIconColorLookup> =
+ darkIconInteractor
+ .darkState(displayId)
.map { (areas: Collection<Rect>, tint: Int) ->
NotificationIconColorLookup { viewBounds: Rect ->
if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
@@ -125,10 +125,8 @@ constructor(
val isolatedIconLocation: Flow<Rect> =
headsUpIconInteractor.isolatedIconLocation.filterNotNull().conflate().distinctUntilChanged()
- private class IconColorsImpl(
- override val tint: Int,
- private val areas: Collection<Rect>,
- ) : NotificationIconColors {
+ private class IconColorsImpl(override val tint: Int, private val areas: Collection<Rect>) :
+ NotificationIconColors {
override fun staticDrawableColor(viewBounds: Rect): Int {
return if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
tint
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index b166defb96a2..2dcb706234b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -95,7 +95,7 @@ constructor(
private val smartReplyStateInflater: SmartReplyStateInflater,
private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
private val headsUpStyleProvider: HeadsUpStyleProvider,
- private val logger: NotificationRowContentBinderLogger
+ private val logger: NotificationRowContentBinderLogger,
) : NotificationRowContentBinder {
init {
@@ -110,7 +110,7 @@ constructor(
@InflationFlag contentToBind: Int,
bindParams: BindParams,
forceInflate: Boolean,
- callback: InflationCallback?
+ callback: InflationCallback?,
) {
if (row.isRemoved) {
// We don't want to reinflate anything for removed notifications. Otherwise views might
@@ -147,7 +147,7 @@ constructor(
/* isMediaFlagEnabled = */ smartReplyStateInflater,
notifLayoutInflaterFactoryProvider,
headsUpStyleProvider,
- logger
+ logger,
)
if (inflateSynchronously) {
task.onPostExecute(task.doInBackground())
@@ -165,7 +165,7 @@ constructor(
@InflationFlag reInflateFlags: Int,
builder: Notification.Builder,
packageContext: Context,
- smartRepliesInflater: SmartReplyStateInflater
+ smartRepliesInflater: SmartReplyStateInflater,
): InflationProgress {
val systemUIContext = row.context
val result =
@@ -229,7 +229,7 @@ constructor(
row,
remoteInputManager.remoteViewsOnClickHandler,
/* callback= */ null,
- logger
+ logger,
)
return result
}
@@ -246,7 +246,7 @@ constructor(
override fun unbindContent(
entry: NotificationEntry,
row: ExpandableNotificationRow,
- @InflationFlag contentToUnbind: Int
+ @InflationFlag contentToUnbind: Int,
) {
logger.logUnbinding(entry, contentToUnbind)
var curFlag = 1
@@ -268,7 +268,7 @@ constructor(
private fun freeNotificationView(
entry: NotificationEntry,
row: ExpandableNotificationRow,
- @InflationFlag inflateFlag: Int
+ @InflationFlag inflateFlag: Int,
) {
when (inflateFlag) {
FLAG_CONTENT_VIEW_CONTRACTED ->
@@ -319,7 +319,7 @@ constructor(
*/
private fun cancelContentViewFrees(
row: ExpandableNotificationRow,
- @InflationFlag contentViews: Int
+ @InflationFlag contentViews: Int,
) {
if (contentViews and FLAG_CONTENT_VIEW_CONTRACTED != 0) {
row.privateLayout.removeContentInactiveRunnable(VISIBLE_TYPE_CONTRACTED)
@@ -372,7 +372,7 @@ constructor(
private val smartRepliesInflater: SmartReplyStateInflater,
private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
private val headsUpStyleProvider: HeadsUpStyleProvider,
- private val logger: NotificationRowContentBinderLogger
+ private val logger: NotificationRowContentBinderLogger,
) : AsyncTask<Void, Void, Result<InflationProgress>>(), InflationCallback, InflationTask {
private val context: Context
get() = row.context
@@ -393,7 +393,7 @@ constructor(
context.packageManager.getApplicationInfoAsUser(
packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES,
- userId
+ userId,
)
} catch (e: PackageManager.NameNotFoundException) {
return
@@ -442,11 +442,11 @@ constructor(
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
headsUpStyleProvider = headsUpStyleProvider,
conversationProcessor = conversationProcessor,
- logger = logger
+ logger = logger,
)
logger.logAsyncTaskProgress(
entry,
- "getting existing smart reply state (on wrong thread!)"
+ "getting existing smart reply state (on wrong thread!)",
)
val previousSmartReplyState: InflatedSmartReplyState? = row.existingSmartReplyState
logger.logAsyncTaskProgress(entry, "inflating smart reply views")
@@ -469,7 +469,7 @@ constructor(
reInflateFlags,
entry,
context,
- logger
+ logger,
)
}
}
@@ -483,7 +483,7 @@ constructor(
reInflateFlags,
entry,
context,
- logger
+ logger,
)
}
}
@@ -513,7 +513,7 @@ constructor(
row,
remoteViewClickHandler,
this /* callback */,
- logger
+ logger,
)
}
.onFailure { error -> handleError(error as Exception) }
@@ -530,7 +530,7 @@ constructor(
Log.e(TAG, "couldn't inflate view for notification $ident", e)
callback?.handleInflationException(
row.entry,
- InflationException("Couldn't inflate contentViews$e")
+ InflationException("Couldn't inflate contentViews$e"),
)
// Cancel any image loading tasks, not useful any more
@@ -618,7 +618,7 @@ constructor(
packageContext: Context,
previousSmartReplyState: InflatedSmartReplyState?,
inflater: SmartReplyStateInflater,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
) {
val inflateContracted =
(reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0 &&
@@ -641,7 +641,7 @@ constructor(
packageContext,
entry,
previousSmartReplyState,
- result.inflatedSmartReplyState!!
+ result.inflatedSmartReplyState!!,
)
}
if (inflateHeadsUp) {
@@ -652,7 +652,7 @@ constructor(
packageContext,
entry,
previousSmartReplyState,
- result.inflatedSmartReplyState!!
+ result.inflatedSmartReplyState!!,
)
}
}
@@ -670,7 +670,7 @@ constructor(
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
headsUpStyleProvider: HeadsUpStyleProvider,
conversationProcessor: ConversationNotificationProcessor,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
): InflationProgress {
// process conversations and extract the messaging style
val messagingStyle =
@@ -713,7 +713,7 @@ constructor(
logger.logAsyncTaskProgress(entry, "inflating public single line view model")
SingleLineViewInflater.inflateRedactedSingleLineViewModel(
systemUIContext,
- entry.ranking.isConversation
+ entry.ranking.isConversation,
)
} else null
@@ -746,7 +746,7 @@ constructor(
row: ExpandableNotificationRow,
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
headsUpStyleProvider: HeadsUpStyleProvider,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
): NewRemoteViews {
return TraceUtils.trace("NotificationContentInflater.createRemoteViews") {
val entryForLogging: NotificationEntry = row.entry
@@ -754,7 +754,7 @@ constructor(
if (reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating contracted remote view"
+ "creating contracted remote view",
)
createContentView(builder, isMinimized, usesIncreasedHeight)
} else null
@@ -762,7 +762,7 @@ constructor(
if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating expanded remote view"
+ "creating expanded remote view",
)
createExpandedView(builder, isMinimized)
} else null
@@ -770,7 +770,7 @@ constructor(
if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating heads up remote view"
+ "creating heads up remote view",
)
val isHeadsUpCompact = headsUpStyleProvider.shouldApplyCompactStyle()
if (isHeadsUpCompact) {
@@ -791,7 +791,7 @@ constructor(
) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating group summary remote view"
+ "creating group summary remote view",
)
builder.makeNotificationGroupHeader()
} else null
@@ -802,7 +802,7 @@ constructor(
) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating low-priority group summary remote view"
+ "creating low-priority group summary remote view",
)
builder.makeLowPriorityContentView(true /* useRegularSubtext */)
} else null
@@ -812,7 +812,7 @@ constructor(
expanded = expanded,
public = public,
normalGroupHeader = normalGroupHeader,
- minimizedGroupHeader = minimizedGroupHeader
+ minimizedGroupHeader = minimizedGroupHeader,
)
.withLayoutInflaterFactory(row, notifLayoutInflaterFactoryProvider)
}
@@ -820,7 +820,7 @@ constructor(
private fun NewRemoteViews.withLayoutInflaterFactory(
row: ExpandableNotificationRow,
- provider: NotifLayoutInflaterFactory.Provider
+ provider: NotifLayoutInflaterFactory.Provider,
): NewRemoteViews {
contracted?.let {
it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_CONTRACTED)
@@ -848,7 +848,7 @@ constructor(
row: ExpandableNotificationRow,
remoteViewClickHandler: InteractionHandler?,
callback: InflationCallback?,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
): CancellationSignal {
Trace.beginAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row))
val privateLayout = row.privateLayout
@@ -859,7 +859,7 @@ constructor(
val isNewView =
!canReapplyRemoteView(
newView = result.remoteViews.contracted,
- oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -890,7 +890,7 @@ constructor(
existingWrapper = privateLayout.getVisibleWrapper(VISIBLE_TYPE_CONTRACTED),
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
flag = FLAG_CONTENT_VIEW_EXPANDED
@@ -898,7 +898,7 @@ constructor(
val isNewView =
!canReapplyRemoteView(
newView = result.remoteViews.expanded,
- oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -929,7 +929,7 @@ constructor(
existingWrapper = privateLayout.getVisibleWrapper(VISIBLE_TYPE_EXPANDED),
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
flag = FLAG_CONTENT_VIEW_HEADS_UP
@@ -937,7 +937,7 @@ constructor(
val isNewView =
!canReapplyRemoteView(
newView = result.remoteViews.headsUp,
- oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -968,7 +968,7 @@ constructor(
existingWrapper = privateLayout.getVisibleWrapper(VISIBLE_TYPE_HEADSUP),
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
flag = FLAG_CONTENT_VIEW_PUBLIC
@@ -976,7 +976,7 @@ constructor(
val isNewView =
!canReapplyRemoteView(
newView = result.remoteViews.public,
- oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -1007,7 +1007,7 @@ constructor(
existingWrapper = publicLayout.getVisibleWrapper(VISIBLE_TYPE_CONTRACTED),
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
if (AsyncGroupHeaderViewInflation.isEnabled) {
@@ -1018,7 +1018,7 @@ constructor(
!canReapplyRemoteView(
newView = result.remoteViews.normalGroupHeader,
oldView =
- remoteViewCache.getCachedView(entry, FLAG_GROUP_SUMMARY_HEADER)
+ remoteViewCache.getCachedView(entry, FLAG_GROUP_SUMMARY_HEADER),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -1049,7 +1049,7 @@ constructor(
existingWrapper = childrenContainer.notificationHeaderWrapper,
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
if (reInflateFlags and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0) {
@@ -1059,15 +1059,15 @@ constructor(
oldView =
remoteViewCache.getCachedView(
entry,
- FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER
- )
+ FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
+ ),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
override fun setResultView(v: View) {
logger.logAsyncTaskProgress(
entry,
- "low-priority group header view applied"
+ "low-priority group header view applied",
)
result.inflatedMinimizedGroupHeaderView =
v as NotificationHeaderView?
@@ -1095,7 +1095,7 @@ constructor(
existingWrapper = childrenContainer.minimizedGroupHeaderWrapper,
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
}
@@ -1110,7 +1110,7 @@ constructor(
callback,
entry,
row,
- logger
+ logger,
)
val cancellationSignal = CancellationSignal()
cancellationSignal.setOnCancelListener {
@@ -1142,7 +1142,7 @@ constructor(
existingWrapper: NotificationViewWrapper?,
runningInflations: HashMap<Int, CancellationSignal>,
applyCallback: ApplyCallback,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
) {
val newContentView: RemoteViews = applyCallback.remoteView
if (inflateSynchronously) {
@@ -1152,7 +1152,7 @@ constructor(
newContentView.apply(
result.packageContext,
parentLayout,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
validateView(v, entry, row.resources)
applyCallback.setResultView(v)
@@ -1162,7 +1162,7 @@ constructor(
newContentView.reapply(
result.packageContext,
existingView,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
validateView(existingView, entry, row.resources)
existingWrapper.onReinflated()
@@ -1174,7 +1174,7 @@ constructor(
row.entry,
callback,
logger,
- "applying view synchronously"
+ "applying view synchronously",
)
// Add a running inflation to make sure we don't trigger callbacks.
// Safe to do because only happens in tests.
@@ -1199,7 +1199,7 @@ constructor(
row.entry,
callback,
logger,
- "applied invalid view"
+ "applied invalid view",
)
runningInflations.remove(inflationId)
return
@@ -1219,7 +1219,7 @@ constructor(
callback,
entry,
row,
- logger
+ logger,
)
}
@@ -1234,20 +1234,20 @@ constructor(
newContentView.apply(
result.packageContext,
parentLayout,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
} else {
newContentView.reapply(
result.packageContext,
existingView,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
existingView!!
}
Log.wtf(
TAG,
"Async Inflation failed but normal inflation finished normally.",
- e
+ e,
)
onViewApplied(newView)
} catch (anotherException: Exception) {
@@ -1258,7 +1258,7 @@ constructor(
row.entry,
callback,
logger,
- "applying view"
+ "applying view",
)
}
}
@@ -1270,7 +1270,7 @@ constructor(
parentLayout,
inflationExecutor,
listener,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
} else {
newContentView.reapplyAsync(
@@ -1278,7 +1278,7 @@ constructor(
existingView,
inflationExecutor,
listener,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
}
runningInflations[inflationId] = cancellationSignal
@@ -1299,7 +1299,7 @@ constructor(
private fun satisfiesMinHeightRequirement(
view: View,
entry: NotificationEntry,
- resources: Resources
+ resources: Resources,
): Boolean {
return if (!requiresHeightCheck(entry)) {
true
@@ -1353,7 +1353,7 @@ constructor(
notification: NotificationEntry,
callback: InflationCallback?,
logger: NotificationRowContentBinderLogger,
- logContext: String
+ logContext: String,
) {
Assert.isMainThread()
logger.logAsyncTaskException(notification, logContext, e)
@@ -1375,7 +1375,7 @@ constructor(
endListener: InflationCallback?,
entry: NotificationEntry,
row: ExpandableNotificationRow,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
): Boolean {
Assert.isMainThread()
if (runningInflations.isNotEmpty()) {
@@ -1439,19 +1439,19 @@ constructor(
FLAG_CONTENT_VIEW_CONTRACTED,
result.remoteViews.contracted,
result.inflatedContentView,
- privateLayout::setContractedChild
+ privateLayout::setContractedChild,
)
remoteViewsUpdater.setContentView(
FLAG_CONTENT_VIEW_EXPANDED,
result.remoteViews.expanded,
result.inflatedExpandedView,
- privateLayout::setExpandedChild
+ privateLayout::setExpandedChild,
)
remoteViewsUpdater.setSmartReplies(
FLAG_CONTENT_VIEW_EXPANDED,
result.remoteViews.expanded,
result.expandedInflatedSmartReplies,
- privateLayout::setExpandedInflatedSmartReplies
+ privateLayout::setExpandedInflatedSmartReplies,
)
if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
row.setExpandable(result.remoteViews.expanded != null)
@@ -1460,19 +1460,19 @@ constructor(
FLAG_CONTENT_VIEW_HEADS_UP,
result.remoteViews.headsUp,
result.inflatedHeadsUpView,
- privateLayout::setHeadsUpChild
+ privateLayout::setHeadsUpChild,
)
remoteViewsUpdater.setSmartReplies(
FLAG_CONTENT_VIEW_HEADS_UP,
result.remoteViews.headsUp,
result.headsUpInflatedSmartReplies,
- privateLayout::setHeadsUpInflatedSmartReplies
+ privateLayout::setHeadsUpInflatedSmartReplies,
)
remoteViewsUpdater.setContentView(
FLAG_CONTENT_VIEW_PUBLIC,
result.remoteViews.public,
result.inflatedPublicView,
- publicLayout::setContractedChild
+ publicLayout::setContractedChild,
)
if (AsyncGroupHeaderViewInflation.isEnabled) {
remoteViewsUpdater.setContentView(
@@ -1540,7 +1540,7 @@ constructor(
private fun createExpandedView(
builder: Notification.Builder,
- isMinimized: Boolean
+ isMinimized: Boolean,
): RemoteViews? {
@Suppress("DEPRECATION")
val bigContentView: RemoteViews? = builder.createBigContentView()
@@ -1558,7 +1558,7 @@ constructor(
private fun createContentView(
builder: Notification.Builder,
isMinimized: Boolean,
- useLarge: Boolean
+ useLarge: Boolean,
): RemoteViews {
return if (isMinimized) {
builder.makeLowPriorityContentView(false /* useRegularSubtext */)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
index 7177a7bd473a..08c1d71b86c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
@@ -73,8 +73,8 @@ constructor(private val userManager: UserManager, dumpManager: DumpManager) :
private val cache = NotifCollectionCache<Boolean>()
override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean {
- val packageContext = notification.getPackageContext(context)
return cache.getOrFetch(notification.packageName) {
+ val packageContext = notification.getPackageContext(context)
!belongsToHeadlessSystemApp(packageContext)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
index 3a650aa19eb0..53d0c2e879e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
@@ -22,6 +22,7 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
import com.android.systemui.util.kotlin.WithPrev
@@ -46,9 +47,9 @@ class HideNotificationsInteractor
@Inject
constructor(
private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
private val animationsStatus: AnimationStatusRepository,
- private val powerInteractor: PowerInteractor
+ private val powerInteractor: PowerInteractor,
) {
val shouldHideNotifications: Flow<Boolean>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index e644815960aa..b6ce70826f9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -47,7 +47,7 @@ class SharedNotificationContainerInteractor
constructor(
@ShadeDisplayAware private val context: Context,
private val splitShadeStateController: Lazy<SplitShadeStateController>,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
keyguardInteractor: KeyguardInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
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 f75c89aeb44c..bffcae99e7f6 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
@@ -20,6 +20,7 @@ import android.view.LayoutInflater
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.TraceUtils.traceAsync
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.nano.MetricsProto
import com.android.systemui.common.ui.ConfigurationState
@@ -30,6 +31,7 @@ import com.android.systemui.lifecycle.repeatWhenAttachedToWindow
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
@@ -70,7 +72,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */
class NotificationListViewBinder
@@ -78,7 +79,7 @@ class NotificationListViewBinder
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val hiderTracker: DisplaySwitchNotificationsHiderTracker,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val falsingManager: FalsingManager,
private val hunBinder: HeadsUpNotificationViewBinder,
private val loggerOptional: Optional<NotificationStatsLogger>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 4a768714b84f..c5bef99f9307 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -28,6 +28,7 @@ import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationScrollViewModel
import com.android.systemui.util.kotlin.FlowDumperImpl
@@ -48,7 +49,7 @@ constructor(
@Main private val mainImmediateDispatcher: CoroutineDispatcher,
private val view: NotificationScrollView,
private val viewModelFactory: NotificationScrollViewModel.Factory,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
) : FlowDumperImpl(dumpManager) {
private val viewLeftOffset = MutableStateFlow(0).dumpValue("viewLeftOffset")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 827e2bfeba0f..fb60f266e188 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -72,6 +72,7 @@ import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode.Dual
import com.android.systemui.shade.shared.model.ShadeMode.Single
@@ -116,7 +117,7 @@ constructor(
dumpManager: DumpManager,
@Application applicationScope: CoroutineScope,
private val context: Context,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val shadeInteractor: ShadeInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 398c1d43d4fc..90b591f173f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -23,11 +23,15 @@ import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.ArrayMap;
+import android.view.Display;
import android.widget.ImageView;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import kotlinx.coroutines.flow.FlowKt;
import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.flow.StateFlow;
@@ -36,17 +40,15 @@ import kotlinx.coroutines.flow.StateFlowKt;
import java.io.PrintWriter;
import java.util.ArrayList;
-import javax.inject.Inject;
-
/**
*/
-@SysUISingleton
public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher,
LightBarTransitionsController.DarkIntensityApplier {
private final LightBarTransitionsController mTransitionsController;
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final ArrayMap<Object, DarkReceiver> mReceivers = new ArrayMap<>();
+ private final DumpManager mDumpManager;
private int mIconTint = DEFAULT_ICON_TINT;
private int mContrastTint = DEFAULT_INVERSE_ICON_TINT;
@@ -61,14 +63,25 @@ public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher,
private final MutableStateFlow<DarkChange> mDarkChangeFlow = StateFlowKt.MutableStateFlow(
DarkChange.EMPTY);
+ private final String mDumpableName;
+
+ /** */
+ @AssistedFactory
+ @FunctionalInterface
+ public interface Factory {
+ /** */
+ DarkIconDispatcherImpl create(int displayId, Context context);
+ }
+
/**
*/
- @Inject
+ @AssistedInject
public DarkIconDispatcherImpl(
- Context context,
+ @Assisted int displayId,
+ @Assisted Context context,
LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
DumpManager dumpManager) {
-
+ mDumpManager = dumpManager;
if (newStatusBarIcons()) {
mDarkModeIconColorSingleTone = Color.BLACK;
mLightModeIconColorSingleTone = Color.WHITE;
@@ -81,7 +94,19 @@ public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher,
mTransitionsController = lightBarTransitionsControllerFactory.create(this);
- dumpManager.registerDumpable(getClass().getSimpleName(), this);
+ mDumpableName = getDumpableName(displayId);
+ dumpManager.registerNormalDumpable(mDumpableName, this);
+ }
+
+ @Override
+ public void stop() {
+ mDumpManager.unregisterDumpable(mDumpableName);
+ }
+
+ private String getDumpableName(int displayId) {
+ String dumpableNameSuffix =
+ displayId == Display.DEFAULT_DISPLAY ? "" : String.valueOf(displayId);
+ return getClass().getSimpleName() + dumpableNameSuffix;
}
public LightBarTransitionsController getTransitionsController() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index d0f4b6f4a4bb..8de03d83d7af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -26,6 +26,7 @@ import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
+import com.android.systemui.dagger.qualifiers.DisplaySpecific;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -117,7 +118,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
PhoneStatusBarTransitions phoneStatusBarTransitions,
KeyguardBypassController bypassController,
NotificationWakeUpCoordinator wakeUpCoordinator,
- DarkIconDispatcher darkIconDispatcher,
+ @DisplaySpecific DarkIconDispatcher darkIconDispatcher,
KeyguardStateController keyguardStateController,
CommandQueue commandQueue,
NotificationStackScrollLayoutController stackScrollerController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
index edc1f88b7579..ccb9a119d92f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
@@ -133,7 +133,7 @@ public class LightBarControllerImpl implements
public LightBarControllerImpl(
@Assisted int displayId,
@Assisted CoroutineScope coroutineScope,
- DarkIconDispatcher darkIconDispatcher,
+ @Assisted DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
@Assisted StatusBarModePerDisplayRepository statusBarModeRepository,
@@ -487,6 +487,7 @@ public class LightBarControllerImpl implements
LightBarControllerImpl create(
int displayId,
CoroutineScope coroutineScope,
+ DarkIconDispatcher darkIconDispatcher,
StatusBarModePerDisplayRepository statusBarModePerDisplayRepository);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 424549453af3..16e023ce17fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -27,6 +27,7 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.Flags
import com.android.systemui.Gefingerpoken
import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.dagger.qualifiers.DisplaySpecific
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS
import com.android.systemui.plugins.DarkIconDispatcher
@@ -341,7 +342,7 @@ private constructor(
private val viewUtil: ViewUtil,
private val configurationController: ConfigurationController,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
- private val darkIconDispatcher: DarkIconDispatcher,
+ @DisplaySpecific private val darkIconDispatcher: DarkIconDispatcher,
private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
) {
fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
index c341bd3466ed..394502b2d31f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
@@ -28,10 +28,13 @@ import androidx.annotation.ColorInt
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore
+import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -40,14 +43,14 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
class StatusOverlayHoverListenerFactory
@Inject
constructor(
@Main private val resources: Resources,
private val configurationController: ConfigurationController,
- private val darkIconDispatcher: SysuiDarkIconDispatcher,
+ private val darkIconDispatcherStore: SysuiDarkIconDispatcherStore,
+ private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
) {
/** Creates listener always using the same light color for overlay */
@@ -63,7 +66,7 @@ constructor(
* Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay
*/
fun createDarkAwareListener(view: View) =
- createDarkAwareListener(view, darkIconDispatcher.darkChangeFlow())
+ createDarkAwareListener(view, view.darkIconDispatcher.darkChangeFlow())
/**
* Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay
@@ -78,7 +81,7 @@ constructor(
) =
createDarkAwareListener(
view,
- darkIconDispatcher.darkChangeFlow(),
+ view.darkIconDispatcher.darkChangeFlow(),
leftHoverMargin,
rightHoverMargin,
topHoverMargin,
@@ -92,8 +95,8 @@ constructor(
fun createDarkAwareListener(view: View, darkFlow: StateFlow<DarkChange>) =
StatusOverlayHoverListener(
view,
- configurationController,
- resources,
+ view.statusBarConfigurationController,
+ view.resources,
darkFlow.map { toHoverTheme(view, it) },
)
@@ -107,8 +110,8 @@ constructor(
) =
StatusOverlayHoverListener(
view,
- configurationController,
- resources,
+ view.statusBarConfigurationController,
+ view.resources,
darkFlow.map { toHoverTheme(view, it) },
leftHoverMargin,
rightHoverMargin,
@@ -116,6 +119,12 @@ constructor(
bottomHoverMargin,
)
+ private val View.statusBarConfigurationController
+ get() = statusBarConfigurationControllerStore.forDisplay(context.displayId)
+
+ private val View.darkIconDispatcher
+ get() = darkIconDispatcherStore.forDisplay(context.displayId)
+
private fun toHoverTheme(view: View, darkChange: DarkChange): HoverTheme {
val calculatedTint = DarkIconDispatcher.getTint(darkChange.areas, view, darkChange.tint)
// currently calculated tint is either white or some shade of black.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt
index ba377497bce4..49356eba2842 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone.data.repository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher
+import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
import dagger.Binds
import dagger.Module
@@ -25,16 +25,16 @@ import kotlinx.coroutines.flow.StateFlow
/** Dark-mode state for tinting icons. */
interface DarkIconRepository {
- val darkState: StateFlow<DarkChange>
+ fun darkState(displayId: Int): StateFlow<DarkChange>
}
@SysUISingleton
class DarkIconRepositoryImpl
@Inject
-constructor(
- darkIconDispatcher: SysuiDarkIconDispatcher,
-) : DarkIconRepository {
- override val darkState: StateFlow<DarkChange> = darkIconDispatcher.darkChangeFlow()
+constructor(private val darkIconDispatcherStore: SysuiDarkIconDispatcherStore) :
+ DarkIconRepository {
+ override fun darkState(displayId: Int): StateFlow<DarkChange> =
+ darkIconDispatcherStore.forDisplay(displayId).darkChangeFlow()
}
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt
index 72f45406b35e..095f0cba8a46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt
@@ -22,7 +22,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
/** States pertaining to calculating colors for icons in dark mode. */
-class DarkIconInteractor @Inject constructor(repository: DarkIconRepository) {
+class DarkIconInteractor @Inject constructor(private val repository: DarkIconRepository) {
/** Dark-mode state for tinting icons. */
- val darkState: Flow<DarkState> = repository.darkState.map { DarkState(it.areas, it.tint) }
+ fun darkState(displayId: Int): Flow<DarkState> =
+ repository.darkState(displayId).map { DarkState(it.areas, it.tint) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 013141b5e142..5cc44762dde8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -351,8 +351,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mStatusBar.restoreHierarchyState(
savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
}
- mDarkIconManager = mDarkIconManagerFactory.create(
- view.findViewById(R.id.statusIcons), StatusBarLocation.HOME);
+ mDarkIconManager =
+ mDarkIconManagerFactory.create(
+ view.findViewById(R.id.statusIcons),
+ StatusBarLocation.HOME,
+ mHomeStatusBarComponent.getDarkIconDispatcher());
mDarkIconManager.setShouldLog(true);
updateBlockedIcons();
mStatusBarIconController.addIconGroup(mDarkIconManager);
@@ -496,11 +499,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
NotificationIconContainer notificationIcons =
notificationIconArea.requireViewById(R.id.notificationIcons);
mNotificationIconAreaInner = notificationIcons;
- if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+ int displayId = mHomeStatusBarComponent.getDisplayId();
+ if (displayId == Display.DEFAULT_DISPLAY) {
//TODO(b/369337701): implement notification icons for all displays.
// Currently if we try to bind for all displays, there is a crash, because the same
// notification icon view can't have multiple parents.
- mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+ mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons, displayId);
}
if (!StatusBarSimpleFragment.isEnabled()) {
@@ -939,7 +943,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
if (mCarrierConfigTracker.getShowOperatorNameInStatusBarConfig(subId)) {
View view = mStatusBar.findViewById(R.id.operator_name);
mOperatorNameViewController =
- mOperatorNameViewControllerFactory.create((OperatorNameView) view);
+ mOperatorNameViewControllerFactory.create(
+ (OperatorNameView) view,
+ mHomeStatusBarComponent.getDarkIconDispatcher());
mOperatorNameViewController.init();
// This view should not be visible on lock-screen
if (mKeyguardStateController.isShowing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
index d4cb625d3722..f8ad0f2324bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
@@ -17,7 +17,9 @@
package com.android.systemui.statusbar.phone.fragment.dagger;
import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.dagger.qualifiers.DisplaySpecific;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.LegacyLightsOutNotifController;
@@ -121,4 +123,12 @@ public interface HomeStatusBarComponent {
/** */
StatusBarBoundsProvider getBoundsProvider();
+
+ /** */
+ @DisplaySpecific
+ DarkIconDispatcher getDarkIconDispatcher();
+
+ /** */
+ @DisplaySpecific
+ int getDisplayId();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
index 45c53b05d478..182f8d7e2fd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
@@ -22,8 +22,10 @@ import android.view.ViewStub;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.DisplaySpecific;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore;
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
@@ -164,4 +166,12 @@ public interface HomeStatusBarModule {
return store.forDisplay(displayId);
}
+ /** */
+ @Provides
+ @HomeStatusBarScope
+ @DisplaySpecific
+ static DarkIconDispatcher darkIconDispatcher(
+ @DisplaySpecific int displayId, DarkIconDispatcherStore store) {
+ return store.forDisplay(displayId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
index 6c303303c8f8..8d314eeb8493 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.phone.ui;
import android.widget.LinearLayout;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
@@ -29,35 +28,34 @@ import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
-import javax.inject.Inject;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
-/**
- * Version of {@link IconManager} that observes state from the DarkIconDispatcher.
- */
+/** Version of {@link IconManager} that observes state from the {@link DarkIconDispatcher}. */
public class DarkIconManager extends IconManager {
private final DarkIconDispatcher mDarkIconDispatcher;
private final int mIconHorizontalMargin;
+ @AssistedInject
public DarkIconManager(
- LinearLayout linearLayout,
- StatusBarLocation location,
+ @Assisted LinearLayout linearLayout,
+ @Assisted StatusBarLocation location,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider,
- DarkIconDispatcher darkIconDispatcher) {
- super(linearLayout,
- location,
- wifiUiAdapter,
- mobileUiAdapter,
- mobileContextProvider);
- mIconHorizontalMargin = mContext.getResources().getDimensionPixelSize(
- com.android.systemui.res.R.dimen.status_bar_icon_horizontal_margin);
+ @Assisted DarkIconDispatcher darkIconDispatcher) {
+ super(linearLayout, location, wifiUiAdapter, mobileUiAdapter, mobileContextProvider);
+ mIconHorizontalMargin =
+ mContext.getResources()
+ .getDimensionPixelSize(
+ com.android.systemui.res.R.dimen.status_bar_icon_horizontal_margin);
mDarkIconDispatcher = darkIconDispatcher;
}
@Override
- protected void onIconAdded(int index, String slot, boolean blocked,
- StatusBarIconHolder holder) {
+ protected void onIconAdded(
+ int index, String slot, boolean blocked, StatusBarIconHolder holder) {
StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
mDarkIconDispatcher.addDarkReceiver(view);
}
@@ -106,34 +104,14 @@ public class DarkIconManager extends IconManager {
super.exitDemoMode();
}
- @SysUISingleton
- public static class Factory {
- private final WifiUiAdapter mWifiUiAdapter;
- private final MobileContextProvider mMobileContextProvider;
- private final MobileUiAdapter mMobileUiAdapter;
- private final DarkIconDispatcher mDarkIconDispatcher;
-
- @Inject
- public Factory(
- WifiUiAdapter wifiUiAdapter,
- MobileContextProvider mobileContextProvider,
- MobileUiAdapter mobileUiAdapter,
- DarkIconDispatcher darkIconDispatcher) {
- mWifiUiAdapter = wifiUiAdapter;
- mMobileContextProvider = mobileContextProvider;
- mMobileUiAdapter = mobileUiAdapter;
- mDarkIconDispatcher = darkIconDispatcher;
- }
+ /** */
+ @AssistedFactory
+ public interface Factory {
- /** Creates a new {@link DarkIconManager} for the given view group and location. */
- public DarkIconManager create(LinearLayout group, StatusBarLocation location) {
- return new DarkIconManager(
- group,
- location,
- mWifiUiAdapter,
- mMobileUiAdapter,
- mMobileContextProvider,
- mDarkIconDispatcher);
- }
+ /** Creates a new {@link DarkIconManager}. */
+ DarkIconManager create(
+ LinearLayout group,
+ StatusBarLocation location,
+ DarkIconDispatcher darkIconDispatcher);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index a472318a1e86..247abc3da175 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -31,7 +31,10 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.phone.PhoneStatusBarView
@@ -44,7 +47,6 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarVie
import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
import javax.inject.Inject
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Factory to simplify the dependency management for [StatusBarRoot] */
class StatusBarRootFactory
@@ -56,6 +58,7 @@ constructor(
private val darkIconManagerFactory: DarkIconManager.Factory,
private val iconController: StatusBarIconController,
private val ongoingCallController: OngoingCallController,
+ private val darkIconDispatcherStore: DarkIconDispatcherStore,
) {
fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView {
val composeView = ComposeView(root.context)
@@ -69,6 +72,7 @@ constructor(
darkIconManagerFactory = darkIconManagerFactory,
iconController = iconController,
ongoingCallController = ongoingCallController,
+ darkIconDispatcher = darkIconDispatcherStore.forDisplay(root.context.displayId),
onViewCreated = andThen,
)
}
@@ -97,6 +101,7 @@ fun StatusBarRoot(
darkIconManagerFactory: DarkIconManager.Factory,
iconController: StatusBarIconController,
ongoingCallController: OngoingCallController,
+ darkIconDispatcher: DarkIconDispatcher,
onViewCreated: (ViewGroup) -> Unit,
) {
// None of these methods are used when [StatusBarSimpleFragment] is on.
@@ -135,7 +140,11 @@ fun StatusBarRoot(
phoneStatusBarView.requireViewById<StatusIconContainer>(R.id.statusIcons)
// TODO(b/364360986): turn this into a repo/intr/viewmodel
val darkIconManager =
- darkIconManagerFactory.create(statusIconContainer, StatusBarLocation.HOME)
+ darkIconManagerFactory.create(
+ statusIconContainer,
+ StatusBarLocation.HOME,
+ darkIconDispatcher,
+ )
iconController.addIconGroup(darkIconManager)
// TODO(b/372657935): This won't be needed once OngoingCallController is
@@ -157,9 +166,13 @@ fun StatusBarRoot(
// TODO(b/369337701): implement notification icons for all displays.
// Currently if we try to bind for all displays, there is a crash, because the
// same notification icon view can't have multiple parents.
- if (context.displayId == Display.DEFAULT_DISPLAY) {
+ val displayId = context.displayId
+ if (displayId == Display.DEFAULT_DISPLAY) {
scope.launch {
- notificationIconsBinder.bindWhileAttached(notificationIconContainer)
+ notificationIconsBinder.bindWhileAttached(
+ notificationIconContainer,
+ displayId,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
index 03499cbdccb2..885a2b0d7305 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
@@ -19,6 +19,7 @@ import android.view.View
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress
@@ -41,7 +42,7 @@ class UnfoldTransitionInteractor
@Inject
constructor(
private val repository: UnfoldTransitionRepository,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
) {
/** Returns availability of fold/unfold transitions on the device */
val isAvailable: Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
index f7ad3205f3dd..9440a9364b62 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.dagger
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
import dagger.BindsInstance
import dagger.Subcomponent
import kotlinx.coroutines.CoroutineScope
@@ -40,6 +41,8 @@ interface VolumeDialogComponent {
@VolumeDialogScope fun volumeDialog(): com.android.systemui.volume.dialog.VolumeDialog
+ fun sliderComponentFactory(): VolumeDialogSliderComponent.Factory
+
@Subcomponent.Factory
interface Factory {
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
new file mode 100644
index 000000000000..538ee47915a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.dagger
+
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
+import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+/**
+ * This component hosts all the stuff, that Volume Dialog sliders need. It's recreated alongside
+ * each slider view.
+ */
+@VolumeDialogSliderScope
+@Subcomponent
+interface VolumeDialogSliderComponent {
+
+ fun sliderViewBinder(): VolumeDialogSliderViewBinder
+
+ @Subcomponent.Factory
+ interface Factory {
+
+ fun create(@BindsInstance sliderType: VolumeDialogSliderType): VolumeDialogSliderComponent
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderScope.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderScope.kt
new file mode 100644
index 000000000000..9f5e0f6ba1aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderScope.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.dagger
+
+import javax.inject.Scope
+
+/**
+ * Volume Panel Slider dependency injection scope. This scope is created for each of the volume
+ * sliders in the dialog.
+ */
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+@Scope
+annotation class VolumeDialogSliderScope
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index 876bf2c4a154..2967fe8ca906 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -17,22 +17,21 @@
package com.android.systemui.volume.dialog.sliders.domain.interactor
import com.android.systemui.plugins.VolumeDialogController
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
/** Operates a state of particular slider of the Volume Dialog. */
+@VolumeDialogSliderScope
class VolumeDialogSliderInteractor
-@AssistedInject
+@Inject
constructor(
- @Assisted private val sliderType: VolumeDialogSliderType,
+ private val sliderType: VolumeDialogSliderType,
volumeDialogStateInteractor: VolumeDialogStateInteractor,
private val volumeDialogController: VolumeDialogController,
) {
@@ -56,11 +55,4 @@ constructor(
setActiveStream(sliderType.audioStream)
}
}
-
- @VolumeDialogScope
- @AssistedFactory
- interface Factory {
-
- fun create(sliderType: VolumeDialogSliderType): VolumeDialogSliderInteractor
- }
}
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 3bf8c54cb9d8..1c231b521bae 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
@@ -24,16 +24,14 @@ import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.launchIn
@@ -41,10 +39,11 @@ import kotlinx.coroutines.flow.onEach
private const val PROGRESS_CHANGE_ANIMATION_DURATION_MS = 80L
+@VolumeDialogSliderScope
class VolumeDialogSliderViewBinder
-@AssistedInject
+@Inject
constructor(
- @Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel,
+ private val viewModelFactory: VolumeDialogSliderViewModel.Factory,
private val jankListenerFactory: JankListenerFactory,
) {
@@ -58,7 +57,7 @@ constructor(
viewModel(
traceName = "VolumeDialogSliderViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelProvider() },
+ factory = { viewModelFactory.create() },
) { viewModel ->
sliderView.addOnChangeListener { _, value, fromUser ->
viewModel.setStreamVolume(value.roundToInt(), fromUser)
@@ -85,15 +84,6 @@ constructor(
)
}
}
-
- @AssistedFactory
- @VolumeDialogScope
- interface Factory {
-
- fun create(
- viewModelProvider: () -> VolumeDialogSliderViewModel
- ): VolumeDialogSliderViewBinder
- }
}
private suspend fun Slider.setValueAnimated(
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 0a4e3f481e88..a17c1e541b5e 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
@@ -28,7 +28,6 @@ import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel
import javax.inject.Inject
-import kotlin.math.abs
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -50,15 +49,17 @@ constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory)
) { viewModel ->
viewModel.sliders
.onEach { uiModel ->
- uiModel.sliderViewBinder.bind(volumeDialog)
+ uiModel.sliderComponent.sliderViewBinder().bind(volumeDialog)
- val floatingSliderViewBinders = uiModel.floatingSliderViewBinders
+ val floatingSliderViewBinders = uiModel.floatingSliderComponent
floatingSlidersContainer.ensureChildCount(
viewLayoutId = R.layout.volume_dialog_slider_floating,
count = floatingSliderViewBinders.size,
)
- floatingSliderViewBinders.fastForEachIndexed { index, viewBinder ->
- viewBinder.bind(floatingSlidersContainer.getChildAt(index))
+ floatingSliderViewBinders.fastForEachIndexed { index, sliderComponent ->
+ sliderComponent
+ .sliderViewBinder()
+ .bind(floatingSlidersContainer.getChildAt(index))
}
}
.launchIn(this)
@@ -76,7 +77,7 @@ private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int)
}
childCountDelta < 0 -> {
val inflater = LayoutInflater.from(context)
- repeat(abs(childCountDelta)) { inflater.inflate(viewLayoutId, this, true) }
+ repeat(-childCountDelta) { inflater.inflate(viewLayoutId, this, true) }
}
}
}
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 ea0b49d5294e..cf04d45d54ff 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
@@ -22,7 +22,6 @@ import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
-import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
@@ -53,7 +52,7 @@ private const val VOLUME_UPDATE_GRACE_PERIOD = 1000
class VolumeDialogSliderViewModel
@AssistedInject
constructor(
- @Assisted private val interactor: VolumeDialogSliderInteractor,
+ private val interactor: VolumeDialogSliderInteractor,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
@VolumeDialog private val coroutineScope: CoroutineScope,
private val systemClock: SystemClock,
@@ -90,11 +89,11 @@ constructor(
private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
+ private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
+
@AssistedFactory
interface Factory {
- fun create(interactor: VolumeDialogSliderInteractor): VolumeDialogSliderViewModel
+ fun create(): VolumeDialogSliderViewModel
}
-
- private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index b5b292fa4a66..d1972231d373 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -17,16 +17,13 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
-import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
-import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -36,29 +33,21 @@ class VolumeDialogSlidersViewModel
constructor(
@VolumeDialog coroutineScope: CoroutineScope,
private val slidersInteractor: VolumeDialogSlidersInteractor,
- private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
- private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory,
- private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory,
+ private val sliderComponentFactory: VolumeDialogSliderComponent.Factory,
) {
val sliders: Flow<VolumeDialogSliderUiModel> =
slidersInteractor.sliders
- .distinctUntilChanged()
.map { slidersModel ->
VolumeDialogSliderUiModel(
- sliderViewBinder = createSliderViewBinder(slidersModel.slider),
- floatingSliderViewBinders =
- slidersModel.floatingSliders.map(::createSliderViewBinder),
+ sliderComponent = sliderComponentFactory.create(slidersModel.slider),
+ floatingSliderComponent =
+ slidersModel.floatingSliders.map(sliderComponentFactory::create),
)
}
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
.filterNotNull()
- private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder =
- sliderViewBinderFactory.create {
- sliderViewModelFactory.create(sliderInteractorFactory.create(type))
- }
-
@AssistedFactory
interface Factory {
@@ -68,6 +57,6 @@ constructor(
/** Models slider ui */
data class VolumeDialogSliderUiModel(
- val sliderViewBinder: VolumeDialogSliderViewBinder,
- val floatingSliderViewBinders: List<VolumeDialogSliderViewBinder>,
+ val sliderComponent: VolumeDialogSliderComponent,
+ val floatingSliderComponent: List<VolumeDialogSliderComponent>,
)
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 9be669f3df0c..869a6a2e87d5 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
@@ -18,16 +18,19 @@ package com.android.systemui.volume.dialog.ui.viewmodel
import android.content.Context
import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
import com.android.systemui.volume.dialog.shared.model.streamLabel
-import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
/** Provides a state for the Volume Dialog. */
@@ -38,18 +41,21 @@ constructor(
private val context: Context,
dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
- private val volumeDialogSliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
+ volumeDialogStateInteractor: VolumeDialogStateInteractor,
) {
val dialogVisibilityModel: Flow<VolumeDialogVisibilityModel> =
dialogVisibilityInteractor.dialogVisibility
val dialogTitle: Flow<String> =
- volumeDialogSlidersInteractor.sliders.flatMapLatest { slidersModel ->
- val interactor = volumeDialogSliderInteractorFactory.create(slidersModel.slider)
- interactor.slider.map { sliderModel ->
- context.getString(R.string.volume_dialog_title, sliderModel.streamLabel(context))
+ combine(
+ volumeDialogStateInteractor.volumeDialogState,
+ volumeDialogSlidersInteractor.sliders.map { it.slider },
+ ) { state: VolumeDialogStateModel, sliderType: VolumeDialogSliderType ->
+ state.streamModels[sliderType.audioStream]?.let { model ->
+ context.getString(R.string.volume_dialog_title, model.streamLabel(context))
+ }
}
- }
+ .filterNotNull()
@AssistedFactory
interface Factory {
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withAnimator.json
index aa8044515ea2..aa8044515ea2 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withAnimator.json
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withSpring.json
index a840d3cb1225..7abff2c74531 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withSpring.json
@@ -33,118 +33,118 @@
"bottom": 0
},
{
- "left": 94,
- "top": 284,
- "right": 206,
+ "left": 104,
+ "top": 285,
+ "right": 215,
"bottom": 414
},
{
- "left": 83,
- "top": 251,
- "right": 219,
+ "left": 92,
+ "top": 252,
+ "right": 227,
"bottom": 447
},
{
- "left": 70,
- "top": 212,
- "right": 234,
- "bottom": 485
+ "left": 77,
+ "top": 213,
+ "right": 242,
+ "bottom": 486
},
{
- "left": 57,
- "top": 173,
- "right": 250,
- "bottom": 522
+ "left": 63,
+ "top": 175,
+ "right": 256,
+ "bottom": 524
},
{
- "left": 46,
- "top": 139,
- "right": 264,
- "bottom": 555
+ "left": 50,
+ "top": 141,
+ "right": 269,
+ "bottom": 558
},
{
- "left": 36,
- "top": 109,
- "right": 276,
- "bottom": 584
+ "left": 40,
+ "top": 112,
+ "right": 279,
+ "bottom": 587
},
{
- "left": 28,
- "top": 84,
- "right": 285,
- "bottom": 608
+ "left": 31,
+ "top": 88,
+ "right": 288,
+ "bottom": 611
},
{
- "left": 21,
- "top": 65,
- "right": 293,
- "bottom": 627
+ "left": 23,
+ "top": 68,
+ "right": 296,
+ "bottom": 631
},
{
- "left": 16,
- "top": 49,
- "right": 300,
- "bottom": 642
+ "left": 18,
+ "top": 53,
+ "right": 301,
+ "bottom": 646
},
{
- "left": 12,
- "top": 36,
- "right": 305,
- "bottom": 653
+ "left": 13,
+ "top": 41,
+ "right": 306,
+ "bottom": 658
},
{
- "left": 9,
- "top": 27,
- "right": 308,
- "bottom": 662
+ "left": 10,
+ "top": 31,
+ "right": 309,
+ "bottom": 667
},
{
"left": 7,
- "top": 20,
+ "top": 24,
"right": 312,
- "bottom": 669
+ "bottom": 673
},
{
"left": 5,
- "top": 14,
+ "top": 18,
"right": 314,
- "bottom": 675
+ "bottom": 678
},
{
"left": 4,
- "top": 11,
+ "top": 13,
"right": 315,
- "bottom": 678
+ "bottom": 681
},
{
"left": 3,
- "top": 8,
+ "top": 10,
"right": 316,
- "bottom": 681
+ "bottom": 684
},
{
"left": 2,
- "top": 5,
+ "top": 7,
"right": 317,
- "bottom": 684
+ "bottom": 685
},
{
"left": 1,
- "top": 4,
+ "top": 5,
"right": 318,
- "bottom": 685
+ "bottom": 687
},
{
"left": 1,
- "top": 3,
+ "top": 4,
"right": 318,
- "bottom": 686
+ "bottom": 688
},
{
"left": 0,
- "top": 2,
+ "top": 3,
"right": 319,
- "bottom": 687
+ "bottom": 688
}
]
},
@@ -371,5 +371,14 @@
0
]
}
- ]
+ ],
+ "\/\/metadata": {
+ "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimation_whenLaunching_withSpring.json",
+ "goldenIdentifier": "backgroundAnimation_whenLaunching_withSpring",
+ "testClassName": "TransitionAnimatorTest",
+ "testMethodName": "backgroundAnimation_whenLaunching[true]",
+ "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots",
+ "result": "FAILED",
+ "videoLocation": "TransitionAnimatorTest\/backgroundAnimation_whenLaunching_withSpring.actual.mp4"
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withAnimator.json
index aa8044515ea2..aa8044515ea2 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withAnimator.json
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withSpring.json
index a840d3cb1225..561961145ca1 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withSpring.json
@@ -33,118 +33,118 @@
"bottom": 0
},
{
- "left": 94,
- "top": 284,
- "right": 206,
+ "left": 104,
+ "top": 285,
+ "right": 215,
"bottom": 414
},
{
- "left": 83,
- "top": 251,
- "right": 219,
+ "left": 92,
+ "top": 252,
+ "right": 227,
"bottom": 447
},
{
- "left": 70,
- "top": 212,
- "right": 234,
- "bottom": 485
+ "left": 77,
+ "top": 213,
+ "right": 242,
+ "bottom": 486
},
{
- "left": 57,
- "top": 173,
- "right": 250,
- "bottom": 522
+ "left": 63,
+ "top": 175,
+ "right": 256,
+ "bottom": 524
},
{
- "left": 46,
- "top": 139,
- "right": 264,
- "bottom": 555
+ "left": 50,
+ "top": 141,
+ "right": 269,
+ "bottom": 558
},
{
- "left": 36,
- "top": 109,
- "right": 276,
- "bottom": 584
+ "left": 40,
+ "top": 112,
+ "right": 279,
+ "bottom": 587
},
{
- "left": 28,
- "top": 84,
- "right": 285,
- "bottom": 608
+ "left": 31,
+ "top": 88,
+ "right": 288,
+ "bottom": 611
},
{
- "left": 21,
- "top": 65,
- "right": 293,
- "bottom": 627
+ "left": 23,
+ "top": 68,
+ "right": 296,
+ "bottom": 631
},
{
- "left": 16,
- "top": 49,
- "right": 300,
- "bottom": 642
+ "left": 18,
+ "top": 53,
+ "right": 301,
+ "bottom": 646
},
{
- "left": 12,
- "top": 36,
- "right": 305,
- "bottom": 653
+ "left": 13,
+ "top": 41,
+ "right": 306,
+ "bottom": 658
},
{
- "left": 9,
- "top": 27,
- "right": 308,
- "bottom": 662
+ "left": 10,
+ "top": 31,
+ "right": 309,
+ "bottom": 667
},
{
"left": 7,
- "top": 20,
+ "top": 24,
"right": 312,
- "bottom": 669
+ "bottom": 673
},
{
"left": 5,
- "top": 14,
+ "top": 18,
"right": 314,
- "bottom": 675
+ "bottom": 678
},
{
"left": 4,
- "top": 11,
+ "top": 13,
"right": 315,
- "bottom": 678
+ "bottom": 681
},
{
"left": 3,
- "top": 8,
+ "top": 10,
"right": 316,
- "bottom": 681
+ "bottom": 684
},
{
"left": 2,
- "top": 5,
+ "top": 7,
"right": 317,
- "bottom": 684
+ "bottom": 685
},
{
"left": 1,
- "top": 4,
+ "top": 5,
"right": 318,
- "bottom": 685
+ "bottom": 687
},
{
"left": 1,
- "top": 3,
+ "top": 4,
"right": 318,
- "bottom": 686
+ "bottom": 688
},
{
"left": 0,
- "top": 2,
+ "top": 3,
"right": 319,
- "bottom": 687
+ "bottom": 688
}
]
},
@@ -371,5 +371,14 @@
0
]
}
- ]
+ ],
+ "\/\/metadata": {
+ "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimation_whenReturning_withSpring.json",
+ "goldenIdentifier": "backgroundAnimation_whenReturning_withSpring",
+ "testClassName": "TransitionAnimatorTest",
+ "testMethodName": "backgroundAnimation_whenReturning[true]",
+ "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots",
+ "result": "FAILED",
+ "videoLocation": "TransitionAnimatorTest\/backgroundAnimation_whenReturning_withSpring.actual.mp4"
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withAnimator.json
index 7f623575fef4..7f623575fef4 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withAnimator.json
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withSpring.json
index 18eedd450751..825190ba7a32 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withSpring.json
@@ -33,118 +33,118 @@
"bottom": 0
},
{
- "left": 94,
- "top": 284,
- "right": 206,
+ "left": 104,
+ "top": 285,
+ "right": 215,
"bottom": 414
},
{
- "left": 83,
- "top": 251,
- "right": 219,
+ "left": 92,
+ "top": 252,
+ "right": 227,
"bottom": 447
},
{
- "left": 70,
- "top": 212,
- "right": 234,
- "bottom": 485
+ "left": 77,
+ "top": 213,
+ "right": 242,
+ "bottom": 486
},
{
- "left": 57,
- "top": 173,
- "right": 250,
- "bottom": 522
+ "left": 63,
+ "top": 175,
+ "right": 256,
+ "bottom": 524
},
{
- "left": 46,
- "top": 139,
- "right": 264,
- "bottom": 555
+ "left": 50,
+ "top": 141,
+ "right": 269,
+ "bottom": 558
},
{
- "left": 36,
- "top": 109,
- "right": 276,
- "bottom": 584
+ "left": 40,
+ "top": 112,
+ "right": 279,
+ "bottom": 587
},
{
- "left": 28,
- "top": 84,
- "right": 285,
- "bottom": 608
+ "left": 31,
+ "top": 88,
+ "right": 288,
+ "bottom": 611
},
{
- "left": 21,
- "top": 65,
- "right": 293,
- "bottom": 627
+ "left": 23,
+ "top": 68,
+ "right": 296,
+ "bottom": 631
},
{
- "left": 16,
- "top": 49,
- "right": 300,
- "bottom": 642
+ "left": 18,
+ "top": 53,
+ "right": 301,
+ "bottom": 646
},
{
- "left": 12,
- "top": 36,
- "right": 305,
- "bottom": 653
+ "left": 13,
+ "top": 41,
+ "right": 306,
+ "bottom": 658
},
{
- "left": 9,
- "top": 27,
- "right": 308,
- "bottom": 662
+ "left": 10,
+ "top": 31,
+ "right": 309,
+ "bottom": 667
},
{
"left": 7,
- "top": 20,
+ "top": 24,
"right": 312,
- "bottom": 669
+ "bottom": 673
},
{
"left": 5,
- "top": 14,
+ "top": 18,
"right": 314,
- "bottom": 675
+ "bottom": 678
},
{
"left": 4,
- "top": 11,
+ "top": 13,
"right": 315,
- "bottom": 678
+ "bottom": 681
},
{
"left": 3,
- "top": 8,
+ "top": 10,
"right": 316,
- "bottom": 681
+ "bottom": 684
},
{
"left": 2,
- "top": 5,
+ "top": 7,
"right": 317,
- "bottom": 684
+ "bottom": 685
},
{
"left": 1,
- "top": 4,
+ "top": 5,
"right": 318,
- "bottom": 685
+ "bottom": 687
},
{
"left": 1,
- "top": 3,
+ "top": 4,
"right": 318,
- "bottom": 686
+ "bottom": 688
},
{
"left": 0,
- "top": 2,
+ "top": 3,
"right": 319,
- "bottom": 687
+ "bottom": 688
}
]
},
@@ -371,5 +371,14 @@
0
]
}
- ]
+ ],
+ "\/\/metadata": {
+ "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimationWithoutFade_whenLaunching_withSpring.json",
+ "goldenIdentifier": "backgroundAnimationWithoutFade_whenLaunching_withSpring",
+ "testClassName": "TransitionAnimatorTest",
+ "testMethodName": "backgroundAnimationWithoutFade_whenLaunching[true]",
+ "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots",
+ "result": "FAILED",
+ "videoLocation": "TransitionAnimatorTest\/backgroundAnimationWithoutFade_whenLaunching_withSpring.actual.mp4"
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withAnimator.json
index 98005c53f6e0..98005c53f6e0 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withAnimator.json
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withSpring.json
index 18eedd450751..63c263175122 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withSpring.json
@@ -33,118 +33,118 @@
"bottom": 0
},
{
- "left": 94,
- "top": 284,
- "right": 206,
+ "left": 104,
+ "top": 285,
+ "right": 215,
"bottom": 414
},
{
- "left": 83,
- "top": 251,
- "right": 219,
+ "left": 92,
+ "top": 252,
+ "right": 227,
"bottom": 447
},
{
- "left": 70,
- "top": 212,
- "right": 234,
- "bottom": 485
+ "left": 77,
+ "top": 213,
+ "right": 242,
+ "bottom": 486
},
{
- "left": 57,
- "top": 173,
- "right": 250,
- "bottom": 522
+ "left": 63,
+ "top": 175,
+ "right": 256,
+ "bottom": 524
},
{
- "left": 46,
- "top": 139,
- "right": 264,
- "bottom": 555
+ "left": 50,
+ "top": 141,
+ "right": 269,
+ "bottom": 558
},
{
- "left": 36,
- "top": 109,
- "right": 276,
- "bottom": 584
+ "left": 40,
+ "top": 112,
+ "right": 279,
+ "bottom": 587
},
{
- "left": 28,
- "top": 84,
- "right": 285,
- "bottom": 608
+ "left": 31,
+ "top": 88,
+ "right": 288,
+ "bottom": 611
},
{
- "left": 21,
- "top": 65,
- "right": 293,
- "bottom": 627
+ "left": 23,
+ "top": 68,
+ "right": 296,
+ "bottom": 631
},
{
- "left": 16,
- "top": 49,
- "right": 300,
- "bottom": 642
+ "left": 18,
+ "top": 53,
+ "right": 301,
+ "bottom": 646
},
{
- "left": 12,
- "top": 36,
- "right": 305,
- "bottom": 653
+ "left": 13,
+ "top": 41,
+ "right": 306,
+ "bottom": 658
},
{
- "left": 9,
- "top": 27,
- "right": 308,
- "bottom": 662
+ "left": 10,
+ "top": 31,
+ "right": 309,
+ "bottom": 667
},
{
"left": 7,
- "top": 20,
+ "top": 24,
"right": 312,
- "bottom": 669
+ "bottom": 673
},
{
"left": 5,
- "top": 14,
+ "top": 18,
"right": 314,
- "bottom": 675
+ "bottom": 678
},
{
"left": 4,
- "top": 11,
+ "top": 13,
"right": 315,
- "bottom": 678
+ "bottom": 681
},
{
"left": 3,
- "top": 8,
+ "top": 10,
"right": 316,
- "bottom": 681
+ "bottom": 684
},
{
"left": 2,
- "top": 5,
+ "top": 7,
"right": 317,
- "bottom": 684
+ "bottom": 685
},
{
"left": 1,
- "top": 4,
+ "top": 5,
"right": 318,
- "bottom": 685
+ "bottom": 687
},
{
"left": 1,
- "top": 3,
+ "top": 4,
"right": 318,
- "bottom": 686
+ "bottom": 688
},
{
"left": 0,
- "top": 2,
+ "top": 3,
"right": 319,
- "bottom": 687
+ "bottom": 688
}
]
},
@@ -371,5 +371,14 @@
0
]
}
- ]
+ ],
+ "\/\/metadata": {
+ "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimationWithoutFade_whenReturning_withSpring.json",
+ "goldenIdentifier": "backgroundAnimationWithoutFade_whenReturning_withSpring",
+ "testClassName": "TransitionAnimatorTest",
+ "testMethodName": "backgroundAnimationWithoutFade_whenReturning[true]",
+ "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots",
+ "result": "FAILED",
+ "videoLocation": "TransitionAnimatorTest\/backgroundAnimationWithoutFade_whenReturning_withSpring.actual.mp4"
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index c6e4e0db8945..fa88f6207c49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -46,7 +46,6 @@ import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
import android.window.InputTransferToken;
@@ -1030,10 +1029,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
callback,
sysUiState,
secureSettings,
- scvhSupplier,
- sfVsyncFrameProvider,
- WindowManagerGlobal::getWindowSession,
- viewCaptureAwareWindowManager);
+ scvhSupplier);
mSpyController = Mockito.mock(WindowMagnificationController.class);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
deleted file mode 100644
index 9b09ec2ec55f..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
+++ /dev/null
@@ -1,1594 +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.accessibility;
-
-import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_UP;
-import static android.view.WindowInsets.Type.systemGestures;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.AdditionalAnswers.returnsSecondArg;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import static java.util.Arrays.asList;
-
-import android.animation.ValueAnimator;
-import android.annotation.IdRes;
-import android.annotation.Nullable;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Insets;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.provider.Settings;
-import android.testing.TestableLooper;
-import android.testing.TestableResources;
-import android.util.Size;
-import android.view.AttachedSurfaceControl;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewRootImpl;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.widget.FrameLayout;
-import android.window.InputTransferToken;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.AnimatorTestRule;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.res.R;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.util.FakeSharedPreferences;
-import com.android.systemui.util.leak.ReferenceTestUtils;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.utils.os.FakeHandler;
-
-import com.google.common.util.concurrent.AtomicDouble;
-
-import org.junit.After;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Supplier;
-
-@LargeTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidJUnit4.class)
-@EnableFlags(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
-public class WindowMagnificationControllerWindowlessMagnifierTest extends SysuiTestCase {
-
- @Rule
- // NOTE: pass 'null' to allow this test advances time on the main thread.
- public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(/* test= */ null);
-
- private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
- @Mock
- private MirrorWindowControl mMirrorWindowControl;
- @Mock
- private WindowMagnifierCallback mWindowMagnifierCallback;
- @Mock
- IRemoteMagnificationAnimationCallback mAnimationCallback;
- @Mock
- IRemoteMagnificationAnimationCallback mAnimationCallback2;
-
- private SurfaceControl.Transaction mTransaction;
- @Mock
- private SecureSettings mSecureSettings;
- @Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
-
- private long mWaitAnimationDuration;
- private long mWaitBounceEffectDuration;
-
- private Handler mHandler;
- private TestableWindowManager mWindowManager;
- private SysUiState mSysUiState;
- private Resources mResources;
- private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
- private WindowMagnificationController mWindowMagnificationController;
- private Instrumentation mInstrumentation;
- private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
- private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-
- private View mSpyView;
- private View.OnTouchListener mTouchListener;
-
- private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
-
- // This list contains all SurfaceControlViewHosts created during a given test. If the
- // magnification window is recreated during a test, the list will contain more than a single
- // element.
- private List<SurfaceControlViewHost> mSurfaceControlViewHosts = new ArrayList<>();
- // The most recently created SurfaceControlViewHost.
- private SurfaceControlViewHost mSurfaceControlViewHost;
- private KosmosJavaAdapter mKosmos;
- private FakeSharedPreferences mSharedPreferences;
-
- /**
- * return whether window magnification is supported for current test context.
- */
- private boolean isWindowModeSupported() {
- return getContext().getPackageManager().hasSystemFeature(FEATURE_WINDOW_MAGNIFICATION);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mContext = spy(mContext);
- mKosmos = new KosmosJavaAdapter(this);
- mContext = Mockito.spy(getContext());
- mHandler = new FakeHandler(TestableLooper.get(this).getLooper());
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- final WindowManager wm = mContext.getSystemService(WindowManager.class);
- mWindowManager = spy(new TestableWindowManager(wm));
-
- mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
- mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin());
- mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
- returnsSecondArg());
- when(mSecureSettings.getFloatForUser(anyString(), anyFloat(), anyInt())).then(
- returnsSecondArg());
-
- mResources = getContext().getOrCreateTestableResources().getResources();
- // prevent the config orientation from undefined, which may cause config.diff method
- // neglecting the orientation update.
- if (mResources.getConfiguration().orientation == ORIENTATION_UNDEFINED) {
- mResources.getConfiguration().orientation = ORIENTATION_PORTRAIT;
- }
-
- // Using the animation duration in WindowMagnificationAnimationController for testing.
- mWaitAnimationDuration = mResources.getInteger(
- com.android.internal.R.integer.config_longAnimTime);
- // Using the bounce effect duration in WindowMagnificationController for testing.
- mWaitBounceEffectDuration = mResources.getInteger(
- com.android.internal.R.integer.config_shortAnimTime);
-
- mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
- mContext, mValueAnimator);
- Supplier<SurfaceControlViewHost> scvhSupplier = () -> {
- mSurfaceControlViewHost = spy(new SurfaceControlViewHost(
- mContext, mContext.getDisplay(), new InputTransferToken(),
- "WindowMagnification"));
- ViewRootImpl viewRoot = mock(ViewRootImpl.class);
- when(mSurfaceControlViewHost.getRootSurfaceControl()).thenReturn(viewRoot);
- mSurfaceControlViewHosts.add(mSurfaceControlViewHost);
- return mSurfaceControlViewHost;
- };
- mTransaction = spy(new SurfaceControl.Transaction());
- mSharedPreferences = new FakeSharedPreferences();
- when(mContext.getSharedPreferences(
- eq("window_magnification_preferences"), anyInt()))
- .thenReturn(mSharedPreferences);
- mWindowMagnificationController =
- new WindowMagnificationController(
- mContext,
- mHandler,
- mWindowMagnificationAnimationController,
- mMirrorWindowControl,
- mTransaction,
- mWindowMagnifierCallback,
- mSysUiState,
- mSecureSettings,
- scvhSupplier,
- /* sfVsyncFrameProvider= */ null,
- /* globalWindowSessionSupplier= */ null,
- mViewCaptureAwareWindowManager);
-
- verify(mMirrorWindowControl).setWindowDelegate(
- any(MirrorWindowControl.MirrorWindowDelegate.class));
- mSpyView = Mockito.spy(new View(mContext));
- doAnswer((invocation) -> {
- mTouchListener = invocation.getArgument(0);
- return null;
- }).when(mSpyView).setOnTouchListener(
- any(View.OnTouchListener.class));
-
- // skip test if window magnification is not supported to prevent fail results. (b/279820875)
- Assume.assumeTrue(isWindowModeSupported());
- }
-
- @After
- public void tearDown() {
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.deleteWindowMagnification();
- });
- mValueAnimator.cancel();
- }
-
- @Test
- public void initWindowMagnificationController_checkAllowDiagonalScrollingWithSecureSettings() {
- verify(mSecureSettings).getIntForUser(
- eq(Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING),
- /* def */ eq(1), /* userHandle= */ anyInt());
- assertThat(mWindowMagnificationController.isDiagonalScrollingEnabled()).isTrue();
- }
-
- @Test
- public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- verify(mMirrorWindowControl).showControl();
- verify(mWindowMagnifierCallback,
- timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeastOnce()).onWindowMagnifierBoundsChanged(
- eq(mContext.getDisplayId()), any(Rect.class));
- }
-
- @Test
- public void enableWindowMagnification_notifySourceBoundsChanged() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN, /* magnificationFrameOffsetRatioX= */ 0,
- /* magnificationFrameOffsetRatioY= */ 0, null));
-
- // Waits for the surface created
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged(
- (eq(mContext.getDisplayId())), any());
- }
-
- @Test
- public void enableWindowMagnification_disabled_notifySourceBoundsChanged() {
- enableWindowMagnification_notifySourceBoundsChanged();
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.deleteWindowMagnification(null));
- Mockito.reset(mWindowMagnifierCallback);
-
- enableWindowMagnification_notifySourceBoundsChanged();
- }
-
- @Test
- public void enableWindowMagnification_withAnimation_schedulesFrame() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(2.0f, 10,
- 10, /* magnificationFrameOffsetRatioX= */ 0,
- /* magnificationFrameOffsetRatioY= */ 0,
- Mockito.mock(IRemoteMagnificationAnimationCallback.class));
- });
- advanceTimeBy(LAYOUT_CHANGE_TIMEOUT_MS);
-
- verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(),
- eq(Surface.ROTATION_0));
- }
-
- @Test
- public void moveWindowMagnifier_enabled_notifySourceBoundsChanged() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN, 0, 0, null);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.moveWindowMagnifier(10, 10);
- });
-
- final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
- verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
- (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertThat(mWindowMagnificationController.getCenterX())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
- assertThat(mWindowMagnificationController.getCenterY())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
- }
-
- @Test
- public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
- // Wait for Rects updated.
- waitForIdleSync();
-
- List<Rect> rects = mSurfaceControlViewHost.getView().getSystemGestureExclusionRects();
- assertThat(rects).isNotEmpty();
- }
-
- @Ignore("The default window size should be constrained after fixing b/288056772")
- @Test
- public void enableWindowMagnification_LargeScreen_windowSizeIsConstrained() {
- final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10;
- mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- final int halfScreenSize = screenSize / 2;
- ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
- // The frame size should be the half of smaller value of window height/width unless it
- //exceed the max frame size.
- assertThat(params.width).isLessThan(halfScreenSize);
- assertThat(params.height).isLessThan(halfScreenSize);
- }
-
- @Test
- public void deleteWindowMagnification_destroyControlAndUnregisterComponentCallback() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN,
- Float.NaN));
-
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.deleteWindowMagnification());
-
- verify(mMirrorWindowControl).destroyControl();
- verify(mContext).unregisterComponentCallbacks(mWindowMagnificationController);
- }
-
- @Test
- public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() {
- final WindowManager wm = mContext.getSystemService(WindowManager.class);
- final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
- setSystemGestureInsets();
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- bounds.bottom);
- });
- ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.deleteWindowMagnification();
- });
-
- verify(mMirrorWindowControl).destroyControl();
- assertThat(hasMagnificationOverlapFlag()).isFalse();
- }
-
- @Test
- public void deleteWindowMagnification_notifySourceBoundsChanged() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN,
- Float.NaN));
-
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.deleteWindowMagnification());
-
- // The first time is for notifying magnification enabled and the second time is for
- // notifying magnification disabled.
- verify(mWindowMagnifierCallback, times(2)).onSourceBoundsChanged(
- (eq(mContext.getDisplayId())), any());
- }
-
- @Test
- public void moveMagnifier_schedulesFrame() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- waitForIdleSync();
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.moveWindowMagnifier(100f, 100f));
-
- verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(),
- eq(Surface.ROTATION_0));
- }
-
- @Test
- public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback()
- throws RemoteException {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN, 0, 0, null);
- });
-
- final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
- .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10;
- final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10;
-
- reset(mWindowMagnifierCallback);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- targetCenterX, targetCenterY, mAnimationCallback);
- });
- advanceTimeBy(mWaitAnimationDuration);
-
- verify(mAnimationCallback, times(1)).onResult(eq(true));
- verify(mAnimationCallback, never()).onResult(eq(false));
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
- .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertThat(mWindowMagnificationController.getCenterX())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
- assertThat(mWindowMagnificationController.getCenterY())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
- assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(targetCenterX);
- assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(targetCenterY);
- }
-
- @Test
- public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback()
- throws RemoteException {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN, 0, 0, null);
- });
-
- final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
- .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- final float centerX = sourceBoundsCaptor.getValue().exactCenterX();
- final float centerY = sourceBoundsCaptor.getValue().exactCenterY();
-
- reset(mWindowMagnifierCallback);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 10, centerY + 10, mAnimationCallback);
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 20, centerY + 20, mAnimationCallback);
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 30, centerY + 30, mAnimationCallback);
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 40, centerY + 40, mAnimationCallback2);
- });
- advanceTimeBy(mWaitAnimationDuration);
-
- // only the last one callback will return true
- verify(mAnimationCallback2).onResult(eq(true));
- // the others will return false
- verify(mAnimationCallback, times(3)).onResult(eq(false));
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
- .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertThat(mWindowMagnificationController.getCenterX())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
- assertThat(mWindowMagnificationController.getCenterY())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
- assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(centerX + 40);
- assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(centerY + 40);
- }
-
- @Test
- public void setScale_enabled_expectedValueAndUpdateStateDescription() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(2.0f,
- Float.NaN, Float.NaN));
-
- mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
-
- assertThat(mWindowMagnificationController.getScale()).isEqualTo(3.0f);
- final View mirrorView = mSurfaceControlViewHost.getView();
- assertThat(mirrorView).isNotNull();
- assertThat(mirrorView.getStateDescription().toString()).contains("300");
- }
-
- @Test
- public void onConfigurationChanged_disabled_withoutException() {
- Display display = Mockito.spy(mContext.getDisplay());
- when(display.getRotation()).thenReturn(Surface.ROTATION_90);
- when(mContext.getDisplay()).thenReturn(display);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
- }
-
- @Test
- public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
- final int newRotation = simulateRotateTheDevice();
- final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
- final float center = Math.min(windowBounds.exactCenterX(), windowBounds.exactCenterY());
- final float displayWidth = windowBounds.width();
- final PointF magnifiedCenter = new PointF(center, center + 5f);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- magnifiedCenter.x, magnifiedCenter.y);
- // Get the center again in case the center we set is out of screen.
- magnifiedCenter.set(mWindowMagnificationController.getCenterX(),
- mWindowMagnificationController.getCenterY());
- });
- // Rotate the window clockwise 90 degree.
- windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
- windowBounds.right);
- mWindowManager.setWindowBounds(windowBounds);
-
- mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
- ActivityInfo.CONFIG_ORIENTATION));
-
- assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
- final PointF expectedCenter = new PointF(magnifiedCenter.y,
- displayWidth - magnifiedCenter.x);
- final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
- mWindowMagnificationController.getCenterY());
- assertThat(actualCenter).isEqualTo(expectedCenter);
- }
-
- @Test
- public void onOrientationChanged_disabled_updateDisplayRotation() {
- final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
- // Rotate the window clockwise 90 degree.
- windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
- windowBounds.right);
- mWindowManager.setWindowBounds(windowBounds);
- final int newRotation = simulateRotateTheDevice();
-
- mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
- ActivityInfo.CONFIG_ORIENTATION));
-
- assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
- }
-
- @Test
- public void onScreenSizeAndDensityChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() {
- // The default position is at the center of the screen.
- final float expectedRatio = 0.5f;
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- // Screen size and density change
- mContext.getResources().getConfiguration().smallestScreenWidthDp =
- mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
- final Rect testWindowBounds = new Rect(
- mWindowManager.getCurrentWindowMetrics().getBounds());
- testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
- testWindowBounds.right + 100, testWindowBounds.bottom + 100);
- mWindowManager.setWindowBounds(testWindowBounds);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
-
- // The ratio of center to window size should be the same.
- assertThat(mWindowMagnificationController.getCenterX() / testWindowBounds.width())
- .isEqualTo(expectedRatio);
- assertThat(mWindowMagnificationController.getCenterY() / testWindowBounds.height())
- .isEqualTo(expectedRatio);
- }
-
- @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
- @Test
- public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierWindow() {
- int newSmallestScreenWidthDp =
- mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
- int windowFrameSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize);
- mSharedPreferences
- .edit()
- .putString(String.valueOf(newSmallestScreenWidthDp),
- preferredWindowSize.toString())
- .commit();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- // Screen density and size change
- mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
- final Rect testWindowBounds = new Rect(
- mWindowManager.getCurrentWindowMetrics().getBounds());
- testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
- testWindowBounds.right + 100, testWindowBounds.bottom + 100);
- mWindowManager.setWindowBounds(testWindowBounds);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
-
- // wait for rect update
- waitForIdleSync();
- ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // The width and height of the view include the magnification frame and the margins.
- assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- }
-
- @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
- @Test
- public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierIndexAndWindow() {
- int newSmallestScreenWidthDp =
- mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
- int windowFrameSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize);
- mSharedPreferences
- .edit()
- .putString(String.valueOf(newSmallestScreenWidthDp),
- WindowMagnificationFrameSpec.serialize(
- WindowMagnificationSettings.MagnificationSize.CUSTOM,
- preferredWindowSize))
- .commit();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- // Screen density and size change
- mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
- final Rect testWindowBounds = new Rect(
- mWindowManager.getCurrentWindowMetrics().getBounds());
- testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
- testWindowBounds.right + 100, testWindowBounds.bottom + 100);
- mWindowManager.setWindowBounds(testWindowBounds);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
-
- // wait for rect update
- waitForIdleSync();
- verify(mWindowMagnifierCallback).onWindowMagnifierBoundsRestored(
- eq(mContext.getDisplayId()),
- eq(WindowMagnificationSettings.MagnificationSize.CUSTOM));
- ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // The width and height of the view include the magnification frame and the margins.
- assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- }
-
- @Test
- public void screenSizeIsChangedToLarge_enabled_defaultWindowSize() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
- final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10;
- // Screen size and density change
- mContext.getResources().getConfiguration().smallestScreenWidthDp =
- mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
- mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
-
- final int defaultWindowSize =
- mWindowMagnificationController.getMagnificationWindowSizeFromIndex(
- WindowMagnificationSettings.MagnificationSize.MEDIUM);
- ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
-
- assertThat(params.width).isEqualTo(defaultWindowSize);
- assertThat(params.height).isEqualTo(defaultWindowSize);
- }
-
- @Test
- public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- Mockito.reset(mWindowManager);
- Mockito.reset(mMirrorWindowControl);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
- });
-
- verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
- verify(mSurfaceControlViewHosts.get(0)).release();
- verify(mMirrorWindowControl).destroyControl();
- verify(mSurfaceControlViewHosts.get(1)).setView(any(), any());
- verify(mMirrorWindowControl).showControl();
- }
-
- @Test
- public void onDensityChanged_disabled_updateDimensions() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
- });
-
- verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
- }
-
- @Test
- public void initializeA11yNode_enabled_expectedValues() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
- Float.NaN);
- });
- final View mirrorView = mSurfaceControlViewHost.getView();
- assertThat(mirrorView).isNotNull();
- final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
-
- mirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
-
- assertThat(nodeInfo.getContentDescription()).isNotNull();
- assertThat(nodeInfo.getStateDescription().toString()).contains("250");
- assertThat(nodeInfo.getActionList()).containsExactlyElementsIn(asList(
- new AccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
- mContext.getResources().getString(
- R.string.magnification_open_settings_click_label)),
- new AccessibilityAction(R.id.accessibility_action_zoom_in,
- mContext.getString(R.string.accessibility_control_zoom_in)),
- new AccessibilityAction(R.id.accessibility_action_zoom_out,
- mContext.getString(R.string.accessibility_control_zoom_out)),
- new AccessibilityAction(R.id.accessibility_action_move_right,
- mContext.getString(R.string.accessibility_control_move_right)),
- new AccessibilityAction(R.id.accessibility_action_move_left,
- mContext.getString(R.string.accessibility_control_move_left)),
- new AccessibilityAction(R.id.accessibility_action_move_down,
- mContext.getString(R.string.accessibility_control_move_down)),
- new AccessibilityAction(R.id.accessibility_action_move_up,
- mContext.getString(R.string.accessibility_control_move_up))));
- }
-
- @Test
- public void performA11yActions_visible_expectedResults() {
- final int displayId = mContext.getDisplayId();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(1.5f, Float.NaN,
- Float.NaN);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null))
- .isTrue();
- // Minimum scale is 1.0.
- verify(mWindowMagnifierCallback).onPerformScaleAction(
- eq(displayId), /* scale= */ eq(1.0f), /* updatePersistence= */ eq(true));
-
- assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null))
- .isTrue();
- verify(mWindowMagnifierCallback).onPerformScaleAction(
- eq(displayId), /* scale= */ eq(2.5f), /* updatePersistence= */ eq(true));
-
- // TODO: Verify the final state when the mirror surface is visible.
- assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null))
- .isTrue();
- assertThat(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null))
- .isTrue();
- assertThat(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null))
- .isTrue();
- assertThat(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null))
- .isTrue();
- verify(mWindowMagnifierCallback, times(4)).onMove(eq(displayId));
-
- assertThat(mirrorView.performAccessibilityAction(
- AccessibilityAction.ACTION_CLICK.getId(), null)).isTrue();
- verify(mWindowMagnifierCallback).onClickSettingsButton(eq(displayId));
- }
-
- @Test
- public void performA11yActions_visible_notifyAccessibilityActionPerformed() {
- final int displayId = mContext.getDisplayId();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
- Float.NaN);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
-
- verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId));
- }
-
- @Test
- public void windowMagnifierEditMode_performA11yClickAction_exitEditMode() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- View closeButton = getInternalView(R.id.close_button);
- View bottomRightCorner = getInternalView(R.id.bottom_right_corner);
- View bottomLeftCorner = getInternalView(R.id.bottom_left_corner);
- View topRightCorner = getInternalView(R.id.top_right_corner);
- View topLeftCorner = getInternalView(R.id.top_left_corner);
-
- assertThat(closeButton.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(topRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(topLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- mInstrumentation.runOnMainSync(() ->
- mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
- null));
-
- assertThat(closeButton.getVisibility()).isEqualTo(View.GONE);
- assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.GONE);
- assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.GONE);
- assertThat(topRightCorner.getVisibility()).isEqualTo(View.GONE);
- assertThat(topLeftCorner.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
-
- public void windowWidthIsNotMax_performA11yActionIncreaseWidth_windowWidthIncreased() {
- final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- final int startingWidth = (int) (windowBounds.width() * 0.8);
- final int startingHeight = (int) (windowBounds.height() * 0.8);
- final float changeWindowSizeAmount = mContext.getResources().getFraction(
- R.fraction.magnification_resize_window_size_amount,
- /* base= */ 1,
- /* pbase= */ 1);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mirrorView.performAccessibilityAction(
- R.id.accessibility_action_increase_window_width, null);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // Window width includes the magnifier frame and the margin. Increasing the window size
- // will be increasing the amount of the frame size only.
- int newWindowWidth =
- (int) ((startingWidth - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
- + 2 * mirrorSurfaceMargin;
- assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
- assertThat(actualWindowHeight.get()).isEqualTo(startingHeight);
- }
-
- @Test
- public void windowHeightIsNotMax_performA11yActionIncreaseHeight_windowHeightIncreased() {
- final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- final int startingWidth = (int) (windowBounds.width() * 0.8);
- final int startingHeight = (int) (windowBounds.height() * 0.8);
- final float changeWindowSizeAmount = mContext.getResources().getFraction(
- R.fraction.magnification_resize_window_size_amount,
- /* base= */ 1,
- /* pbase= */ 1);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mirrorView.performAccessibilityAction(
- R.id.accessibility_action_increase_window_height, null);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // Window height includes the magnifier frame and the margin. Increasing the window size
- // will be increasing the amount of the frame size only.
- int newWindowHeight =
- (int) ((startingHeight - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
- + 2 * mirrorSurfaceMargin;
- assertThat(actualWindowWidth.get()).isEqualTo(startingWidth);
- assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
- }
-
- @Test
- public void windowWidthIsMax_noIncreaseWindowWidthA11yAction() {
- final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- final int startingWidth = windowBounds.width();
- final int startingHeight = windowBounds.height();
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AccessibilityNodeInfo accessibilityNodeInfo =
- mirrorView.createAccessibilityNodeInfo();
- assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
- new AccessibilityAction(
- R.id.accessibility_action_increase_window_width,
- mContext.getString(
- R.string.accessibility_control_increase_window_width)));
- }
-
- @Test
- public void windowHeightIsMax_noIncreaseWindowHeightA11yAction() {
- final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- final int startingWidth = windowBounds.width();
- final int startingHeight = windowBounds.height();
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AccessibilityNodeInfo accessibilityNodeInfo =
- mirrorView.createAccessibilityNodeInfo();
- assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
- new AccessibilityAction(
- R.id.accessibility_action_increase_window_height, null));
- }
-
- @Test
- public void windowWidthIsNotMin_performA11yActionDecreaseWidth_windowWidthDecreased() {
- int mMinWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int startingSize = (int) (mMinWindowSize * 1.1);
- final float changeWindowSizeAmount = mContext.getResources().getFraction(
- R.fraction.magnification_resize_window_size_amount,
- /* base= */ 1,
- /* pbase= */ 1);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mirrorView.performAccessibilityAction(
- R.id.accessibility_action_decrease_window_width, null);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // Window width includes the magnifier frame and the margin. Decreasing the window size
- // will be decreasing the amount of the frame size only.
- int newWindowWidth =
- (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
- + 2 * mirrorSurfaceMargin;
- assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
- assertThat(actualWindowHeight.get()).isEqualTo(startingSize);
- }
-
- @Test
- public void windowHeightIsNotMin_performA11yActionDecreaseHeight_windowHeightDecreased() {
- int mMinWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int startingSize = (int) (mMinWindowSize * 1.1);
- final float changeWindowSizeAmount = mContext.getResources().getFraction(
- R.fraction.magnification_resize_window_size_amount,
- /* base= */ 1,
- /* pbase= */ 1);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mirrorView.performAccessibilityAction(
- R.id.accessibility_action_decrease_window_height, null);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // Window height includes the magnifier frame and the margin. Decreasing the window size
- // will be decreasing the amount of the frame size only.
- int newWindowHeight =
- (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
- + 2 * mirrorSurfaceMargin;
- assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
- assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
- }
-
- @Test
- public void windowWidthIsMin_noDecreaseWindowWidthA11yAction() {
- int mMinWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int startingSize = mMinWindowSize;
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AccessibilityNodeInfo accessibilityNodeInfo =
- mirrorView.createAccessibilityNodeInfo();
- assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
- new AccessibilityAction(
- R.id.accessibility_action_decrease_window_width, null));
- }
-
- @Test
- public void windowHeightIsMin_noDecreaseWindowHeightA11yAction() {
- int mMinWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int startingSize = mMinWindowSize;
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AccessibilityNodeInfo accessibilityNodeInfo =
- mirrorView.createAccessibilityNodeInfo();
- assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
- new AccessibilityAction(
- R.id.accessibility_action_decrease_window_height, null));
- }
-
- @Test
- public void enableWindowMagnification_hasA11yWindowTitle() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- assertThat(getAccessibilityWindowTitle()).isEqualTo(getContext().getResources().getString(
- com.android.internal.R.string.android_system_label));
- }
-
- @Test
- public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(0.9f, Float.NaN,
- Float.NaN);
- });
-
- assertThat(mWindowMagnificationController.getScale()).isEqualTo(Float.NaN);
- }
-
- @Test
- public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
- // the config orientation should not be undefined, since it would cause config.diff
- // returning 0 and thus the orientation changed would not be detected
- assertThat(mResources.getConfiguration().orientation).isNotEqualTo(ORIENTATION_UNDEFINED);
-
- final Configuration config = mResources.getConfiguration();
- config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
- : ORIENTATION_LANDSCAPE;
- final int newRotation = simulateRotateTheDevice();
-
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
- }
-
- @Test
- public void enableWindowMagnification_registerComponentCallback() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN,
- Float.NaN));
-
- verify(mContext).registerComponentCallbacks(mWindowMagnificationController);
- }
-
- @Test
- public void onLocaleChanged_enabled_updateA11yWindowTitle() {
- final String newA11yWindowTitle = "new a11y window title";
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
- final TestableResources testableResources = getContext().getOrCreateTestableResources();
- testableResources.addOverride(com.android.internal.R.string.android_system_label,
- newA11yWindowTitle);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
- });
-
- assertThat(getAccessibilityWindowTitle()).isEqualTo(newA11yWindowTitle);
- }
-
- @Ignore("it's flaky in presubmit but works in abtd, filter for now. b/305654925")
- @Test
- public void onSingleTap_enabled_scaleAnimates() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onSingleTap(mSpyView);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
-
- final AtomicDouble maxScaleX = new AtomicDouble();
- advanceTimeBy(mWaitBounceEffectDuration, /* runnableOnEachRefresh= */ () -> {
- // For some reason the fancy way doesn't compile...
- // maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
- final double oldMax = maxScaleX.get();
- final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
- assertThat(maxScaleX.compareAndSet(oldMax, newMax)).isTrue();
- });
-
- assertThat(maxScaleX.get()).isGreaterThan(1.0);
- }
-
- @Test
- public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- setSystemGestureInsets();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.moveWindowMagnifier(0, bounds.height());
- });
-
- ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
- }
-
- @Test
- public void moveWindowMagnificationToRightEdge_dragHandleMovesToLeftAndUpdatesTapExcludeRegion()
- throws RemoteException {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- setSystemGestureInsets();
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN);
- });
- // Wait for Region updated.
- waitForIdleSync();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.moveWindowMagnifier(bounds.width(), 0);
- });
- // Wait for Region updated.
- waitForIdleSync();
-
- AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl();
- // Verifying two times in: (1) enable window magnification (2) reposition drag handle
- verify(viewRoot, times(2)).setTouchableRegion(any());
-
- View dragButton = getInternalView(R.id.drag_handle);
- FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
- assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.LEFT);
- }
-
- @Test
- public void moveWindowMagnificationToLeftEdge_dragHandleMovesToRightAndUpdatesTapExcludeRegion()
- throws RemoteException {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- setSystemGestureInsets();
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN);
- });
- // Wait for Region updated.
- waitForIdleSync();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.moveWindowMagnifier(-bounds.width(), 0);
- });
- // Wait for Region updated.
- waitForIdleSync();
-
- AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl();
- // Verifying one times in: (1) enable window magnification
- verify(viewRoot).setTouchableRegion(any());
-
- View dragButton = getInternalView(R.id.drag_handle);
- FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
- assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.RIGHT);
- }
-
- @Test
- public void setMinimumWindowSize_enabled_expectedWindowSize() {
- final int minimumWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int expectedWindowHeight = minimumWindowSize;
- final int expectedWindowWidth = minimumWindowSize;
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
- actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
-
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
- assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
- }
-
- @Test
- public void setMinimumWindowSizeThenEnable_expectedWindowSize() {
- final int minimumWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int expectedWindowHeight = minimumWindowSize;
- final int expectedWindowWidth = minimumWindowSize;
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN);
- actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
- assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
- }
-
- @Test
- public void setWindowSizeLessThanMin_enabled_minimumWindowSize() {
- final int minimumWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSize(minimumWindowSize - 10,
- minimumWindowSize - 10);
- actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(minimumWindowSize);
- assertThat(actualWindowWidth.get()).isEqualTo(minimumWindowSize);
- }
-
- @Test
- public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10);
- actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(bounds.height());
- assertThat(actualWindowWidth.get()).isEqualTo(bounds.width());
- }
-
- @Test
- public void changeMagnificationSize_expectedWindowSize() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-
- final float magnificationScaleLarge = 2.5f;
- final int initSize = Math.min(bounds.width(), bounds.height()) / 3;
- final int magnificationSize = (int) (initSize * magnificationScaleLarge)
- - (int) (initSize * magnificationScaleLarge) % 2;
-
- final int expectedWindowHeight = magnificationSize;
- final int expectedWindowWidth = magnificationSize;
-
- mInstrumentation.runOnMainSync(
- () ->
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.changeMagnificationSize(
- WindowMagnificationSettings.MagnificationSize.LARGE);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
- assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
- }
-
- @Test
- public void editModeOnDragCorner_resizesWindow() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-
- final int startingSize = (int) (bounds.width() / 2);
-
- mInstrumentation.runOnMainSync(
- () ->
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- waitForIdleSync();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController
- .onDrag(getInternalView(R.id.bottom_right_corner), 2f, 1f);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
- assertThat(actualWindowWidth.get()).isEqualTo(startingSize + 2);
- }
-
- @Test
- public void editModeOnDragEdge_resizesWindowInOnlyOneDirection() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-
- final int startingSize = (int) (bounds.width() / 2f);
-
- mInstrumentation.runOnMainSync(
- () ->
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- mWindowMagnificationController
- .onDrag(getInternalView(R.id.bottom_handle), 2f, 1f);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
- assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
- assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
- }
-
- @Test
- public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() {
-
- final int minimumWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- final AtomicInteger magnificationCenterX = new AtomicInteger();
- final AtomicInteger magnificationCenterY = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize,
- minimumWindowSize, bounds.right, bounds.bottom);
- magnificationCenterX.set((int) mWindowMagnificationController.getCenterX());
- magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
- });
-
- assertThat(magnificationCenterX.get()).isLessThan(bounds.right);
- assertThat(magnificationCenterY.get()).isLessThan(bounds.bottom);
- }
-
- @Test
- public void performSingleTap_DragHandle() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(
- 1.5f, bounds.centerX(), bounds.centerY());
- });
- View dragButton = getInternalView(R.id.drag_handle);
-
- // Perform a single-tap
- final long downTime = SystemClock.uptimeMillis();
- dragButton.dispatchTouchEvent(
- obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100));
- dragButton.dispatchTouchEvent(
- obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100));
-
- verify(mSurfaceControlViewHost).setView(any(View.class), any());
- }
-
- private <T extends View> T getInternalView(@IdRes int idRes) {
- View mirrorView = mSurfaceControlViewHost.getView();
- T view = mirrorView.findViewById(idRes);
- assertThat(view).isNotNull();
- return view;
- }
-
- private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x,
- float y) {
- return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y);
- }
-
- private String getAccessibilityWindowTitle() {
- final View mirrorView = mSurfaceControlViewHost.getView();
- if (mirrorView == null) {
- return null;
- }
- WindowManager.LayoutParams layoutParams =
- (WindowManager.LayoutParams) mirrorView.getLayoutParams();
- return layoutParams.accessibilityTitle.toString();
- }
-
- private boolean hasMagnificationOverlapFlag() {
- return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0;
- }
-
- private void setSystemGestureInsets() {
- final WindowInsets testInsets = new WindowInsets.Builder()
- .setInsets(systemGestures(), Insets.of(0, 0, 0, 10))
- .build();
- mWindowManager.setWindowInsets(testInsets);
- }
-
- private int updateMirrorSurfaceMarginDimension() {
- return mContext.getResources().getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- }
-
- @Surface.Rotation
- private int simulateRotateTheDevice() {
- final Display display = Mockito.spy(mContext.getDisplay());
- final int currentRotation = display.getRotation();
- final int newRotation = (currentRotation + 1) % 4;
- when(display.getRotation()).thenReturn(newRotation);
- when(mContext.getDisplay()).thenReturn(display);
- return newRotation;
- }
-
- // advance time based on the device frame refresh rate
- private void advanceTimeBy(long timeDelta) {
- advanceTimeBy(timeDelta, /* runnableOnEachRefresh= */ null);
- }
-
- // advance time based on the device frame refresh rate, and trigger runnable on each refresh
- private void advanceTimeBy(long timeDelta, @Nullable Runnable runnableOnEachRefresh) {
- final float frameRate = mContext.getDisplay().getRefreshRate();
- final int timeSlot = (int) (1000 / frameRate);
- int round = (int) Math.ceil((double) timeDelta / timeSlot);
- for (; round >= 0; round--) {
- mInstrumentation.runOnMainSync(() -> {
- mAnimatorTestRule.advanceTimeBy(timeSlot);
- if (runnableOnEachRefresh != null) {
- runnableOnEachRefresh.run();
- }
- });
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
index 288ed4dc9d4f..a1f59c2cc2b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
@@ -20,18 +20,20 @@ import android.animation.AnimatorRuleRecordingSpec
import android.animation.AnimatorTestRuleToolkit
import android.animation.MotionControl
import android.animation.recordMotion
+import android.graphics.Color
+import android.graphics.PointF
import android.graphics.drawable.GradientDrawable
import android.platform.test.annotations.MotionTest
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.EmptyTestActivity
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.runOnMainThreadAndWaitForIdleSync
import kotlin.test.assertTrue
import org.junit.Rule
import org.junit.Test
@@ -47,13 +49,25 @@ import platform.test.screenshot.PathConfig
@SmallTest
@MotionTest
@RunWith(ParameterizedAndroidJunit4::class)
-class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() {
+class TransitionAnimatorTest(
+ private val fadeWindowBackgroundLayer: Boolean,
+ private val isLaunching: Boolean,
+ private val useSpring: Boolean,
+) : SysuiTestCase() {
companion object {
private const val GOLDENS_PATH = "frameworks/base/packages/SystemUI/tests/goldens"
- @get:Parameters(name = "{0}")
+ @get:Parameters(name = "fadeBackground={0}, isLaunching={1}, useSpring={2}")
@JvmStatic
- val useSpringValues = booleanArrayOf(false, true).toList()
+ val parameterValues = buildList {
+ booleanArrayOf(true, false).forEach { fadeBackground ->
+ booleanArrayOf(true, false).forEach { isLaunching ->
+ booleanArrayOf(true, false).forEach { useSpring ->
+ add(arrayOf(fadeBackground, isLaunching, useSpring))
+ }
+ }
+ }
+ }
}
private val kosmos = Kosmos()
@@ -66,11 +80,23 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() {
ActivityTransitionAnimator.SPRING_TIMINGS,
ActivityTransitionAnimator.SPRING_INTERPOLATORS,
)
- private val withSpring =
+ private val fade =
+ if (fadeWindowBackgroundLayer) {
+ "withFade"
+ } else {
+ "withoutFade"
+ }
+ private val direction =
+ if (isLaunching) {
+ "whenLaunching"
+ } else {
+ "whenReturning"
+ }
+ private val mode =
if (useSpring) {
- "_withSpring"
+ "withSpring"
} else {
- ""
+ "withAnimator"
}
@get:Rule(order = 1) val activityRule = ActivityScenarioRule(EmptyTestActivity::class.java)
@@ -83,113 +109,75 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() {
)
@Test
- fun backgroundAnimation_whenLaunching() {
- val backgroundLayer = GradientDrawable().apply { alpha = 0 }
- val animator =
- setUpTest(backgroundLayer, isLaunching = true).apply {
- getInstrumentation().runOnMainSync { start() }
- }
+ fun backgroundAnimationTimeSeries() {
+ val transitionContainer = createScene()
+ val backgroundLayer = createBackgroundLayer()
+ val animation = createAnimation(transitionContainer, backgroundLayer)
- val recordedMotion = recordMotion(backgroundLayer, animator)
+ val recordedMotion = record(backgroundLayer, animation)
motionRule
.assertThat(recordedMotion)
- .timeSeriesMatchesGolden("backgroundAnimation_whenLaunching$withSpring")
+ .timeSeriesMatchesGolden("backgroundAnimationTimeSeries_${fade}_${direction}_$mode")
}
- @Test
- fun backgroundAnimation_whenReturning() {
- val backgroundLayer = GradientDrawable().apply { alpha = 0 }
- val animator =
- setUpTest(backgroundLayer, isLaunching = false).apply {
- getInstrumentation().runOnMainSync { start() }
- }
-
- val recordedMotion = recordMotion(backgroundLayer, animator)
-
- motionRule
- .assertThat(recordedMotion)
- .timeSeriesMatchesGolden("backgroundAnimation_whenReturning$withSpring")
- }
-
- @Test
- fun backgroundAnimationWithoutFade_whenLaunching() {
- val backgroundLayer = GradientDrawable().apply { alpha = 0 }
- val animator =
- setUpTest(backgroundLayer, isLaunching = true, fadeWindowBackgroundLayer = false)
- .apply { getInstrumentation().runOnMainSync { start() } }
-
- val recordedMotion = recordMotion(backgroundLayer, animator)
-
- motionRule
- .assertThat(recordedMotion)
- .timeSeriesMatchesGolden("backgroundAnimationWithoutFade_whenLaunching$withSpring")
- }
-
- @Test
- fun backgroundAnimationWithoutFade_whenReturning() {
- val backgroundLayer = GradientDrawable().apply { alpha = 0 }
- val animator =
- setUpTest(backgroundLayer, isLaunching = false, fadeWindowBackgroundLayer = false)
- .apply { getInstrumentation().runOnMainSync { start() } }
-
- val recordedMotion = recordMotion(backgroundLayer, animator)
-
- motionRule
- .assertThat(recordedMotion)
- .timeSeriesMatchesGolden("backgroundAnimationWithoutFade_whenReturning$withSpring")
- }
-
- private fun setUpTest(
- backgroundLayer: GradientDrawable,
- isLaunching: Boolean,
- fadeWindowBackgroundLayer: Boolean = true,
- ): TransitionAnimator.Animation {
+ private fun createScene(): ViewGroup {
lateinit var transitionContainer: ViewGroup
activityRule.scenario.onActivity { activity ->
- transitionContainer = FrameLayout(activity).apply { setBackgroundColor(0x00FF00) }
+ transitionContainer = FrameLayout(activity)
activity.setContentView(transitionContainer)
}
waitForIdleSync()
+ return transitionContainer
+ }
+ private fun createBackgroundLayer() =
+ GradientDrawable().apply {
+ setColor(Color.BLACK)
+ alpha = 0
+ }
+
+ private fun createAnimation(
+ transitionContainer: ViewGroup,
+ backgroundLayer: GradientDrawable,
+ ): TransitionAnimator.Animation {
val controller = TestController(transitionContainer, isLaunching)
- return transitionAnimator.createAnimation(
- controller,
- controller.createAnimatorState(),
- createEndState(transitionContainer),
- backgroundLayer,
- fadeWindowBackgroundLayer,
- useSpring = useSpring,
- )
- }
- private fun createEndState(container: ViewGroup): TransitionAnimator.State {
val containerLocation = IntArray(2)
- container.getLocationOnScreen(containerLocation)
- return TransitionAnimator.State(
- left = containerLocation[0],
- top = containerLocation[1],
- right = containerLocation[0] + 320,
- bottom = containerLocation[1] + 690,
- topCornerRadius = 0f,
- bottomCornerRadius = 0f,
- )
+ transitionContainer.getLocationOnScreen(containerLocation)
+ val endState =
+ TransitionAnimator.State(
+ left = containerLocation[0],
+ top = containerLocation[1],
+ right = containerLocation[0] + 320,
+ bottom = containerLocation[1] + 690,
+ topCornerRadius = 0f,
+ bottomCornerRadius = 0f,
+ )
+
+ val startVelocity =
+ if (useSpring) {
+ PointF(2500f, 30000f)
+ } else {
+ null
+ }
+
+ return transitionAnimator
+ .createAnimation(
+ controller,
+ controller.createAnimatorState(),
+ endState,
+ backgroundLayer,
+ fadeWindowBackgroundLayer,
+ startVelocity = startVelocity,
+ )
+ .apply { runOnMainThreadAndWaitForIdleSync { start() } }
}
- private fun recordMotion(
+ private fun record(
backgroundLayer: GradientDrawable,
animation: TransitionAnimator.Animation,
): RecordedMotion {
- fun record(motionControl: MotionControl, sampleIntervalMs: Long): RecordedMotion {
- return motionRule.recordMotion(
- AnimatorRuleRecordingSpec(backgroundLayer, motionControl, sampleIntervalMs) {
- feature(DrawableFeatureCaptures.bounds, "bounds")
- feature(DrawableFeatureCaptures.cornerRadii, "corner_radii")
- feature(DrawableFeatureCaptures.alpha, "alpha")
- }
- )
- }
-
val motionControl: MotionControl
val sampleIntervalMs: Long
if (useSpring) {
@@ -204,9 +192,13 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() {
sampleIntervalMs = 20L
}
- var recording: RecordedMotion? = null
- getInstrumentation().runOnMainSync { recording = record(motionControl, sampleIntervalMs) }
- return recording!!
+ return motionRule.recordMotion(
+ AnimatorRuleRecordingSpec(backgroundLayer, motionControl, sampleIntervalMs) {
+ feature(DrawableFeatureCaptures.bounds, "bounds")
+ feature(DrawableFeatureCaptures.cornerRadii, "corner_radii")
+ feature(DrawableFeatureCaptures.alpha, "alpha")
+ }
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 6061063db903..562481567536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -20,6 +20,7 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.systemui.Flags.FLAG_CLIPBOARD_SHARED_TRANSITIONS;
import static com.android.systemui.Flags.FLAG_CLIPBOARD_USE_DESCRIPTION_MIMETYPE;
+import static com.android.systemui.Flags.FLAG_SHOW_CLIPBOARD_INDICATION;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_EXPANDED_FROM_MINIMIZED;
@@ -121,6 +122,24 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private class FakeClipboardIndicationProvider implements ClipboardIndicationProvider {
+ private ClipboardIndicationCallback mIndicationCallback;
+
+ public void notifyIndicationTextChanged(CharSequence indicationText) {
+ if (mIndicationCallback != null) {
+ mIndicationCallback.onIndicationTextChanged(indicationText);
+ }
+ }
+
+ @Override
+ public void getIndicationText(ClipboardIndicationCallback callback) {
+ mIndicationCallback = callback;
+ }
+ }
+
+ private FakeClipboardIndicationProvider mClipboardIndicationProvider =
+ new FakeClipboardIndicationProvider();
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -156,6 +175,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
mExecutor,
mClipboardImageLoader,
mClipboardTransitionExecutor,
+ mClipboardIndicationProvider,
mUiEventLogger);
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
@@ -305,6 +325,17 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(FLAG_SHOW_CLIPBOARD_INDICATION)
+ public void test_onIndicationTextChanged_setIndicationTextCorrectly() {
+ initController();
+ mOverlayController.setClipData(mSampleClipData, "");
+
+ mClipboardIndicationProvider.notifyIndicationTextChanged("copied");
+
+ verify(mClipboardOverlayView).setIndicationText("copied");
+ }
+
+ @Test
@DisableFlags(FLAG_CLIPBOARD_SHARED_TRANSITIONS)
public void test_viewCallbacks_onShareTapped_sharedTransitionsOff() {
initController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index b3bd7d15cbf3..c7beb158c2de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -23,7 +23,6 @@ import android.view.ViewGroup
import android.view.accessibility.AccessibilityManager
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewUiEventLogger
@@ -43,7 +42,6 @@ class FakeMediaTttChipControllerReceiver(
dumpManager: DumpManager,
powerManager: PowerManager,
mainHandler: Handler,
- mediaTttFlags: MediaTttFlags,
uiEventLogger: MediaTttReceiverUiEventLogger,
viewUtil: ViewUtil,
wakeLockBuilder: WakeLock.Builder,
@@ -62,7 +60,6 @@ class FakeMediaTttChipControllerReceiver(
dumpManager,
powerManager,
mainHandler,
- mediaTttFlags,
uiEventLogger,
viewUtil,
wakeLockBuilder,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 9afa5ad1dd43..378dd452d030 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -37,7 +37,6 @@ import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -55,7 +54,6 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -66,32 +64,18 @@ import org.mockito.MockitoAnnotations
class MediaTttChipControllerReceiverTest : SysuiTestCase() {
private lateinit var controllerReceiver: MediaTttChipControllerReceiver
- @Mock
- private lateinit var packageManager: PackageManager
- @Mock
- private lateinit var applicationInfo: ApplicationInfo
- @Mock
- private lateinit var logger: MediaTttReceiverLogger
- @Mock
- private lateinit var accessibilityManager: AccessibilityManager
- @Mock
- private lateinit var configurationController: ConfigurationController
- @Mock
- private lateinit var dumpManager: DumpManager
- @Mock
- private lateinit var mediaTttFlags: MediaTttFlags
- @Mock
- private lateinit var powerManager: PowerManager
- @Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
- private lateinit var windowManager: WindowManager
- @Mock
- private lateinit var commandQueue: CommandQueue
- @Mock
- private lateinit var rippleController: MediaTttReceiverRippleController
- @Mock
- private lateinit var lazyViewCapture: Lazy<ViewCapture>
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var applicationInfo: ApplicationInfo
+ @Mock private lateinit var logger: MediaTttReceiverLogger
+ @Mock private lateinit var accessibilityManager: AccessibilityManager
+ @Mock private lateinit var configurationController: ConfigurationController
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var powerManager: PowerManager
+ @Mock private lateinit var viewUtil: ViewUtil
+ @Mock private lateinit var windowManager: WindowManager
+ @Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var rippleController: MediaTttReceiverRippleController
+ @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -106,14 +90,17 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
- whenever(packageManager.getApplicationInfo(
- eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
- )).thenReturn(applicationInfo)
+ whenever(
+ packageManager.getApplicationInfo(
+ eq(PACKAGE_NAME),
+ any<PackageManager.ApplicationInfoFlags>(),
+ )
+ )
+ .thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
fakeClock = FakeSystemClock()
@@ -127,27 +114,31 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
fakeWakeLockBuilder = WakeLockFake.Builder(context)
fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
- viewCaptureAwareWindowManager = ViewCaptureAwareWindowManager(windowManager,
- lazyViewCapture, isViewCaptureEnabled = false)
- controllerReceiver = FakeMediaTttChipControllerReceiver(
- commandQueue,
- context,
- logger,
- viewCaptureAwareWindowManager,
- fakeExecutor,
- accessibilityManager,
- configurationController,
- dumpManager,
- powerManager,
- Handler.getMain(),
- mediaTttFlags,
- receiverUiEventLogger,
- viewUtil,
- fakeWakeLockBuilder,
- fakeClock,
- rippleController,
- temporaryViewUiEventLogger,
- )
+ viewCaptureAwareWindowManager =
+ ViewCaptureAwareWindowManager(
+ windowManager,
+ lazyViewCapture,
+ isViewCaptureEnabled = false,
+ )
+ controllerReceiver =
+ FakeMediaTttChipControllerReceiver(
+ commandQueue,
+ context,
+ logger,
+ viewCaptureAwareWindowManager,
+ fakeExecutor,
+ accessibilityManager,
+ configurationController,
+ dumpManager,
+ powerManager,
+ Handler.getMain(),
+ receiverUiEventLogger,
+ viewUtil,
+ fakeWakeLockBuilder,
+ fakeClock,
+ rippleController,
+ temporaryViewUiEventLogger,
+ )
controllerReceiver.start()
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
@@ -156,48 +147,18 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
}
@Test
- fun commandQueueCallback_flagOff_noCallbackAdded() {
- reset(commandQueue)
- whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(false)
-
- controllerReceiver = MediaTttChipControllerReceiver(
- commandQueue,
- context,
- logger,
- viewCaptureAwareWindowManager,
- FakeExecutor(FakeSystemClock()),
- accessibilityManager,
- configurationController,
- dumpManager,
- powerManager,
- Handler.getMain(),
- mediaTttFlags,
- receiverUiEventLogger,
- viewUtil,
- fakeWakeLockBuilder,
- fakeClock,
- rippleController,
- temporaryViewUiEventLogger,
- )
- controllerReceiver.start()
-
- verify(commandQueue, never()).addCallback(any())
- }
-
- @Test
fun commandQueueCallback_closeToSender_triggersChip() {
val appName = "FakeAppName"
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
/* appIcon= */ null,
- appName
+ appName,
)
assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(appName)
- assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
- MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER.id
- )
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER.id)
assertThat(uiEventLoggerFake.logs[0].instanceId).isNotNull()
}
@@ -207,45 +168,44 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
routeInfo,
null,
- null
+ null,
)
verify(windowManager, never()).addView(any(), any())
- assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
- MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER.id
- )
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER.id)
assertThat(uiEventLoggerFake.logs[0].instanceId).isNotNull()
}
@Test
fun commandQueueCallback_transferToReceiverSucceeded_noChipShown() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ routeInfo,
+ null,
+ null,
)
verify(windowManager, never()).addView(any(), any())
- assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(
MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_SUCCEEDED.id
- )
+ )
assertThat(uiEventLoggerFake.logs[0].instanceId).isNotNull()
}
@Test
fun commandQueueCallback_transferToReceiverFailed_noChipShown() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED,
+ routeInfo,
+ null,
+ null,
)
verify(windowManager, never()).addView(any(), any())
- assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
- MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_FAILED.id
- )
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_FAILED.id)
assertThat(uiEventLoggerFake.logs[0].instanceId).isNotNull()
}
@@ -255,14 +215,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
routeInfo,
null,
- null
+ null,
)
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -276,14 +236,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
null,
- null
+ null,
)
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -297,14 +257,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
null,
- null
+ null,
)
assertThat(uiEventLoggerFake[0].instanceId).isEqualTo(uiEventLoggerFake[1].instanceId)
@@ -316,14 +276,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED,
routeInfo,
null,
- null
+ null,
)
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -334,19 +294,19 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Test
fun commandQueueCallback_closeThenFar_wakeLockAcquiredThenReleased() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ routeInfo,
+ null,
+ null,
)
assertThat(fakeWakeLock.isHeld).isTrue()
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo,
+ null,
+ null,
)
assertThat(fakeWakeLock.isHeld).isFalse()
@@ -355,10 +315,10 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Test
fun commandQueueCallback_closeThenFar_wakeLockNeverAcquired() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo,
+ null,
+ null,
)
assertThat(fakeWakeLock.isHeld).isFalse()
@@ -370,7 +330,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
verify(logger).logStateChange(any(), any(), any())
@@ -391,10 +351,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
val view = getChipView()
assertThat(view.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(view.getAppIconView().contentDescription)
- .isEqualTo(context.getString(
- R.string.media_transfer_receiver_content_description_with_app_name,
- APP_NAME,
- ))
+ .isEqualTo(
+ context.getString(
+ R.string.media_transfer_receiver_content_description_with_app_name,
+ APP_NAME,
+ )
+ )
}
@Test
@@ -463,7 +425,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
null,
- APP_NAME
+ APP_NAME,
)
verify(windowManager, never()).addView(any(), any())
@@ -476,10 +438,11 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
}
private fun getChipReceiverInfo(packageName: String?): ChipReceiverInfo {
- val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
- .addFeature("feature")
- .setClientPackageName(packageName)
- .build()
+ val routeInfo =
+ MediaRoute2Info.Builder("id", "Test route name")
+ .addFeature("feature")
+ .setClientPackageName(packageName)
+ .build()
return ChipReceiverInfo(
routeInfo,
null,
@@ -495,7 +458,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
private const val APP_NAME = "Fake app name"
private const val PACKAGE_NAME = "com.android.systemui"
-private val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
- .addFeature("feature")
- .setClientPackageName(PACKAGE_NAME)
- .build()
+private val routeInfo =
+ MediaRoute2Info.Builder("id", "Test route name")
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index b4cad6bb057b..c90ac5993c31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -41,7 +41,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
@@ -95,7 +94,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var chipbarLogger: ChipbarLogger
@Mock private lateinit var logger: MediaTttSenderLogger
- @Mock private lateinit var mediaTttFlags: MediaTttFlags
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var powerManager: PowerManager
@Mock private lateinit var viewUtil: ViewUtil
@@ -118,7 +116,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
@@ -127,7 +124,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
whenever(
packageManager.getApplicationInfo(
eq(PACKAGE_NAME),
- any<PackageManager.ApplicationInfoFlags>()
+ any<PackageManager.ApplicationInfoFlags>(),
)
)
.thenReturn(applicationInfo)
@@ -148,8 +145,11 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
ChipbarCoordinator(
context,
chipbarLogger,
- ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
- isViewCaptureEnabled = false),
+ ViewCaptureAwareWindowManager(
+ windowManager,
+ lazyViewCapture,
+ isViewCaptureEnabled = false,
+ ),
fakeExecutor,
accessibilityManager,
configurationController,
@@ -174,7 +174,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -183,30 +182,11 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
}
@Test
- fun commandQueueCallback_flagOff_noCallbackAdded() {
- reset(commandQueue)
- whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(false)
- underTest =
- MediaTttSenderCoordinator(
- chipbarCoordinator,
- commandQueue,
- context,
- dumpManager,
- logger,
- mediaTttFlags,
- uiEventLogger,
- )
- underTest.start()
-
- verify(commandQueue, never()).addCallback(any())
- }
-
- @Test
fun commandQueueCallback_almostCloseToStartCast_triggersCorrectChip() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -220,13 +200,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -249,7 +223,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -263,13 +237,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -277,7 +245,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -291,13 +259,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -320,7 +282,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -334,13 +296,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -350,7 +306,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -364,13 +320,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(2))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED.id)
verify(vibratorHelper, never())
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -380,7 +330,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
// Event index 2 since initially displaying the triggered chip would also log two events.
@@ -397,7 +347,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- /* undoCallback= */ null
+ /* undoCallback= */ null,
)
val chipbarView = getChipbarView()
@@ -452,7 +402,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -466,13 +416,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(2))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED.id)
verify(vibratorHelper, never())
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -481,7 +425,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- /* undoCallback= */ null
+ /* undoCallback= */ null,
)
val chipbarView = getChipbarView()
@@ -538,7 +482,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -553,13 +497,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(2))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -567,13 +505,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
reset(vibratorHelper)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -588,13 +526,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(2))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -602,7 +534,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
verify(windowManager, never()).addView(any(), any())
@@ -615,13 +547,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -635,7 +567,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
assertThat(fakeWakeLock.isHeld).isTrue()
@@ -643,7 +575,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
assertThat(fakeWakeLock.isHeld).isFalse()
@@ -654,7 +586,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
assertThat(fakeWakeLock.isHeld).isFalse()
@@ -672,7 +604,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -680,7 +612,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -692,7 +624,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -700,7 +632,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -713,14 +645,14 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
reset(windowManager)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -733,14 +665,14 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
reset(windowManager)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -752,7 +684,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -760,7 +692,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -772,7 +704,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -780,7 +712,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -792,7 +724,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -800,7 +732,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -812,7 +744,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -820,7 +752,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -925,7 +857,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
verify(logger).logStateChange(any(), any(), any())
@@ -936,13 +868,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -959,13 +891,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -983,13 +915,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -1007,13 +939,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -1051,7 +983,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -1091,7 +1023,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -1116,7 +1048,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1144,7 +1075,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1178,7 +1108,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1211,7 +1140,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1230,7 +1158,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
// THEN the media coordinator unregisters the listener
@@ -1248,7 +1176,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1549,7 +1476,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
private fun ChipStateSender.getExpectedStateText(
- otherDeviceName: String = OTHER_DEVICE_NAME,
+ otherDeviceName: String = OTHER_DEVICE_NAME
): String? {
return this.getChipTextString(context, otherDeviceName).loadText(context)
}
@@ -1560,7 +1487,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null
+ null,
)
}
@@ -1570,7 +1497,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index 963973588236..991f78a3147c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -27,7 +27,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.SessionCreationSource
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
@@ -56,7 +55,6 @@ import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
-import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -150,8 +148,6 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
@Test
fun screenCaptureDisabledDialog_isShown_whenFunctionalityIsDisabled() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
- .thenReturn(true)
whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
.thenReturn(true)
@@ -170,48 +166,6 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
}
@Test
- fun screenCapturePermissionDialog_isShown_correctly() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
- .thenReturn(false)
- whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
- .thenReturn(false)
- whenever(state.hasUserApprovedScreenRecording).thenReturn(false)
-
- val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
- screenRecordSwitch.isChecked = true
-
- bgExecutor.runAllReady()
- mainExecutor.runAllReady()
-
- verify(mediaProjectionMetricsLogger)
- .notifyProjectionInitiated(
- anyInt(),
- eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
- )
- verify(factory, times(2)).create(any(SystemUIDialog.Delegate::class.java))
- }
-
- @Test
- fun noDialogsAreShown_forScreenRecord_whenApprovalIsAlreadyGiven() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
- .thenReturn(false)
- whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
- .thenReturn(false)
-
- val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
- screenRecordSwitch.isChecked = true
-
- bgExecutor.runAllReady()
-
- verify(mediaProjectionMetricsLogger)
- .notifyProjectionInitiated(
- anyInt(),
- eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
- )
- verify(factory, never()).create()
- }
-
- @Test
fun startButton_isDisabled_beforeIssueTypeIsSelected() {
assertThat(dialog.getButton(Dialog.BUTTON_POSITIVE).isEnabled).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 6b16e78436d4..afff4858499a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -42,15 +42,11 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -79,10 +75,6 @@ public class RecordingControllerTest extends SysuiTestCase {
@Mock
private ScreenCaptureDevicePolicyResolver mDevicePolicyResolver;
@Mock
- private DialogTransitionAnimator mDialogTransitionAnimator;
- @Mock
- private ActivityStarter mActivityStarter;
- @Mock
private UserTracker mUserTracker;
@Mock
private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
@@ -92,10 +84,6 @@ public class RecordingControllerTest extends SysuiTestCase {
@Mock
private SystemUIDialog mScreenCaptureDisabledDialog;
@Mock
- private ScreenRecordDialogDelegate.Factory mScreenRecordDialogFactory;
- @Mock
- private ScreenRecordDialogDelegate mScreenRecordDialogDelegate;
- @Mock
private ScreenRecordPermissionDialogDelegate.Factory
mScreenRecordPermissionDialogDelegateFactory;
@Mock
@@ -103,7 +91,6 @@ public class RecordingControllerTest extends SysuiTestCase {
@Mock
private SystemUIDialog mScreenRecordSystemUIDialog;
- private FakeFeatureFlags mFeatureFlags;
private RecordingController mController;
private static final int USER_ID = 10;
@@ -114,12 +101,8 @@ public class RecordingControllerTest extends SysuiTestCase {
Context spiedContext = spy(mContext);
when(spiedContext.getUserId()).thenReturn(TEST_USER_ID);
- mFeatureFlags = new FakeFeatureFlags();
when(mScreenCaptureDisabledDialogDelegate.createSysUIDialog())
.thenReturn(mScreenCaptureDisabledDialog);
- when(mScreenRecordDialogFactory.create(any(), any()))
- .thenReturn(mScreenRecordDialogDelegate);
- when(mScreenRecordDialogDelegate.createDialog()).thenReturn(mScreenRecordSystemUIDialog);
when(mScreenRecordPermissionDialogDelegateFactory.create(any(), any(), anyInt(), any()))
.thenReturn(mScreenRecordPermissionDialogDelegate);
when(mScreenRecordPermissionDialogDelegate.createDialog())
@@ -127,13 +110,11 @@ public class RecordingControllerTest extends SysuiTestCase {
mController = new RecordingController(
mMainExecutor,
mBroadcastDispatcher,
- mFeatureFlags,
() -> mDevicePolicyResolver,
mUserTracker,
new RecordingControllerLogger(logcatLogBuffer("RecordingControllerTest")),
mMediaProjectionMetricsLogger,
mScreenCaptureDisabledDialogDelegate,
- mScreenRecordDialogFactory,
mScreenRecordPermissionDialogDelegateFactory
);
mController.addCallback(mCallback);
@@ -236,46 +217,19 @@ public class RecordingControllerTest extends SysuiTestCase {
}
@Test
- public void testPoliciesFlagDisabled_screenCapturingNotAllowed_returnsNullDevicePolicyDialog() {
- mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
- when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
-
- Dialog dialog =
- mController.createScreenRecordDialog(
- mContext,
- mFeatureFlags,
- mDialogTransitionAnimator,
- mActivityStarter,
- /* onStartRecordingClicked= */ null);
-
- assertThat(dialog).isSameInstanceAs(mScreenRecordSystemUIDialog);
- assertThat(mScreenRecordPermissionDialogDelegate)
- .isInstanceOf(ScreenRecordPermissionDialogDelegate.class);
- }
-
- @Test
- public void testPoliciesFlagEnabled_screenCapturingNotAllowed_returnsDevicePolicyDialog() {
- mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+ public void testScreenCapturingNotAllowed_returnsDevicePolicyDialog() {
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
- Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
- mDialogTransitionAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+ Dialog dialog = mController.createScreenRecordDialog(/* onStartRecordingClicked= */ null);
assertThat(dialog).isEqualTo(mScreenCaptureDisabledDialog);
}
@Test
- public void testPoliciesFlagEnabled_screenCapturingAllowed_returnsNullDevicePolicyDialog() {
- mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+ public void testScreenCapturingAllowed_returnsNullDevicePolicyDialog() {
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
- Dialog dialog =
- mController.createScreenRecordDialog(
- mContext,
- mFeatureFlags,
- mDialogTransitionAnimator,
- mActivityStarter,
- /* onStartRecordingClicked= */ null);
+ Dialog dialog = mController.createScreenRecordDialog(/* onStartRecordingClicked= */ null);
assertThat(dialog).isSameInstanceAs(mScreenRecordSystemUIDialog);
assertThat(mScreenRecordPermissionDialogDelegate)
@@ -283,12 +237,10 @@ public class RecordingControllerTest extends SysuiTestCase {
}
@Test
- public void testPoliciesFlagEnabled_screenCapturingAllowed_logsProjectionInitiated() {
- mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+ public void testScreenCapturingAllowed_logsProjectionInitiated() {
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
- mController.createScreenRecordDialog(mContext, mFeatureFlags,
- mDialogTransitionAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+ mController.createScreenRecordDialog(/* onStartRecordingClicked= */ null);
verify(mMediaProjectionMetricsLogger)
.notifyProjectionInitiated(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
deleted file mode 100644
index 0427011c06f6..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * Copyright (C) 2018 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.row;
-
-import static android.app.AppOpsManager.OP_CAMERA;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
-
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
-
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.content.Intent;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.content.pm.ShortcutManager;
-import android.graphics.Color;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
-import android.testing.TestableLooper;
-import android.util.ArraySet;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
-import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository;
-import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
-import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.notification.AssistantFeedbackController;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.kotlin.JavaAdapter;
-import com.android.systemui.wmshell.BubblesManager;
-
-import kotlinx.coroutines.test.TestScope;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.Optional;
-
-/**
- * Tests for {@link NotificationGutsManager}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-public class NotificationGutsManagerTest extends SysuiTestCase {
- private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
-
- private NotificationChannel mTestNotificationChannel = new NotificationChannel(
- TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
-
- private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
- private final TestScope mTestScope = mKosmos.getTestScope();
- private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
- private final FakeExecutor mExecutor = mKosmos.getFakeExecutor();
- private final Handler mHandler = mKosmos.getFakeExecutorHandler();
- private NotificationTestHelper mHelper;
- private NotificationGutsManager mGutsManager;
-
- @Rule public MockitoRule mockito = MockitoJUnit.rule();
- @Mock private MetricsLogger mMetricsLogger;
- @Mock private OnUserInteractionCallback mOnUserInteractionCallback;
- @Mock private NotificationPresenter mPresenter;
- @Mock private NotificationActivityStarter mNotificationActivityStarter;
- @Mock private NotificationListContainer mNotificationListContainer;
- @Mock private OnSettingsClickListener mOnSettingsClickListener;
- @Mock private DeviceProvisionedController mDeviceProvisionedController;
- @Mock private AccessibilityManager mAccessibilityManager;
- @Mock private HighPriorityProvider mHighPriorityProvider;
- @Mock private INotificationManager mINotificationManager;
- @Mock private IStatusBarService mBarService;
- @Mock private LauncherApps mLauncherApps;
- @Mock private ShortcutManager mShortcutManager;
- @Mock private ChannelEditorDialogController mChannelEditorDialogController;
- @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- @Mock private UserContextProvider mContextTracker;
- @Mock private BubblesManager mBubblesManager;
- @Mock private ShadeController mShadeController;
- @Mock private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
- @Mock private AssistantFeedbackController mAssistantFeedbackController;
- @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
- @Mock private StatusBarStateController mStatusBarStateController;
- @Mock private HeadsUpManager mHeadsUpManager;
- @Mock private ActivityStarter mActivityStarter;
-
- @Mock private UserManager mUserManager;
-
- private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
-
- @Before
- public void setUp() {
- allowTestableLooperAsMainThread();
- mHelper = new NotificationTestHelper(mContext, mDependency);
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
-
- mWindowRootViewVisibilityInteractor = new WindowRootViewVisibilityInteractor(
- mTestScope.getBackgroundScope(),
- new WindowRootViewVisibilityRepository(mBarService, mExecutor),
- new FakeKeyguardRepository(),
- mHeadsUpManager,
- PowerInteractorFactory.create().getPowerInteractor(),
- mKosmos.getActiveNotificationsInteractor(),
- () -> mKosmos.getSceneInteractor()
- );
-
- mGutsManager = new NotificationGutsManager(
- mContext,
- mHandler,
- mHandler,
- mJavaAdapter,
- mAccessibilityManager,
- mHighPriorityProvider,
- mINotificationManager,
- mUserManager,
- mPeopleSpaceWidgetManager,
- mLauncherApps,
- mShortcutManager,
- mChannelEditorDialogController,
- mContextTracker,
- mAssistantFeedbackController,
- Optional.of(mBubblesManager),
- new UiEventLoggerFake(),
- mOnUserInteractionCallback,
- mShadeController,
- mWindowRootViewVisibilityInteractor,
- mNotificationLockscreenUserManager,
- mStatusBarStateController,
- mBarService,
- mDeviceProvisionedController,
- mMetricsLogger,
- mHeadsUpManager,
- mActivityStarter);
- mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
- mOnSettingsClickListener);
- mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
- mGutsManager.start();
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Test methods:
-
- @Test
- public void testOpenAndCloseGuts() {
- NotificationGuts guts = spy(new NotificationGuts(mContext));
- when(guts.post(any())).thenAnswer(invocation -> {
- mHandler.post(((Runnable) invocation.getArguments()[0]));
- return null;
- });
-
- // Test doesn't support animation since the guts view is not attached.
- doNothing().when(guts).openControls(
- anyInt(),
- anyInt(),
- anyBoolean(),
- any(Runnable.class));
-
- ExpandableNotificationRow realRow = createTestNotificationRow();
- NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
-
- ExpandableNotificationRow row = spy(realRow);
- when(row.getWindowToken()).thenReturn(new Binder());
- when(row.getGuts()).thenReturn(guts);
-
- assertTrue(mGutsManager.openGutsInternal(row, 0, 0, menuItem));
- assertEquals(View.INVISIBLE, guts.getVisibility());
- mExecutor.runAllReady();
- verify(guts).openControls(
- anyInt(),
- anyInt(),
- anyBoolean(),
- any(Runnable.class));
- verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), true);
-
- assertEquals(View.VISIBLE, guts.getVisibility());
- mGutsManager.closeAndSaveGuts(false, false, true, 0, 0, false);
-
- verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
- verify(row, times(1)).setGutsView(any());
- mExecutor.runAllReady();
- verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), false);
- }
-
- @Test
- public void testLockscreenShadeVisible_visible_gutsNotClosed() {
- // First, start out lockscreen or shade as not visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
- mTestScope.getTestScheduler().runCurrent();
-
- NotificationGuts guts = mock(NotificationGuts.class);
- mGutsManager.setExposedGuts(guts);
-
- // WHEN the lockscreen or shade becomes visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
- mTestScope.getTestScheduler().runCurrent();
-
- // THEN the guts are not closed
- verify(guts, never()).removeCallbacks(any());
- verify(guts, never()).closeControls(
- anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
- }
-
- @Test
- public void testLockscreenShadeVisible_notVisible_gutsClosed() {
- // First, start out lockscreen or shade as visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
- mTestScope.getTestScheduler().runCurrent();
-
- NotificationGuts guts = mock(NotificationGuts.class);
- mGutsManager.setExposedGuts(guts);
-
- // WHEN the lockscreen or shade is no longer visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
- mTestScope.getTestScheduler().runCurrent();
-
- // THEN the guts are closed
- verify(guts).removeCallbacks(any());
- verify(guts).closeControls(
- /* leavebehinds= */ eq(true),
- /* controls= */ eq(true),
- /* x= */ anyInt(),
- /* y= */ anyInt(),
- /* force= */ eq(true));
- }
-
- @Test
- public void testLockscreenShadeVisible_notVisible_listContainerReset() {
- // First, start out lockscreen or shade as visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
- mTestScope.getTestScheduler().runCurrent();
-
- // WHEN the lockscreen or shade is no longer visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
- mTestScope.getTestScheduler().runCurrent();
-
- // THEN the list container is reset
- verify(mNotificationListContainer).resetExposedMenuView(anyBoolean(), anyBoolean());
- }
-
- @Test
- public void testChangeDensityOrFontScale() {
- NotificationGuts guts = spy(new NotificationGuts(mContext));
- when(guts.post(any())).thenAnswer(invocation -> {
- mHandler.post(((Runnable) invocation.getArguments()[0]));
- return null;
- });
-
- // Test doesn't support animation since the guts view is not attached.
- doNothing().when(guts).openControls(
- anyInt(),
- anyInt(),
- anyBoolean(),
- any(Runnable.class));
-
- ExpandableNotificationRow realRow = createTestNotificationRow();
- NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
-
- ExpandableNotificationRow row = spy(realRow);
-
- when(row.getWindowToken()).thenReturn(new Binder());
- when(row.getGuts()).thenReturn(guts);
- doNothing().when(row).ensureGutsInflated();
-
- NotificationEntry realEntry = realRow.getEntry();
- NotificationEntry entry = spy(realEntry);
-
- when(entry.getRow()).thenReturn(row);
- when(entry.getGuts()).thenReturn(guts);
-
- assertTrue(mGutsManager.openGutsInternal(row, 0, 0, menuItem));
- mExecutor.runAllReady();
- verify(guts).openControls(
- anyInt(),
- anyInt(),
- anyBoolean(),
- any(Runnable.class));
-
- // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
- verify(row).setGutsView(any());
-
- row.onDensityOrFontScaleChanged();
- mGutsManager.onDensityOrFontScaleChanged(entry);
-
- mExecutor.runAllReady();
-
- mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
-
- verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
-
- // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
- verify(row, times(2)).setGutsView(any());
- }
-
- @Test
- public void testAppOpsSettingsIntent_camera() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_CAMERA);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_mic() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_RECORD_AUDIO);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_camera_mic() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_CAMERA);
- ops.add(OP_RECORD_AUDIO);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_overlay() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_SYSTEM_ALERT_WINDOW);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_camera_mic_overlay() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_CAMERA);
- ops.add(OP_RECORD_AUDIO);
- ops.add(OP_SYSTEM_ALERT_WINDOW);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_camera_overlay() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_CAMERA);
- ops.add(OP_SYSTEM_ALERT_WINDOW);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_mic_overlay() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_RECORD_AUDIO);
- ops.add(OP_SYSTEM_ALERT_WINDOW);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
- }
-
- @Test
- public void testInitializeNotificationInfoView_highPriority() throws Exception {
- NotificationInfo notificationInfoView = mock(NotificationInfo.class);
- ExpandableNotificationRow row = spy(mHelper.createRow());
- final NotificationEntry entry = row.getEntry();
- modifyRanking(entry)
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- when(row.getIsNonblockable()).thenReturn(false);
- when(mHighPriorityProvider.isHighPriority(entry)).thenReturn(true);
- StatusBarNotification statusBarNotification = entry.getSbn();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
-
- verify(notificationInfoView).bindNotification(
- any(PackageManager.class),
- any(INotificationManager.class),
- eq(mOnUserInteractionCallback),
- eq(mChannelEditorDialogController),
- eq(statusBarNotification.getPackageName()),
- any(NotificationChannel.class),
- eq(entry),
- any(NotificationInfo.OnSettingsClickListener.class),
- any(NotificationInfo.OnAppSettingsClickListener.class),
- any(UiEventLogger.class),
- eq(false),
- eq(false),
- eq(true), /* wasShownHighPriority */
- eq(mAssistantFeedbackController),
- any(MetricsLogger.class));
- }
-
- @Test
- public void testInitializeNotificationInfoView_PassesAlongProvisionedState() throws Exception {
- NotificationInfo notificationInfoView = mock(NotificationInfo.class);
- ExpandableNotificationRow row = spy(mHelper.createRow());
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
- when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getEntry().getSbn();
- NotificationEntry entry = row.getEntry();
-
- when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
-
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
-
- verify(notificationInfoView).bindNotification(
- any(PackageManager.class),
- any(INotificationManager.class),
- eq(mOnUserInteractionCallback),
- eq(mChannelEditorDialogController),
- eq(statusBarNotification.getPackageName()),
- any(NotificationChannel.class),
- eq(entry),
- any(NotificationInfo.OnSettingsClickListener.class),
- any(NotificationInfo.OnAppSettingsClickListener.class),
- any(UiEventLogger.class),
- eq(true),
- eq(false),
- eq(false), /* wasShownHighPriority */
- eq(mAssistantFeedbackController),
- any(MetricsLogger.class));
- }
-
- @Test
- public void testInitializeNotificationInfoView_withInitialAction() throws Exception {
- NotificationInfo notificationInfoView = mock(NotificationInfo.class);
- ExpandableNotificationRow row = spy(mHelper.createRow());
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
- when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getEntry().getSbn();
- NotificationEntry entry = row.getEntry();
-
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
-
- verify(notificationInfoView).bindNotification(
- any(PackageManager.class),
- any(INotificationManager.class),
- eq(mOnUserInteractionCallback),
- eq(mChannelEditorDialogController),
- eq(statusBarNotification.getPackageName()),
- any(NotificationChannel.class),
- eq(entry),
- any(NotificationInfo.OnSettingsClickListener.class),
- any(NotificationInfo.OnAppSettingsClickListener.class),
- any(UiEventLogger.class),
- eq(false),
- eq(false),
- eq(false), /* wasShownHighPriority */
- eq(mAssistantFeedbackController),
- any(MetricsLogger.class));
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Utility methods:
-
- private ExpandableNotificationRow createTestNotificationRow() {
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setContentTitle("foo")
- .setColorized(true).setColor(Color.RED)
- .setFlag(Notification.FLAG_CAN_COLORIZE, true)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- try {
- ExpandableNotificationRow row = mHelper.createRow(nb.build());
- modifyRanking(row.getEntry())
- .setChannel(mTestNotificationChannel)
- .build();
- return row;
- } catch (Exception e) {
- fail();
- return null;
- }
- }
-
- private NotificationMenuRowPlugin.MenuItem createTestMenuItem(ExpandableNotificationRow row) {
- NotificationMenuRowPlugin menuRow =
- new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- menuRow.createMenu(row, row.getEntry().getSbn());
-
- NotificationMenuRowPlugin.MenuItem menuItem = menuRow.getLongpressMenuItem(mContext);
- assertNotNull(menuItem);
- return menuItem;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 7d019bf1be92..b142fc2deea9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -510,7 +510,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
mShadeController.setNotificationPresenter(mNotificationPresenter);
- when(mOperatorNameViewControllerFactory.create(any()))
+ when(mOperatorNameViewControllerFactory.create(any(), any()))
.thenReturn(mOperatorNameViewController);
when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
when(mUserTracker.getUserHandle()).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index d01c1ca36c4e..4b648a3e76e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -1184,9 +1184,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mKeyguardStateController = mock(KeyguardStateController.class);
mOperatorNameViewController = mock(OperatorNameViewController.class);
mOperatorNameViewControllerFactory = mock(OperatorNameViewController.Factory.class);
- when(mOperatorNameViewControllerFactory.create(any()))
+ when(mOperatorNameViewControllerFactory.create(any(), any()))
.thenReturn(mOperatorNameViewController);
- when(mIconManagerFactory.create(any(), any())).thenReturn(mIconManager);
+ when(mIconManagerFactory.create(any(), any(), any())).thenReturn(mIconManager);
mSecureSettings = mock(SecureSettings.class);
mShadeExpansionStateManager = new ShadeExpansionStateManager();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt
index 7e7eea216584..f49e3771763a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt
@@ -16,7 +16,48 @@
package com.android.systemui.media.controls.domain.pipeline
+import android.content.applicationContext
+import android.os.Bundle
+import android.os.Handler
+import android.os.looper
+import androidx.media3.session.CommandButton
+import androidx.media3.session.MediaController
+import androidx.media3.session.SessionToken
+import com.android.systemui.Flags
+import com.android.systemui.graphics.imageLoader
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.shared.mediaLogger
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
+import com.android.systemui.media.controls.util.fakeSessionTokenFactory
+import com.google.common.collect.ImmutableList
import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
-var Kosmos.media3ActionFactory: Media3ActionFactory by Kosmos.Fixture { mock {} }
+/**
+ * Set up fake [Media3ActionFactory]. Note that tests using this fake will need to be
+ * annotated @RunWithLooper
+ */
+var Kosmos.media3ActionFactory: Media3ActionFactory by
+ Kosmos.Fixture {
+ if (Flags.mediaControlsButtonMedia3()) {
+ val customLayout = ImmutableList.of<CommandButton>()
+ val media3Controller =
+ mock<MediaController>().also {
+ whenever(it.customLayout).thenReturn(customLayout)
+ whenever(it.sessionExtras).thenReturn(Bundle())
+ }
+ fakeMediaControllerFactory.setMedia3Controller(media3Controller)
+ fakeSessionTokenFactory.setMedia3SessionToken(mock<SessionToken>())
+ }
+ Media3ActionFactory(
+ context = applicationContext,
+ imageLoader = imageLoader,
+ controllerFactory = fakeMediaControllerFactory,
+ tokenFactory = fakeSessionTokenFactory,
+ logger = mediaLogger,
+ looper = looper,
+ handler = Handler(looper),
+ bgScope = testScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
index bda3192085ed..4ed491233f3c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -29,6 +29,7 @@ import com.android.systemui.qs.footerActionsController
import com.android.systemui.qs.footerActionsViewModelFactory
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
import com.android.systemui.qs.panels.ui.viewmodel.inFirstPageViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.mediaInRowInLandscapeViewModelFactory
import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModelFactory
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.shade.transition.largeScreenShadeInterpolator
@@ -57,6 +58,7 @@ val Kosmos.qsFragmentComposeViewModelFactory by
largeScreenHeaderHelper,
tileSquishinessInteractor,
inFirstPageViewModel,
+ mediaInRowInLandscapeViewModelFactory,
qqsMediaHost,
qsMediaHost,
usingMediaInComposeFragment,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt
index 7613ea31c622..57aa20ae5f02 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt
@@ -25,7 +25,7 @@ val Kosmos.infiniteGridViewModelFactory by
override fun create(): InfiniteGridViewModel {
return InfiniteGridViewModel(
dynamicIconTilesViewModelFactory,
- qsColumnsViewModel,
+ qsColumnsViewModelFactory,
tileSquishinessViewModel,
qsResetDialogDelegateKosmos,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt
new file mode 100644
index 000000000000..d1b613fe7f6e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.content.res.Configuration
+import android.content.res.mainResources
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
+
+val Kosmos.mediaInRowInLandscapeViewModelFactory by
+ Kosmos.Fixture {
+ object : MediaInRowInLandscapeViewModel.Factory {
+ override fun create(inLocation: Int): MediaInRowInLandscapeViewModel {
+ return MediaInRowInLandscapeViewModel(
+ mainResources,
+ configurationInteractor,
+ shadeModeInteractor,
+ mediaHostStatesManager,
+ usingMediaInComposeFragment,
+ inLocation,
+ )
+ }
+ }
+ }
+
+fun Kosmos.setConfigurationForMediaInRow(mediaInRow: Boolean) {
+ shadeRepository.setShadeLayoutWide(!mediaInRow) // media in row only in non wide
+ val config =
+ Configuration(mainResources.configuration).apply {
+ orientation =
+ if (mediaInRow) {
+ Configuration.ORIENTATION_LANDSCAPE
+ } else {
+ Configuration.ORIENTATION_PORTRAIT
+ }
+ screenLayout = Configuration.SCREENLAYOUT_LONG_YES
+ }
+ mainResources.configuration.updateFrom(config)
+ fakeConfigurationRepository.onConfigurationChange(config)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index 5c8ca83ff2ae..0e5edb75846d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -23,7 +23,7 @@ val Kosmos.paginatedGridViewModel by
Kosmos.Fixture {
PaginatedGridViewModel(
iconTilesViewModel,
- qsColumnsViewModel,
+ qsColumnsViewModelFactory,
paginatedGridInteractor,
inFirstPageViewModel,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt
index 16b2f5438797..d63b1b0b4224 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt
@@ -19,4 +19,15 @@ package com.android.systemui.qs.panels.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.domain.interactor.qsColumnsInteractor
-val Kosmos.qsColumnsViewModel by Kosmos.Fixture { QSColumnsSizeViewModelImpl(qsColumnsInteractor) }
+val Kosmos.qsColumnsViewModelFactory by
+ Kosmos.Fixture {
+ object : QSColumnsViewModel.Factory {
+ override fun create(mediaLocation: Int?): QSColumnsViewModel {
+ return QSColumnsViewModel(
+ qsColumnsInteractor,
+ mediaInRowInLandscapeViewModelFactory,
+ mediaLocation,
+ )
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
index 20be5c675851..81beb20706db 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
@@ -27,8 +27,9 @@ val Kosmos.quickQuickSettingsViewModelFactory by
override fun create(): QuickQuickSettingsViewModel {
return QuickQuickSettingsViewModel(
currentTilesInteractor,
- qsColumnsViewModel,
+ qsColumnsViewModelFactory,
quickQuickSettingsRowInteractor,
+ mediaInRowInLandscapeViewModelFactory,
tileSquishinessViewModel,
iconTilesViewModel,
tileHapticsViewModelFactoryProvider,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/notes/NotesTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/notes/NotesTileKosmos.kt
new file mode 100644
index 000000000000..75c98cb07338
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/notes/NotesTileKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.notetask.NoteTaskModule
+import com.android.systemui.qs.qsEventLogger
+
+val Kosmos.qsNotesTileConfig by
+ Kosmos.Fixture { NoteTaskModule.provideNotesTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 4228c3c0b110..7e6a7271c561 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -32,7 +32,6 @@ import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testScope
@@ -78,7 +77,6 @@ val Kosmos.sceneContainerStartable by Fixture {
uiEventLogger = uiEventLogger,
sceneBackInteractor = sceneBackInteractor,
shadeSessionStorage = shadeSessionStorage,
- windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor,
keyguardEnabledInteractor = keyguardEnabledInteractor,
dismissCallbackRegistry = dismissCallbackRegistry,
statusBarStateController = sysuiStatusBarStateController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
index 718347fc3490..20e4523fda0f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory
val Kosmos.notificationsShadeOverlayContentViewModel:
@@ -30,5 +31,6 @@ val Kosmos.notificationsShadeOverlayContentViewModel:
notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory,
sceneInteractor = sceneInteractor,
shadeInteractor = shadeInteractor,
+ activeNotificationsInteractor = activeNotificationsInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
index ad2654a6b471..45aab860cde7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -29,6 +29,7 @@ import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.shade.mockNotificationShadeWindowViewController
import com.android.systemui.shade.mockShadeSurface
import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.data.repository.lightBarControllerStore
import com.android.systemui.statusbar.data.repository.privacyDotWindowControllerStore
import com.android.systemui.statusbar.data.repository.statusBarModeRepository
import com.android.systemui.statusbar.mockNotificationRemoteInputManager
@@ -79,5 +80,6 @@ val Kosmos.multiDisplayStatusBarStarter by
statusBarWindowControllerStore,
statusBarInitializerStore,
privacyDotWindowControllerStore,
+ lightBarControllerStore,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStoreKosmos.kt
new file mode 100644
index 000000000000..34a828176a08
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStoreKosmos.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.statusbar.data.repository
+
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import org.mockito.kotlin.mock
+
+val Kosmos.multiDisplayDarkIconDispatcherStore by
+ Kosmos.Fixture {
+ MultiDisplayDarkIconDispatcherStore(
+ backgroundApplicationScope = applicationCoroutineScope,
+ displayRepository = displayRepository,
+ factory = { _, _ -> mock() },
+ displayWindowPropertiesRepository = displayWindowPropertiesRepository,
+ )
+ }
+
+val Kosmos.fakeDarkIconDispatcherStore by Kosmos.Fixture { FakeDarkIconDispatcherStore() }
+
+var Kosmos.darkIconDispatcherStore by Kosmos.Fixture { fakeDarkIconDispatcherStore }
+
+val Kosmos.fakeSysUiDarkIconDispatcherStore by Kosmos.Fixture { FakeSysUiDarkIconDispatcherStore() }
+
+var Kosmos.sysUiDarkIconDispatcherStore by Kosmos.Fixture { fakeSysUiDarkIconDispatcherStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeDarkIconDispatcherStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeDarkIconDispatcherStore.kt
new file mode 100644
index 000000000000..871b277d7c82
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeDarkIconDispatcherStore.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.statusbar.data.repository
+
+import android.view.Display
+import com.android.systemui.plugins.DarkIconDispatcher
+import org.mockito.kotlin.mock
+
+class FakeDarkIconDispatcherStore : DarkIconDispatcherStore {
+
+ private val perDisplayMocks = mutableMapOf<Int, DarkIconDispatcher>()
+
+ override val defaultDisplay: DarkIconDispatcher
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): DarkIconDispatcher {
+ return perDisplayMocks.computeIfAbsent(displayId) { mock() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeLightBarControllerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeLightBarControllerStore.kt
new file mode 100644
index 000000000000..28232465e633
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeLightBarControllerStore.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.statusbar.data.repository
+
+import android.view.Display
+import com.android.systemui.statusbar.phone.LightBarController
+import org.mockito.kotlin.mock
+
+class FakeLightBarControllerStore : LightBarControllerStore {
+
+ val perDisplayMocks = mutableMapOf<Int, LightBarController>()
+
+ override val defaultDisplay: LightBarController
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): LightBarController {
+ return perDisplayMocks.computeIfAbsent(displayId) { mock() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSysUiDarkIconDispatcherStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSysUiDarkIconDispatcherStore.kt
new file mode 100644
index 000000000000..4ee323ac40b4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSysUiDarkIconDispatcherStore.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.statusbar.data.repository
+
+import android.view.Display
+import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher
+import org.mockito.kotlin.mock
+
+class FakeSysUiDarkIconDispatcherStore : SysuiDarkIconDispatcherStore {
+
+ private val perDisplayMocks = mutableMapOf<Int, SysuiDarkIconDispatcher>()
+
+ override val defaultDisplay: SysuiDarkIconDispatcher
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): SysuiDarkIconDispatcher {
+ return perDisplayMocks.computeIfAbsent(displayId) { mock() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreKosmos.kt
index 5f337326b546..13fa3feaa453 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreKosmos.kt
@@ -27,8 +27,13 @@ val Kosmos.lightBarControllerStoreImpl by
LightBarControllerStoreImpl(
backgroundApplicationScope = applicationCoroutineScope,
displayRepository = displayRepository,
- factory = { _, _, _ -> mock() },
+ factory = { _, _, _, _ -> mock() },
displayScopeRepository = displayScopeRepository,
statusBarModeRepositoryStore = statusBarModeRepository,
+ darkIconDispatcherStore = darkIconDispatcherStore,
)
}
+
+val Kosmos.fakeLightBarControllerStore by Kosmos.Fixture { FakeLightBarControllerStore() }
+
+var Kosmos.lightBarControllerStore by Kosmos.Fixture { fakeLightBarControllerStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt
index 282e2e859afe..cb092ced9c72 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt
@@ -24,7 +24,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
@SysUISingleton
class FakeDarkIconRepository @Inject constructor() : DarkIconRepository {
- override val darkState = MutableStateFlow(DarkChange.EMPTY)
+ private val perDisplayStates = mutableMapOf<Int, MutableStateFlow<DarkChange>>()
+
+ override fun darkState(displayId: Int) =
+ perDisplayStates.computeIfAbsent(displayId) { MutableStateFlow(DarkChange.EMPTY) }
}
@Module
diff --git a/packages/Vcn/framework-b/Android.bp b/packages/Vcn/framework-b/Android.bp
new file mode 100644
index 000000000000..be64bb1ae404
--- /dev/null
+++ b/packages/Vcn/framework-b/Android.bp
@@ -0,0 +1,44 @@
+//
+// 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 {
+ default_team: "trendy_team_enigma",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_defaults {
+ name: "framework-connectivity-b-defaults",
+ sdk_version: "module_current",
+ min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+ defaults: ["framework-module-defaults"], // This is a boot jar
+
+ srcs: [
+ "src/**/*.java",
+ ],
+}
+
+java_sdk_library {
+ name: "framework-connectivity-b",
+ defaults: [
+ "framework-connectivity-b-defaults",
+ ],
+
+ permitted_packages: [
+ "android.net.vcn",
+ ],
+
+ // TODO: b/375213246 Expose this library to Tethering module
+}
diff --git a/packages/Vcn/framework-b/api/current.txt b/packages/Vcn/framework-b/api/current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/module-lib-current.txt b/packages/Vcn/framework-b/api/module-lib-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/module-lib-removed.txt b/packages/Vcn/framework-b/api/module-lib-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/removed.txt b/packages/Vcn/framework-b/api/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/system-current.txt b/packages/Vcn/framework-b/api/system-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/system-removed.txt b/packages/Vcn/framework-b/api/system-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java b/packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java
new file mode 100644
index 000000000000..fb5e15386cc7
--- /dev/null
+++ b/packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+/**
+ * Placeholder class so new framework-vcn isn't empty
+ *
+ * @hide
+ */
+// This class will be removed once source code is migrated
+public class Placeholder {}
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
new file mode 100644
index 000000000000..a462297c07af
--- /dev/null
+++ b/packages/Vcn/service-b/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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 {
+ default_team: "trendy_team_enigma",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+ name: "service-connectivity-b-pre-jarjar",
+ sdk_version: "system_server_current",
+ min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+ defaults: ["framework-system-server-module-defaults"], // This is a system server jar
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ // TODO: b/375213246 Expose this library to Tethering module
+ visibility: [
+ "//frameworks/base/services",
+ ],
+}
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java b/packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java
new file mode 100644
index 000000000000..e79914531c38
--- /dev/null
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java
@@ -0,0 +1,25 @@
+/*
+ * 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.vcn;
+
+/**
+ * Placeholder class so new service-vcn isn't empty
+ *
+ * @hide
+ */
+// This class will be removed once source code is migrated
+public class Placeholder {}
diff --git a/packages/overlays/Android.bp b/packages/overlays/Android.bp
index 5075f6322d1b..44abb9ffb34f 100644
--- a/packages/overlays/Android.bp
+++ b/packages/overlays/Android.bp
@@ -21,6 +21,7 @@ package {
phony {
name: "frameworks-base-overlays",
+ product_specific: true,
required: [
"DisplayCutoutEmulationCornerOverlay",
"DisplayCutoutEmulationDoubleOverlay",
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index b3f78ab30021..4731cfbfc935 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -279,6 +279,15 @@ cc_defaults {
shared_libs: [
"liblog",
],
+ visibility: ["//visibility:private"],
+}
+
+cc_library_host_shared {
+ name: "libravenwood_initializer",
+ defaults: ["ravenwood_jni_defaults"],
+ srcs: [
+ "runtime-jni/ravenwood_initializer.cpp",
+ ],
}
// We need this as a separate library because we need to overload the
@@ -301,7 +310,6 @@ cc_library_host_shared {
"libutils",
"libcutils",
],
- visibility: ["//frameworks/base"],
}
// For collecting the *stats.csv files in a known directory under out/host/linux-x86/testcases/.
@@ -659,6 +667,7 @@ android_ravenwood_libgroup {
],
jni_libs: [
// Libraries has to be loaded in the following order
+ "libravenwood_initializer",
"libravenwood_sysprop",
"libravenwood_runtime",
"libandroid_runtime",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index d20773844df3..99fc31b258e9 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -214,7 +214,8 @@ java_genrule {
java_genrule {
name: "services.core.ravenwood",
- defaults: ["ravenwood-internal-only-visibility-genrule"],
+ // This is used by unit tests too (so tests will be able to access HSG-processed implementation)
+ // so it's visible to all.
cmd: "cp $(in) $(out)",
srcs: [
":services.core.ravenwood-base{ravenwood.jar}",
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index a1243e37003e..607592b4cbbe 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -117,7 +117,11 @@
"host": true
},
{
- "name": "FrameworksServicesTestsRavenwood",
+ "name": "FrameworksServicesTestsRavenwood_Compat",
+ "host": true
+ },
+ {
+ "name": "FrameworksServicesTestsRavenwood_Uri",
"host": true
},
{
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
index d29b93c0c171..a208d6dce2ce 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
@@ -40,7 +40,7 @@ public final class RavenwoodNativeLoader {
* See frameworks/base/core/jni/platform/host/HostRuntime.cpp
*/
private static final Class<?>[] sLibandroidClasses = {
- android.util.Log.class,
+// android.util.Log.class, // Not using native log: b/377377826
android.os.Parcel.class,
android.os.Binder.class,
android.os.SystemProperties.class,
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 28c262d53ff1..e61a054c4c39 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -31,9 +31,12 @@ import static org.mockito.Mockito.mock;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.AppCompatCallbacks;
import android.app.Instrumentation;
import android.app.ResourcesManager;
import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Build;
@@ -42,6 +45,7 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process_ravenwood;
import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemProperties;
import android.provider.DeviceConfig_host;
import android.system.ErrnoException;
@@ -51,6 +55,7 @@ import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.hoststubgen.hosthelper.HostTestUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.RuntimeInit;
import com.android.ravenwood.RavenwoodRuntimeNative;
import com.android.ravenwood.RavenwoodRuntimeState;
@@ -58,6 +63,7 @@ import com.android.ravenwood.common.RavenwoodCommonUtils;
import com.android.ravenwood.common.RavenwoodRuntimeException;
import com.android.ravenwood.common.SneakyThrow;
import com.android.server.LocalServices;
+import com.android.server.compat.PlatformCompat;
import org.junit.runner.Description;
@@ -86,6 +92,7 @@ public class RavenwoodRuntimeEnvironmentController {
}
private static final String MAIN_THREAD_NAME = "RavenwoodMain";
+ private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop";
private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
private static final String RAVENWOOD_BUILD_PROP =
@@ -139,23 +146,61 @@ public class RavenwoodRuntimeEnvironmentController {
return res;
}
+ private static final Object sInitializationLock = new Object();
+
+ @GuardedBy("sInitializationLock")
+ private static boolean sInitialized = false;
+
+ @GuardedBy("sInitializationLock")
+ private static Throwable sExceptionFromGlobalInit;
+
private static RavenwoodAwareTestRunner sRunner;
private static RavenwoodSystemProperties sProps;
- private static boolean sInitialized = false;
/**
* Initialize the global environment.
*/
public static void globalInitOnce() {
- if (sInitialized) {
- return;
+ synchronized (sInitializationLock) {
+ if (!sInitialized) {
+ // globalInitOnce() is called from class initializer, which cause
+ // this method to be called recursively,
+ sInitialized = true;
+
+ // This is the first call.
+ try {
+ globalInitInner();
+ } catch (Throwable th) {
+ Log.e(TAG, "globalInit() failed", th);
+
+ sExceptionFromGlobalInit = th;
+ throw th;
+ }
+ } else {
+ // Subsequent calls. If the first call threw, just throw the same error, to prevent
+ // the test from running.
+ if (sExceptionFromGlobalInit != null) {
+ Log.e(TAG, "globalInit() failed re-throwing the same exception",
+ sExceptionFromGlobalInit);
+
+ SneakyThrow.sneakyThrow(sExceptionFromGlobalInit);
+ }
+ }
+ }
+ }
+
+ private static void globalInitInner() {
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "globalInit() called here...", new RuntimeException("NOT A CRASH"));
}
- sInitialized = true;
+
+ // Some process-wide initialization. (maybe redirect stdout/stderr)
+ RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME);
// We haven't initialized liblog yet, so directly write to System.out here.
- RavenwoodCommonUtils.log(TAG, "globalInit()");
+ RavenwoodCommonUtils.log(TAG, "globalInitInner()");
- // Load libravenwood_sysprop first
+ // Load libravenwood_sysprop before other libraries that may use SystemProperties.
var libProp = RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_SYSPROP_NAME);
System.load(libProp);
RavenwoodRuntimeNative.reloadNativeLibrary(libProp);
@@ -294,6 +339,8 @@ public class RavenwoodRuntimeEnvironmentController {
RavenwoodSystemServer.init(config);
+ initializeCompatIds(config);
+
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout = sTimeoutExecutor.schedule(
RavenwoodRuntimeEnvironmentController::dumpStacks,
@@ -309,6 +356,31 @@ public class RavenwoodRuntimeEnvironmentController {
Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
}
+ private static void initializeCompatIds(RavenwoodConfig config) {
+ // Set up compat-IDs for the app side.
+ // TODO: Inside the system server, all the compat-IDs should be enabled,
+ // Due to the `AppCompatCallbacks.install(new long[0], new long[0])` call in
+ // SystemServer.
+
+ // Compat framework only uses the package name and the target SDK level.
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = config.mTargetPackageName;
+ appInfo.targetSdkVersion = config.mTargetSdkLevel;
+
+ PlatformCompat platformCompat = null;
+ try {
+ platformCompat = (PlatformCompat) ServiceManager.getServiceOrThrow(
+ Context.PLATFORM_COMPAT_SERVICE);
+ } catch (ServiceNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ var disabledChanges = platformCompat.getDisabledChanges(appInfo);
+ var loggableChanges = platformCompat.getLoggableChanges(appInfo);
+
+ AppCompatCallbacks.install(disabledChanges, loggableChanges);
+ }
+
/**
* De-initialize.
*/
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index f198a08a50e3..438a2bfa7a14 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -17,7 +17,9 @@
package android.platform.test.ravenwood;
import android.content.ClipboardManager;
+import android.content.Context;
import android.hardware.SerialManager;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.ravenwood.example.BlueManager;
import android.ravenwood.example.RedManager;
@@ -27,6 +29,8 @@ import android.util.ArraySet;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
+import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.PlatformCompatNative;
import com.android.server.utils.TimingsTraceAndSlog;
import java.util.List;
@@ -65,6 +69,14 @@ public class RavenwoodSystemServer {
private static SystemServiceManager sServiceManager;
public static void init(RavenwoodConfig config) {
+ // Always start PlatformCompat, regardless of the requested services.
+ // PlatformCompat is not really a SystemService, so it won't receive boot phases / etc.
+ // This initialization code is copied from SystemServer.java.
+ PlatformCompat platformCompat = new PlatformCompat(config.mState.mSystemServerContext);
+ ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
+ ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
+ new PlatformCompatNative(platformCompat));
+
// Avoid overhead if no services required
if (config.mServicesRequired.isEmpty()) return;
diff --git a/ravenwood/runtime-jni/ravenwood_initializer.cpp b/ravenwood/runtime-jni/ravenwood_initializer.cpp
new file mode 100644
index 000000000000..89fb7c3c3510
--- /dev/null
+++ b/ravenwood/runtime-jni/ravenwood_initializer.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+ /*
+ * This file is compiled into a single SO file, which we load at the very first.
+ * We can do process-wide initialization here.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "jni_helper.h"
+
+static void maybeRedirectLog() {
+ auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
+ if (ravenwoodLogOut == NULL) {
+ return;
+ }
+ ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
+
+ // Redirect stdin / stdout to /dev/tty.
+ int ttyFd = open(ravenwoodLogOut, O_WRONLY | O_APPEND);
+ if (ttyFd == -1) {
+ ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
+ strerror(errno));
+ return;
+ }
+ dup2(ttyFd, 1);
+ dup2(ttyFd, 2);
+}
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+ ALOGI("%s: JNI_OnLoad", __FILE__);
+
+ maybeRedirectLog();
+ return JNI_VERSION_1_4;
+}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index 5b75e9854758..c1993f691686 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -180,24 +180,6 @@ static jint Linux_gettid(JNIEnv* env, jobject) {
return syscall(__NR_gettid);
}
-static void maybeRedirectLog() {
- auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
- if (ravenwoodLogOut == NULL) {
- return;
- }
- ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
-
- // Redirect stdin / stdout to /dev/tty.
- int ttyFd = open(ravenwoodLogOut, O_WRONLY);
- if (ttyFd == -1) {
- ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
- strerror(errno));
- return;
- }
- dup2(ttyFd, 1);
- dup2(ttyFd, 2);
-}
-
// ---- Registration ----
extern void register_android_system_OsConstants(JNIEnv* env);
@@ -218,8 +200,6 @@ static const JNINativeMethod sMethods[] =
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- maybeRedirectLog();
-
ALOGI("%s: JNI_OnLoad", __FILE__);
JNIEnv* env = GetJNIEnvOrDie(vm);
diff --git a/ravenwood/scripts/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh
index 672c685aa6d7..1910100a7f5d 100755
--- a/ravenwood/scripts/run-ravenwood-tests.sh
+++ b/ravenwood/scripts/run-ravenwood-tests.sh
@@ -26,7 +26,7 @@
# Regex to identify slow tests, in PCRE
-SLOW_TEST_RE='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood)$'
+SLOW_TEST_RE='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood|CarSystemUIRavenTests)$'
smoke=0
include_re=""
@@ -67,7 +67,7 @@ filter() {
if [[ "$re" == "" ]] ; then
cat # No filtering
else
- grep $grep_arg -P "$re"
+ grep $grep_arg -iP "$re"
fi
}
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index 4895a1a6d1a2..40e6672a3c63 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -58,6 +58,9 @@ java_defaults {
java_defaults {
name: "ravenwood-bivalent-device-defaults",
defaults: ["ravenwood-bivalent-defaults"],
+
+ target_sdk_version: "34", // For compat-framework tests
+
// TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture
exclude_srcs: [
"test/**/ravenizer/*.java",
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
new file mode 100644
index 000000000000..a95760db1a61
--- /dev/null
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.ravenwoodtest.bivalenttest.compat
+
+import android.app.compat.CompatChanges
+import android.os.Build
+import android.platform.test.ravenwood.RavenwoodConfig
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.ravenwood.RavenwoodEnvironment.CompatIdsForTest
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RavenwoodCompatFrameworkTest {
+ companion object {
+ @JvmField // Expose as a raw field, not as a property.
+ @RavenwoodConfig.Config
+ val config = RavenwoodConfig.Builder()
+ .setTargetSdkLevel(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ .build()
+ }
+
+ @Test
+ fun testEnabled() {
+ Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_1))
+ }
+
+ @Test
+ fun testDisabled() {
+ Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_2))
+ }
+
+ @Test
+ fun testEnabledAfterSForUApps() {
+ Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_3))
+ }
+
+ @Test
+ fun testEnabledAfterUForUApps() {
+ Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_4))
+ }
+} \ No newline at end of file
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 70c1d781587f..82be2c0db24e 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -365,3 +365,9 @@ com.android.server.utils.TimingsTraceAndSlog
android.os.IpcDataCache
android.app.PropertyInvalidatedCache
+
+android.app.compat.*
+com.android.server.compat.*
+com.android.internal.compat.*
+android.app.AppCompatCallbacks
+
diff --git a/ravenwood/texts/ravenwood-services-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt
index cc2fa602b3c3..530e5c8f5986 100644
--- a/ravenwood/texts/ravenwood-services-policies.txt
+++ b/ravenwood/texts/ravenwood-services-policies.txt
@@ -1 +1,12 @@
# Ravenwood "policy" file for services.core.
+
+# Auto-generated from XSD
+class com.android.server.compat.config.Change keepclass
+class com.android.server.compat.config.Config keepclass
+class com.android.server.compat.config.XmlParser keepclass
+class com.android.server.compat.overrides.ChangeOverrides keepclass
+class com.android.server.compat.overrides.OverrideValue keepclass
+class com.android.server.compat.overrides.Overrides keepclass
+class com.android.server.compat.overrides.RawOverrideValue keepclass
+class com.android.server.compat.overrides.XmlParser keepclass
+class com.android.server.compat.overrides.XmlWriter keepclass \ No newline at end of file
diff --git a/services/Android.bp b/services/Android.bp
index 899e224c6fd7..fc0bb33e6e4e 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -282,6 +282,7 @@ system_java_library {
"services.wifi",
"service-blobstore",
"service-jobscheduler",
+ "service-connectivity-b-pre-jarjar", // Move it to mainline module
"android.hidl.base-V1.0-java",
],
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 51034d24df14..7cba9e0ccca8 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -31,16 +31,13 @@ import static android.os.UserHandle.getCallingUserId;
import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.Preconditions.checkState;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
-import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.MINUTES;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
@@ -69,31 +66,22 @@ import android.companion.datatransfer.PermissionSyncRequest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.net.MacAddress;
-import android.net.NetworkPolicyManager;
import android.os.Binder;
-import android.os.Environment;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PowerExemptionManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.permission.flags.Flags;
-import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.Slog;
-import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
@@ -114,35 +102,25 @@ import com.android.server.companion.devicepresence.DevicePresenceProcessor;
import com.android.server.companion.devicepresence.ObservableUuid;
import com.android.server.companion.devicepresence.ObservableUuidStore;
import com.android.server.companion.transport.CompanionTransportManager;
-import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
-import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.List;
-import java.util.Set;
@SuppressLint("LongLogTag")
public class CompanionDeviceManagerService extends SystemService {
private static final String TAG = "CDM_CompanionDeviceManagerService";
private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min
-
- private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
- private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
private static final int MAX_CN_LENGTH = 500;
- private final ActivityTaskManagerInternal mAtmInternal;
- private final ActivityManagerInternal mAmInternal;
- private final IAppOpsService mAppOpsManager;
- private final PowerExemptionManager mPowerExemptionManager;
- private final PackageManagerInternal mPackageManagerInternal;
-
private final AssociationStore mAssociationStore;
private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
private final ObservableUuidStore mObservableUuidStore;
+
+ private final CompanionExemptionProcessor mCompanionExemptionProcessor;
private final AssociationRequestsProcessor mAssociationRequestsProcessor;
private final SystemDataTransferProcessor mSystemDataTransferProcessor;
private final BackupRestoreProcessor mBackupRestoreProcessor;
@@ -156,12 +134,15 @@ public class CompanionDeviceManagerService extends SystemService {
super(context);
final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
- mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
- mAppOpsManager = IAppOpsService.Stub.asInterface(
- ServiceManager.getService(Context.APP_OPS_SERVICE));
- mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
- mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
- mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ final PowerExemptionManager powerExemptionManager = context.getSystemService(
+ PowerExemptionManager.class);
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ final ActivityTaskManagerInternal atmInternal = LocalServices.getService(
+ ActivityTaskManagerInternal.class);
+ final ActivityManagerInternal amInternal = LocalServices.getService(
+ ActivityManagerInternal.class);
+ final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
final UserManager userManager = context.getSystemService(UserManager.class);
final PowerManagerInternal powerManagerInternal = LocalServices.getService(
PowerManagerInternal.class);
@@ -173,25 +154,29 @@ public class CompanionDeviceManagerService extends SystemService {
// Init processors
mAssociationRequestsProcessor = new AssociationRequestsProcessor(context,
- mPackageManagerInternal, mAssociationStore);
- mBackupRestoreProcessor = new BackupRestoreProcessor(context, mPackageManagerInternal,
+ packageManagerInternal, mAssociationStore);
+ mBackupRestoreProcessor = new BackupRestoreProcessor(context, packageManagerInternal,
mAssociationStore, associationDiskStore, mSystemDataTransferRequestStore,
mAssociationRequestsProcessor);
mCompanionAppBinder = new CompanionAppBinder(context);
+ mCompanionExemptionProcessor = new CompanionExemptionProcessor(context,
+ powerExemptionManager, appOpsManager, packageManagerInternal, atmInternal,
+ amInternal, mAssociationStore);
+
mDevicePresenceProcessor = new DevicePresenceProcessor(context,
mCompanionAppBinder, userManager, mAssociationStore, mObservableUuidStore,
- powerManagerInternal);
+ powerManagerInternal, mCompanionExemptionProcessor);
mTransportManager = new CompanionTransportManager(context, mAssociationStore);
mDisassociationProcessor = new DisassociationProcessor(context, activityManager,
- mAssociationStore, mPackageManagerInternal, mDevicePresenceProcessor,
+ mAssociationStore, packageManagerInternal, mDevicePresenceProcessor,
mCompanionAppBinder, mSystemDataTransferRequestStore, mTransportManager);
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this,
- mPackageManagerInternal, mAssociationStore,
+ packageManagerInternal, mAssociationStore,
mSystemDataTransferRequestStore, mTransportManager);
// TODO(b/279663946): move context sync to a dedicated system service
@@ -202,7 +187,6 @@ public class CompanionDeviceManagerService extends SystemService {
public void onStart() {
// Init association stores
mAssociationStore.refreshCache();
- mAssociationStore.registerLocalListener(mAssociationStoreChangeListener);
// Init UUID store
mObservableUuidStore.getObservableUuidsForUser(getContext().getUserId());
@@ -240,11 +224,8 @@ public class CompanionDeviceManagerService extends SystemService {
if (associations.isEmpty()) return;
- updateAtm(userId, associations);
-
- BackgroundThread.getHandler().sendMessageDelayed(
- obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this),
- MINUTES.toMillis(10));
+ mCompanionExemptionProcessor.updateAtm(userId, associations);
+ mCompanionExemptionProcessor.updateAutoRevokeExemptions();
}
@Override
@@ -262,9 +243,12 @@ public class CompanionDeviceManagerService extends SystemService {
if (!associationsForPackage.isEmpty()) {
Slog.i(TAG, "Package removed or data cleared for user=[" + userId + "], package=["
+ packageName + "]. Cleaning up CDM data...");
- }
- for (AssociationInfo association : associationsForPackage) {
- mDisassociationProcessor.disassociate(association.getId());
+
+ for (AssociationInfo association : associationsForPackage) {
+ mDisassociationProcessor.disassociate(association.getId());
+ }
+
+ mCompanionAppBinder.onPackageChanged(userId);
}
// Clear observable UUIDs for the package.
@@ -273,19 +257,16 @@ public class CompanionDeviceManagerService extends SystemService {
for (ObservableUuid uuid : uuidsTobeObserved) {
mObservableUuidStore.removeObservableUuid(userId, uuid.getUuid(), packageName);
}
-
- mCompanionAppBinder.onPackagesChanged(userId);
}
private void onPackageModifiedInternal(@UserIdInt int userId, @NonNull String packageName) {
- final List<AssociationInfo> associationsForPackage =
+ final List<AssociationInfo> associations =
mAssociationStore.getAssociationsByPackage(userId, packageName);
- for (AssociationInfo association : associationsForPackage) {
- updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
- association.getPackageName());
- }
+ if (!associations.isEmpty()) {
+ mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);
- mCompanionAppBinder.onPackagesChanged(userId);
+ mCompanionAppBinder.onPackageChanged(userId);
+ }
}
private void onPackageAddedInternal(@UserIdInt int userId, @NonNull String packageName) {
@@ -765,130 +746,6 @@ public class CompanionDeviceManagerService extends SystemService {
}
}
- /**
- * Update special access for the association's package
- */
- public void updateSpecialAccessPermissionForAssociatedPackage(int userId, String packageName) {
- final PackageInfo packageInfo =
- getPackageInfo(getContext(), userId, packageName);
-
- Binder.withCleanCallingIdentity(() -> updateSpecialAccessPermissionAsSystem(packageInfo));
- }
-
- private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) {
- if (packageInfo == null) {
- return;
- }
-
- if (containsEither(packageInfo.requestedPermissions,
- android.Manifest.permission.RUN_IN_BACKGROUND,
- android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
- mPowerExemptionManager.addToPermanentAllowList(packageInfo.packageName);
- } else {
- try {
- mPowerExemptionManager.removeFromPermanentAllowList(packageInfo.packageName);
- } catch (UnsupportedOperationException e) {
- Slog.w(TAG, packageInfo.packageName + " can't be removed from power save"
- + " whitelist. It might due to the package is whitelisted by the system.");
- }
- }
-
- NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
- try {
- if (containsEither(packageInfo.requestedPermissions,
- android.Manifest.permission.USE_DATA_IN_BACKGROUND,
- android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) {
- networkPolicyManager.addUidPolicy(
- packageInfo.applicationInfo.uid,
- NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
- } else {
- networkPolicyManager.removeUidPolicy(
- packageInfo.applicationInfo.uid,
- NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
- }
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, e.getMessage());
- }
-
- exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
- }
-
- private void exemptFromAutoRevoke(String packageName, int uid) {
- try {
- mAppOpsManager.setMode(
- AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
- uid,
- packageName,
- AppOpsManager.MODE_IGNORED);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error while granting auto revoke exemption for " + packageName, e);
- }
- }
-
- private void updateAtm(int userId, List<AssociationInfo> associations) {
- final Set<Integer> companionAppUids = new ArraySet<>();
- for (AssociationInfo association : associations) {
- final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
- 0, userId);
- if (uid >= 0) {
- companionAppUids.add(uid);
- }
- }
- if (mAtmInternal != null) {
- mAtmInternal.setCompanionAppUids(userId, companionAppUids);
- }
- if (mAmInternal != null) {
- // Make a copy of the set and send it to ActivityManager.
- mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
- }
- }
-
- private void maybeGrantAutoRevokeExemptions() {
- Slog.d(TAG, "maybeGrantAutoRevokeExemptions()");
-
- PackageManager pm = getContext().getPackageManager();
- for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
- SharedPreferences pref = getContext().getSharedPreferences(
- new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
- Context.MODE_PRIVATE);
- if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
- continue;
- }
-
- try {
- final List<AssociationInfo> associations =
- mAssociationStore.getActiveAssociationsByUser(userId);
- for (AssociationInfo a : associations) {
- try {
- int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
- exemptFromAutoRevoke(a.getPackageName(), uid);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e);
- }
- }
- } finally {
- pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
- }
- }
- }
-
- private final AssociationStore.OnChangeListener mAssociationStoreChangeListener =
- new AssociationStore.OnChangeListener() {
- @Override
- public void onAssociationChanged(int changeType, AssociationInfo association) {
- Slog.d(TAG, "onAssociationChanged changeType=[" + changeType
- + "], association=[" + association);
-
- final int userId = association.getUserId();
- final List<AssociationInfo> updatedAssociations =
- mAssociationStore.getActiveAssociationsByUser(userId);
-
- updateAtm(userId, updatedAssociations);
- updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
- association.getPackageName());
- }
- };
-
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
public void onPackageRemoved(String packageName, int uid) {
@@ -911,10 +768,6 @@ public class CompanionDeviceManagerService extends SystemService {
}
};
- private static <T> boolean containsEither(T[] array, T a, T b) {
- return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
- }
-
private class LocalService implements CompanionDeviceManagerServiceInternal {
@Override
diff --git a/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java b/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java
new file mode 100644
index 000000000000..ea2bc17dafcd
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java
@@ -0,0 +1,225 @@
+/*
+ * 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.companion;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+
+import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.companion.AssociationInfo;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.NetworkPolicyManager;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.PowerExemptionManager;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
+import com.android.server.companion.association.AssociationStore;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressLint("LongLogTag")
+public class CompanionExemptionProcessor {
+
+ private static final String TAG = "CDM_CompanionExemptionProcessor";
+
+ private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
+ private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
+
+ private final Context mContext;
+ private final PowerExemptionManager mPowerExemptionManager;
+ private final AppOpsManager mAppOpsManager;
+ private final PackageManagerInternal mPackageManager;
+ private final ActivityTaskManagerInternal mAtmInternal;
+ private final ActivityManagerInternal mAmInternal;
+ private final AssociationStore mAssociationStore;
+
+ public CompanionExemptionProcessor(Context context, PowerExemptionManager powerExemptionManager,
+ AppOpsManager appOpsManager, PackageManagerInternal packageManager,
+ ActivityTaskManagerInternal atmInternal, ActivityManagerInternal amInternal,
+ AssociationStore associationStore) {
+ mContext = context;
+ mPowerExemptionManager = powerExemptionManager;
+ mAppOpsManager = appOpsManager;
+ mPackageManager = packageManager;
+ mAtmInternal = atmInternal;
+ mAmInternal = amInternal;
+ mAssociationStore = associationStore;
+
+ mAssociationStore.registerLocalListener(new AssociationStore.OnChangeListener() {
+ @Override
+ public void onAssociationChanged(int changeType, AssociationInfo association) {
+ final int userId = association.getUserId();
+ final List<AssociationInfo> updatedAssociations =
+ mAssociationStore.getActiveAssociationsByUser(userId);
+
+ updateAtm(userId, updatedAssociations);
+ }
+ });
+ }
+
+ /**
+ * Update ActivityManager and ActivityTaskManager exemptions
+ */
+ public void updateAtm(int userId, List<AssociationInfo> associations) {
+ final Set<Integer> companionAppUids = new ArraySet<>();
+ for (AssociationInfo association : associations) {
+ int uid = mPackageManager.getPackageUid(association.getPackageName(), 0, userId);
+ if (uid >= 0) {
+ companionAppUids.add(uid);
+ }
+ }
+ if (mAtmInternal != null) {
+ mAtmInternal.setCompanionAppUids(userId, companionAppUids);
+ }
+ if (mAmInternal != null) {
+ // Make a copy of the set and send it to ActivityManager.
+ mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
+ }
+ }
+
+ /**
+ * Update special access for the association's package
+ */
+ public void exemptPackage(int userId, String packageName, boolean hasPresentDevices) {
+ Slog.i(TAG, "Exempting package [" + packageName + "]...");
+
+ final PackageInfo packageInfo = getPackageInfo(mContext, userId, packageName);
+
+ Binder.withCleanCallingIdentity(
+ () -> exemptPackageAsSystem(userId, packageInfo, hasPresentDevices));
+ }
+
+ @SuppressLint("MissingPermission")
+ private void exemptPackageAsSystem(int userId, PackageInfo packageInfo,
+ boolean hasPresentDevices) {
+ if (packageInfo == null) {
+ return;
+ }
+
+ // If the app has run-in-bg permission and present devices, add it to power saver allowlist.
+ if (containsEither(packageInfo.requestedPermissions,
+ android.Manifest.permission.RUN_IN_BACKGROUND,
+ android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)
+ && hasPresentDevices) {
+ mPowerExemptionManager.addToPermanentAllowList(packageInfo.packageName);
+ } else {
+ try {
+ mPowerExemptionManager.removeFromPermanentAllowList(packageInfo.packageName);
+ } catch (UnsupportedOperationException e) {
+ Slog.w(TAG, packageInfo.packageName + " can't be removed from power save"
+ + " allowlist. It might be due to the package being allowlisted by the"
+ + " system.");
+ }
+ }
+
+ // If the app has run-in-bg permission and present device, allow metered network use.
+ NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(mContext);
+ try {
+ if (containsEither(packageInfo.requestedPermissions,
+ android.Manifest.permission.USE_DATA_IN_BACKGROUND,
+ android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)
+ && hasPresentDevices) {
+ networkPolicyManager.addUidPolicy(
+ packageInfo.applicationInfo.uid,
+ NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+ } else {
+ networkPolicyManager.removeUidPolicy(
+ packageInfo.applicationInfo.uid,
+ NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, e.getMessage());
+ }
+
+ updateAutoRevokeExemption(packageInfo.packageName, packageInfo.applicationInfo.uid,
+ !mAssociationStore.getActiveAssociationsByPackage(userId,
+ packageInfo.packageName).isEmpty());
+ }
+
+ /**
+ * Update auto revoke exemptions.
+ * If the app has any association, exempt it from permission auto revoke.
+ */
+ public void updateAutoRevokeExemptions() {
+ Slog.d(TAG, "maybeGrantAutoRevokeExemptions()");
+
+ PackageManager pm = mContext.getPackageManager();
+ for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
+ SharedPreferences pref = mContext.getSharedPreferences(
+ new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
+ Context.MODE_PRIVATE);
+ if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
+ continue;
+ }
+
+ try {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getActiveAssociationsByUser(userId);
+ Set<Pair<String, Integer>> exemptedPackages = new HashSet<>();
+ for (AssociationInfo a : associations) {
+ try {
+ int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
+ exemptedPackages.add(new Pair<>(a.getPackageName(), uid));
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e);
+ }
+ }
+ for (Pair<String, Integer> exemptedPackage : exemptedPackages) {
+ updateAutoRevokeExemption(exemptedPackage.first, exemptedPackage.second, true);
+ }
+ } finally {
+ pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
+ }
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ private void updateAutoRevokeExemption(String packageName, int uid, boolean hasAssociations) {
+ try {
+ mAppOpsManager.setMode(
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ uid,
+ packageName,
+ hasAssociations ? MODE_IGNORED : MODE_ALLOWED);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error while granting auto revoke exemption for " + packageName, e);
+ }
+ }
+
+ private <T> boolean containsEither(T[] array, T a, T b) {
+ return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
+ }
+
+}
diff --git a/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java b/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
index 60f46887fa5c..8307da5f7218 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
@@ -95,7 +95,9 @@ public class CompanionAppBinder {
/**
* On package changed.
*/
- public void onPackagesChanged(@UserIdInt int userId) {
+ public void onPackageChanged(@UserIdInt int userId) {
+ // Note: To invalidate the user space for simplicity. We could alternatively manage each
+ // package, but that would easily cause errors if one case is mis-handled.
mCompanionServicesRegister.invalidate(userId);
}
@@ -299,12 +301,14 @@ public class CompanionAppBinder {
private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> {
@Override
- public synchronized @NonNull Map<String, List<ComponentName>> forUser(
+ @NonNull
+ public synchronized Map<String, List<ComponentName>> forUser(
@UserIdInt int userId) {
return super.forUser(userId);
}
- synchronized @NonNull List<ComponentName> forPackage(
+ @NonNull
+ synchronized List<ComponentName> forPackage(
@UserIdInt int userId, @NonNull String packageName) {
return forUser(userId).getOrDefault(packageName, Collections.emptyList());
}
@@ -314,7 +318,8 @@ public class CompanionAppBinder {
}
@Override
- protected final @NonNull Map<String, List<ComponentName>> create(@UserIdInt int userId) {
+ @NonNull
+ protected final Map<String, List<ComponentName>> create(@UserIdInt int userId) {
return PackageUtils.getCompanionServicesForUser(mContext, userId);
}
}
diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
index a374d279af0b..7b4dd7df8be3 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
@@ -57,6 +57,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.CollectionUtils;
+import com.android.server.companion.CompanionExemptionProcessor;
import com.android.server.companion.association.AssociationStore;
import java.io.PrintWriter;
@@ -101,6 +102,8 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
private final PowerManagerInternal mPowerManagerInternal;
@NonNull
private final UserManager mUserManager;
+ @NonNull
+ private final CompanionExemptionProcessor mCompanionExemptionProcessor;
// NOTE: Same association may appear in more than one of the following sets at the same time.
// (E.g. self-managed devices that have MAC addresses, could be reported as present by their
@@ -111,7 +114,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
@NonNull
private final Set<Integer> mNearbyBleDevices = new HashSet<>();
@NonNull
- private final Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
+ private final Set<Integer> mConnectedSelfManagedDevices = new HashSet<>();
@NonNull
private final Set<ParcelUuid> mConnectedUuidDevices = new HashSet<>();
@NonNull
@@ -146,7 +149,8 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
@NonNull UserManager userManager,
@NonNull AssociationStore associationStore,
@NonNull ObservableUuidStore observableUuidStore,
- @NonNull PowerManagerInternal powerManagerInternal) {
+ @NonNull PowerManagerInternal powerManagerInternal,
+ @NonNull CompanionExemptionProcessor companionExemptionProcessor) {
mContext = context;
mCompanionAppBinder = companionAppBinder;
mAssociationStore = associationStore;
@@ -156,6 +160,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
mObservableUuidStore, this);
mBleDeviceProcessor = new BleDeviceProcessor(associationStore, this);
mPowerManagerInternal = powerManagerInternal;
+ mCompanionExemptionProcessor = companionExemptionProcessor;
}
/** Initialize {@link DevicePresenceProcessor} */
@@ -404,7 +409,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* nearby (for "self-managed" associations).
*/
public boolean isDevicePresent(int associationId) {
- return mReportedSelfManagedDevices.contains(associationId)
+ return mConnectedSelfManagedDevices.contains(associationId)
|| mConnectedBtDevices.contains(associationId)
|| mNearbyBleDevices.contains(associationId)
|| mSimulated.contains(associationId);
@@ -451,7 +456,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* notifyDeviceAppeared()}
*/
public void onSelfManagedDeviceConnected(int associationId) {
- onDevicePresenceEvent(mReportedSelfManagedDevices,
+ onDevicePresenceEvent(mConnectedSelfManagedDevices,
associationId, EVENT_SELF_MANAGED_APPEARED);
}
@@ -467,7 +472,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* notifyDeviceDisappeared()}
*/
public void onSelfManagedDeviceDisconnected(int associationId) {
- onDevicePresenceEvent(mReportedSelfManagedDevices,
+ onDevicePresenceEvent(mConnectedSelfManagedDevices,
associationId, EVENT_SELF_MANAGED_DISAPPEARED);
}
@@ -475,7 +480,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* Marks a "self-managed" device as disconnected when binderDied.
*/
public void onSelfManagedDeviceReporterBinderDied(int associationId) {
- onDevicePresenceEvent(mReportedSelfManagedDevices,
+ onDevicePresenceEvent(mConnectedSelfManagedDevices,
associationId, EVENT_SELF_MANAGED_DISAPPEARED);
}
@@ -683,6 +688,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
if (association.shouldBindWhenPresent()) {
bindApplicationIfNeeded(userId, packageName, association.isSelfManaged());
+ mCompanionExemptionProcessor.exemptPackage(userId, packageName, true);
} else {
return;
}
@@ -715,6 +721,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
// Check if there are other devices associated to the app that are present.
if (!shouldBindPackage(userId, packageName)) {
mCompanionAppBinder.unbindCompanionApp(userId, packageName);
+ mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);
}
break;
default:
@@ -940,7 +947,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
mConnectedBtDevices.remove(id);
mNearbyBleDevices.remove(id);
- mReportedSelfManagedDevices.remove(id);
+ mConnectedSelfManagedDevices.remove(id);
mSimulated.remove(id);
synchronized (mBtDisconnectedDevices) {
mBtDisconnectedDevices.remove(id);
@@ -1100,7 +1107,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
out.append("Companion Device Present: ");
if (mConnectedBtDevices.isEmpty()
&& mNearbyBleDevices.isEmpty()
- && mReportedSelfManagedDevices.isEmpty()) {
+ && mConnectedSelfManagedDevices.isEmpty()) {
out.append("<empty>\n");
return;
} else {
@@ -1130,11 +1137,11 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
}
out.append(" Self-Reported Devices: ");
- if (mReportedSelfManagedDevices.isEmpty()) {
+ if (mConnectedSelfManagedDevices.isEmpty()) {
out.append("<empty>\n");
} else {
out.append("\n");
- for (int associationId : mReportedSelfManagedDevices) {
+ for (int associationId : mConnectedSelfManagedDevices) {
AssociationInfo a = mAssociationStore.getAssociationById(associationId);
out.append(" ").append(a.toShortString()).append('\n');
}
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index ea6351baf597..fd18fa856916 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -24,7 +24,8 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
-import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -261,24 +262,28 @@ public class ContextualSearchManagerService extends SystemService {
}
}
- private Intent getResolvedLaunchIntent() {
+ private Intent getResolvedLaunchIntent(int userId) {
synchronized (this) {
+ if(DEBUG_USER) Log.d(TAG, "Attempting to getResolvedLaunchIntent");
// If mTemporaryPackage is not null, use it to get the ContextualSearch intent.
String csPkgName = getContextualSearchPackageName();
if (csPkgName.isEmpty()) {
// Return null if csPackageName is not specified.
+ if (DEBUG_USER) Log.w(TAG, "getContextualSearchPackageName is empty");
return null;
}
Intent launchIntent = new Intent(
ContextualSearchManager.ACTION_LAUNCH_CONTEXTUAL_SEARCH);
launchIntent.setPackage(csPkgName);
- ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
- launchIntent, MATCH_FACTORY_ONLY);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(
+ launchIntent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
if (resolveInfo == null) {
+ if (DEBUG_USER) Log.w(TAG, "resolveInfo is null");
return null;
}
ComponentName componentName = resolveInfo.getComponentInfo().getComponentName();
if (componentName == null) {
+ if (DEBUG_USER) Log.w(TAG, "componentName is null");
return null;
}
launchIntent.setComponent(componentName);
@@ -286,9 +291,10 @@ public class ContextualSearchManagerService extends SystemService {
}
}
- private Intent getContextualSearchIntent(int entrypoint, CallbackToken mToken) {
- final Intent launchIntent = getResolvedLaunchIntent();
+ private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
+ final Intent launchIntent = getResolvedLaunchIntent(userId);
if (launchIntent == null) {
+ if (DEBUG_USER) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
return null;
}
@@ -341,6 +347,7 @@ public class ContextualSearchManagerService extends SystemService {
TYPE_NAVIGATION_BAR_PANEL,
TYPE_POINTER));
} else {
+ if (DEBUG_USER) Log.w(TAG, "Can't capture contextual screenshot: mWmInternal is null");
shb = null;
}
final Bitmap bm = shb != null ? shb.asBitmap() : null;
@@ -444,7 +451,7 @@ public class ContextualSearchManagerService extends SystemService {
@Override
public void startContextualSearch(int entrypoint) {
synchronized (this) {
- if (DEBUG_USER) Log.d(TAG, "startContextualSearch");
+ if (DEBUG_USER) Log.d(TAG, "startContextualSearch entrypoint: " + entrypoint);
enforcePermission("startContextualSearch");
final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
@@ -455,7 +462,8 @@ public class ContextualSearchManagerService extends SystemService {
// server has READ_FRAME_BUFFER permission to get the screenshot and because only
// the system server can invoke non-exported activities.
Binder.withCleanCallingIdentity(() -> {
- Intent launchIntent = getContextualSearchIntent(entrypoint, mToken);
+ Intent launchIntent =
+ getContextualSearchIntent(entrypoint, callingUserId, mToken);
if (launchIntent != null) {
int result = invokeContextualSearchIntent(launchIntent, callingUserId);
if (DEBUG_USER) Log.d(TAG, "Launch result: " + result);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6cfd44bb2d1a..3ccad16073a7 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -240,6 +240,7 @@ java_library_static {
"aconfig_new_storage_flags_lib",
"powerstats_flags_lib",
"locksettings_flags_lib",
+ "profiling_flags_lib",
],
javac_shard_size: 50,
javacflags: [
@@ -255,6 +256,13 @@ java_library_static {
"FlaggedApi",
],
},
+ jarjar_rules: ":services-jarjar-rules",
+ apex_available: ["//apex_available:platform"],
+}
+
+filegroup {
+ name: "services-jarjar-rules",
+ srcs: ["services-jarjar-rules.txt"],
}
java_genrule {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 9d27731cecd4..b7bc4e4827ef 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -96,6 +96,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
+import android.os.PermissionEnforcer;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -3653,10 +3654,16 @@ class StorageManagerService extends IStorageManager.Stub
return mInternalStorageSize;
}
- @EnforcePermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@Override
public int getInternalStorageRemainingLifetime() throws RemoteException {
- super.getInternalStorageRemainingLifetime_enforcePermission();
+ PermissionEnforcer.fromContext(mContext)
+ .enforcePermissionAnyOf(
+ new String[] {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.ALLOCATE_AGGRESSIVE
+ },
+ getCallingPid(),
+ getCallingUid());
return mVold.getStorageRemainingLifetime();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3e7bcb81c47f..b5dcdb1ac287 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -337,6 +337,8 @@ import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.Process;
+import android.os.ProfilingServiceHelper;
+import android.os.ProfilingTrigger;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -1089,7 +1091,18 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void onReportFullyDrawn(long id, long timestampNanos) {
- mProcessList.getAppStartInfoTracker().onActivityReportFullyDrawn(id, timestampNanos);
+ ApplicationStartInfo startInfo = mProcessList.getAppStartInfoTracker()
+ .onActivityReportFullyDrawn(id, timestampNanos);
+
+ if (android.os.profiling.Flags.systemTriggeredProfilingNew()
+ && startInfo != null
+ && startInfo.getStartType() == ApplicationStartInfo.START_TYPE_COLD
+ && startInfo.getPackageName() != null) {
+ ProfilingServiceHelper.getInstance().onProfilingTriggerOccurred(
+ startInfo.getRealUid(),
+ startInfo.getPackageName(),
+ ProfilingTrigger.TRIGGER_TYPE_APP_COLD_START_ACTIVITY);
+ }
}
};
@@ -11337,7 +11350,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
pw.println(" mFgsStartTempAllowList:");
final long currentTimeNow = System.currentTimeMillis();
- final long elapsedRealtimeNow = SystemClock.elapsedRealtime();
+ final long tempAllowlistCurrentTime =
+ com.android.server.deviceidle.Flags.useCpuTimeForTempAllowlist()
+ ? SystemClock.uptimeMillis() : SystemClock.elapsedRealtime();
mFgsStartTempAllowList.forEach((uid, entry) -> {
pw.print(" " + UserHandle.formatUid(uid) + ": ");
entry.second.dump(pw);
@@ -11345,7 +11360,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Convert entry.mExpirationTime, which is an elapsed time since boot,
// to a time since epoch (i.e. System.currentTimeMillis()-based time.)
final long expirationInCurrentTime =
- currentTimeNow - elapsedRealtimeNow + entry.first;
+ currentTimeNow - tempAllowlistCurrentTime + entry.first;
TimeUtils.dumpTimeWithDelta(pw, expirationInCurrentTime, currentTimeNow);
pw.println();
});
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 9fc0bf920969..6d594ac38187 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -20,7 +20,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.content.pm.ApplicationInfo;
-import android.os.Process;
+import android.os.ProfilingServiceHelper;
+import android.os.ProfilingTrigger;
import android.os.SystemClock;
import android.os.Trace;
import android.util.ArraySet;
@@ -240,6 +241,15 @@ class AnrHelper {
|| startTime < SELF_ONLY_AFTER_BOOT_MS;
r.appNotResponding(onlyDumpSelf);
final long endTime = SystemClock.uptimeMillis();
+
+ if (android.os.profiling.Flags.systemTriggeredProfilingNew() && r.mAppInfo != null
+ && r.mAppInfo.packageName != null) {
+ ProfilingServiceHelper.getInstance().onProfilingTriggerOccurred(
+ r.mUid,
+ r.mAppInfo.packageName,
+ ProfilingTrigger.TRIGGER_TYPE_ANR);
+ }
+
Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in "
+ (endTime - startTime) + "ms, latency " + reportLatency
+ (onlyDumpSelf ? "ms (expired, only dump ANR app)" : "ms"));
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index aca6d0b0b967..3913d2f2ea92 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ApplicationStartInfo;
import android.app.Flags;
import android.app.IApplicationStartInfoCompleteListener;
@@ -419,23 +420,25 @@ public final class AppStartInfoTracker {
* Should only be called for Activity launch sequences from an instance of
* {@link ActivityMetricsLaunchObserver}.
*/
- void onActivityReportFullyDrawn(long id, long timestampNanos) {
+ @Nullable
+ ApplicationStartInfo onActivityReportFullyDrawn(long id, long timestampNanos) {
synchronized (mLock) {
if (!mEnabled) {
- return;
+ return null;
}
int index = mInProgressRecords.indexOfKey(id);
if (index < 0) {
- return;
+ return null;
}
ApplicationStartInfo info = mInProgressRecords.valueAt(index);
if (info == null) {
mInProgressRecords.removeAt(index);
- return;
+ return null;
}
info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN,
timestampNanos);
mInProgressRecords.removeAt(index);
+ return info;
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 7c563abea22f..23092ed83809 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -453,7 +453,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
com.android.internal.R.integer.config_accumulatedBatteryUsageStatsSpanSize);
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context,
mPowerAttributor, mPowerProfile, mCpuScalingPolicies,
- mPowerStatsStore, accumulatedBatteryUsageStatsSpanSize, Clock.SYSTEM_CLOCK);
+ mPowerStatsStore, accumulatedBatteryUsageStatsSpanSize, Clock.SYSTEM_CLOCK,
+ mMonotonicClock);
mDumpHelper = new BatteryStatsDumpHelperImpl(mBatteryUsageStatsProvider);
mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map, mHandler);
mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config"));
diff --git a/services/core/java/com/android/server/am/FgsTempAllowList.java b/services/core/java/com/android/server/am/FgsTempAllowList.java
index c28655655765..5569b6db3285 100644
--- a/services/core/java/com/android/server/am/FgsTempAllowList.java
+++ b/services/core/java/com/android/server/am/FgsTempAllowList.java
@@ -29,7 +29,7 @@ import java.util.function.BiConsumer;
/**
* List of keys that have expiration time.
- * If the expiration time is less than current elapsedRealtime, the key has expired.
+ * If the expiration time is less than current uptime, the key has expired.
* Otherwise it is valid (or allowed).
*
* <p>This is used for both FGS-BG-start restriction, and FGS-while-in-use permissions check.</p>
@@ -42,7 +42,7 @@ public class FgsTempAllowList<E> {
private static final int DEFAULT_MAX_SIZE = 100;
/**
- * The value is Pair type, Pair.first is the expirationTime(an elapsedRealtime),
+ * The value is Pair type, Pair.first is the expirationTime(in cpu uptime),
* Pair.second is the optional information entry about this key.
*/
private final SparseArray<Pair<Long, E>> mTempAllowList = new SparseArray<>();
@@ -82,7 +82,9 @@ public class FgsTempAllowList<E> {
}
// The temp allowlist should be a short list with only a few entries in it.
// for a very large list, HashMap structure should be used.
- final long now = SystemClock.elapsedRealtime();
+ final long now = com.android.server.deviceidle.Flags.useCpuTimeForTempAllowlist()
+ ? SystemClock.uptimeMillis()
+ : SystemClock.elapsedRealtime();
final int size = mTempAllowList.size();
if (size > mMaxSize) {
Slog.w(TAG_AM, "FgsTempAllowList length:" + size + " exceeds maxSize"
@@ -112,12 +114,15 @@ public class FgsTempAllowList<E> {
final int index = mTempAllowList.indexOfKey(uid);
if (index < 0) {
return null;
- } else if (mTempAllowList.valueAt(index).first < SystemClock.elapsedRealtime()) {
+ }
+ final long timeNow = com.android.server.deviceidle.Flags.useCpuTimeForTempAllowlist()
+ ? SystemClock.uptimeMillis()
+ : SystemClock.elapsedRealtime();
+ if (mTempAllowList.valueAt(index).first < timeNow) {
mTempAllowList.removeAt(index);
return null;
- } else {
- return mTempAllowList.valueAt(index);
}
+ return mTempAllowList.valueAt(index);
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
index ae93991d3945..0855815b67a9 100644
--- a/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
@@ -65,27 +65,31 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
@Override
public boolean setGlobalRestriction(Object clientToken, int code, boolean restricted) {
+ boolean changed;
if (restricted) {
if (!mGlobalRestrictions.containsKey(clientToken)) {
mGlobalRestrictions.put(clientToken, new SparseBooleanArray());
}
SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken);
Objects.requireNonNull(restrictedCodes);
- boolean changed = !restrictedCodes.get(code);
+ changed = !restrictedCodes.get(code);
restrictedCodes.put(code, true);
- return changed;
} else {
SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken);
if (restrictedCodes == null) {
return false;
}
- boolean changed = restrictedCodes.get(code);
+ changed = restrictedCodes.get(code);
restrictedCodes.delete(code);
if (restrictedCodes.size() == 0) {
mGlobalRestrictions.remove(clientToken);
}
- return changed;
}
+
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
+ return changed;
}
@Override
@@ -104,7 +108,11 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
@Override
public boolean clearGlobalRestrictions(Object clientToken) {
- return mGlobalRestrictions.remove(clientToken) != null;
+ boolean changed = mGlobalRestrictions.remove(clientToken) != null;
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
+ return changed;
}
@RequiresPermission(anyOf = {
@@ -122,6 +130,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
changed |= putUserRestrictionExclusions(clientToken, userIds[i],
excludedPackageTags);
}
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
return changed;
}
@@ -191,6 +202,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
changed |= mUserRestrictions.remove(clientToken) != null;
changed |= mUserRestrictionExcludedPackageTags.remove(clientToken) != null;
notifyAllUserRestrictions(allUserRestrictedCodes);
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
return changed;
}
@@ -244,6 +258,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
}
}
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
return changed;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 702ad9541af8..5e74d67905a6 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -998,6 +998,7 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void onUidModeChanged(int uid, int code, int mode,
String persistentDeviceId) {
+ AppOpsManager.invalidateAppOpModeCache();
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForAllPkgsInUid, AppOpsService.this,
code, uid, false, persistentDeviceId));
@@ -1006,6 +1007,7 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void onPackageModeChanged(String packageName, int userId, int code,
int mode) {
+ AppOpsManager.invalidateAppOpModeCache();
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForPkg, AppOpsService.this,
packageName, code, mode, userId));
@@ -1032,6 +1034,11 @@ public class AppOpsService extends IAppOpsService.Stub {
// To migrate storageFile to recentAccessesFile, these reads must be called in this order.
readRecentAccesses();
mAppOpsCheckingService.readState();
+ // The system property used by the cache is created the first time it is written, that only
+ // happens inside invalidateCache(). Until the service calls invalidateCache() the property
+ // will not exist and the nonce will be UNSET.
+ AppOpsManager.invalidateAppOpModeCache();
+ AppOpsManager.disableAppOpModeCache();
}
public void publish() {
@@ -2830,6 +2837,13 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public int checkOperationRaw(int code, int uid, String packageName,
@Nullable String attributionTag) {
+ if (Binder.getCallingPid() != Process.myPid()
+ && Flags.appopAccessTrackingLoggingEnabled()) {
+ FrameworkStatsLog.write(
+ APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code,
+ APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION,
+ false);
+ }
return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
Context.DEVICE_ID_DEFAULT, true /*raw*/);
}
@@ -2837,6 +2851,13 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public int checkOperationRawForDevice(int code, int uid, @Nullable String packageName,
@Nullable String attributionTag, int virtualDeviceId) {
+ if (Binder.getCallingPid() != Process.myPid()
+ && Flags.appopAccessTrackingLoggingEnabled()) {
+ FrameworkStatsLog.write(
+ APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code,
+ APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION,
+ false);
+ }
return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
virtualDeviceId, true /*raw*/);
}
@@ -2894,8 +2915,14 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_IGNORED;
}
}
- return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
- virtualDeviceId, raw);
+
+ if (Flags.appopModeCachingEnabled()) {
+ return getAppOpMode(code, uid, resolvedPackageName, attributionTag, virtualDeviceId,
+ raw, true);
+ } else {
+ return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
+ virtualDeviceId, raw);
+ }
}
/**
@@ -2961,6 +2988,54 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ /**
+ * This method unifies mode checking logic between checkOperationUnchecked and
+ * noteOperationUnchecked. It can replace those two methods once the flag is fully rolled out.
+ *
+ * @param isCheckOp This param is only used in user's op restriction. When checking if a package
+ * can bypass user's restriction we should account for attributionTag as well.
+ * But existing checkOp APIs don't accept attributionTag so we added a hack to
+ * skip attributionTag check for checkOp. After we add an overload of checkOp
+ * that accepts attributionTag we should remove this param.
+ */
+ private @Mode int getAppOpMode(int code, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, int virtualDeviceId, boolean raw, boolean isCheckOp) {
+ PackageVerificationResult pvr;
+ try {
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag);
+ } catch (SecurityException e) {
+ logVerifyAndGetBypassFailure(uid, e, "getAppOpMode");
+ return MODE_IGNORED;
+ }
+
+ if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
+ return MODE_IGNORED;
+ }
+
+ synchronized (this) {
+ if (isOpRestrictedLocked(uid, code, packageName, attributionTag, virtualDeviceId,
+ pvr.bypass, isCheckOp)) {
+ return MODE_IGNORED;
+ }
+ if (isOpAllowedForUid(uid)) {
+ return MODE_ALLOWED;
+ }
+
+ int switchCode = AppOpsManager.opToSwitch(code);
+ int rawUidMode = mAppOpsCheckingService.getUidMode(uid,
+ getPersistentId(virtualDeviceId), switchCode);
+
+ if (rawUidMode != AppOpsManager.opToDefaultMode(switchCode)) {
+ return raw ? rawUidMode : evaluateForegroundMode(uid, switchCode, rawUidMode);
+ }
+
+ int rawPackageMode = mAppOpsCheckingService.getPackageMode(packageName, switchCode,
+ UserHandle.getUserId(uid));
+ return raw ? rawPackageMode : evaluateForegroundMode(uid, switchCode, rawPackageMode);
+ }
+ }
+
+
@Override
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
return mCheckOpsDelegateDispatcher.checkAudioOperation(code, usage, uid, packageName);
@@ -3213,7 +3288,6 @@ public class AppOpsService extends IAppOpsService.Stub {
PackageVerificationResult pvr;
try {
pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
- boolean wasNull = attributionTag == null;
if (!pvr.isAttributionTagValid) {
attributionTag = null;
}
diff --git a/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java b/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java
index d094629cc57b..d7d1ac96d650 100644
--- a/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java
+++ b/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java
@@ -23,10 +23,10 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
-import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.UserHandle;
import android.util.IntArray;
+import android.util.Log;
import android.util.SparseIntArray;
import java.util.HashSet;
@@ -48,7 +48,7 @@ import java.util.Set;
// A map between device internal type (e.g. AudioSystem.DEVICE_IN_BUILTIN_MIC) to its input gain
// index.
private final SparseIntArray mInputGainIndexMap;
- private final Set<Integer> mSupportedDeviceTypes;
+ private final Set<Integer> mSupportedDeviceTypes = new HashSet<>();
InputDeviceVolumeHelper(
SettingsAdapter settings,
@@ -60,20 +60,16 @@ import java.util.Set;
IntArray internalDeviceTypes = new IntArray();
int status = AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS, internalDeviceTypes);
- mInputGainIndexMap =
- new SparseIntArray(
- status == AudioManager.SUCCESS
- ? internalDeviceTypes.size()
- : AudioSystem.DEVICE_IN_ALL_SET.size());
-
- if (status == AudioManager.SUCCESS) {
- Set<Integer> supportedDeviceTypes = new HashSet<>();
- for (int i = 0; i < internalDeviceTypes.size(); i++) {
- supportedDeviceTypes.add(internalDeviceTypes.get(i));
- }
- mSupportedDeviceTypes = supportedDeviceTypes;
- } else {
- mSupportedDeviceTypes = AudioSystem.DEVICE_IN_ALL_SET;
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, "AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS) failed. status:"
+ + status);
+ }
+
+ // Note that in a rare case, if AudioSystem.getSupportedDeviceTypes call fails, both
+ // mInputGainIndexMap and mSupportedDeviceTypes will be empty.
+ mInputGainIndexMap = new SparseIntArray(internalDeviceTypes.size());
+ for (int i = 0; i < internalDeviceTypes.size(); i++) {
+ mSupportedDeviceTypes.add(internalDeviceTypes.get(i));
}
readSettings();
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index a40dd7919402..c3d88e3e6eb1 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -50,6 +50,7 @@ import java.util.concurrent.ConcurrentHashMap;
*
* <p>Note, this class is not thread safe so callers must ensure thread safety.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatChange extends CompatibilityChangeInfo {
/**
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 79025d00d128..e89f43bd7196 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -42,6 +42,7 @@ import com.android.internal.compat.CompatibilityOverridesToRemoveByPackageConfig
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
import com.android.server.compat.config.Change;
import com.android.server.compat.config.Config;
import com.android.server.compat.overrides.ChangeOverrides;
@@ -63,6 +64,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -72,12 +74,16 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <p>It stores the default configuration for each change, and any per-package overrides that have
* been configured.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
final class CompatConfig {
private static final String TAG = "CompatConfig";
private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat";
private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
+ private static final String APP_COMPAT_DATA_DIR_RAVENWOOD = "/ravenwood-data/";
+ private static final String OVERRIDES_FILE_RAVENWOOD = "compat-config.xml";
+
private final ConcurrentHashMap<Long, CompatChange> mChanges = new ConcurrentHashMap<>();
private final OverrideValidatorImpl mOverrideValidator;
@@ -98,19 +104,32 @@ final class CompatConfig {
static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
CompatConfig config = new CompatConfig(androidBuildClassifier, context);
- config.initConfigFromLib(Environment.buildPath(
+ config.loadConfigFiles();
+ config.initOverrides();
+ config.invalidateCache();
+ return config;
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private void loadConfigFiles() {
+ initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "etc", "compatconfig"));
- config.initConfigFromLib(Environment.buildPath(
+ initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos();
for (ApexManager.ActiveApexInfo apex : apexes) {
- config.initConfigFromLib(Environment.buildPath(
+ initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
- config.initOverrides();
- config.invalidateCache();
- return config;
+ }
+
+ @SuppressWarnings("unused")
+ private void loadConfigFiles$ravenwood() {
+ final var configDir = new File(
+ RavenwoodEnvironment.getInstance().getRavenwoodRuntimePath()
+ + APP_COMPAT_DATA_DIR_RAVENWOOD);
+ initConfigFromLib(configDir, (file) -> file.getName().endsWith(OVERRIDES_FILE_RAVENWOOD));
}
/**
@@ -678,12 +697,25 @@ final class CompatConfig {
return changeInfos;
}
+ /**
+ * Load all config files in a given directory.
+ */
void initConfigFromLib(File libraryDir) {
+ initConfigFromLib(libraryDir, (file) -> true);
+ }
+
+ /**
+ * Load config files in a given directory, but only the ones that match {@code includingFilter}.
+ */
+ void initConfigFromLib(File libraryDir, Predicate<File> includingFilter) {
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.d(TAG, "No directory " + libraryDir + ", skipping");
return;
}
for (File f : libraryDir.listFiles()) {
+ if (!includingFilter.test(f)) {
+ continue;
+ }
Slog.d(TAG, "Found a config file: " + f.getPath());
//TODO(b/138222363): Handle duplicate ids across config files.
readConfig(f);
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index e3b6d032b7f0..362c69797161 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -45,6 +45,7 @@ import com.android.internal.compat.OverrideAllowedState;
/**
* Implementation of the policy for allowing compat change overrides.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class OverrideValidatorImpl extends IOverrideValidator.Stub {
private AndroidBuildClassifier mAndroidBuildClassifier;
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 8d64383b32b9..97f4a5c570b5 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -36,6 +36,7 @@ import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.PermissionEnforcer;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -65,6 +66,7 @@ import java.util.Map;
/**
* System server internal API for gating and reporting compatibility changes.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PlatformCompat extends IPlatformCompat.Stub {
private static final String TAG = "Compatibility";
@@ -75,6 +77,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private final AndroidBuildClassifier mBuildClassifier;
public PlatformCompat(Context context) {
+ super(PermissionEnforcer.fromContext(context));
mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
mBuildClassifier = new AndroidBuildClassifier();
@@ -85,6 +88,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
PlatformCompat(Context context, CompatConfig compatConfig,
AndroidBuildClassifier buildClassifier,
ChangeReporter changeReporter) {
+ super(PermissionEnforcer.fromContext(context));
mContext = context;
mChangeReporter = changeReporter;
mCompatConfig = compatConfig;
@@ -515,6 +519,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
return appInfo;
}
+ @android.ravenwood.annotation.RavenwoodReplace
private void killPackage(String packageName) {
int uid = LocalServices.getService(PackageManagerInternal.class).getPackageUid(packageName,
0, UserHandle.myUserId());
@@ -528,6 +533,13 @@ public class PlatformCompat extends IPlatformCompat.Stub {
killUid(UserHandle.getAppId(uid));
}
+ @SuppressWarnings("unused")
+ private void killPackage$ravenwood(String packageName) {
+ // TODO Maybe crash if the package is the self.
+ Slog.w(TAG, "killPackage() is ignored on Ravenwood: packageName=" + packageName);
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
private void killUid(int appId) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -542,6 +554,12 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
}
+ @SuppressWarnings("unused")
+ private void killUid$ravenwood(int appId) {
+ // TODO Maybe crash if the UID is the self.
+ Slog.w(TAG, "killUid() is ignored on Ravenwood: appId=" + appId);
+ }
+
private void checkAllCompatOverridesAreOverridable(Collection<Long> changeIds) {
for (Long changeId : changeIds) {
if (isKnownChangeId(changeId) && !mCompatConfig.isOverridable(changeId)) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java
index 5d7af650db0b..7a3feb515706 100644
--- a/services/core/java/com/android/server/compat/PlatformCompatNative.java
+++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java
@@ -23,6 +23,7 @@ import com.android.internal.compat.IPlatformCompatNative;
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PlatformCompatNative extends IPlatformCompatNative.Stub {
private final PlatformCompat mPlatformCompat;
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
index e8762a3e935c..0ec68792a886 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
@@ -46,6 +46,7 @@ import java.util.regex.Pattern;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
final class AppCompatOverridesParser {
/**
* Flag for specifying all compat change IDs owned by a namespace. See {@link
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
index fe002ce00d32..8637d2dfe565 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -68,6 +68,7 @@ import java.util.Set;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class AppCompatOverridesService {
private static final String TAG = "AppCompatOverridesService";
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 421145390190..dabef84fec31 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -51,6 +51,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.ArrayMap;
@@ -64,6 +65,7 @@ import android.view.SurfaceControl;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.brightness.BrightnessUtils;
import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
@@ -323,7 +325,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private int mWidth;
private int mHeight;
private int mDensityDpi;
- private float mRequestedRefreshRate;
+ private final float mRequestedRefreshRate;
private Surface mSurface;
private DisplayDeviceInfo mInfo;
private int mDisplayState;
@@ -332,7 +334,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private Display.Mode mMode;
private int mDisplayIdToMirror;
private boolean mIsWindowManagerMirroring;
- private DisplayCutout mDisplayCutout;
+ private final DisplayCutout mDisplayCutout;
+ private final float mDefaultBrightness;
+ private float mCurrentBrightness;
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
int ownerUid, String ownerPackageName, Surface surface, int flags,
@@ -349,6 +353,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mDensityDpi = virtualDisplayConfig.getDensityDpi();
mRequestedRefreshRate = virtualDisplayConfig.getRequestedRefreshRate();
mDisplayCutout = virtualDisplayConfig.getDisplayCutout();
+ mDefaultBrightness = virtualDisplayConfig.getDefaultBrightness();
+ mCurrentBrightness = mDefaultBrightness;
mMode = createMode(mWidth, mHeight, getRefreshRate());
mSurface = surface;
mFlags = flags;
@@ -457,6 +463,12 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mCallback.dispatchDisplayResumed();
}
}
+ if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()
+ && BrightnessUtils.isValidBrightnessValue(brightnessState)
+ && brightnessState != mCurrentBrightness) {
+ mCurrentBrightness = brightnessState;
+ mCallback.dispatchRequestedBrightnessChanged(mCurrentBrightness);
+ }
return null;
}
@@ -623,6 +635,10 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mInfo.state = mDisplayState;
}
+ mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN;
+ mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX;
+ mInfo.brightnessDefault = mDefaultBrightness;
+
mInfo.ownerUid = mOwnerUid;
mInfo.ownerPackageName = mOwnerPackageName;
@@ -642,6 +658,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private static final int MSG_ON_DISPLAY_PAUSED = 0;
private static final int MSG_ON_DISPLAY_RESUMED = 1;
private static final int MSG_ON_DISPLAY_STOPPED = 2;
+ private static final int MSG_ON_REQUESTED_BRIGHTNESS_CHANGED = 3;
private final IVirtualDisplayCallback mCallback;
@@ -663,6 +680,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
case MSG_ON_DISPLAY_STOPPED:
mCallback.onStopped();
break;
+ case MSG_ON_REQUESTED_BRIGHTNESS_CHANGED:
+ mCallback.onRequestedBrightnessChanged((Float) msg.obj);
+ break;
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify listener of virtual display event.", e);
@@ -677,6 +697,11 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
sendEmptyMessage(MSG_ON_DISPLAY_RESUMED);
}
+ public void dispatchRequestedBrightnessChanged(float brightness) {
+ Message msg = obtainMessage(MSG_ON_REQUESTED_BRIGHTNESS_CHANGED, brightness);
+ sendMessage(msg);
+ }
+
public void dispatchDisplayStopped() {
sendEmptyMessage(MSG_ON_DISPLAY_STOPPED);
}
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index cf1cdaf55e5c..8cb51ce35a89 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -17,6 +17,8 @@
package com.android.server.input;
import static android.hardware.input.InputGestureData.createKeyTrigger;
+
+import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut;
@@ -209,12 +211,12 @@ final class InputGestureManager {
KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY
));
}
- if (keyboardA11yShortcutControl()) {
- systemShortcuts.add(createKeyGesture(
- KeyEvent.KEYCODE_T,
+ if (enableTalkbackAndMagnifierKeyGestures()) {
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_T,
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK
- ));
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK));
+ }
+ if (keyboardA11yShortcutControl()) {
if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) {
systemShortcuts.add(createKeyGesture(
KeyEvent.KEYCODE_3,
@@ -362,6 +364,22 @@ final class InputGestureManager {
}
@Nullable
+ public InputGestureData getCustomGestureForTouchpadGesture(@UserIdInt int userId,
+ int touchpadGestureType) {
+ if (touchpadGestureType == InputGestureData.TOUCHPAD_GESTURE_TYPE_UNKNOWN) {
+ return null;
+ }
+ synchronized (mGestureLock) {
+ Map<InputGestureData.Trigger, InputGestureData> customGestures =
+ mCustomInputGestures.get(userId);
+ if (customGestures == null) {
+ return null;
+ }
+ return customGestures.get(InputGestureData.createTouchpadTrigger(touchpadGestureType));
+ }
+ }
+
+ @Nullable
public InputGestureData getSystemShortcutForKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 78e3b846f9dc..e0f3a9bd427a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -64,6 +64,7 @@ import android.hardware.input.IKeyboardBacklightListener;
import android.hardware.input.IStickyModifierStateListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.InputDeviceIdentifier;
+import android.hardware.input.InputGestureData;
import android.hardware.input.InputManager;
import android.hardware.input.InputSensorInfo;
import android.hardware.input.InputSettings;
@@ -2314,7 +2315,8 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
@SuppressWarnings("unused")
private void notifyTouchpadThreeFingerTap() {
- mKeyGestureController.handleTouchpadThreeFingerTap();
+ mKeyGestureController.handleTouchpadGesture(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP);
}
// Native callback.
@@ -2995,35 +2997,35 @@ public class InputManagerService extends IInputManager.Stub
@Override
@PermissionManuallyEnforced
- public int addCustomInputGesture(@NonNull AidlInputGestureData inputGestureData) {
+ public int addCustomInputGesture(@UserIdInt int userId,
+ @NonNull AidlInputGestureData inputGestureData) {
enforceManageKeyGesturePermission();
Objects.requireNonNull(inputGestureData);
- return mKeyGestureController.addCustomInputGesture(UserHandle.getCallingUserId(),
- inputGestureData);
+ return mKeyGestureController.addCustomInputGesture(userId, inputGestureData);
}
@Override
@PermissionManuallyEnforced
- public int removeCustomInputGesture(@NonNull AidlInputGestureData inputGestureData) {
+ public int removeCustomInputGesture(@UserIdInt int userId,
+ @NonNull AidlInputGestureData inputGestureData) {
enforceManageKeyGesturePermission();
Objects.requireNonNull(inputGestureData);
- return mKeyGestureController.removeCustomInputGesture(UserHandle.getCallingUserId(),
- inputGestureData);
+ return mKeyGestureController.removeCustomInputGesture(userId, inputGestureData);
}
@Override
@PermissionManuallyEnforced
- public void removeAllCustomInputGestures() {
+ public void removeAllCustomInputGestures(@UserIdInt int userId) {
enforceManageKeyGesturePermission();
- mKeyGestureController.removeAllCustomInputGestures(UserHandle.getCallingUserId());
+ mKeyGestureController.removeAllCustomInputGestures(userId);
}
@Override
- public AidlInputGestureData[] getCustomInputGestures() {
- return mKeyGestureController.getCustomInputGestures(UserHandle.getCallingUserId());
+ public AidlInputGestureData[] getCustomInputGestures(@UserIdInt int userId) {
+ return mKeyGestureController.getCustomInputGestures(userId);
}
@Override
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index e0991ec20f9e..fc106404049c 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -847,6 +847,13 @@ final class KeyGestureController {
/* appLaunchData = */null);
}
+ private void handleTouchpadGesture(@KeyGestureEvent.KeyGestureType int keyGestureType,
+ @Nullable AppLaunchData appLaunchData) {
+ handleKeyGesture(KeyCharacterMap.VIRTUAL_KEYBOARD, new int[0], /* modifierState= */0,
+ keyGestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ Display.DEFAULT_DISPLAY, /* focusedToken = */null, /* flags = */0, appLaunchData);
+ }
+
@VisibleForTesting
boolean handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
@KeyGestureEvent.KeyGestureType int gestureType, int action, int displayId,
@@ -897,11 +904,18 @@ final class KeyGestureController {
handleKeyGesture(event, null /*focusedToken*/);
}
- public void handleTouchpadThreeFingerTap() {
- // TODO(b/365063048): trigger a custom shortcut based on the three-finger tap.
- if (DEBUG) {
- Slog.d(TAG, "Three-finger touchpad tap occurred");
+ public void handleTouchpadGesture(int touchpadGestureType) {
+ // Handle custom shortcuts
+ InputGestureData customGesture;
+ synchronized (mUserLock) {
+ customGesture = mInputGestureManager.getCustomGestureForTouchpadGesture(mCurrentUserId,
+ touchpadGestureType);
+ }
+ if (customGesture == null) {
+ return;
}
+ handleTouchpadGesture(customGesture.getAction().keyGestureType(),
+ customGesture.getAction().appLaunchData());
}
@MainThread
@@ -1214,6 +1228,7 @@ final class KeyGestureController {
public void dump(IndentingPrintWriter ipw) {
ipw.println("KeyGestureController:");
ipw.increaseIndent();
+ ipw.println("mCurrentUserId = " + mCurrentUserId);
ipw.println("mSystemPid = " + mSystemPid);
ipw.println("mPendingMetaAction = " + mPendingMetaAction);
ipw.println("mPendingCapsLockToggle = " + mPendingCapsLockToggle);
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
index 146ce1732070..46be1ca0eec2 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
@@ -27,6 +27,7 @@ import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
+import android.view.inputmethod.Flags;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -59,8 +60,14 @@ final class AdditionalSubtypeUtils {
private static final String NODE_SUBTYPE = "subtype";
private static final String NODE_IMI = "imi";
private static final String ATTR_ID = "id";
+ /** The resource ID of the subtype name. */
private static final String ATTR_LABEL = "label";
+ /** The untranslatable name of the subtype. */
private static final String ATTR_NAME_OVERRIDE = "nameOverride";
+ /** The layout label string resource identifier. */
+ private static final String ATTR_LAYOUT_LABEL = "layoutLabel";
+ /** The non-localized layout label. */
+ private static final String ATTR_LAYOUT_LABEL_NON_LOCALIZED = "layoutLabelNonLocalized";
private static final String ATTR_NAME_PK_LANGUAGE_TAG = "pkLanguageTag";
private static final String ATTR_NAME_PK_LAYOUT_TYPE = "pkLayoutType";
private static final String ATTR_ICON = "icon";
@@ -173,6 +180,11 @@ final class AdditionalSubtypeUtils {
out.attributeInt(null, ATTR_ICON, subtype.getIconResId());
out.attributeInt(null, ATTR_LABEL, subtype.getNameResId());
out.attribute(null, ATTR_NAME_OVERRIDE, subtype.getNameOverride().toString());
+ if (Flags.imeSwitcherRevampApi()) {
+ out.attributeInt(null, ATTR_LAYOUT_LABEL, subtype.getLayoutLabelResource());
+ out.attribute(null, ATTR_LAYOUT_LABEL_NON_LOCALIZED,
+ subtype.getLayoutLabelNonLocalized().toString());
+ }
ULocale pkLanguageTag = subtype.getPhysicalKeyboardHintLanguageTag();
if (pkLanguageTag != null) {
out.attribute(null, ATTR_NAME_PK_LANGUAGE_TAG,
@@ -264,6 +276,16 @@ final class AdditionalSubtypeUtils {
final int label = parser.getAttributeInt(null, ATTR_LABEL);
final String untranslatableName = parser.getAttributeValue(null,
ATTR_NAME_OVERRIDE);
+ final int layoutLabelResource;
+ final String layoutLabelNonLocalized;
+ if (Flags.imeSwitcherRevampApi()) {
+ layoutLabelResource = parser.getAttributeInt(null, ATTR_LAYOUT_LABEL);
+ layoutLabelNonLocalized = parser.getAttributeValue(null,
+ ATTR_LAYOUT_LABEL_NON_LOCALIZED);
+ } else {
+ layoutLabelResource = 0;
+ layoutLabelNonLocalized = null;
+ }
final String pkLanguageTag = parser.getAttributeValue(null,
ATTR_NAME_PK_LANGUAGE_TAG);
final String pkLayoutType = parser.getAttributeValue(null,
@@ -283,6 +305,7 @@ final class AdditionalSubtypeUtils {
final InputMethodSubtype.InputMethodSubtypeBuilder
builder = new InputMethodSubtype.InputMethodSubtypeBuilder()
.setSubtypeNameResId(label)
+ .setLayoutLabelResource(layoutLabelResource)
.setPhysicalKeyboardHint(
pkLanguageTag == null ? null : new ULocale(pkLanguageTag),
pkLayoutType == null ? "" : pkLayoutType)
@@ -301,6 +324,9 @@ final class AdditionalSubtypeUtils {
if (untranslatableName != null) {
builder.setSubtypeNameOverride(untranslatableName);
}
+ if (layoutLabelNonLocalized != null) {
+ builder.setLayoutLabelNonLocalized(layoutLabelNonLocalized);
+ }
tempSubtypesArray.add(builder.build());
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
index 6abd5aabfabf..9f94905686fe 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
@@ -238,8 +238,8 @@ final class InputMethodMenuControllerNew {
prevImeId = imeId;
}
- menuItems.add(new SubtypeItem(item.mImeName, item.mSubtypeName, item.mImi,
- item.mSubtypeIndex));
+ menuItems.add(new SubtypeItem(item.mImeName, item.mSubtypeName, item.mLayoutName,
+ item.mImi, item.mSubtypeIndex));
}
return menuItems;
@@ -348,6 +348,13 @@ final class InputMethodMenuControllerNew {
@Nullable
final CharSequence mSubtypeName;
+ /**
+ * The name of the subtype's layout, or {@code null} if this item doesn't have a subtype,
+ * or doesn't specify a layout.
+ */
+ @Nullable
+ private final CharSequence mLayoutName;
+
/** The info of the input method. */
@NonNull
final InputMethodInfo mImi;
@@ -360,10 +367,11 @@ final class InputMethodMenuControllerNew {
final int mSubtypeIndex;
SubtypeItem(@NonNull CharSequence imeName, @Nullable CharSequence subtypeName,
- @NonNull InputMethodInfo imi,
+ @Nullable CharSequence layoutName, @NonNull InputMethodInfo imi,
@IntRange(from = NOT_A_SUBTYPE_INDEX) int subtypeIndex) {
mImeName = imeName;
mSubtypeName = subtypeName;
+ mLayoutName = layoutName;
mImi = imi;
mSubtypeIndex = subtypeIndex;
}
@@ -521,6 +529,9 @@ final class InputMethodMenuControllerNew {
/** The name of the item. */
@NonNull
private final TextView mName;
+ /** The layout name. */
+ @NonNull
+ private final TextView mLayout;
/** Indicator for the selected status of the item. */
@NonNull
private final ImageView mCheckmark;
@@ -536,6 +547,7 @@ final class InputMethodMenuControllerNew {
mContainer = itemView;
mName = itemView.requireViewById(com.android.internal.R.id.text);
+ mLayout = itemView.requireViewById(com.android.internal.R.id.text2);
mCheckmark = itemView.requireViewById(com.android.internal.R.id.image);
mContainer.setOnClickListener((v) -> {
@@ -563,6 +575,9 @@ final class InputMethodMenuControllerNew {
// Trigger the ellipsize marquee behaviour by selecting the name.
mName.setSelected(isSelected);
mName.setText(name);
+ mLayout.setText(item.mLayoutName);
+ mLayout.setVisibility(
+ !TextUtils.isEmpty(item.mLayoutName) ? View.VISIBLE : View.GONE);
mCheckmark.setVisibility(isSelected ? View.VISIBLE : View.GONE);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 96b3e084d102..51b85e90c447 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -85,6 +85,12 @@ final class InputMethodSubtypeSwitchingController {
public final CharSequence mImeName;
@Nullable
public final CharSequence mSubtypeName;
+ /**
+ * The subtype's layout name, or {@code null} if this item doesn't have a subtype,
+ * or doesn't specify a layout.
+ */
+ @Nullable
+ public final CharSequence mLayoutName;
@NonNull
public final InputMethodInfo mImi;
/**
@@ -96,10 +102,11 @@ final class InputMethodSubtypeSwitchingController {
public final boolean mIsSystemLanguage;
ImeSubtypeListItem(@NonNull CharSequence imeName, @Nullable CharSequence subtypeName,
- @NonNull InputMethodInfo imi, int subtypeIndex, @Nullable String subtypeLocale,
- @NonNull String systemLocale) {
+ @Nullable CharSequence layoutName, @NonNull InputMethodInfo imi, int subtypeIndex,
+ @Nullable String subtypeLocale, @NonNull String systemLocale) {
mImeName = imeName;
mSubtypeName = subtypeName;
+ mLayoutName = layoutName;
mImi = imi;
mSubtypeIndex = subtypeIndex;
if (TextUtils.isEmpty(subtypeLocale)) {
@@ -252,8 +259,11 @@ final class InputMethodSubtypeSwitchingController {
subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
.getDisplayName(userAwareContext, imi.getPackageName(),
imi.getServiceInfo().applicationInfo);
- imList.add(new ImeSubtypeListItem(imeLabel,
- subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
+ final var layoutName = subtype.overridesImplicitlyEnabledSubtype() ? null
+ : subtype.getLayoutDisplayName(userAwareContext,
+ imi.getServiceInfo().applicationInfo);
+ imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, layoutName,
+ imi, j, subtype.getLocale(), mSystemLocaleStr));
// Removing this subtype from enabledSubtypeSet because we no
// longer need to add an entry of this subtype to imList to avoid
@@ -262,8 +272,8 @@ final class InputMethodSubtypeSwitchingController {
}
}
} else {
- imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_INDEX, null,
- mSystemLocaleStr));
+ imList.add(new ImeSubtypeListItem(imeLabel, null /* subtypeName */,
+ null /* layoutName */, imi, NOT_A_SUBTYPE_INDEX, null, mSystemLocaleStr));
}
}
Collections.sort(imList);
@@ -311,13 +321,16 @@ final class InputMethodSubtypeSwitchingController {
subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
.getDisplayName(userAwareContext, imi.getPackageName(),
imi.getServiceInfo().applicationInfo);
- imList.add(new ImeSubtypeListItem(imeLabel,
- subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
+ final var layoutName = subtype.overridesImplicitlyEnabledSubtype() ? null
+ : subtype.getLayoutDisplayName(userAwareContext,
+ imi.getServiceInfo().applicationInfo);
+ imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, layoutName,
+ imi, j, subtype.getLocale(), mSystemLocaleStr));
}
}
} else {
- imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_INDEX, null,
- mSystemLocaleStr));
+ imList.add(new ImeSubtypeListItem(imeLabel, null /* subtypeName */,
+ null /* layoutName */, imi, NOT_A_SUBTYPE_INDEX, null, mSystemLocaleStr));
}
}
return imList;
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 89555a9f1de4..c8a87994ee16 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -161,6 +161,11 @@ public class MediaSession2Record extends MediaSessionRecordImpl {
}
@Override
+ public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) {
+ // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
+ }
+
+ @Override
public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
KeyEvent ke, int sequenceId, ResultReceiver cb) {
// TODO(jaewan): Implement.
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index d752429e64f7..668ee2adbd9f 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -230,51 +230,49 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
private final Runnable mUserEngagementTimeoutExpirationRunnable =
() -> {
synchronized (mLock) {
- updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ updateUserEngagedStateIfNeededLocked(
+ /* isTimeoutExpired= */ true,
+ /* isGlobalPrioritySessionActive= */ false);
}
};
@GuardedBy("mLock")
private @UserEngagementState int mUserEngagementState = USER_DISENGAGED;
- @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED})
+ @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARILY_ENGAGED, USER_DISENGAGED})
@Retention(RetentionPolicy.SOURCE)
private @interface UserEngagementState {}
/**
- * Indicates that the session is active and in one of the user engaged states.
+ * Indicates that the session is {@linkplain MediaSession#isActive() active} and in one of the
+ * {@linkplain PlaybackState#isActive() active states}.
*
* @see #updateUserEngagedStateIfNeededLocked(boolean)
*/
private static final int USER_PERMANENTLY_ENGAGED = 0;
/**
- * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state.
+ * Indicates that the session is {@linkplain MediaSession#isActive() active} and has recently
+ * switched to one of the {@linkplain PlaybackState#isActive() inactive states}.
*
* @see #updateUserEngagedStateIfNeededLocked(boolean)
*/
- private static final int USER_TEMPORARY_ENGAGED = 1;
+ private static final int USER_TEMPORARILY_ENGAGED = 1;
/**
- * Indicates that the session is either not active or in one of the user disengaged states
+ * Indicates that the session is either not {@linkplain MediaSession#isActive() active} or in
+ * one of the {@linkplain PlaybackState#isActive() inactive states}.
*
* @see #updateUserEngagedStateIfNeededLocked(boolean)
*/
private static final int USER_DISENGAGED = 2;
/**
- * Indicates the duration of the temporary engaged states, in milliseconds.
+ * Indicates the duration of the temporary engaged state, in milliseconds.
*
- * <p>Some {@link MediaSession} states like {@link PlaybackState#STATE_PAUSED} are temporarily
- * engaged, meaning the corresponding session is only considered in an engaged state for the
- * duration of this timeout, and only if coming from an engaged state.
- *
- * <p>For example, if a session is transitioning from a user-engaged state {@link
- * PlaybackState#STATE_PLAYING} to a temporary user-engaged state {@link
- * PlaybackState#STATE_PAUSED}, then the session will be considered in a user-engaged state for
- * the duration of this timeout, starting at the transition instant. However, a temporary
- * user-engaged state is not considered user-engaged when transitioning from a non-user engaged
- * state {@link PlaybackState#STATE_STOPPED}.
+ * <p>When switching to an {@linkplain PlaybackState#isActive() inactive state}, the user is
+ * treated as temporarily engaged, meaning the corresponding session is only considered in an
+ * engaged state for the duration of this timeout, and only if coming from an engaged state.
*/
private static final int TEMP_USER_ENGAGED_TIMEOUT_MS = 600000;
@@ -598,7 +596,8 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
mSessionCb.mCb.asBinder().unlinkToDeath(this, 0);
mDestroyed = true;
mPlaybackState = null;
- updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ updateUserEngagedStateIfNeededLocked(
+ /* isTimeoutExpired= */ true, /* isGlobalPrioritySessionActive= */ false);
mHandler.post(MessageHandler.MSG_DESTROYED);
}
}
@@ -615,6 +614,24 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
mHandler.post(mUserEngagementTimeoutExpirationRunnable);
}
+ @Override
+ public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) {
+ mHandler.post(
+ () -> {
+ synchronized (mLock) {
+ if (isGlobalPrioritySessionActive) {
+ mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable);
+ } else {
+ if (mUserEngagementState == USER_TEMPORARILY_ENGAGED) {
+ mHandler.postDelayed(
+ mUserEngagementTimeoutExpirationRunnable,
+ TEMP_USER_ENGAGED_TIMEOUT_MS);
+ }
+ }
+ }
+ });
+ }
+
/**
* Sends media button.
*
@@ -1063,21 +1080,20 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
@GuardedBy("mLock")
- private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) {
+ private void updateUserEngagedStateIfNeededLocked(
+ boolean isTimeoutExpired, boolean isGlobalPrioritySessionActive) {
if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
return;
}
int oldUserEngagedState = mUserEngagementState;
int newUserEngagedState;
- if (!isActive() || mPlaybackState == null || mDestroyed) {
+ if (!isActive() || mPlaybackState == null) {
newUserEngagedState = USER_DISENGAGED;
- } else if (isActive() && mPlaybackState.isActive()) {
+ } else if (mPlaybackState.isActive()) {
newUserEngagedState = USER_PERMANENTLY_ENGAGED;
- } else if (mPlaybackState.getState() == PlaybackState.STATE_PAUSED) {
- newUserEngagedState =
- oldUserEngagedState == USER_PERMANENTLY_ENGAGED || !isTimeoutExpired
- ? USER_TEMPORARY_ENGAGED
- : USER_DISENGAGED;
+ } else if (oldUserEngagedState == USER_PERMANENTLY_ENGAGED
+ || (oldUserEngagedState == USER_TEMPORARILY_ENGAGED && !isTimeoutExpired)) {
+ newUserEngagedState = USER_TEMPORARILY_ENGAGED;
} else {
newUserEngagedState = USER_DISENGAGED;
}
@@ -1086,7 +1102,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
mUserEngagementState = newUserEngagedState;
- if (newUserEngagedState == USER_TEMPORARY_ENGAGED) {
+ if (newUserEngagedState == USER_TEMPORARILY_ENGAGED && !isGlobalPrioritySessionActive) {
mHandler.postDelayed(
mUserEngagementTimeoutExpirationRunnable, TEMP_USER_ENGAGED_TIMEOUT_MS);
} else {
@@ -1141,9 +1157,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
.logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK,
callingUid, callingPid);
}
+ boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive();
synchronized (mLock) {
mIsActive = active;
- updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
+ updateUserEngagedStateIfNeededLocked(
+ /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive);
}
long token = Binder.clearCallingIdentity();
try {
@@ -1300,9 +1318,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState)
|| (!TRANSITION_PRIORITY_STATES.contains(oldState)
&& TRANSITION_PRIORITY_STATES.contains(newState));
+ boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive();
synchronized (mLock) {
mPlaybackState = state;
- updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
+ updateUserEngagedStateIfNeededLocked(
+ /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive);
}
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 15f90d4fdd0e..6c3b1234935a 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -206,6 +206,10 @@ public abstract class MediaSessionRecordImpl {
*/
public abstract void expireTempEngaged();
+ /** Notifies record that the global priority session active state changed. */
+ public abstract void onGlobalPrioritySessionActiveChanged(
+ boolean isGlobalPrioritySessionActive);
+
@Override
public final boolean equals(Object o) {
if (this == o) return true;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 1ebc856af2d8..2b29fbd9c5b5 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -362,6 +362,7 @@ public class MediaSessionService extends SystemService implements Monitor {
+ record.isActive());
}
user.pushAddressedPlayerChangedLocked();
+ mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
} else {
if (!user.mPriorityStack.contains(record)) {
Log.w(TAG, "Unknown session updated. Ignoring.");
@@ -394,11 +395,16 @@ public class MediaSessionService extends SystemService implements Monitor {
// Currently only media1 can become global priority session.
void setGlobalPrioritySession(MediaSessionRecord record) {
+ boolean globalPrioritySessionActiveChanged = false;
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
if (mGlobalPrioritySession != record) {
Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
+ " to " + record);
+ globalPrioritySessionActiveChanged =
+ (mGlobalPrioritySession == null && record.isActive())
+ || (mGlobalPrioritySession != null
+ && mGlobalPrioritySession.isActive() != record.isActive());
mGlobalPrioritySession = record;
if (user != null && user.mPriorityStack.contains(record)) {
// Handle the global priority session separately.
@@ -409,6 +415,30 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
}
+ if (globalPrioritySessionActiveChanged) {
+ mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
+ }
+ }
+
+ /** Returns whether the global priority session is active. */
+ boolean isGlobalPrioritySessionActive() {
+ synchronized (mLock) {
+ return isGlobalPriorityActiveLocked();
+ }
+ }
+
+ private void notifyGlobalPrioritySessionActiveChanged() {
+ if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+ return;
+ }
+ synchronized (mLock) {
+ boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
+ for (Set<MediaSessionRecordImpl> records : mUserEngagedSessionsForFgs.values()) {
+ for (MediaSessionRecordImpl record : records) {
+ record.onGlobalPrioritySessionActiveChanged(isGlobalPriorityActive);
+ }
+ }
+ }
}
private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
@@ -646,8 +676,11 @@ public class MediaSessionService extends SystemService implements Monitor {
if (mGlobalPrioritySession == session) {
mGlobalPrioritySession = null;
- if (session.isActive() && user != null) {
- user.pushAddressedPlayerChangedLocked();
+ if (session.isActive()) {
+ if (user != null) {
+ user.pushAddressedPlayerChangedLocked();
+ }
+ mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
}
} else {
if (user != null) {
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 6681e36e00ee..5febd5c07e3a 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -25,8 +25,10 @@ import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.Notification.VISIBILITY_PUBLIC;
import static android.service.notification.Flags.notificationForceGrouping;
+import static android.service.notification.Flags.notificationRegroupOnClassification;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -49,6 +51,9 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -83,10 +88,22 @@ public class GroupHelper {
// with less than this value, they will be forced grouped
private static final int MIN_CHILD_COUNT_TO_AVOID_FORCE_GROUPING = 3;
+ // Regrouping needed because the channel was updated, ie. importance changed
+ static final int REGROUP_REASON_CHANNEL_UPDATE = 0;
+ // Regrouping needed because of notification bundling
+ static final int REGROUP_REASON_BUNDLE = 1;
+
+ @IntDef(prefix = { "REGROUP_REASON_" }, value = {
+ REGROUP_REASON_CHANNEL_UPDATE,
+ REGROUP_REASON_BUNDLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface RegroupingReason {}
private final Callback mCallback;
private final int mAutoGroupAtCount;
private final int mAutogroupSparseGroupsAtCount;
+ private final int mAutoGroupRegroupingAtCount;
private final Context mContext;
private final PackageManager mPackageManager;
private boolean mIsTestHarnessExempted;
@@ -173,6 +190,11 @@ public class GroupHelper {
mContext = context;
mPackageManager = packageManager;
mAutogroupSparseGroupsAtCount = autoGroupSparseGroupsAtCount;
+ if (notificationRegroupOnClassification()) {
+ mAutoGroupRegroupingAtCount = 1;
+ } else {
+ mAutoGroupRegroupingAtCount = mAutoGroupAtCount;
+ }
NOTIFICATION_SHADE_SECTIONS = getNotificationShadeSections();
}
@@ -865,7 +887,8 @@ public class GroupHelper {
}
}
- regroupNotifications(userId, pkgName, notificationsToCheck);
+ regroupNotifications(userId, pkgName, notificationsToCheck,
+ REGROUP_REASON_CHANNEL_UPDATE);
}
}
@@ -883,13 +906,14 @@ public class GroupHelper {
ArrayMap<String, NotificationRecord> notificationsToCheck = new ArrayMap<>();
notificationsToCheck.put(record.getKey(), record);
regroupNotifications(record.getUserId(), record.getSbn().getPackageName(),
- notificationsToCheck);
+ notificationsToCheck, REGROUP_REASON_BUNDLE);
}
}
@GuardedBy("mAggregatedNotifications")
private void regroupNotifications(int userId, String pkgName,
- ArrayMap<String, NotificationRecord> notificationsToCheck) {
+ ArrayMap<String, NotificationRecord> notificationsToCheck,
+ @RegroupingReason int regroupingReason) {
// The list of notification operations required after the channel update
final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
@@ -904,12 +928,42 @@ public class GroupHelper {
notificationsToMove.addAll(
getUngroupedNotificationsMoveOps(userId, pkgName, notificationsToCheck));
+ // Handle "grouped correctly" notifications that were re-classified (bundled)
+ if (notificationRegroupOnClassification()) {
+ if (regroupingReason == REGROUP_REASON_BUNDLE) {
+ notificationsToMove.addAll(
+ getReclassifiedNotificationsMoveOps(userId, pkgName, notificationsToCheck));
+ }
+ }
+
// Batch move to new section
if (!notificationsToMove.isEmpty()) {
moveNotificationsToNewSection(userId, pkgName, notificationsToMove);
}
}
+ private List<NotificationMoveOp> getReclassifiedNotificationsMoveOps(int userId,
+ String pkgName, ArrayMap<String, NotificationRecord> notificationsToCheck) {
+ final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
+ for (NotificationRecord record : notificationsToCheck.values()) {
+ if (isChildOfValidAppGroup(record)) {
+ // Check if section changes
+ NotificationSectioner sectioner = getSection(record);
+ if (sectioner != null) {
+ FullyQualifiedGroupKey newFullAggregateGroupKey =
+ new FullyQualifiedGroupKey(userId, pkgName, sectioner);
+ if (DEBUG) {
+ Slog.v(TAG, "Regroup after classification: " + record + " to: "
+ + newFullAggregateGroupKey);
+ }
+ notificationsToMove.add(
+ new NotificationMoveOp(record, null, newFullAggregateGroupKey));
+ }
+ }
+ }
+ return notificationsToMove;
+ }
+
@GuardedBy("mAggregatedNotifications")
private List<NotificationMoveOp> getAutogroupedNotificationsMoveOps(int userId, String pkgName,
ArrayMap<String, NotificationRecord> notificationsToCheck) {
@@ -1010,6 +1064,10 @@ public class GroupHelper {
// Bundled operations to apply to groups affected by the channel update
ArrayMap<FullyQualifiedGroupKey, GroupUpdateOp> groupsToUpdate = new ArrayMap<>();
+ // App-provided (valid) groups of notifications that were classified (bundled).
+ // Summaries will be canceled if all child notifications have been bundled.
+ ArrayMap<String, String> originalGroupsOfBundledNotifications = new ArrayMap<>();
+
for (NotificationMoveOp moveOp: notificationsToMove) {
final NotificationRecord record = moveOp.record;
final FullyQualifiedGroupKey oldFullAggregateGroupKey = moveOp.oldGroup;
@@ -1035,6 +1093,13 @@ public class GroupHelper {
groupsToUpdate.put(oldFullAggregateGroupKey,
new GroupUpdateOp(oldFullAggregateGroupKey, record, true));
}
+ } else {
+ if (notificationRegroupOnClassification()) {
+ // Null "old aggregate group" => this notification was re-classified from
+ // a valid app-provided group => maybe cancel the original summary
+ // if no children are left
+ originalGroupsOfBundledNotifications.put(record.getKey(), record.getGroupKey());
+ }
}
// Add moved notifications to the ungrouped list for new group and do grouping
@@ -1076,7 +1141,7 @@ public class GroupHelper {
NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record;
boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary;
//Group needs to be created/updated
- if (ungrouped.size() >= mAutoGroupAtCount
+ if (ungrouped.size() >= mAutoGroupRegroupingAtCount
|| (hasSummary && !aggregatedNotificationsAttrs.isEmpty())) {
NotificationSectioner sectioner = getSection(triggeringNotification);
if (sectioner == null) {
@@ -1092,6 +1157,18 @@ public class GroupHelper {
}
}
}
+
+ if (notificationRegroupOnClassification()) {
+ // Cancel the summary if it's the last notification of the original app-provided group
+ for (String triggeringKey : originalGroupsOfBundledNotifications.keySet()) {
+ NotificationRecord canceledSummary =
+ mCallback.removeAppProvidedSummaryOnClassification(triggeringKey,
+ originalGroupsOfBundledNotifications.getOrDefault(triggeringKey, null));
+ if (canceledSummary != null) {
+ cacheCanceledSummary(canceledSummary);
+ }
+ }
+ }
}
static String getFullAggregateGroupKey(String pkgName,
@@ -1113,6 +1190,42 @@ public class GroupHelper {
return (record.mOriginalFlags & Notification.FLAG_AUTOGROUP_SUMMARY) != 0;
}
+ private boolean isNotificationAggregatedInSection(NotificationRecord record,
+ NotificationSectioner sectioner) {
+ final FullyQualifiedGroupKey fullAggregateGroupKey = new FullyQualifiedGroupKey(
+ record.getUserId(), record.getSbn().getPackageName(), sectioner);
+ return record.getGroupKey().equals(fullAggregateGroupKey.toString());
+ }
+
+ private boolean isChildOfValidAppGroup(NotificationRecord record) {
+ final StatusBarNotification sbn = record.getSbn();
+ if (!sbn.isAppGroup()) {
+ return false;
+ }
+
+ if (!sbn.getNotification().isGroupChild()) {
+ return false;
+ }
+
+ if (record.isCanceled) {
+ return false;
+ }
+
+ final NotificationSectioner sectioner = getSection(record);
+ if (sectioner == null) {
+ if (DEBUG) {
+ Slog.i(TAG, "Skipping autogrouping for " + record + " no valid section found.");
+ }
+ return false;
+ }
+
+ if (isNotificationAggregatedInSection(record, sectioner)) {
+ return false;
+ }
+
+ return true;
+ }
+
private static int getNumChildrenForGroup(@NonNull final String groupKey,
final List<NotificationRecord> notificationList) {
//TODO (b/349072751): track grouping state in GroupHelper -> do not use notificationList
@@ -1438,6 +1551,48 @@ public class GroupHelper {
}
}
+ protected void dump(PrintWriter pw, String prefix) {
+ synchronized (mAggregatedNotifications) {
+ if (!mUngroupedAbuseNotifications.isEmpty()) {
+ pw.println(prefix + "Ungrouped notifications:");
+ for (FullyQualifiedGroupKey groupKey: mUngroupedAbuseNotifications.keySet()) {
+ if (!mUngroupedAbuseNotifications.getOrDefault(groupKey, new ArrayMap<>())
+ .isEmpty()) {
+ pw.println(prefix + prefix + groupKey.toString());
+ for (String notifKey : mUngroupedAbuseNotifications.get(groupKey)
+ .keySet()) {
+ pw.println(prefix + prefix + prefix + notifKey);
+ }
+ }
+ }
+ pw.println("");
+ }
+
+ if (!mAggregatedNotifications.isEmpty()) {
+ pw.println(prefix + "Autogrouped notifications:");
+ for (FullyQualifiedGroupKey groupKey: mAggregatedNotifications.keySet()) {
+ if (!mAggregatedNotifications.getOrDefault(groupKey, new ArrayMap<>())
+ .isEmpty()) {
+ pw.println(prefix + prefix + groupKey.toString());
+ for (String notifKey : mAggregatedNotifications.get(groupKey).keySet()) {
+ pw.println(prefix + prefix + prefix + notifKey);
+ }
+ }
+ }
+ pw.println("");
+ }
+
+ if (!mCanceledSummaries.isEmpty()) {
+ pw.println(prefix + "Cached canceled summaries:");
+ for (CachedSummary summary: mCanceledSummaries.values()) {
+ pw.println(prefix + prefix + prefix + summary.key + " -> "
+ + summary.originalGroupKey);
+ }
+ pw.println("");
+ }
+ }
+ }
+
protected static class NotificationSectioner {
final String mName;
final int mSummaryId;
@@ -1551,5 +1706,16 @@ public class GroupHelper {
void removeNotificationFromCanceledGroup(int userId, String pkg, String groupKey,
int cancelReason);
+
+ /**
+ * Cancels the group summary of a notification that was regrouped because of classification
+ * (bundling). Only cancels if the summary is the last notification of the original group.
+ * @param triggeringKey the triggering child notification key
+ * @param groupKey the original group key
+ * @return the canceled group summary or null if the summary was not canceled
+ */
+ @Nullable
+ NotificationRecord removeAppProvidedSummaryOnClassification(String triggeringKey,
+ @Nullable String groupKey);
}
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 122836e19d58..93482e769a2b 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -21,9 +21,6 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.DEVICE_POLICY_SERVICE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-import static android.content.pm.PackageManager.MATCH_INSTANT;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
@@ -109,8 +106,7 @@ abstract public class ManagedServices {
protected final String TAG = getClass().getSimpleName().replace('$', '.');
protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- protected static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
- protected static final int ON_BINDING_DIED_REBIND_MSG = 1234;
+ private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
protected static final String ENABLED_SERVICES_SEPARATOR = ":";
private static final String DB_VERSION_1 = "1";
private static final String DB_VERSION_2 = "2";
@@ -879,21 +875,7 @@ abstract public class ManagedServices {
String approvedItem = getApprovedValue(pkgOrComponent);
if (approvedItem != null) {
- final ComponentName component = ComponentName.unflattenFromString(approvedItem);
if (enabled) {
- if (Flags.notificationNlsRebind()) {
- if (component != null && !isValidService(component, userId)) {
- // Only fail if package is available
- // If not, it will be validated again in onPackagesChanged
- final PackageManager pm = mContext.getPackageManager();
- if (pm.isPackageAvailable(component.getPackageName())) {
- Slog.w(TAG, "Skip allowing " + mConfig.caption
- + " " + pkgOrComponent + " (userSet: " + userSet
- + ") for invalid service");
- return;
- }
- }
- }
approved.add(approvedItem);
} else {
approved.remove(approvedItem);
@@ -991,7 +973,7 @@ abstract public class ManagedServices {
|| isPackageOrComponentAllowed(component.getPackageName(), userId))) {
return false;
}
- return isValidService(component, userId);
+ return componentHasBindPermission(component, userId);
}
private boolean componentHasBindPermission(ComponentName component, int userId) {
@@ -1238,21 +1220,12 @@ abstract public class ManagedServices {
if (!TextUtils.isEmpty(packageName)) {
queryIntent.setPackage(packageName);
}
-
- if (Flags.notificationNlsRebind()) {
- // Expand the package query
- extraFlags |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
- extraFlags |= MATCH_INSTANT;
- }
-
List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
queryIntent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA | extraFlags,
userId);
- if (DEBUG) {
- Slog.v(TAG, mConfig.serviceInterface + " pkg: " + packageName + " services: "
- + installedServices);
- }
+ if (DEBUG)
+ Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
if (installedServices != null) {
for (int i = 0, count = installedServices.size(); i < count; i++) {
ResolveInfo resolveInfo = installedServices.get(i);
@@ -1352,12 +1325,11 @@ abstract public class ManagedServices {
if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
final ComponentName component = ComponentName.unflattenFromString(
approvedPackageOrComponent);
- if (component != null && !isValidService(component, userId)) {
+ if (component != null && !componentHasBindPermission(component, userId)) {
approved.removeAt(j);
if (DEBUG) {
Slog.v(TAG, "Removing " + approvedPackageOrComponent
- + " from approved list; no bind permission or "
- + "service interface filter found "
+ + " from approved list; no bind permission found "
+ mConfig.bindPermission);
}
}
@@ -1376,15 +1348,6 @@ abstract public class ManagedServices {
}
}
- protected boolean isValidService(ComponentName component, int userId) {
- if (Flags.notificationNlsRebind()) {
- return componentHasBindPermission(component, userId) && queryPackageForServices(
- component.getPackageName(), userId).contains(component);
- } else {
- return componentHasBindPermission(component, userId);
- }
- }
-
protected boolean isValidEntry(String packageOrComponent, int userId) {
return hasMatchingServices(packageOrComponent, userId);
}
@@ -1542,27 +1505,23 @@ abstract public class ManagedServices {
* Called when user switched to unbind all services from other users.
*/
@VisibleForTesting
- void unbindOtherUserServices(int switchedToUser) {
+ void unbindOtherUserServices(int currentUser) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("ManagedServices.unbindOtherUserServices_current" + switchedToUser);
- unbindServicesImpl(switchedToUser, true /* allExceptUser */);
+ t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser);
+ unbindServicesImpl(currentUser, true /* allExceptUser */);
t.traceEnd();
}
- void unbindUserServices(int removedUser) {
+ void unbindUserServices(int user) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("ManagedServices.unbindUserServices" + removedUser);
- unbindServicesImpl(removedUser, false /* allExceptUser */);
+ t.traceBegin("ManagedServices.unbindUserServices" + user);
+ unbindServicesImpl(user, false /* allExceptUser */);
t.traceEnd();
}
void unbindServicesImpl(int user, boolean allExceptUser) {
final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
synchronized (mMutex) {
- if (Flags.notificationNlsRebind()) {
- // Remove enqueued rebinds to avoid rebinding services for a switched user
- mHandler.removeMessages(ON_BINDING_DIED_REBIND_MSG);
- }
final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
for (ManagedServiceInfo info : removableBoundServices) {
if ((allExceptUser && (info.userid != user))
@@ -1757,7 +1716,6 @@ abstract public class ManagedServices {
mServicesRebinding.add(servicesBindingTag);
mHandler.postDelayed(() ->
reregisterService(name, userid),
- ON_BINDING_DIED_REBIND_MSG,
ON_BINDING_DIED_REBIND_DELAY_MS);
} else {
Slog.v(TAG, getCaption() + " not rebinding in user " + userid
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2c45fc89ff0b..4d0c7ec64317 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -82,6 +82,8 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BA
import static android.app.NotificationManager.zenModeFromInterruptionFilter;
import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING;
import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
@@ -162,8 +164,6 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
-import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
-import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING;
import static com.android.server.notification.Flags.expireBitmaps;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
@@ -474,6 +474,10 @@ public class NotificationManagerService extends SystemService {
Adjustment.KEY_TYPE
};
+ static final Integer[] DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES = new Integer[] {
+ TYPE_PROMOTION
+ };
+
static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
RoleManager.ROLE_DIALER,
RoleManager.ROLE_EMERGENCY
@@ -1929,6 +1933,12 @@ public class NotificationManagerService extends SystemService {
hasSensitiveContent, lifespanMs);
}
+ protected void logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting,
+ int classification, int lifespanMs) {
+ FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_CLASSIFICATION,
+ hasPosted, isAlerting, classification, lifespanMs);
+ }
+
protected final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -2998,6 +3008,16 @@ public class NotificationManagerService extends SystemService {
groupKey, REASON_APP_CANCEL, SystemClock.elapsedRealtime());
}
}
+
+ @Override
+ @Nullable
+ public NotificationRecord removeAppProvidedSummaryOnClassification(String triggeringKey,
+ @Nullable String oldGroupKey) {
+ synchronized (mNotificationLock) {
+ return removeAppProvidedSummaryOnClassificationLocked(triggeringKey,
+ oldGroupKey);
+ }
+ }
});
}
@@ -4189,6 +4209,22 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public @NonNull int[] getAllowedAdjustmentKeyTypes() {
+ checkCallerIsSystemOrSystemUiOrShell();
+ return mAssistants.getAllowedAdjustmentKeyTypes();
+ }
+
+ @Override
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void setAssistantAdjustmentKeyTypeState(int type, boolean enabled) {
+ checkCallerIsSystemOrSystemUiOrShell();
+ mAssistants.setAssistantAdjustmentKeyTypeState(type, enabled);
+
+ handleSavePolicyFile();
+ }
+
+ @Override
@FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
public boolean appCanBePromoted(String pkg, int uid) {
checkCallerIsSystemOrSystemUiOrShell();
@@ -6977,19 +7013,30 @@ public class NotificationManagerService extends SystemService {
if (!mAssistants.isAdjustmentAllowed(potentialKey)) {
toRemove.add(potentialKey);
}
+ if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) {
+ if (!mAssistants.isAdjustmentKeyTypeAllowed(adjustments.getInt(KEY_TYPE))) {
+ toRemove.add(potentialKey);
+ }
+ }
}
for (String removeKey : toRemove) {
adjustments.remove(removeKey);
}
- if (android.service.notification.Flags.notificationClassification()
- && adjustments.containsKey(KEY_TYPE)) {
+ if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) {
final NotificationChannel newChannel = getClassificationChannelLocked(r,
adjustments);
if (newChannel == null || newChannel.getId().equals(r.getChannel().getId())) {
adjustments.remove(KEY_TYPE);
} else {
+ // Save the app-provided type for logging.
+ int classification = adjustments.getInt(KEY_TYPE);
// swap app provided type with the real thing
adjustments.putParcelable(KEY_TYPE, newChannel);
+ // Note that this value of isAlerting does not fully indicate whether a notif
+ // would make a sound or HUN on device; it is an approximation for metrics.
+ boolean isAlerting = r.getChannel().getImportance() >= IMPORTANCE_DEFAULT;
+ logClassificationChannelAdjustmentReceived(isPosted, isAlerting, classification,
+ r.getLifespanMs(System.currentTimeMillis()));
}
}
r.addAdjustment(adjustment);
@@ -7114,6 +7161,50 @@ public class NotificationManagerService extends SystemService {
}
@GuardedBy("mNotificationLock")
+ @Nullable
+ NotificationRecord removeAppProvidedSummaryOnClassificationLocked(String triggeringKey,
+ @Nullable String oldGroupKey) {
+ NotificationRecord canceledSummary = null;
+ NotificationRecord r = mNotificationsByKey.get(triggeringKey);
+ if (r == null || oldGroupKey == null) {
+ return null;
+ }
+
+ if (r.getSbn().isAppGroup() && r.getNotification().isGroupChild()) {
+ NotificationRecord groupSummary = mSummaryByGroupKey.get(oldGroupKey);
+ // We only care about app-provided valid groups
+ if (groupSummary != null && !GroupHelper.isAggregatedGroup(groupSummary)) {
+ List<NotificationRecord> notificationsInGroup =
+ findGroupNotificationsLocked(r.getSbn().getPackageName(),
+ oldGroupKey, r.getUserId());
+ // Remove the app-provided summary if only the summary is left in the
+ // original group, or summary + triggering notification that will be
+ // regrouped
+ boolean isOnlySummaryLeft =
+ (notificationsInGroup.size() <= 1)
+ || (notificationsInGroup.size() == 2
+ && notificationsInGroup.contains(r)
+ && notificationsInGroup.contains(groupSummary));
+ if (isOnlySummaryLeft) {
+ if (DBG) {
+ Slog.i(TAG, "Removing app summary (all children bundled): "
+ + groupSummary);
+ }
+ canceledSummary = groupSummary;
+ mSummaryByGroupKey.remove(oldGroupKey);
+ cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(),
+ groupSummary.getSbn().getPackageName(),
+ groupSummary.getSbn().getTag(),
+ groupSummary.getSbn().getId(), 0, 0, false, groupSummary.getUserId(),
+ NotificationListenerService.REASON_GROUP_OPTIMIZATION, null);
+ }
+ }
+ }
+
+ return canceledSummary;
+ }
+
+ @GuardedBy("mNotificationLock")
private boolean hasAutoGroupSummaryLocked(NotificationRecord record) {
final String autbundledGroupKey;
if (notificationForceGrouping()) {
@@ -7493,6 +7584,11 @@ public class NotificationManagerService extends SystemService {
mTtlHelper.dump(pw, " ");
}
}
+
+ if (notificationForceGrouping()) {
+ pw.println("\n GroupHelper:");
+ mGroupHelper.dump(pw, " ");
+ }
}
}
@@ -7770,10 +7866,11 @@ public class NotificationManagerService extends SystemService {
// Make Notification silent
r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
- // Repost
+ // Repost as the original app (even if it was posted by a delegate originally
+ // because the delegate may now be revoked)
enqueueNotificationInternal(r.getSbn().getPackageName(),
- r.getSbn().getOpPkg(), r.getSbn().getUid(),
- r.getSbn().getInitialPid(), r.getSbn().getTag(),
+ r.getSbn().getPackageName(), r.getSbn().getUid(),
+ MY_PID, r.getSbn().getTag(),
r.getSbn().getId(), r.getNotification(),
r.getSbn().getUserId(), /* postSilently= */ true,
/* byForegroundService= */ false,
@@ -8012,7 +8109,6 @@ public class NotificationManagerService extends SystemService {
r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg));
boolean isImportanceFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
r.setImportanceFixed(isImportanceFixed);
-
if (notification.isFgsOrUij()) {
if (((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
|| !channel.isUserVisibleTaskShown())
@@ -11552,11 +11648,15 @@ public class NotificationManagerService extends SystemService {
private static final String ATT_TYPES = "types";
private static final String ATT_DENIED = "denied_adjustments";
+ private static final String ATT_ENABLED_TYPES = "enabled_key_types";
private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments";
private final Object mLock = new Object();
@GuardedBy("mLock")
+ private Set<Integer> mAllowedAdjustmentKeyTypes = new ArraySet<>();
+
+ @GuardedBy("mLock")
private Set<String> mAllowedAdjustments = new ArraySet<>();
@GuardedBy("mLock")
@@ -11639,6 +11739,8 @@ public class NotificationManagerService extends SystemService {
for (int i = 0; i < DEFAULT_ALLOWED_ADJUSTMENTS.length; i++) {
mAllowedAdjustments.add(DEFAULT_ALLOWED_ADJUSTMENTS[i]);
}
+ } else {
+ mAllowedAdjustmentKeyTypes.addAll(List.of(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES));
}
}
@@ -11726,6 +11828,42 @@ public class NotificationManagerService extends SystemService {
}
}
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ protected @NonNull boolean isAdjustmentKeyTypeAllowed(@Adjustment.Types int type) {
+ synchronized (mLock) {
+ if (notificationClassification()) {
+ return mAllowedAdjustmentKeyTypes.contains(type);
+ }
+ }
+ return false;
+ }
+
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ protected @NonNull int[] getAllowedAdjustmentKeyTypes() {
+ synchronized (mLock) {
+ if (notificationClassification()) {
+ return mAllowedAdjustmentKeyTypes.stream()
+ .mapToInt(Integer::intValue).toArray();
+ }
+ }
+ return new int[]{};
+ }
+
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void setAssistantAdjustmentKeyTypeState(@Adjustment.Types int type,
+ boolean enabled) {
+ if (!android.service.notification.Flags.notificationClassification()) {
+ return;
+ }
+ synchronized (mLock) {
+ if (enabled) {
+ mAllowedAdjustmentKeyTypes.add(type);
+ } else {
+ mAllowedAdjustmentKeyTypes.remove(type);
+ }
+ }
+ }
+
protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) {
for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
ArrayList<String> keys = new ArrayList<>(records.size());
@@ -12165,27 +12303,46 @@ public class NotificationManagerService extends SystemService {
@Override
protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
- if (!android.service.notification.Flags.notificationClassification()) {
+ if (!notificationClassification()) {
return;
}
synchronized (mLock) {
out.startTag(null, ATT_DENIED);
out.attribute(null, ATT_TYPES, TextUtils.join(",", mDeniedAdjustments));
out.endTag(null, ATT_DENIED);
+ out.startTag(null, ATT_ENABLED_TYPES);
+ out.attribute(null, ATT_TYPES,
+ TextUtils.join(",", mAllowedAdjustmentKeyTypes));
+ out.endTag(null, ATT_ENABLED_TYPES);
}
}
@Override
protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
- if (!android.service.notification.Flags.notificationClassification()) {
+ if (!notificationClassification()) {
return;
}
if (ATT_DENIED.equals(tag)) {
- final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
+ final String keys = XmlUtils.readStringAttribute(parser, ATT_TYPES);
synchronized (mLock) {
mDeniedAdjustments.clear();
+ if (!TextUtils.isEmpty(keys)) {
+ mDeniedAdjustments.addAll(Arrays.asList(keys.split(",")));
+ }
+ }
+ } else if (ATT_ENABLED_TYPES.equals(tag)) {
+ final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
+ synchronized (mLock) {
+ mAllowedAdjustmentKeyTypes.clear();
if (!TextUtils.isEmpty(types)) {
- mDeniedAdjustments.addAll(Arrays.asList(types.split(",")));
+ List<String> typeList = Arrays.asList(types.split(","));
+ for (String type : typeList) {
+ try {
+ mAllowedAdjustmentKeyTypes.add(Integer.parseInt(type));
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Bad type specified", e);
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index d5f13a8ff495..cfeacdf2bb0d 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -2422,7 +2422,7 @@ public class ZenModeHelper {
|| (mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
// call restrictions
final boolean muteCalls = zenAlarmsOnly
- || (zenPriorityOnly && !(allowCalls || allowRepeatCallers))
+ || (zenPriorityOnly && (!allowCalls || !allowRepeatCallers))
|| (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
// alarm restrictions
final boolean muteAlarms = zenPriorityOnly && !allowAlarms;
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index c479acfd6228..f79d9ef174ea 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -194,13 +194,3 @@ flag {
description: "Enables sound uri with vibration source in notification channel"
bug: "351975435"
}
-
-flag {
- name: "notification_nls_rebind"
- namespace: "systemui"
- description: "Check for NLS service intent filter when rebinding services"
- bug: "347674739"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 015b7fd74211..38f39393a025 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -19,6 +19,7 @@ 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;
@@ -162,11 +163,15 @@ public class OverlayActorEnforcer {
return ActorState.UNABLE_TO_GET_TARGET_OVERLAYABLE;
}
- if (targetOverlayable == null) {
+ // 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"))) {
return ActorState.MISSING_OVERLAYABLE;
}
- String actor = targetOverlayable.actor;
+ final String actor = targetOverlayable == null ? null : 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/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5653da07779b..2c0942337b1f 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -88,6 +88,7 @@ import android.content.pm.ShortcutServiceInternal;
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
import android.content.pm.UserInfo;
import android.content.pm.UserProperties;
+import android.database.ContentObserver;
import android.graphics.Rect;
import android.multiuser.Flags;
import android.net.Uri;
@@ -95,6 +96,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IInterface;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -249,6 +251,7 @@ public class LauncherAppsService extends SystemService {
private PackageInstallerService mPackageInstallerService;
final LauncherAppsServiceInternal mInternal;
+ private SecureSettingsObserver mSecureSettingsObserver;
@NonNull
private final RemoteCallbackList<IDumpCallback> mDumpCallbacks =
@@ -278,6 +281,7 @@ public class LauncherAppsService extends SystemService {
mCallbackHandler = BackgroundThread.getHandler();
mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
mInternal = new LocalService();
+ registerSettingsObserver();
}
@VisibleForTesting
@@ -2312,6 +2316,13 @@ public class LauncherAppsService extends SystemService {
}
}
+ void registerSettingsObserver() {
+ if (Flags.addLauncherUserConfig()) {
+ mSecureSettingsObserver = new SecureSettingsObserver();
+ mSecureSettingsObserver.register();
+ }
+ }
+
public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback {
private final UserManagerInternal mUserManagerInternal;
@@ -2837,5 +2848,84 @@ public class LauncherAppsService extends SystemService {
shortcutId, sourceBounds, startActivityOptions, targetUserId);
}
}
+
+ class SecureSettingsObserver extends ContentObserver {
+
+ SecureSettingsObserver() {
+ super(new Handler(Looper.getMainLooper()));
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ if (uri.equals(
+ Settings.Secure.getUriFor(Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT))) {
+
+ // This setting key only apply to private profile at the moment
+ UserHandle privateProfile = getPrivateProfile();
+ if (privateProfile.getIdentifier() == UserHandle.USER_NULL) {
+ return;
+ }
+
+ final int n = mListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < n; i++) {
+ final IOnAppsChangedListener listener =
+ mListeners.getBroadcastItem(i);
+ final BroadcastCookie cookie =
+ (BroadcastCookie) mListeners.getBroadcastCookie(
+ i);
+ if (!isEnabledProfileOf(cookie, privateProfile,
+ "onSecureSettingsChange")) {
+ Log.d(TAG, "onSecureSettingsChange: Skipping - profile not enabled"
+ + " or not accessible for package=" + cookie.packageName
+ + ", packageUid=" + cookie.callingUid);
+ } else {
+ try {
+ Log.d(TAG,
+ "onUserConfigChanged: triggering onUserConfigChanged");
+ listener.onUserConfigChanged(
+ mUserManagerInternal.getLauncherUserInfo(
+ privateProfile.getIdentifier()));
+ } catch (RemoteException re) {
+ Slog.d(TAG, "onUserConfigChanged: Callback failed ", re);
+ }
+ }
+ }
+ } finally {
+ mListeners.finishBroadcast();
+ }
+ }
+ }
+
+ public void register() {
+ UserHandle privateProfile = getPrivateProfile();
+ int parentUserId;
+ if (privateProfile.getIdentifier() == UserHandle.USER_NULL) {
+ // No private space available, register the observer for the current user
+ parentUserId = mContext.getUserId();
+ } else {
+ parentUserId = mUserManagerInternal.getProfileParentId(
+ privateProfile.getIdentifier());
+ }
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT),
+ true, this, parentUserId);
+ }
+
+ public void unregister() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ }
+
+ private UserHandle getPrivateProfile() {
+ UserInfo[] userInfos = mUserManagerInternal.getUserInfos();
+ for (UserInfo u : userInfos) {
+ if (u.isPrivateProfile()) {
+ return UserHandle.of(u.id);
+ }
+ }
+ return UserHandle.of(UserHandle.USER_NULL);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7ecfe7f64ffe..06e29c2c1408 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -21,6 +21,7 @@ import static android.content.Intent.ACTION_SCREEN_ON;
import static android.content.Intent.EXTRA_USER_ID;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.LauncherUserInfo.PRIVATE_SPACE_ENTRYPOINT_HIDDEN;
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -32,6 +33,7 @@ import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN;
import static android.os.UserManager.USER_OPERATION_ERROR_USER_RESTRICTED;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
+import static android.provider.Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT;
import static com.android.internal.app.SetScreenLockDialogActivity.EXTRA_ORIGIN_USER_ID;
import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_DISABLE_QUIET_MODE;
@@ -341,6 +343,10 @@ public class UserManagerService extends IUserManager.Stub {
private static final String TRON_USER_CREATED = "users_user_created";
private static final String TRON_DEMO_CREATED = "users_demo_created";
+ // The boot user strategy for HSUM.
+ private static final int BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER = 0;
+ private static final int BOOT_TO_HSU_FOR_PROVISIONED_DEVICE = 1;
+
private final Context mContext;
private final PackageManagerService mPm;
@@ -1391,37 +1397,77 @@ public class UserManagerService extends IUserManager.Stub {
}
if (isHeadlessSystemUserMode()) {
- if (mContext.getResources()
- .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser)) {
- return UserHandle.USER_SYSTEM;
- }
- // Return the previous foreground user, if there is one.
- final int previousUser = getPreviousFullUserToEnterForeground();
- if (previousUser != UserHandle.USER_NULL) {
- Slogf.i(LOG_TAG, "Boot user is previous user %d", previousUser);
- return previousUser;
+ final int bootStrategy = mContext.getResources()
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
+ switch (bootStrategy) {
+ case BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER:
+ return getPreviousOrFirstSwitchableUser();
+ case BOOT_TO_HSU_FOR_PROVISIONED_DEVICE:
+ return getBootUserBasedOnProvisioning();
+ default:
+ Slogf.w(LOG_TAG, "Unknown HSUM boot strategy: %d", bootStrategy);
+ return getPreviousOrFirstSwitchableUser();
}
- // No previous user. Return the first switchable user if there is one.
- synchronized (mUsersLock) {
- final int userSize = mUsers.size();
- for (int i = 0; i < userSize; i++) {
- final UserData userData = mUsers.valueAt(i);
- if (userData.info.supportsSwitchToByUser()) {
- int firstSwitchable = userData.info.id;
- Slogf.i(LOG_TAG,
- "Boot user is first switchable user %d", firstSwitchable);
- return firstSwitchable;
- }
- }
- }
- // No switchable users found. Uh oh!
- throw new UserManager.CheckedUserOperationException(
- "No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
}
// Not HSUM, return system user.
return UserHandle.USER_SYSTEM;
}
+ private @UserIdInt int getBootUserBasedOnProvisioning()
+ throws UserManager.CheckedUserOperationException {
+ final boolean provisioned = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ if (provisioned) {
+ return UserHandle.USER_SYSTEM;
+ } else {
+ final int firstSwitchableFullUser = getFirstSwitchableUser(true);
+ if (firstSwitchableFullUser != UserHandle.USER_NULL) {
+ Slogf.i(LOG_TAG,
+ "Boot user is first switchable full user %d",
+ firstSwitchableFullUser);
+ return firstSwitchableFullUser;
+ }
+ // No switchable full user found. Uh oh!
+ throw new UserManager.CheckedUserOperationException(
+ "No switchable full user found", USER_OPERATION_ERROR_UNKNOWN);
+ }
+ }
+
+ private @UserIdInt int getPreviousOrFirstSwitchableUser()
+ throws UserManager.CheckedUserOperationException {
+ // Return the previous foreground user, if there is one.
+ final int previousUser = getPreviousFullUserToEnterForeground();
+ if (previousUser != UserHandle.USER_NULL) {
+ Slogf.i(LOG_TAG, "Boot user is previous user %d", previousUser);
+ return previousUser;
+ }
+ // No previous user. Return the first switchable user if there is one.
+ final int firstSwitchableUser = getFirstSwitchableUser(false);
+ if (firstSwitchableUser != UserHandle.USER_NULL) {
+ Slogf.i(LOG_TAG,
+ "Boot user is first switchable user %d", firstSwitchableUser);
+ return firstSwitchableUser;
+ }
+ // No switchable users found. Uh oh!
+ throw new UserManager.CheckedUserOperationException(
+ "No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
+ }
+
+ private @UserIdInt int getFirstSwitchableUser(boolean fullUserOnly) {
+ synchronized (mUsersLock) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ final UserData userData = mUsers.valueAt(i);
+ if (userData.info.supportsSwitchToByUser() &&
+ (!fullUserOnly || userData.info.isFull())) {
+ int firstSwitchable = userData.info.id;
+ return firstSwitchable;
+ }
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
@Override
public int getPreviousFullUserToEnterForeground() {
@@ -7902,11 +7948,25 @@ public class UserManagerService extends IUserManager.Stub {
}
if (userInfo != null) {
final UserTypeDetails userDetails = getUserTypeDetails(userInfo);
- final LauncherUserInfo uiInfo = new LauncherUserInfo.Builder(
- userDetails.getName(),
- userInfo.serialNumber)
- .build();
- return uiInfo;
+
+ if (Flags.addLauncherUserConfig()) {
+ Bundle config = new Bundle();
+ if (userInfo.isPrivateProfile()) {
+ try {
+ int parentId = getProfileParentIdUnchecked(userId);
+ config.putBoolean(PRIVATE_SPACE_ENTRYPOINT_HIDDEN,
+ Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ HIDE_PRIVATESPACE_ENTRY_POINT, parentId) == 1);
+ } catch (Settings.SettingNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return new LauncherUserInfo.Builder(userDetails.getName(),
+ userInfo.serialNumber, config).build();
+ }
+
+ return new LauncherUserInfo.Builder(userDetails.getName(),
+ userInfo.serialNumber).build();
} else {
return null;
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 07fd1cb544f6..acf62dcdd1ce 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -235,6 +235,7 @@ final class DefaultPermissionGrantPolicy {
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.UWB_RANGING);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.NEARBY_WIFI_DEVICES);
+ NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.RANGING);
}
private static final Set<String> NOTIFICATION_PERMISSIONS = new ArraySet<>();
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 24933cab2a32..5fc3e332b95c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1015,8 +1015,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
permission, attributionSource, message, forDataDelivery, startDataDelivery,
fromDatasource, attributedOp);
// Finish any started op if some step in the attribution chain failed.
- if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED
- && result != PermissionChecker.PERMISSION_SOFT_DENIED) {
+ if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) {
if (attributedOp == AppOpsManager.OP_NONE) {
finishDataDelivery(AppOpsManager.permissionToOpCode(permission),
attributionSource.asState(), fromDatasource);
@@ -1245,7 +1244,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final boolean hasChain = attributionChainId != ATTRIBUTION_CHAIN_ID_NONE;
AttributionSource current = attributionSource;
AttributionSource next = null;
- AttributionSource prev = null;
// We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
// every attributionSource in the chain is registered with the system.
final boolean isChainStartTrusted = !hasChain || checkPermission(context,
@@ -1312,21 +1310,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
selfAccess, singleReceiverFromDatasource, attributedOp,
proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
- if (opMode != AppOpsManager.MODE_ALLOWED) {
- // Current failed the perm check, so if we are part-way through an attr chain,
- // we need to clean up the already started proxy op higher up the chain. Note,
- // proxy ops are verified two by two, which means we have to clear the 2nd next
- // from the previous iteration (since it is actually curr.next which failed
- // to pass the perm check).
- if (prev != null) {
- final var cutAttrSourceState = prev.asState();
- if (cutAttrSourceState.next.length > 0) {
- cutAttrSourceState.next[0].next = new AttributionSourceState[0];
- }
- finishDataDelivery(context, attributedOp,
- cutAttrSourceState, fromDatasource);
- }
- if (opMode == AppOpsManager.MODE_ERRORED) {
+ switch (opMode) {
+ case AppOpsManager.MODE_ERRORED: {
if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as op"
+ " mode is MODE_ERRORED. Permission check was requested for: "
@@ -1334,7 +1319,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
+ current);
}
return PermissionChecker.PERMISSION_HARD_DENIED;
- } else {
+ }
+ case AppOpsManager.MODE_IGNORED: {
return PermissionChecker.PERMISSION_SOFT_DENIED;
}
}
@@ -1349,8 +1335,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return PermissionChecker.PERMISSION_GRANTED;
}
- // an attribution we have already possibly started an op for
- prev = current;
current = next;
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fc24e62de04e..1af3ec0b4b63 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -84,6 +84,7 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;
import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable;
+import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
import static com.android.hardware.input.Flags.modifierShortcutDump;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
@@ -3612,7 +3613,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
break;
case KeyEvent.KEYCODE_T:
- if (keyboardA11yShortcutControl()) {
+ if (enableTalkbackAndMagnifierKeyGestures()) {
if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
mTalkbackShortcutController.toggleTalkback(mCurrentUserId,
TalkbackShortcutController.ShortcutSource.KEYBOARD);
@@ -4112,7 +4113,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return mDefaultDisplayPolicy.isAwake() && mAccessibilityShortcutController
.isAccessibilityShortcutAvailable(false);
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
- return keyboardA11yShortcutControl();
+ return enableTalkbackAndMagnifierKeyGestures();
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
return InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
&& keyboardA11yShortcutControl();
@@ -4345,7 +4346,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
- if (keyboardA11yShortcutControl()) {
+ if (enableTalkbackAndMagnifierKeyGestures()) {
if (complete) {
mTalkbackShortcutController.toggleTalkback(mCurrentUserId,
TalkbackShortcutController.ShortcutSource.KEYBOARD);
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 600fe59215b6..606bd1dd0f3f 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -51,6 +51,7 @@ public class BatteryUsageStatsProvider {
private final CpuScalingPolicies mCpuScalingPolicies;
private final int mAccumulatedBatteryUsageStatsSpanSize;
private final Clock mClock;
+ private final MonotonicClock mMonotonicClock;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
private UserPowerCalculator mUserPowerCalculator;
@@ -67,7 +68,7 @@ public class BatteryUsageStatsProvider {
@NonNull PowerAttributor powerAttributor,
@NonNull PowerProfile powerProfile, @NonNull CpuScalingPolicies cpuScalingPolicies,
@NonNull PowerStatsStore powerStatsStore, int accumulatedBatteryUsageStatsSpanSize,
- @NonNull Clock clock) {
+ @NonNull Clock clock, @NonNull MonotonicClock monotonicClock) {
mContext = context;
mPowerAttributor = powerAttributor;
mPowerStatsStore = powerStatsStore;
@@ -75,6 +76,7 @@ public class BatteryUsageStatsProvider {
mCpuScalingPolicies = cpuScalingPolicies;
mAccumulatedBatteryUsageStatsSpanSize = accumulatedBatteryUsageStatsSpanSize;
mClock = clock;
+ mMonotonicClock = monotonicClock;
mUserPowerCalculator = new UserPowerCalculator();
mPowerStatsStore.addSectionReader(new BatteryUsageStatsSection.Reader());
@@ -213,7 +215,7 @@ public class BatteryUsageStatsProvider {
powerStatsSpan.addTimeFrame(accumulatedStats.startMonotonicTime,
accumulatedStats.startWallClockTime,
accumulatedStats.endMonotonicTime - accumulatedStats.startMonotonicTime);
- stats.commitMonotonicClock();
+ mMonotonicClock.write();
mPowerStatsStore.storePowerStatsSpanAsync(powerStatsSpan,
accumulatedStats.builder::discard);
}
@@ -308,23 +310,29 @@ public class BatteryUsageStatsProvider {
private void updateAccumulatedBatteryUsageStats(AccumulatedBatteryUsageStats accumulatedStats,
BatteryStatsImpl stats, BatteryUsageStatsQuery query) {
- // TODO(b/366493365): add the current batteryusagestats directly into
- // `accumulatedStats.builder` to avoid allocating a second CursorWindow
- BatteryUsageStats.Builder remainingBatteryUsageStats = computeBatteryUsageStats(stats,
- query, accumulatedStats.endMonotonicTime, query.getMonotonicEndTime(),
- mClock.currentTimeMillis());
+ long startMonotonicTime = accumulatedStats.endMonotonicTime;
+ if (startMonotonicTime == MonotonicClock.UNDEFINED) {
+ startMonotonicTime = stats.getMonotonicStartTime();
+ }
+ long endWallClockTime = mClock.currentTimeMillis();
+ long endMonotonicTime = mMonotonicClock.monotonicTime();
if (accumulatedStats.builder == null) {
- accumulatedStats.builder = remainingBatteryUsageStats;
+ accumulatedStats.builder = new BatteryUsageStats.Builder(
+ stats.getCustomEnergyConsumerNames(), false, true, true, true, 0);
accumulatedStats.startWallClockTime = stats.getStartClockTime();
- accumulatedStats.startMonotonicTime = stats.getMonotonicStartTime();
- accumulatedStats.endMonotonicTime = accumulatedStats.startMonotonicTime
- + accumulatedStats.builder.getStatsDuration();
- } else {
- accumulatedStats.builder.add(remainingBatteryUsageStats.build());
- accumulatedStats.endMonotonicTime += remainingBatteryUsageStats.getStatsDuration();
- remainingBatteryUsageStats.discard();
+ accumulatedStats.builder.setStatsStartTimestamp(accumulatedStats.startWallClockTime);
}
+
+ accumulatedStats.endMonotonicTime = endMonotonicTime;
+
+ accumulatedStats.builder.setStatsEndTimestamp(endWallClockTime);
+ accumulatedStats.builder.setStatsDuration(endWallClockTime - startMonotonicTime);
+
+ mPowerAttributor.estimatePowerConsumption(accumulatedStats.builder, stats.getHistory(),
+ startMonotonicTime, MonotonicClock.UNDEFINED);
+
+ populateGeneralInfo(accumulatedStats.builder, stats);
}
private BatteryUsageStats.Builder computeBatteryUsageStats(BatteryStatsImpl stats,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index cf178046d2e6..4857b02eaf7c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1725,9 +1725,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
return;
}
Transition transit = task.mTransitionController.requestCloseTransitionIfNeeded(task);
- if (transit == null) {
- transit = task.mTransitionController.getCollectingTransition();
- }
if (transit != null) {
transit.collectClose(task);
if (!task.mTransitionController.useFullReadyTracking()) {
@@ -1739,7 +1736,15 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// before anything that may need it to wait (setReady(false)).
transit.setReady(task, true);
}
+ } else {
+ // If we failed to create a transition, there might be already a currently collecting
+ // transition. Let's use it if possible.
+ transit = task.mTransitionController.getCollectingTransition();
+ if (transit != null) {
+ transit.collectClose(task);
+ }
}
+
// Consume the stopping activities immediately so activity manager won't skip killing
// the process because it is still foreground state, i.e. RESUMED -> PAUSING set from
// removeActivities -> finishIfPossible.
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index db76eb9ac5d9..ebb50db54693 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -164,41 +164,46 @@ final class AppCompatUtils {
appCompatTaskInfo.setIsFromLetterboxDoubleTap(reachabilityOverrides.isFromDoubleTap());
- final Rect bounds = top.getBounds();
- final Rect appBounds = getAppBounds(top);
- appCompatTaskInfo.topActivityLetterboxWidth = bounds.width();
- appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
- appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
- appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
+ final boolean isTopActivityLetterboxed = top.areBoundsLetterboxed();
+ appCompatTaskInfo.setTopActivityLetterboxed(isTopActivityLetterboxed);
+ if (isTopActivityLetterboxed) {
+ final Rect bounds = top.getBounds();
+ final Rect appBounds = getAppBounds(top);
+ appCompatTaskInfo.topActivityLetterboxWidth = bounds.width();
+ appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
+ appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
+ appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
- // We need to consider if letterboxed or pillarboxed.
- // TODO(b/336807329) Encapsulate reachability logic
- appCompatTaskInfo.setLetterboxDoubleTapEnabled(reachabilityOverrides
- .isLetterboxDoubleTapEducationEnabled());
- if (appCompatTaskInfo.isLetterboxDoubleTapEnabled()) {
- if (appCompatTaskInfo.isTopActivityPillarboxed()) {
- if (reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox()) {
- // Pillarboxed.
- appCompatTaskInfo.topActivityLetterboxHorizontalPosition =
- reachabilityOverrides.getLetterboxPositionForHorizontalReachability();
- } else {
- appCompatTaskInfo.setLetterboxDoubleTapEnabled(false);
- }
- } else {
- if (reachabilityOverrides.allowVerticalReachabilityForThinLetterbox()) {
- // Letterboxed.
- appCompatTaskInfo.topActivityLetterboxVerticalPosition =
- reachabilityOverrides.getLetterboxPositionForVerticalReachability();
+ // We need to consider if letterboxed or pillarboxed.
+ // TODO(b/336807329) Encapsulate reachability logic
+ appCompatTaskInfo.setLetterboxDoubleTapEnabled(reachabilityOverrides
+ .isLetterboxDoubleTapEducationEnabled());
+ if (appCompatTaskInfo.isLetterboxDoubleTapEnabled()) {
+ if (appCompatTaskInfo.isTopActivityPillarboxShaped()) {
+ if (reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox()) {
+ // Pillarboxed.
+ appCompatTaskInfo.topActivityLetterboxHorizontalPosition =
+ reachabilityOverrides
+ .getLetterboxPositionForHorizontalReachability();
+ } else {
+ appCompatTaskInfo.setLetterboxDoubleTapEnabled(false);
+ }
} else {
- appCompatTaskInfo.setLetterboxDoubleTapEnabled(false);
+ if (reachabilityOverrides.allowVerticalReachabilityForThinLetterbox()) {
+ // Letterboxed.
+ appCompatTaskInfo.topActivityLetterboxVerticalPosition =
+ reachabilityOverrides.getLetterboxPositionForVerticalReachability();
+ } else {
+ appCompatTaskInfo.setLetterboxDoubleTapEnabled(false);
+ }
}
}
}
+
final boolean eligibleForAspectRatioButton =
!info.isTopActivityTransparent && !appCompatTaskInfo.isTopActivityInSizeCompat()
&& aspectRatioOverrides.shouldEnableUserAspectRatioSettings();
appCompatTaskInfo.setEligibleForUserAspectRatioButton(eligibleForAspectRatioButton);
- appCompatTaskInfo.setTopActivityLetterboxed(top.areBoundsLetterboxed());
appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode =
AppCompatCameraPolicy.getCameraCompatFreeformMode(top);
appCompatTaskInfo.setHasMinAspectRatioOverride(top.mAppCompatController
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index ee07d2e58389..76e8a70768c1 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1736,7 +1736,7 @@ public class DisplayPolicy {
// Show IME over the keyguard if the target allows it.
final boolean showImeOverKeyguard =
- imeTarget != null && imeTarget.isOnScreen() && win.mIsImWindow && (
+ imeTarget != null && win.mIsImWindow && imeTarget.isDisplayed() && (
imeTarget.canShowWhenLocked() || !imeTarget.canBeHiddenByKeyguard());
if (showImeOverKeyguard) {
return false;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 72d45e42125d..a6034664af5a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -3586,6 +3586,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (wc.mWmService.mAtmService.mBackNavigationController.isMonitorTransitionTarget(wc)) {
flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
}
+ final TaskDisplayArea tda = wc.asTaskDisplayArea();
+ if (tda != null) {
+ flags |= TransitionInfo.FLAG_IS_TASK_DISPLAY_AREA;
+ }
final Task task = wc.asTask();
if (task != null) {
final ActivityRecord topActivity = task.getTopNonFinishingActivity();
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 5bde8b5a507c..44e237aa27de 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -614,6 +614,12 @@ class WindowToken extends WindowContainer<WindowState> {
final int rotation = getRelativeDisplayRotation();
if (rotation == Surface.ROTATION_0) return mFixedRotationTransformLeash;
if (mFixedRotationTransformLeash != null) return mFixedRotationTransformLeash;
+ if (ActivityTaskManagerService.isPip2ExperimentEnabled() && asActivityRecord() != null
+ && mTransitionController.getWindowingModeAtStart(
+ asActivityRecord()) == WINDOWING_MODE_PINNED) {
+ // PiP handles fixed rotation animation in Shell, so do not create the rotation leash.
+ return null;
+ }
final SurfaceControl leash = makeSurface().setContainerLayer()
.setParent(getParentSurfaceControl())
diff --git a/services/core/services-jarjar-rules.txt b/services/core/services-jarjar-rules.txt
new file mode 100644
index 000000000000..0d296b238c7a
--- /dev/null
+++ b/services/core/services-jarjar-rules.txt
@@ -0,0 +1,2 @@
+# For profiling flags
+rule android.os.profiling.** android.internal.os.profiling.@1
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index aca6f7235714..c6530381443f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -255,7 +255,6 @@ import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPR
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.provider.DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
@@ -462,7 +461,6 @@ import android.permission.PermissionControllerManager;
import android.provider.CalendarContract;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsInternal;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Telephony;
@@ -908,10 +906,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ "management app's authentication policy";
private static final String NOT_SYSTEM_CALLER_MSG = "Only the system can %s";
- private static final String PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG =
- "enable_permission_based_access";
- private static final boolean DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG = false;
-
private static final int RETRY_COPY_ACCOUNT_ATTEMPTS = 3;
/**
@@ -3486,7 +3480,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@GuardedBy("getLockObject()")
private boolean maybeMigrateSuspendedPackagesLocked(String backupId) {
Slog.i(LOG_TAG, "Migrating suspended packages to policy engine");
- if (!Flags.unmanagedModeMigration()) {
+ if (!Flags.suspendPackagesCoexistence()) {
return false;
}
if (mOwners.isSuspendedPackagesMigrated()) {
@@ -3557,6 +3551,46 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return true;
}
+
+
+ @GuardedBy("getLockObject()")
+ private boolean maybeMigrateMemoryTaggingLocked(String backupId) {
+ if (!Flags.setMtePolicyCoexistence()) {
+ Slog.i(LOG_TAG, "Memory Tagging not migrated because coexistence "
+ + "support is disabled.");
+ return false;
+ }
+ if (mOwners.isMemoryTaggingMigrated()) {
+ // TODO: Remove log after Flags.setMtePolicyCoexistence full rollout.
+ Slog.v(LOG_TAG, "Memory Tagging was previously migrated to policy engine.");
+ return false;
+ }
+
+ Slog.i(LOG_TAG, "Migrating Memory Tagging to policy engine");
+
+ // Create backup if none exists
+ mDevicePolicyEngine.createBackup(backupId);
+ try {
+ iterateThroughDpcAdminsLocked((admin, enforcingAdmin) -> {
+ if (admin.mtePolicy != 0) {
+ Slog.i(LOG_TAG, "Setting Memory Tagging policy");
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.MEMORY_TAGGING,
+ enforcingAdmin,
+ new IntegerPolicyValue(admin.mtePolicy),
+ true /* No need to re-set system properties */);
+ }
+ });
+ } catch (Exception e) {
+ Slog.wtf(LOG_TAG,
+ "Failed to migrate Memory Tagging to policy engine", e);
+ }
+
+ Slog.i(LOG_TAG, "Marking Memory Tagging migration complete");
+ mOwners.markMemoryTaggingMigrated();
+ return true;
+ }
+
/** Register callbacks for statsd pulled atoms. */
private void registerStatsCallbacks() {
final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
@@ -4646,22 +4680,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@GuardedBy("getLockObject()")
private List<ActiveAdmin> getActiveAdminsForLockscreenPoliciesLocked(int userHandle) {
if (isSeparateProfileChallengeEnabled(userHandle)) {
-
- if (isPermissionCheckFlagEnabled()) {
- return getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(userHandle);
- }
// If this user has a separate challenge, only return its restrictions.
return getUserDataUnchecked(userHandle).mAdminList;
}
// If isSeparateProfileChallengeEnabled is false and userHandle points to a managed profile
// we need to query the parent user who owns the credential.
- if (isPermissionCheckFlagEnabled()) {
- return getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(getProfileParentId(userHandle),
- (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
- } else {
- return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
- (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
- }
+ return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
+ (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
}
@@ -4684,33 +4709,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
(user) -> mLockPatternUtils.isProfileWithUnifiedChallenge(user.id));
}
- /**
- * Get the list of active admins for an affected user:
- * <ul>
- * <li>The active admins associated with the userHandle itself</li>
- * <li>The parent active admins for each managed profile associated with the userHandle</li>
- * <li>The permission based admin associated with the userHandle itself</li>
- * </ul>
- *
- * @param userHandle the affected user for whom to get the active admins
- * @return the list of active admins for the affected user
- */
- @GuardedBy("getLockObject()")
- private List<ActiveAdmin> getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(
- int userHandle) {
- List<ActiveAdmin> list;
-
- if (isManagedProfile(userHandle)) {
- list = getUserDataUnchecked(userHandle).mAdminList;
- }
- list = getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(userHandle,
- /* shouldIncludeProfileAdmins */ (user) -> false);
-
- if (getUserData(userHandle).mPermissionBasedAdmin != null) {
- list.add(getUserData(userHandle).mPermissionBasedAdmin);
- }
- return list;
- }
/**
* Returns the list of admins on the given user, as well as parent admins for each managed
@@ -4763,44 +4761,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return mDevicePolicyEngine.getResolvedPolicyAcrossUsers(policyDefinition, users);
}
- /**
- * Returns the list of admins on the given user, as well as parent admins for each managed
- * profile associated with the given user. Optionally also include the admin of each managed
- * profile.
- * <p> Should not be called on a profile user.
- */
- @GuardedBy("getLockObject()")
- private List<ActiveAdmin> getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(int userHandle,
- Predicate<UserInfo> shouldIncludeProfileAdmins) {
- ArrayList<ActiveAdmin> admins = new ArrayList<>();
- mInjector.binderWithCleanCallingIdentity(() -> {
- for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
- DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
- if (userInfo.id == userHandle) {
- admins.addAll(policy.mAdminList);
- if (policy.mPermissionBasedAdmin != null) {
- admins.add(policy.mPermissionBasedAdmin);
- }
- } else if (userInfo.isManagedProfile()) {
- for (int i = 0; i < policy.mAdminList.size(); i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (admin.hasParentActiveAdmin()) {
- admins.add(admin.getParentActiveAdmin());
- }
- if (shouldIncludeProfileAdmins.test(userInfo)) {
- admins.add(admin);
- }
- }
- if (policy.mPermissionBasedAdmin != null
- && shouldIncludeProfileAdmins.test(userInfo)) {
- admins.add(policy.mPermissionBasedAdmin);
- }
- }
- }
- });
- return admins;
- }
-
private boolean isSeparateProfileChallengeEnabled(int userHandle) {
return mInjector.binderWithCleanCallingIdentity(() ->
mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle));
@@ -4893,25 +4853,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(who, "ComponentName is null");
- }
+ Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkArgumentNonnegative(timeout, "Timeout must be >= 0 ms");
int userHandle = mInjector.userHandleGetCallingUserId();
int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
synchronized (getLockObject()) {
ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- CallerIdentity caller = getCallerIdentity(who, callerPackageName);
- ap = enforcePermissionAndGetEnforcingAdmin(
- who, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- caller.getPackageName(), affectedUserId)
- .getActiveAdmin();
- } else {
- ap = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD, parent);
- }
+ ap = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD, parent);
// Calling this API automatically bumps the expiration date
final long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
ap.passwordExpirationDate = expiration;
@@ -4972,28 +4922,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean addCrossProfileWidgetProvider(ComponentName admin, String callerPackageName,
String packageName) {
- CallerIdentity caller;
+ CallerIdentity caller = getCallerIdentity(admin);
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- } else {
- caller = getCallerIdentity(admin);
- }
- ActiveAdmin activeAdmin;
+ Objects.requireNonNull(admin, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isProfileOwner(caller));
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- admin,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- caller.getUserId());
- activeAdmin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(admin, "ComponentName is null");
- Preconditions.checkCallAuthorization(isProfileOwner(caller));
- synchronized (getLockObject()) {
- activeAdmin = getProfileOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin activeAdmin;
+ synchronized (getLockObject()) {
+ activeAdmin = getProfileOwnerLocked(caller.getUserId());
}
List<String> changedProviders = null;
@@ -5026,28 +4962,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean removeCrossProfileWidgetProvider(ComponentName admin, String callerPackageName,
String packageName) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- } else {
- caller = getCallerIdentity(admin);
- }
+ CallerIdentity caller = getCallerIdentity(admin);
- ActiveAdmin activeAdmin;
+ Objects.requireNonNull(admin, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isProfileOwner(caller));
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- admin,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- caller.getUserId());
- activeAdmin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(admin, "ComponentName is null");
- Preconditions.checkCallAuthorization(isProfileOwner(caller));
- synchronized (getLockObject()) {
- activeAdmin = getProfileOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin activeAdmin;
+ synchronized (getLockObject()) {
+ activeAdmin = getProfileOwnerLocked(caller.getUserId());
}
List<String> changedProviders = null;
@@ -5080,27 +5002,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public List<String> getCrossProfileWidgetProviders(ComponentName admin,
String callerPackageName) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- } else {
- caller = getCallerIdentity(admin);
- }
- ActiveAdmin activeAdmin;
+ CallerIdentity caller = getCallerIdentity(admin);
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin(
- admin,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- caller.getUserId());
- activeAdmin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(admin, "ComponentName is null");
- Preconditions.checkCallAuthorization(isProfileOwner(caller));
- synchronized (getLockObject()) {
- activeAdmin = getProfileOwnerLocked(caller.getUserId());
- }
+ Objects.requireNonNull(admin, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isProfileOwner(caller));
+
+ ActiveAdmin activeAdmin;
+ synchronized (getLockObject()) {
+ activeAdmin = getProfileOwnerLocked(caller.getUserId());
}
synchronized (getLockObject()) {
@@ -5449,24 +5358,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforceUserUnlocked(userHandle, parent);
synchronized (getLockObject()) {
- if (isPermissionCheckFlagEnabled()) {
- int affectedUser = parent ? getProfileParentId(userHandle) : userHandle;
- enforcePermission(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- callerPackageName, affectedUser);
- } else {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- }
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(
+ null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
int credentialOwner = getCredentialOwner(userHandle, parent);
DevicePolicyData policy = getUserDataUnchecked(credentialOwner);
PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
final int userToCheck = getProfileParentUserIfRequested(userHandle, parent);
- boolean activePasswordSufficientForUserLocked = isActivePasswordSufficientForUserLocked(
+ return isActivePasswordSufficientForUserLocked(
policy.mPasswordValidAtLastCheckpoint, metrics, userToCheck);
- return activePasswordSufficientForUserLocked;
}
}
@@ -5622,21 +5524,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
isDefaultDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller),
"Only profile owner, device owner and system may call this method on parent.");
} else {
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY)
- || hasCallingOrSelfPermission(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS)
- || isDefaultDeviceOwner(caller) || isProfileOwner(caller),
- "Must have " + REQUEST_PASSWORD_COMPLEXITY + " or " +
- MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS
- + " permissions, or be a profile owner or device owner.");
- } else {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY)
- || isDefaultDeviceOwner(caller) || isProfileOwner(caller),
- "Must have " + REQUEST_PASSWORD_COMPLEXITY
- + " permission, or be a profile owner or device owner.");
- }
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY)
+ || isDefaultDeviceOwner(caller) || isProfileOwner(caller),
+ "Must have " + REQUEST_PASSWORD_COMPLEXITY
+ + " permission, or be a profile owner or device owner.");
}
synchronized (getLockObject()) {
@@ -5728,26 +5620,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void setRequiredPasswordComplexityPreCoexistence(
String callerPackageName, int passwordComplexity, boolean calledOnParent) {
CallerIdentity caller = getCallerIdentity(callerPackageName);
- if (!isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
- Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
- }
+
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
synchronized (getLockObject()) {
ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- // TODO: Make sure this returns the parent of the fake admin
- // TODO: Deal with null componentname
- int affectedUser = calledOnParent
- ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- admin = enforcePermissionAndGetEnforcingAdmin(
- null, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- caller.getPackageName(), affectedUser).getActiveAdmin();
- } else {
- admin = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), calledOnParent);
- }
+ admin = getParentOfAdminIfRequired(
+ getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), calledOnParent);
if (admin.mPasswordComplexity != passwordComplexity) {
// We require the caller to explicitly clear any password quality requirements set
@@ -5907,14 +5788,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!isSystemUid(caller)) {
// This API can be called by an active device admin or by keyguard code.
if (!hasCallingPermission(permission.ACCESS_KEYGUARD_SECURE_STORAGE)) {
- if (isPermissionCheckFlagEnabled()) {
- int affectedUser = parent ? getProfileParentId(userHandle) : userHandle;
- enforcePermission(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- callerPackageName, affectedUser);
- } else {
- getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
- }
+ getActiveAdminForCallerLocked(
+ null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
}
}
@@ -5931,31 +5806,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(who, "ComponentName is null");
- }
-
+ Objects.requireNonNull(who, "ComponentName is null");
int userId = mInjector.userHandleGetCallingUserId();
int affectedUserId = parent ? getProfileParentId(userId) : userId;
synchronized (getLockObject()) {
- ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- CallerIdentity caller = getCallerIdentity(who, callerPackageName);
- ap = enforcePermissionAndGetEnforcingAdmin(
- who,
- /*permission=*/ MANAGE_DEVICE_POLICY_WIPE_DATA,
- /* adminPolicy=*/ DeviceAdminInfo.USES_POLICY_WIPE_DATA,
- caller.getPackageName(), affectedUserId).getActiveAdmin();
- } else {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_WIPE_DATA, parent);
- ap = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
- }
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_WIPE_DATA, parent);
+ ActiveAdmin ap = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
if (ap.maximumFailedPasswordsForWipe != num) {
ap.maximumFailedPasswordsForWipe = num;
@@ -6210,25 +6072,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(who, "ComponentName is null");
- }
+
+ Objects.requireNonNull(who, "ComponentName is null");
+
int userHandle = mInjector.userHandleGetCallingUserId();
int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
synchronized (getLockObject()) {
- ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- CallerIdentity caller = getCallerIdentity(who, callerPackageName);
- ap = enforcePermissionAndGetEnforcingAdmin(
- who,
- /*permission=*/ MANAGE_DEVICE_POLICY_LOCK,
- /*AdminPolicy=*/DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
- caller.getPackageName(),
- affectedUserId).getActiveAdmin();
- } else {
- ap = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent);
- }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent);
if (ap.maximumTimeToUnlock != timeMs) {
ap.maximumTimeToUnlock = timeMs;
@@ -6334,16 +6185,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
+
Preconditions.checkArgument(timeoutMs >= 0, "Timeout must not be a negative number.");
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
+
// timeoutMs with value 0 means that the admin doesn't participate
// timeoutMs is clamped to the interval in case the internal constants change in the future
final long minimumStrongAuthTimeout = getMinimumStrongAuthTimeoutMs();
@@ -6357,17 +6205,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final int userHandle = caller.getUserId();
boolean changed = false;
synchronized (getLockObject()) {
- ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- int affectedUser = parent
- ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- ap = enforcePermissionAndGetEnforcingAdmin(
- who, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- caller.getPackageName(), affectedUser).getActiveAdmin();
- } else {
- ap = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
- }
+ ActiveAdmin ap = getParentOfAdminIfRequired(
+ getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
if (ap.strongAuthUnlockTimeout != timeoutMs) {
ap.strongAuthUnlockTimeout = timeoutMs;
saveSettingsLocked(userHandle);
@@ -6664,16 +6503,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId())
- || isCredentialManagementApp);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
- }
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(!isUserSelectable, "The credential "
+ "management app is not allowed to install a user selectable key pair");
@@ -6733,16 +6565,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId())
- || isCredentialManagementApp);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
- }
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
isAliasInCredentialManagementAppPolicy(caller, alias),
@@ -6802,13 +6627,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
private boolean canInstallCertificates(CallerIdentity caller) {
- if (isPermissionCheckFlagEnabled()) {
- return hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId());
- } else {
- return isProfileOwner(caller) || isDefaultDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
- }
+ return isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
}
private boolean canChooseCertificates(CallerIdentity caller) {
@@ -7001,16 +6821,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
caller.getPackageName(), caller.getUid()));
enforceIndividualAttestationSupportedIfRequested(attestationUtilsFlags);
} else {
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId())
- || isCredentialManagementApp);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(
- caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && (
- isCallerDelegate || isCredentialManagementApp)));
- }
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(
+ caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && (
+ isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
isAliasInCredentialManagementAppPolicy(caller, alias),
@@ -7143,16 +6956,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId())
- || isCredentialManagementApp);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
- }
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
isAliasInCredentialManagementAppPolicy(caller, alias),
@@ -8285,29 +8091,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Preconditions.checkNotNull(who, "ComponentName is null");
- }
+
+ Preconditions.checkNotNull(who, "ComponentName is null");
+
CallerIdentity caller = getCallerIdentity(who, callerPackageName);
- if (!isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
+
checkCanExecuteOrThrowUnsafe(DevicePolicyManager
.OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY);
final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow();
synchronized (getLockObject()) {
ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- admin = enforcePermissionAndGetEnforcingAdmin(
- who, MANAGE_DEVICE_POLICY_FACTORY_RESET, caller.getPackageName(),
- UserHandle.USER_ALL)
- .getActiveAdmin();
- } else {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
admin.mFactoryResetProtectionPolicy = policy;
saveSettingsLocked(caller.getUserId());
}
@@ -8347,7 +8145,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|| hasCallingPermission(permission.MASTER_CLEAR)
|| hasCallingPermission(MANAGE_DEVICE_POLICY_FACTORY_RESET),
"Must be called by the FRP management agent on device");
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
} else {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
@@ -10247,15 +10045,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin;
}
- ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked() {
- ensureLocked();
- ActiveAdmin doOrPo = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- if (isPermissionCheckFlagEnabled() && doOrPo == null) {
- return getUserData(0).mPermissionBasedAdmin;
- }
- return doOrPo;
- }
-
@Override
public void clearDeviceOwner(String packageName) {
Objects.requireNonNull(packageName, "packageName is null");
@@ -10998,8 +10787,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* (2.1.1) The caller is the profile owner.
* (2.1.2) The caller is from another app in the same user as the profile owner, AND
* the caller is the delegated cert installer.
- * (3) The caller holds the
- * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CERTIFICATES} permission.
*
* For the device owner case, simply check that the caller is the device owner or the
* delegated certificate installer.
@@ -11013,24 +10800,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@VisibleForTesting
boolean hasDeviceIdAccessUnchecked(String packageName, int uid) {
final int userId = UserHandle.getUserId(uid);
- // TODO(b/280048070): Introduce a permission to handle device ID access
- if (isPermissionCheckFlagEnabled()
- && !(isUidProfileOwnerLocked(uid) || isUidDeviceOwnerLocked(uid))) {
- return hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES, packageName, userId);
- } else {
- ComponentName deviceOwner = getDeviceOwnerComponent(true);
- if (deviceOwner != null && (deviceOwner.getPackageName().equals(packageName)
- || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL))) {
- return true;
- }
- ComponentName profileOwner = getProfileOwnerAsUser(userId);
- final boolean isCallerProfileOwnerOrDelegate = profileOwner != null
- && (profileOwner.getPackageName().equals(packageName)
- || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL));
- if (isCallerProfileOwnerOrDelegate && (isProfileOwnerOfOrganizationOwnedDevice(userId)
- || isUserAffiliatedWithDevice(userId))) {
- return true;
- }
+ ComponentName deviceOwner = getDeviceOwnerComponent(true);
+ if (deviceOwner != null && (deviceOwner.getPackageName().equals(packageName)
+ || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL))) {
+ return true;
+ }
+ ComponentName profileOwner = getProfileOwnerAsUser(userId);
+ final boolean isCallerProfileOwnerOrDelegate = profileOwner != null
+ && (profileOwner.getPackageName().equals(packageName)
+ || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL));
+ if (isCallerProfileOwnerOrDelegate && (isProfileOwnerOfOrganizationOwnedDevice(userId)
+ || isUserAffiliatedWithDevice(userId))) {
+ return true;
}
return false;
}
@@ -11731,25 +11512,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setDefaultSmsApplication(ComponentName admin, String callerPackageName,
String packageName, boolean parent) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- } else {
- caller = getCallerIdentity(admin);
- }
+ CallerIdentity caller = getCallerIdentity(admin);
- final int userId;
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(
- MANAGE_DEVICE_POLICY_DEFAULT_SMS,
- caller.getPackageName(),
- getAffectedUser(parent));
- } else {
- Objects.requireNonNull(admin, "ComponentName is null");
- Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ Objects.requireNonNull(admin, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
if (!parent && isManagedProfile(caller.getUserId())
&& getManagedSubscriptionsPolicy().getPolicyType()
@@ -11759,6 +11527,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ "ManagedSubscriptions policy is set");
}
+ final int userId;
if (parent) {
userId = getProfileParentId(mInjector.userHandleGetCallingUserId());
mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage(
@@ -11957,10 +11726,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(admin, "admin is null");
- }
-
+ Objects.requireNonNull(admin, "admin is null");
Objects.requireNonNull(agent, "agent is null");
PolicySizeVerifier.enforceMaxPackageNameLength(agent.getPackageName());
@@ -11972,19 +11738,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
- ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
- int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
- ap = enforcePermissionAndGetEnforcingAdmin(
- admin,
- /*permission=*/MANAGE_DEVICE_POLICY_KEYGUARD,
- /*adminPolicy=*/DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES,
- caller.getPackageName(), affectedUserId).getActiveAdmin();
- } else {
- ap = getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent);
- }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
+ DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent);
checkCanExecuteOrThrowUnsafe(
DevicePolicyManager.OPERATION_SET_TRUST_AGENT_CONFIGURATION);
@@ -12080,27 +11835,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void addCrossProfileIntentFilter(ComponentName who, String callerPackageName,
IntentFilter filter, int flags) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
- int callingUserId = caller.getUserId();
+ CallerIdentity caller = getCallerIdentity(who);
+
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- callingUserId);
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isProfileOwner(caller) || isDefaultDeviceOwner(caller));
- }
synchronized (getLockObject()) {
long id = mInjector.binderClearCallingIdentity();
try {
+ int callingUserId = caller.getUserId();
UserInfo parent = mUserManager.getProfileParent(callingUserId);
if (parent == null) {
Slogf.e(LOG_TAG, "Cannot call addCrossProfileIntentFilter if there is no "
@@ -12144,28 +11888,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void clearCrossProfileIntentFilters(ComponentName who, String callerPackageName) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
- int callingUserId = caller.getUserId();
+ CallerIdentity caller = getCallerIdentity(who);
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- callingUserId);
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isProfileOwner(caller) || isDefaultDeviceOwner(caller));
- }
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
long id = mInjector.binderClearCallingIdentity();
try {
+ int callingUserId = caller.getUserId();
UserInfo parent = mUserManager.getProfileParent(callingUserId);
if (parent == null) {
Slogf.e(LOG_TAG, "Cannot call clearCrossProfileIntentFilter if there is no "
@@ -13360,7 +13092,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public String[] setPackagesSuspended(ComponentName who, String callerPackage,
String[] packageNames, boolean suspended) {
- if (!Flags.unmanagedModeMigration()) {
+ if (!Flags.suspendPackagesCoexistence()) {
return setPackagesSuspendedPreCoexistence(who, callerPackage, packageNames, suspended);
}
@@ -13450,7 +13182,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- if (Flags.unmanagedModeMigration()) {
+ if (Flags.suspendPackagesCoexistence()) {
enforcePermission(
MANAGE_DEVICE_POLICY_PACKAGE_STATE,
caller.getPackageName(),
@@ -15166,19 +14898,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return;
}
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- enforcePermission(MANAGE_DEVICE_POLICY_WIFI, caller.getPackageName(),
- UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(who);
- Preconditions.checkNotNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
@@ -15197,16 +14922,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
CallerIdentity caller = getCallerIdentity(who);
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(MANAGE_DEVICE_POLICY_WIFI, who.getPackageName(),
- UserHandle.USER_ALL);
- } else {
- Preconditions.checkNotNull(who, "ComponentName is null");
-
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
return mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalGetInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) > 0);
@@ -15294,18 +15013,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean setTime(@Nullable ComponentName who, String callerPackageName, long millis) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- // This is a global action.
- enforcePermission(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
// Don't allow set time when auto time is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
@@ -15322,18 +15034,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean setTimeZone(@Nullable ComponentName who, String callerPackageName,
String timeZone) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- // This is a global action.
- enforcePermission(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
// Don't allow set timezone when auto timezone is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
@@ -16537,22 +16242,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
policy.validateAgainstPreviousFreezePeriod(record.first, record.second,
LocalDate.now());
}
- CallerIdentity caller;
- synchronized (getLockObject()) {
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- enforcePermission(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, caller.getPackageName(),
- UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller)
+ CallerIdentity caller = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(
+ isProfileOwnerOfOrganizationOwnedDevice(caller)
|| isDefaultDeviceOwner(caller));
- }
- checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_UPDATE_POLICY);
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_UPDATE_POLICY);
+ synchronized (getLockObject()) {
if (policy == null) {
mOwners.clearSystemUpdatePolicy();
} else {
@@ -16699,7 +16397,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mUserManager.getUserInfo(UserHandle.getCallingUserId()).isMain()) {
Slogf.w(LOG_TAG, "Only the system update service in the main user can broadcast "
+ "update information.");
- return;
}
});
@@ -16723,7 +16420,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
// Get running users.
- final int runningUserIds[];
+ final int[] runningUserIds;
try {
runningUserIds = mInjector.getIActivityManager().getRunningUserIds();
} catch (RemoteException e) {
@@ -16966,10 +16663,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
}
- if (!isRuntimePermission(permission)) {
- return false;
- }
- return true;
+ return isRuntimePermission(permission);
}
private void enforcePermissionGrantStateOnFinancedDevice(
@@ -17384,18 +17078,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public String getWifiMacAddress(ComponentName admin, String callerPackageName) {
-// if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(admin, "ComponentName is null");
-// }
+ Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
-// if (isPermissionCheckFlagEnabled()) {
-// enforcePermission(MANAGE_DEVICE_POLICY_WIFI, UserHandle.USER_ALL);
-// } else {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
-// }
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
return mInjector.binderWithCleanCallingIdentity(() -> {
String[] macAddresses = mInjector.getWifiManager().getFactoryMacAddresses();
@@ -17462,25 +17150,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return;
}
- CallerIdentity caller;
- ActiveAdmin admin;
message = PolicySizeVerifier.truncateIfLonger(message, MAX_SHORT_SUPPORT_MESSAGE_LENGTH);
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- synchronized (getLockObject()) {
- admin = getActiveAdminForUidLocked(who, caller.getUid());
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+
+ ActiveAdmin admin;
+ synchronized (getLockObject()) {
+ admin = getActiveAdminForUidLocked(who, caller.getUid());
}
synchronized (getLockObject()) {
@@ -17501,23 +17179,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return null;
}
- CallerIdentity caller;
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- synchronized (getLockObject()) {
- admin = getActiveAdminForUidLocked(who, caller.getUid());
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+
+ ActiveAdmin admin;
+ synchronized (getLockObject()) {
+ admin = getActiveAdminForUidLocked(who, caller.getUid());
}
return admin.shortSupportMessage;
}
@@ -17680,26 +17348,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
CallerIdentity caller = getCallerIdentity(who);
- ActiveAdmin admin = null;
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
- }
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
text = PolicySizeVerifier.truncateIfLonger(text, MAX_ORG_NAME_LENGTH);
synchronized (getLockObject()) {
- if (!isPermissionCheckFlagEnabled()) {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
if (!TextUtils.equals(admin.organizationName, text)) {
admin.organizationName = (text == null || text.length() == 0)
? null : text.toString();
@@ -17714,23 +17370,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return null;
}
CallerIdentity caller = getCallerIdentity(who);
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
- synchronized (getLockObject()) {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin admin;
+ synchronized (getLockObject()) {
+ admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
}
return admin.organizationName;
@@ -18214,28 +17861,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- if (isPermissionCheckFlagEnabled()) {
- synchronized (getLockObject()) {
- Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
- || areAllUsersAffiliatedWithDeviceLocked());
- enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
- UserHandle.USER_ALL);
- }
+ if (admin != null) {
+ Preconditions.checkCallAuthorization(
+ isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || isDefaultDeviceOwner(caller));
} else {
- if (admin != null) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDefaultDeviceOwner(caller));
- } else {
- // A delegate app passes a null admin component, which is expected
- Preconditions.checkCallAuthorization(
- isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
- }
+ // A delegate app passes a null admin component, which is expected
+ Preconditions.checkCallAuthorization(
+ isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
+ }
- synchronized (getLockObject()) {
- Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
- || areAllUsersAffiliatedWithDeviceLocked());
- }
+ synchronized (getLockObject()) {
+ Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
+ || areAllUsersAffiliatedWithDeviceLocked());
}
DevicePolicyEventLogger
@@ -18259,7 +17897,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return new ParceledListSlice<SecurityEvent>(output);
} catch (IOException e) {
Slogf.w(LOG_TAG, "Fail to read previous events" , e);
- return new ParceledListSlice<SecurityEvent>(Collections.<SecurityEvent>emptyList());
+ return new ParceledListSlice<SecurityEvent>(Collections.emptyList());
}
}
@@ -18752,8 +18390,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
private boolean hasIncompatibleAccounts(int userId) {
- return mHasIncompatibleAccounts == null ? true
- : mHasIncompatibleAccounts.getOrDefault(userId, /* default= */ false);
+ return mHasIncompatibleAccounts == null || mHasIncompatibleAccounts.getOrDefault(
+ userId, /* default= */ false);
}
/**
@@ -18870,7 +18508,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
}
- };
+ }
private boolean isAdb(CallerIdentity caller) {
return isShellUid(caller) || isRootUid(caller);
@@ -20168,21 +19806,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void installUpdateFromFile(ComponentName admin, String callerPackageName,
ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback callback) {
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(admin, "ComponentName is null");
- }
+ Objects.requireNonNull(admin, "ComponentName is null");
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- enforcePermission(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, caller.getPackageName(),
- UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ CallerIdentity caller = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_SYSTEM_UPDATE);
DevicePolicyEventLogger
@@ -20752,32 +20381,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setCommonCriteriaModeEnabled(ComponentName who, String callerPackageName,
boolean enabled) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
- final ActiveAdmin admin;
+ CallerIdentity caller = getCallerIdentity(who);
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
- "Common Criteria mode can only be controlled by a device owner or "
- + "a profile owner on an organization-owned device.");
- synchronized (getLockObject()) {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
- }
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "Common Criteria mode can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
admin.mCommonCriteriaMode = enabled;
saveSettingsLocked(caller.getUserId());
}
@@ -20809,7 +20421,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// their ActiveAdmin, instead of iterating through all admins.
ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- return admin != null ? admin.mCommonCriteriaMode : false;
+ return admin != null && admin.mCommonCriteriaMode;
}
}
@@ -22209,7 +21821,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else {
owner = getDeviceOrProfileOwnerAdminLocked(userId);
}
- boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
+ boolean canGrant = owner != null && owner.mAdminCanGrantSensorsPermissions;
mPolicyCache.setAdminCanGrantSensorsPermissions(canGrant);
}
}
@@ -22408,27 +22020,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setMinimumRequiredWifiSecurityLevel(String callerPackageName, int level) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(callerPackageName);
- } else {
- caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
- "Wi-Fi minimum security level can only be controlled by a device owner or "
- + "a profile owner on an organization-owned device.");
- }
+ CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "Wi-Fi minimum security level can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
boolean valueChanged = false;
synchronized (getLockObject()) {
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- admin = enforcePermissionAndGetEnforcingAdmin(/* admin= */ null,
- MANAGE_DEVICE_POLICY_WIFI, caller.getPackageName(), caller.getUserId())
- .getActiveAdmin();
- } else {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
if (admin.mWifiMinimumSecurityLevel != level) {
admin.mWifiMinimumSecurityLevel = level;
saveSettingsLocked(caller.getUserId());
@@ -22450,21 +22050,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public WifiSsidPolicy getWifiSsidPolicy(String callerPackageName) {
final CallerIdentity caller = getCallerIdentity();
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(MANAGE_DEVICE_POLICY_WIFI, callerPackageName,
- caller.getUserId());
- } else {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller)
- || canQueryAdminPolicy(caller),
- "SSID policy can only be retrieved by a device owner or "
- + "a profile owner on an organization-owned device or "
- + "an app with the QUERY_ADMIN_POLICY permission.");
- }
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || canQueryAdminPolicy(caller),
+ "SSID policy can only be retrieved by a device owner or "
+ + "a profile owner on an organization-owned device or "
+ + "an app with the QUERY_ADMIN_POLICY permission.");
synchronized (getLockObject()) {
ActiveAdmin admin;
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
return admin != null ? admin.mWifiSsidPolicy : null;
}
}
@@ -22485,29 +22080,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setWifiSsidPolicy(String callerPackageName, WifiSsidPolicy policy) {
- CallerIdentity caller;
-
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(callerPackageName);
- } else {
- caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
- "SSID denylist can only be controlled by a device owner or "
- + "a profile owner on an organization-owned device.");
- }
+ CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "SSID denylist can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
boolean changed = false;
synchronized (getLockObject()) {
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- admin = enforcePermissionAndGetEnforcingAdmin(
- /* admin= */ null, MANAGE_DEVICE_POLICY_WIFI,
- caller.getPackageName(),
- caller.getUserId()).getActiveAdmin();
- } else {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
if (!Objects.equals(policy, admin.mWifiSsidPolicy)) {
admin.mWifiSsidPolicy = policy;
changed = true;
@@ -22715,7 +22296,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
private final class DevicePolicyManagementRoleObserver implements OnRoleHoldersChangedListener {
- private RoleManager mRm;
+ private final RoleManager mRm;
private final Executor mExecutor;
private final Context mContext;
@@ -22732,13 +22313,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
mDevicePolicyEngine.handleRoleChanged(roleName, user.getIdentifier());
- if (RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT.equals(roleName)) {
- handleDevicePolicyManagementRoleChange(user);
- return;
- }
- if (RoleManager.ROLE_FINANCED_DEVICE_KIOSK.equals(roleName)) {
- handleFinancedDeviceKioskRoleChange();
- return;
+ switch (roleName) {
+ case RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT ->
+ handleDevicePolicyManagementRoleChange(user);
+ case RoleManager.ROLE_FINANCED_DEVICE_KIOSK ->
+ handleFinancedDeviceKioskRoleChange();
}
}
@@ -23390,26 +22969,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
/**
* Checks if the calling process has been granted permission to apply a device policy on a
- * specific user.
- * The given permission will be checked along with its associated cross-user permission if it
- * exists and the target user is different to the calling user.
- * Returns an {@link EnforcingAdmin} for the caller.
- *
- * @param admin the component name of the admin.
- * @param callerPackageName The package name of the calling application.
- * @param permission The name of the permission being checked.
- * @param deviceAdminPolicy The userId of the user which the caller needs permission to act on.
- * @throws SecurityException if the caller has not been granted the given permission,
- * the associated cross-user permission if the caller's user is different to the target user.
- */
- private EnforcingAdmin enforcePermissionAndGetEnforcingAdmin(@Nullable ComponentName admin,
- String permission, int deviceAdminPolicy, String callerPackageName, int targetUserId) {
- enforcePermission(permission, deviceAdminPolicy, callerPackageName, targetUserId);
- return getEnforcingAdminForCaller(admin, callerPackageName);
- }
-
- /**
- * Checks if the calling process has been granted permission to apply a device policy on a
* specific user. Only one permission provided in the list needs to be granted to pass this
* check.
* The given permissions will be checked along with their associated cross-user permissions if
@@ -23431,23 +22990,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
/**
- * Checks whether the calling process has been granted permission to query a device policy on
- * a specific user.
- * The given permission will be checked along with its associated cross-user permission if it
- * exists and the target user is different to the calling user.
- *
- * @param permission The name of the permission being checked.
- * @param targetUserId The userId of the user which the caller needs permission to act on.
- * @throws SecurityException if the caller has not been granted the given permission,
- * the associated cross-user permission if the caller's user is different to the target user.
- */
- private EnforcingAdmin enforceCanQueryAndGetEnforcingAdmin(@Nullable ComponentName admin,
- String permission, String callerPackageName, int targetUserId) {
- enforceCanQuery(permission, callerPackageName, targetUserId);
- return getEnforcingAdminForCaller(admin, callerPackageName);
- }
-
- /**
* Checks if the calling process has been granted permission to apply a device policy.
*
* @param callerPackageName The package name of the calling application.
@@ -23754,13 +23296,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return NOT_A_DPC;
}
- private boolean isPermissionCheckFlagEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE_DEVICE_POLICY_MANAGER,
- PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG,
- DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
- }
-
private static boolean isSetStatusBarDisabledCoexistenceEnabled() {
return false;
}
@@ -23837,58 +23372,83 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
}
- if (isPermissionCheckFlagEnabled()) {
+ if (Flags.setMtePolicyCoexistence()) {
enforcePermission(MANAGE_DEVICE_POLICY_MTE, caller.getPackageName(),
UserHandle.USER_ALL);
} else {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
}
+
synchronized (getLockObject()) {
- ActiveAdmin admin =
+ if (Flags.setMtePolicyCoexistence()) {
+ final EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(null,
+ MANAGE_DEVICE_POLICY_MTE, callerPackageName, caller.getUserId());
+ if (flags != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.MEMORY_TAGGING,
+ admin,
+ new IntegerPolicyValue(flags));
+ } else {
+ mDevicePolicyEngine.removeGlobalPolicy(
+ PolicyDefinition.MEMORY_TAGGING,
+ admin);
+ }
+ } else {
+ ActiveAdmin admin =
getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
-
- if (admin != null) {
- final String memtagProperty = "arm64.memtag.bootctl";
- if (flags == DevicePolicyManager.MTE_ENABLED) {
- mInjector.systemPropertiesSet(memtagProperty, "memtag");
- } else if (flags == DevicePolicyManager.MTE_DISABLED) {
- mInjector.systemPropertiesSet(memtagProperty, "memtag-off");
- } else if (flags == DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
- if (admin.mtePolicy != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
- mInjector.systemPropertiesSet(memtagProperty, "default");
+ if (admin != null) {
+ final String memtagProperty = "arm64.memtag.bootctl";
+ if (flags == DevicePolicyManager.MTE_ENABLED) {
+ mInjector.systemPropertiesSet(memtagProperty, "memtag");
+ } else if (flags == DevicePolicyManager.MTE_DISABLED) {
+ mInjector.systemPropertiesSet(memtagProperty, "memtag-off");
+ } else if (flags == DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+ if (admin.mtePolicy != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+ mInjector.systemPropertiesSet(memtagProperty, "default");
+ }
}
+ admin.mtePolicy = flags;
+ saveSettingsLocked(caller.getUserId());
}
- admin.mtePolicy = flags;
- saveSettingsLocked(caller.getUserId());
-
- DevicePolicyEventLogger.createEvent(DevicePolicyEnums.SET_MTE_POLICY)
- .setInt(flags)
- .setAdmin(caller.getPackageName())
- .write();
}
+
+ DevicePolicyEventLogger.createEvent(DevicePolicyEnums.SET_MTE_POLICY)
+ .setInt(flags)
+ .setAdmin(caller.getPackageName())
+ .write();
}
}
@Override
public int getMtePolicy(String callerPackageName) {
final CallerIdentity caller = getCallerIdentity(callerPackageName);
- if (isPermissionCheckFlagEnabled()) {
+ if (Flags.setMtePolicyCoexistence()) {
enforcePermission(MANAGE_DEVICE_POLICY_MTE, caller.getPackageName(),
UserHandle.USER_ALL);
} else {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isSystemUid(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || isSystemUid(caller));
}
+
synchronized (getLockObject()) {
- ActiveAdmin admin =
+ if (Flags.setMtePolicyCoexistence()) {
+ final EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(null,
+ MANAGE_DEVICE_POLICY_MTE, callerPackageName, caller.getUserId());
+ final Integer policyFromAdmin = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+ PolicyDefinition.MEMORY_TAGGING, admin);
+ return (policyFromAdmin != null ? policyFromAdmin
+ : DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY);
+ } else {
+ ActiveAdmin admin =
getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- return admin != null
- ? admin.mtePolicy
- : DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
+ return admin != null
+ ? admin.mtePolicy
+ : DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
+ }
}
}
@@ -24235,21 +23795,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
maybeMigrateSecurityLoggingPolicyLocked();
// ID format: <sdk-int>.<auto_increment_id>.<descriptions>'
String unmanagedBackupId = "35.1.unmanaged-mode";
- boolean unmanagedMigrated = false;
- unmanagedMigrated =
- unmanagedMigrated | maybeMigrateRequiredPasswordComplexityLocked(unmanagedBackupId);
- unmanagedMigrated =
- unmanagedMigrated | maybeMigrateSuspendedPackagesLocked(unmanagedBackupId);
+ boolean unmanagedMigrated = maybeMigrateRequiredPasswordComplexityLocked(unmanagedBackupId);
if (unmanagedMigrated) {
Slogf.i(LOG_TAG, "Backup made: " + unmanagedBackupId);
}
String supervisionBackupId = "36.2.supervision-support";
boolean supervisionMigrated = maybeMigrateResetPasswordTokenLocked(supervisionBackupId);
+ supervisionMigrated |= maybeMigrateSuspendedPackagesLocked(supervisionBackupId);
if (supervisionMigrated) {
Slogf.i(LOG_TAG, "Backup made: " + supervisionBackupId);
}
+ String memoryTaggingBackupId = "36.3.memory-tagging";
+ boolean memoryTaggingMigrated = maybeMigrateMemoryTaggingLocked(memoryTaggingBackupId);
+ if (memoryTaggingMigrated) {
+ Slogf.i(LOG_TAG, "Backup made: " + memoryTaggingBackupId);
+ }
+
// Additional migration steps should repeat the pattern above with a new backupId.
}
@@ -24666,7 +24229,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|| isCallerDevicePolicyManagementRoleHolder(caller)
|| isCallerSystemSupervisionRoleHolder(caller));
return getFinancedDeviceKioskRoleHolderOnAnyUser() != null;
- };
+ }
@Override
public String getFinancedDeviceKioskRoleHolder(String callerPackageName) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index b3c8408ff54b..be4eea42f09e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -682,6 +682,19 @@ class Owners {
}
}
+ void markMemoryTaggingMigrated() {
+ synchronized (mData) {
+ mData.mMemoryTaggingMigrated = true;
+ mData.writeDeviceOwner();
+ }
+ }
+
+ boolean isMemoryTaggingMigrated() {
+ synchronized (mData) {
+ return mData.mMemoryTaggingMigrated;
+ }
+ }
+
@GuardedBy("mData")
void pushToAppOpsLocked() {
if (!mSystemReady) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 10e43d955fab..1cae924a8cd1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -93,6 +93,9 @@ class OwnersData {
private static final String ATTR_SUSPENDED_PACKAGES_MIGRATED = "suspendedPackagesMigrated";
private static final String ATTR_RESET_PASSWORD_WITH_TOKEN_MIGRATED =
"resetPasswordWithTokenMigrated";
+ private static final String ATTR_MEMORY_TAGGING_MIGRATED =
+ "memoryTaggingMigrated";
+
private static final String ATTR_MIGRATED_POST_UPGRADE = "migratedPostUpgrade";
// Internal state for the device owner package.
@@ -125,6 +128,7 @@ class OwnersData {
boolean mRequiredPasswordComplexityMigrated = false;
boolean mSuspendedPackagesMigrated = false;
boolean mResetPasswordWithTokenMigrated = false;
+ boolean mMemoryTaggingMigrated = false;
boolean mPoliciesMigratedPostUpdate = false;
@@ -416,6 +420,8 @@ class OwnersData {
if (Flags.unmanagedModeMigration()) {
out.attributeBoolean(null, ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED,
mRequiredPasswordComplexityMigrated);
+ }
+ if (Flags.suspendPackagesCoexistence()) {
out.attributeBoolean(null, ATTR_SUSPENDED_PACKAGES_MIGRATED,
mSuspendedPackagesMigrated);
@@ -424,6 +430,10 @@ class OwnersData {
out.attributeBoolean(null, ATTR_RESET_PASSWORD_WITH_TOKEN_MIGRATED,
mResetPasswordWithTokenMigrated);
}
+ if (Flags.setMtePolicyCoexistence()) {
+ out.attributeBoolean(null, ATTR_MEMORY_TAGGING_MIGRATED,
+ mMemoryTaggingMigrated);
+ }
out.endTag(null, TAG_POLICY_ENGINE_MIGRATION);
}
@@ -491,13 +501,15 @@ class OwnersData {
mRequiredPasswordComplexityMigrated = Flags.unmanagedModeMigration()
&& parser.getAttributeBoolean(null,
ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED, false);
- mSuspendedPackagesMigrated = Flags.unmanagedModeMigration()
+ mSuspendedPackagesMigrated = Flags.suspendPackagesCoexistence()
&& parser.getAttributeBoolean(null,
ATTR_SUSPENDED_PACKAGES_MIGRATED, false);
mResetPasswordWithTokenMigrated = Flags.resetPasswordWithTokenCoexistence()
&& parser.getAttributeBoolean(null,
ATTR_RESET_PASSWORD_WITH_TOKEN_MIGRATED, false);
-
+ mMemoryTaggingMigrated = Flags.setMtePolicyCoexistence()
+ && parser.getAttributeBoolean(null,
+ ATTR_MEMORY_TAGGING_MIGRATED, false);
break;
default:
Slog.e(TAG, "Unexpected tag: " + tag);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 6cb1756f93eb..f1711f5f8c0b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -337,6 +337,13 @@ final class PolicyDefinition<V> {
PolicyEnforcerCallbacks::noOp,
new PackageSetPolicySerializer());
+ static PolicyDefinition<Integer> MEMORY_TAGGING = new PolicyDefinition<>(
+ new NoArgsPolicyKey(
+ DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY),
+ new TopPriority<>(List.of(EnforcingAdmin.DPC_AUTHORITY)),
+ PolicyEnforcerCallbacks::setMtePolicy,
+ new IntegerPolicySerializer());
+
private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
@@ -383,6 +390,8 @@ final class PolicyDefinition<V> {
PASSWORD_COMPLEXITY);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PACKAGES_SUSPENDED_POLICY,
PACKAGES_SUSPENDED);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY,
+ MEMORY_TAGGING);
// User Restriction Policies
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 145416215cb0..fdc0ec1a0471 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -47,6 +47,7 @@ import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.permission.AdminPermissionControlParams;
import android.permission.PermissionControllerManager;
@@ -210,6 +211,7 @@ final class PolicyEnforcerCallbacks {
private static class BlockingCallback {
private final CountDownLatch mLatch = new CountDownLatch(1);
private final AtomicReference<Boolean> mValue = new AtomicReference<>();
+
public void trigger(Boolean value) {
mValue.set(value);
mLatch.countDown();
@@ -435,4 +437,43 @@ final class PolicyEnforcerCallbacks {
return AndroidFuture.completedFuture(true);
});
}
+
+ static CompletableFuture<Boolean> setMtePolicy(
+ @Nullable Integer mtePolicy, @NonNull Context context, int userId,
+ @NonNull PolicyKey policyKey) {
+ if (mtePolicy == null) {
+ mtePolicy = DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
+ }
+ final Set<Integer> allowedModes =
+ Set.of(
+ DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY,
+ DevicePolicyManager.MTE_DISABLED,
+ DevicePolicyManager.MTE_ENABLED);
+ if (!allowedModes.contains(mtePolicy)) {
+ Slog.wtf(LOG_TAG, "MTE policy is not a known one: " + mtePolicy);
+ return AndroidFuture.completedFuture(false);
+ }
+
+ final String mteDpmSystemProperty =
+ "ro.arm64.memtag.bootctl_device_policy_manager";
+ final String mteSettingsSystemProperty =
+ "ro.arm64.memtag.bootctl_settings_toggle";
+ final String mteControlProperty = "arm64.memtag.bootctl";
+
+ final boolean isAvailable = SystemProperties.getBoolean(mteDpmSystemProperty,
+ SystemProperties.getBoolean(mteSettingsSystemProperty, false));
+ if (!isAvailable) {
+ return AndroidFuture.completedFuture(false);
+ }
+
+ if (mtePolicy == DevicePolicyManager.MTE_ENABLED) {
+ SystemProperties.set(mteControlProperty, "memtag");
+ } else if (mtePolicy == DevicePolicyManager.MTE_DISABLED) {
+ SystemProperties.set(mteControlProperty, "memtag-off");
+ } else if (mtePolicy == DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+ SystemProperties.set(mteControlProperty, "default");
+ }
+
+ return AndroidFuture.completedFuture(true);
+ }
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index a804f24acc8f..c30ab738b098 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -101,13 +101,13 @@ public final class InputMethodSubtypeSwitchingControllerTest {
TEST_SETTING_ACTIVITY_NAME, subtypes, TEST_IS_DEFAULT_RES_ID,
TEST_FORCE_DEFAULT, supportsSwitchingToNextInputMethod, TEST_IS_VR_IME);
if (subtypes == null) {
- items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
- NOT_A_SUBTYPE_INDEX, null, SYSTEM_LOCALE));
+ items.add(new ImeSubtypeListItem(imeName, null /* subtypeName */, null /* layoutName */,
+ imi, NOT_A_SUBTYPE_INDEX, null, SYSTEM_LOCALE));
} else {
for (int i = 0; i < subtypes.size(); ++i) {
final String subtypeLocale = subtypeLocales.get(i);
- items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
- SYSTEM_LOCALE));
+ items.add(new ImeSubtypeListItem(imeName, subtypeLocale, null /* layoutName */,
+ imi, i, subtypeLocale, SYSTEM_LOCALE));
}
}
}
@@ -138,8 +138,8 @@ public final class InputMethodSubtypeSwitchingControllerTest {
final InputMethodInfo imi = new InputMethodInfo(ri, TEST_IS_AUX_IME,
TEST_SETTING_ACTIVITY_NAME, subtypes, TEST_IS_DEFAULT_RES_ID,
TEST_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */, TEST_IS_VR_IME);
- return new ImeSubtypeListItem(imeName, subtypeName, imi, subtypeIndex, subtypeLocale,
- SYSTEM_LOCALE);
+ return new ImeSubtypeListItem(imeName, subtypeName, null /* layoutName */, imi,
+ subtypeIndex, subtypeLocale, SYSTEM_LOCALE);
}
@NonNull
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 3ac7fb0dbe53..e0b0fec380dd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -335,6 +335,10 @@ public class VirtualDisplayAdapterTest {
@Override
public void onStopped() {
}
+
+ @Override
+ public void onRequestedBrightnessChanged(float brightness) {
+ }
};
}
}
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index c81d6be43223..9a300fb3c9fd 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
java_defaults {
- name: "FrameworkMockingServicesTests-jni-defaults",
+ name: "FrameworksMockingServicesTests-jni-defaults",
jni_libs: [
"libmockingservicestestjni",
],
@@ -30,7 +30,7 @@ package {
android_test {
name: "FrameworksMockingServicesTests",
defaults: [
- "FrameworkMockingServicesTests-jni-defaults",
+ "FrameworksMockingServicesTests-jni-defaults",
"modules-utils-testable-device-config-defaults",
],
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index cbc8538cf9fb..37d1c30f76f5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -15,7 +15,12 @@
*/
package com.android.server;
+import static android.os.PowerExemptionManager.REASON_OTHER;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT;
+
import static androidx.test.InstrumentationRegistry.getContext;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -31,6 +36,7 @@ import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
+import static com.android.server.DeviceIdleController.MSG_TEMP_APP_WHITELIST_TIMEOUT;
import static com.android.server.DeviceIdleController.STATE_ACTIVE;
import static com.android.server.DeviceIdleController.STATE_IDLE;
import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
@@ -41,6 +47,7 @@ import static com.android.server.DeviceIdleController.STATE_QUICK_DOZE_DELAY;
import static com.android.server.DeviceIdleController.STATE_SENSING;
import static com.android.server.DeviceIdleController.lightStateToString;
import static com.android.server.DeviceIdleController.stateToString;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -83,6 +90,8 @@ import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.SystemClock;
import android.os.WearModeManagerInternal;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -90,12 +99,16 @@ import android.telephony.emergency.EmergencyNumber;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.am.BatteryStatsService;
import com.android.server.deviceidle.ConstraintController;
+import com.android.server.deviceidle.Flags;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -115,6 +128,9 @@ import java.util.concurrent.Executor;
@SuppressWarnings("GuardedBy")
@RunWith(AndroidJUnit4.class)
public class DeviceIdleControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT);
+
private DeviceIdleController mDeviceIdleController;
private DeviceIdleController.MyHandler mHandler;
private AnyMotionDetectorForTest mAnyMotionDetector;
@@ -157,7 +173,8 @@ public class DeviceIdleControllerTest {
LocationManager locationManager;
ConstraintController constraintController;
// Freeze time for testing.
- long nowElapsed;
+ volatile long nowElapsed;
+ volatile long nowUptime;
boolean useMotionSensor = true;
boolean isLocationPrefetchEnabled = true;
@@ -193,6 +210,11 @@ public class DeviceIdleControllerTest {
}
@Override
+ long getUptimeMillis() {
+ return nowUptime;
+ }
+
+ @Override
LocationManager getLocationManager() {
return locationManager;
}
@@ -314,11 +336,13 @@ public class DeviceIdleControllerTest {
mMockingSession = mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
+ .mockStatic(BatteryStatsService.class)
.spyStatic(DeviceConfig.class)
.spyStatic(LocalServices.class)
.startMocking();
spyOn(getContext());
doReturn(null).when(getContext()).registerReceiver(any(), any());
+ doReturn(mock(IBatteryStats.class)).when(() -> BatteryStatsService.getService());
doReturn(mock(ActivityManagerInternal.class))
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mock(ActivityTaskManagerInternal.class))
@@ -401,6 +425,46 @@ public class DeviceIdleControllerTest {
}
@Test
+ @EnableFlags(Flags.FLAG_USE_CPU_TIME_FOR_TEMP_ALLOWLIST)
+ public void testTempAllowlistCountsUptime() {
+ doNothing().when(getContext()).sendBroadcastAsUser(any(), any(), any(), any());
+ final int testUid = 12345;
+ final long durationMs = 4300;
+ final long startTime = 100; // Arbitrary starting point in time.
+ mInjector.nowUptime = mInjector.nowElapsed = startTime;
+
+ mDeviceIdleController.addPowerSaveTempWhitelistAppDirectInternal(0, testUid, durationMs,
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, true, REASON_OTHER, "test");
+
+ assertEquals(startTime + durationMs,
+ mDeviceIdleController.mTempWhitelistAppIdEndTimes.get(testUid).first.value);
+
+ final InOrder inorder = inOrder(mHandler);
+ // mHandler is already stubbed to do nothing on handleMessage.
+ inorder.verify(mHandler).sendMessageDelayed(
+ argThat(m -> m.what == MSG_TEMP_APP_WHITELIST_TIMEOUT && m.arg1 == testUid),
+ eq(durationMs));
+
+ mInjector.nowElapsed += durationMs;
+ mInjector.nowUptime += 2;
+ // Elapsed time moved past the expiration but not uptime. The check should be rescheduled.
+ mDeviceIdleController.checkTempAppWhitelistTimeout(testUid);
+ inorder.verify(mHandler).sendMessageDelayed(
+ argThat(m -> m.what == MSG_TEMP_APP_WHITELIST_TIMEOUT && m.arg1 == testUid),
+ eq(durationMs - 2));
+ assertEquals(startTime + durationMs,
+ mDeviceIdleController.mTempWhitelistAppIdEndTimes.get(testUid).first.value);
+
+ mInjector.nowUptime += durationMs;
+ // Uptime moved past the expiration time. Uid should be removed from the temp allowlist.
+ mDeviceIdleController.checkTempAppWhitelistTimeout(testUid);
+ inorder.verify(mHandler, never()).sendMessageDelayed(
+ argThat(m -> m.what == MSG_TEMP_APP_WHITELIST_TIMEOUT && m.arg1 == testUid),
+ anyLong());
+ assertFalse(mDeviceIdleController.mTempWhitelistAppIdEndTimes.contains(testUid));
+ }
+
+ @Test
public void testUpdateInteractivityLocked() {
doReturn(false).when(mPowerManager).isInteractive();
mDeviceIdleController.updateInteractivityLocked();
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
index 197342874b2a..f7c2e8b72d6b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
@@ -21,6 +21,7 @@ import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static org.junit.Assert.assertEquals;
+import android.app.PropertyInvalidatedCache;
import android.content.Context;
import android.os.Handler;
@@ -63,6 +64,7 @@ public class AppOpsLegacyRestrictionsTest {
@Before
public void setUp() {
+ PropertyInvalidatedCache.disableForTestMode();
mSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 4e1f741b1398..dd7ce21e3628 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -2351,6 +2351,7 @@ public class JobSchedulerServiceTest {
/** Tests that jobs are removed from the pending list if the user stops the app. */
@Test
+ @RequiresFlagsDisabled(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API)
public void testUserStopRemovesPending() {
spyOn(mService);
@@ -2402,6 +2403,60 @@ public class JobSchedulerServiceTest {
assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job2b));
}
+ /** Tests that jobs are removed from the pending list if the user stops the app. */
+ @Test
+ @RequiresFlagsEnabled(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API)
+ public void testUserStopRemovesPending_withPendingJobReasonsApi() {
+ spyOn(mService);
+
+ JobStatus job1a = createJobStatus("testUserStopRemovesPending",
+ createJobInfo(1), 1, "pkg1");
+ JobStatus job1b = createJobStatus("testUserStopRemovesPending",
+ createJobInfo(2), 1, "pkg1");
+ JobStatus job2a = createJobStatus("testUserStopRemovesPending",
+ createJobInfo(1), 2, "pkg2");
+ JobStatus job2b = createJobStatus("testUserStopRemovesPending",
+ createJobInfo(2), 2, "pkg2");
+ doReturn(1).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 0);
+ doReturn(11).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 1);
+ doReturn(2).when(mPackageManagerInternal).getPackageUid("pkg2", 0, 0);
+
+ mService.getPendingJobQueue().clear();
+ mService.getPendingJobQueue().add(job1a);
+ mService.getPendingJobQueue().add(job1b);
+ mService.getPendingJobQueue().add(job2a);
+ mService.getPendingJobQueue().add(job2b);
+ mService.getJobStore().add(job1a);
+ mService.getJobStore().add(job1b);
+ mService.getJobStore().add(job2a);
+ mService.getJobStore().add(job2b);
+
+ mService.notePendingUserRequestedAppStopInternal("pkg1", 1, "test");
+ assertEquals(4, mService.getPendingJobQueue().size());
+ assertTrue(mService.getPendingJobQueue().contains(job1a));
+ assertTrue(mService.getPendingJobQueue().contains(job1b));
+ assertTrue(mService.getPendingJobQueue().contains(job2a));
+ assertTrue(mService.getPendingJobQueue().contains(job2b));
+
+ mService.notePendingUserRequestedAppStopInternal("pkg1", 0, "test");
+ assertEquals(2, mService.getPendingJobQueue().size());
+ assertFalse(mService.getPendingJobQueue().contains(job1a));
+ assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReasons(job1a)[0]);
+ assertFalse(mService.getPendingJobQueue().contains(job1b));
+ assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReasons(job1b)[0]);
+ assertTrue(mService.getPendingJobQueue().contains(job2a));
+ assertTrue(mService.getPendingJobQueue().contains(job2b));
+
+ mService.notePendingUserRequestedAppStopInternal("pkg2", 0, "test");
+ assertEquals(0, mService.getPendingJobQueue().size());
+ assertFalse(mService.getPendingJobQueue().contains(job1a));
+ assertFalse(mService.getPendingJobQueue().contains(job1b));
+ assertFalse(mService.getPendingJobQueue().contains(job2a));
+ assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReasons(job2a)[0]);
+ assertFalse(mService.getPendingJobQueue().contains(job2b));
+ assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReasons(job2b)[0]);
+ }
+
/**
* Unit tests {@link JobSchedulerService#checkIfRestricted(JobStatus)} with single {@link
* JobRestriction} registered.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 1cba3c574543..8a10040f986f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -195,12 +195,12 @@ public final class UserManagerServiceTest {
doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
mockIsLowRamDevice(false);
- // Called when getting boot user. config_bootToHeadlessSystemUser is false by default.
+ // Called when getting boot user. config_bootToHeadlessSystemUser is 0 by default.
mSpyResources = spy(mSpiedContext.getResources());
when(mSpiedContext.getResources()).thenReturn(mSpyResources);
- doReturn(false)
+ doReturn(0)
.when(mSpyResources)
- .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser);
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
// Must construct UserManagerService in the UiThread
mTestDir = new File(mRealContext.getDataDir(), "umstest");
@@ -859,15 +859,50 @@ public final class UserManagerServiceTest {
}
@Test
- public void testGetBootUser_enableBootToHeadlessSystemUser() {
+ public void testGetBootUser_Headless_BootToSystemUserWhenDeviceIsProvisioned() {
setSystemUserHeadless(true);
- doReturn(true)
+ addUser(USER_ID);
+ addUser(OTHER_USER_ID);
+ mockProvisionedDevice(true);
+ doReturn(1)
.when(mSpyResources)
- .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser);
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
assertThat(mUms.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
}
+ @Test
+ public void testGetBootUser_Headless_BootToFirstSwitchableFullUserWhenDeviceNotProvisioned() {
+ setSystemUserHeadless(true);
+ addUser(USER_ID);
+ addUser(OTHER_USER_ID);
+ mockProvisionedDevice(false);
+ doReturn(1)
+ .when(mSpyResources)
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
+ // Even if the headless system user switchable flag is true, the boot user should be the
+ // first switchable full user.
+ doReturn(true)
+ .when(mSpyResources)
+ .getBoolean(com.android.internal.R.bool.config_canSwitchToHeadlessSystemUser);
+
+ assertThat(mUms.getBootUser()).isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetBootUser_Headless_ThrowsIfBootFailsNoFullUserWhenDeviceNotProvisioned()
+ throws Exception {
+ setSystemUserHeadless(true);
+ removeNonSystemUsers();
+ mockProvisionedDevice(false);
+ doReturn(1)
+ .when(mSpyResources)
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
+
+ assertThrows(ServiceSpecificException.class,
+ () -> mUms.getBootUser());
+ }
+
/**
* Returns true if the user's XML file has Default restrictions
* @param userId Id of the user.
@@ -935,6 +970,11 @@ public final class UserManagerServiceTest {
any(), eq(android.provider.Settings.Global.USER_SWITCHER_ENABLED), anyInt()));
}
+ private void mockProvisionedDevice(boolean isProvisionedDevice) {
+ doReturn(isProvisionedDevice ? 1 : 0).when(() -> Settings.Global.getInt(
+ any(), eq(android.provider.Settings.Global.DEVICE_PROVISIONED), anyInt()));
+ }
+
private void mockIsLowRamDevice(boolean isLowRamDevice) {
doReturn(isLowRamDevice).when(ActivityManager::isLowRamDeviceStatic);
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index f02a389a160e..d83dc110800a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -67,6 +67,7 @@ import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelSingleUidTimeReader;
import com.android.internal.os.LongArrayMultiStateCounter;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.server.power.feature.flags.Flags;
@@ -120,6 +121,7 @@ public class BatteryStatsImplTest {
}});
private final MockClock mMockClock = new MockClock();
+ private final MonotonicClock mMonotonicClock = new MonotonicClock(777666, mMockClock);
private MockBatteryStatsImpl mBatteryStatsImpl;
private Handler mHandler;
private PowerStatsStore mPowerStatsStore;
@@ -160,7 +162,7 @@ public class BatteryStatsImplTest {
mPowerStatsStore = new PowerStatsStore(systemDir, mHandler);
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerAttributor,
mPowerProfile, mBatteryStatsImpl.getCpuScalingPolicies(), mPowerStatsStore, 0,
- mMockClock);
+ mMockClock, mMonotonicClock);
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
index 813dd841b2b9..5d50e6c9f715 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
@@ -191,7 +191,7 @@ public class BatteryUsageStatsAtomTest {
"cpu",
1650.0f,
9300.0f,
- 8400L
+ 8300L
);
verify(statsLogger).buildStatsEvent(
1000L,
@@ -205,7 +205,7 @@ public class BatteryUsageStatsAtomTest {
"cpu",
1650.0f,
9400.0f,
- 0L
+ 8400L
);
verify(statsLogger).buildStatsEvent(
1000L,
@@ -502,17 +502,17 @@ public class BatteryUsageStatsAtomTest {
.setPackageWithHighestDrain("myPackage0")
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 2000)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 400)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 450)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 500)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, 600)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 800);
final BatteryConsumer.Key keyFg = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU,
@@ -524,14 +524,14 @@ public class BatteryUsageStatsAtomTest {
final BatteryConsumer.Key keyCached = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_CACHED);
- uidBuilder.setConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(keyFg, 8100)
- .setConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
- .setUsageDurationMillis(keyBg, 8200)
- .setConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
- .setUsageDurationMillis(keyFgs, 8300)
- .setConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
- .setUsageDurationMillis(keyFgs, 8400);
+ uidBuilder.addConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .addUsageDurationMillis(keyFg, 8100)
+ .addConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ .addUsageDurationMillis(keyBg, 8200)
+ .addConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ .addUsageDurationMillis(keyFgs, 8300)
+ .addConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ .addUsageDurationMillis(keyCached, 8400);
final BatteryConsumer.Key keyCustomFg = uidBuilder.getKey(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
@@ -539,9 +539,9 @@ public class BatteryUsageStatsAtomTest {
final BatteryConsumer.Key keyCustomBg = uidBuilder.getKey(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
BatteryConsumer.PROCESS_STATE_BACKGROUND);
- uidBuilder.setConsumedPower(
+ uidBuilder.addConsumedPower(
keyCustomFg, 100, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
- uidBuilder.setConsumedPower(
+ uidBuilder.addConsumedPower(
keyCustomBg, 350, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
builder.getOrCreateUidBatteryConsumerBuilder(UID_1)
@@ -549,36 +549,36 @@ public class BatteryUsageStatsAtomTest {
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1234);
builder.getOrCreateUidBatteryConsumerBuilder(UID_2)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
766);
builder.getOrCreateUidBatteryConsumerBuilder(UID_3);
builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setConsumedPower(30000)
- .setConsumedPower(
+ .addConsumedPower(30000)
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 20100,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_AUDIO, 0,
BatteryConsumer.POWER_MODEL_POWER_PROFILE) // Empty
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CAMERA, 20150,
BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, 20300)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20400);
// Not used; just to make sure extraneous data doesn't mess things up.
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 10100,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
return builder.build();
@@ -596,8 +596,8 @@ public class BatteryUsageStatsAtomTest {
BatteryConsumer.PROCESS_STATE_FOREGROUND, 1 * 60 * 1000)
.setTimeInProcessStateMs(
BatteryConsumer.PROCESS_STATE_BACKGROUND, 2 * 60 * 1000)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 30)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 40);
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 30)
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 40);
}
// Add a UID with much larger battery footprint
@@ -605,16 +605,16 @@ public class BatteryUsageStatsAtomTest {
builder.getOrCreateUidBatteryConsumerBuilder(largeConsumerUid)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 10 * 60 * 1000)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 20 * 60 * 1000)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
// Add a UID with much larger usage duration
final int highUsageUid = 3002;
builder.getOrCreateUidBatteryConsumerBuilder(highUsageUid)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 60 * 60 * 1000)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 120 * 60 * 1000)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 3)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 4);
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 3)
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 4);
BatteryUsageStats batteryUsageStats = builder.build();
final byte[] bytes = batteryUsageStats.getStatsProto();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 8239e0955032..709f83ba907d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -47,6 +47,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.server.power.stats.processor.MultiStatePowerAttributor;
@@ -80,6 +81,7 @@ public class BatteryUsageStatsProviderTest {
.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
private MockClock mMockClock = mStatsRule.getMockClock();
+ private MonotonicClock mMonotonicClock = new MonotonicClock(666777, mMockClock);
private Context mContext;
@Before
@@ -146,7 +148,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats,
@@ -273,7 +276,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
powerAttributor, mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
return provider.getBatteryUsageStats(batteryStats, BatteryUsageStatsQuery.DEFAULT);
}
@@ -303,7 +307,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats,
@@ -396,7 +401,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats,
@@ -487,7 +493,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock,
+ mMonotonicClock);
batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore,
/* accumulateBatteryUsageStats */ false);
@@ -590,7 +597,10 @@ public class BatteryUsageStatsProviderTest {
private void accumulateBatteryUsageStats(int accumulatedBatteryUsageStatsSpanSize,
int expectedNumberOfUpdates) throws Throwable {
- BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ BatteryStatsImpl batteryStats = spy(mStatsRule.getBatteryStats());
+ // Note - these two are in microseconds
+ when(batteryStats.computeBatteryTimeRemaining(anyLong())).thenReturn(111_000L);
+ when(batteryStats.computeChargeTimeRemaining(anyLong())).thenReturn(777_000L);
batteryStats.forceRecordAllHistory();
setTime(5 * MINUTE_IN_MS);
@@ -623,7 +633,7 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
powerAttributor, mStatsRule.getPowerProfile(),
mStatsRule.getCpuScalingPolicies(), powerStatsStore,
- accumulatedBatteryUsageStatsSpanSize, mMockClock);
+ accumulatedBatteryUsageStatsSpanSize, mMockClock, mMonotonicClock);
provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
@@ -677,9 +687,14 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats,
new BatteryUsageStatsQuery.Builder().accumulated().build());
+
assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
assertThat(stats.getStatsEndTimestamp()).isEqualTo(115 * MINUTE_IN_MS);
+ assertThat(stats.getBatteryTimeRemainingMs()).isEqualTo(111);
+ assertThat(stats.getChargeTimeRemainingMs()).isEqualTo(777);
+ assertThat(stats.getBatteryCapacity()).isEqualTo(4000); // from PowerProfile
+
// Total: 10 + 20 + 30 = 60
assertThat(stats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
@@ -729,7 +744,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
PowerStatsStore powerStatsStore = mock(PowerStatsStore.class);
doAnswer(invocation -> {
@@ -796,7 +812,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock,
+ mMonotonicClock);
BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
.aggregateSnapshots(0, 3000)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 1b6b8c49461e..9771da590e37 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -119,7 +119,7 @@ public class BatteryUsageStatsTest {
BatteryStatsImpl.Uid mockUid = mock(BatteryStatsImpl.Uid.class);
when(mockUid.getUid()).thenReturn(i);
builder.getOrCreateUidBatteryConsumerBuilder(mockUid)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, i * 100);
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, i * 100);
}
BatteryUsageStats outBatteryUsageStats = builder.build();
@@ -355,13 +355,13 @@ public class BatteryUsageStatsTest {
if (includeUserBatteryConsumer) {
builder.getOrCreateUserBatteryConsumerBuilder(USER_ID)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 10)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, 30)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
}
return builder;
@@ -422,15 +422,15 @@ public class BatteryUsageStatsTest {
.setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND, timeInProcessStateBackground)
.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
timeInProcessStateForegroundService)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration);
if (builder.isProcessStateDataNeeded()) {
final BatteryConsumer.Key cpuFgKey = builder.isScreenStateDataNeeded()
@@ -461,21 +461,21 @@ public class BatteryUsageStatsTest {
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
BatteryConsumer.PROCESS_STATE_BACKGROUND);
uidBuilder
- .setConsumedPower(cpuFgKey, cpuPowerForeground,
+ .addConsumedPower(cpuFgKey, cpuPowerForeground,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuFgKey, cpuDurationForeground)
- .setConsumedPower(cpuBgKey, cpuPowerBackground,
+ .addUsageDurationMillis(cpuFgKey, cpuDurationForeground)
+ .addConsumedPower(cpuBgKey, cpuPowerBackground,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuBgKey, cpuDurationBackground)
- .setConsumedPower(cpuFgsKey, cpuPowerFgs,
+ .addUsageDurationMillis(cpuBgKey, cpuDurationBackground)
+ .addConsumedPower(cpuFgsKey, cpuPowerFgs,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuFgsKey, cpuDurationFgs)
- .setConsumedPower(cachedKey, cpuPowerCached,
+ .addUsageDurationMillis(cpuFgsKey, cpuDurationFgs)
+ .addConsumedPower(cachedKey, cpuPowerCached,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cachedKey, cpuDurationCached)
- .setConsumedPower(customBgKey, customComponentPower,
+ .addUsageDurationMillis(cachedKey, cpuDurationCached)
+ .addConsumedPower(customBgKey, customComponentPower,
BatteryConsumer.POWER_MODEL_UNDEFINED)
- .setUsageDurationMillis(customBgKey, customComponentDuration);
+ .addUsageDurationMillis(customBgKey, customComponentDuration);
}
}
@@ -486,15 +486,15 @@ public class BatteryUsageStatsTest {
long cpuDurationChgScrOn, double cpuPowerChgScrOff, long cpuDurationChgScrOff) {
final AggregateBatteryConsumer.Builder aggBuilder =
builder.getAggregateBatteryConsumerBuilder(scope)
- .setConsumedPower(consumedPower)
- .setConsumedPower(
+ .addConsumedPower(consumedPower)
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, cpuPower)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
customComponentPower)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
customComponentDuration);
if (builder.isPowerStateDataNeeded() || builder.isScreenStateDataNeeded()) {
@@ -519,18 +519,18 @@ public class BatteryUsageStatsTest {
BatteryConsumer.SCREEN_STATE_OTHER,
BatteryConsumer.POWER_STATE_OTHER);
aggBuilder
- .setConsumedPower(cpuBatScrOn, cpuPowerBatScrOn,
+ .addConsumedPower(cpuBatScrOn, cpuPowerBatScrOn,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuBatScrOn, cpuDurationBatScrOn)
- .setConsumedPower(cpuBatScrOff, cpuPowerBatScrOff,
+ .addUsageDurationMillis(cpuBatScrOn, cpuDurationBatScrOn)
+ .addConsumedPower(cpuBatScrOff, cpuPowerBatScrOff,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuBatScrOff, cpuDurationBatScrOff)
- .setConsumedPower(cpuChgScrOn, cpuPowerChgScrOn,
+ .addUsageDurationMillis(cpuBatScrOff, cpuDurationBatScrOff)
+ .addConsumedPower(cpuChgScrOn, cpuPowerChgScrOn,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuChgScrOn, cpuDurationChgScrOn)
- .setConsumedPower(cpuChgScrOff, cpuPowerChgScrOff,
+ .addUsageDurationMillis(cpuChgScrOn, cpuDurationChgScrOn)
+ .addConsumedPower(cpuChgScrOff, cpuPowerChgScrOff,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuChgScrOff, cpuDurationChgScrOff);
+ .addUsageDurationMillis(cpuChgScrOff, cpuDurationChgScrOff);
}
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 359755a7ad08..5b35af1de88d 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -157,21 +157,49 @@ android_test {
resource_zips: [":FrameworksServicesTests_apks_as_resources"],
}
-android_ravenwood_test {
- name: "FrameworksServicesTestsRavenwood",
+java_defaults {
+ name: "FrameworksServicesTestsRavenwood-defaults",
libs: [
"android.test.mock.stubs.system",
],
static_libs: [
"androidx.annotation_annotation",
"androidx.test.rules",
- "services.core",
"flag-junit",
],
+ auto_gen_config: true,
+}
+
+// Unit tests for UriGrantManager, running on ravenwood.
+// Note UriGrantManager does not support Ravenwood (yet). We're just running the original
+// unit tests as is on Ravenwood. So here, we use the original "services.core", because
+// "services.core.ravenwood" doesn't have the target code.
+// (Compare to FrameworksServicesTestsRavenwood_Compat, which does support Ravenwood.)
+android_ravenwood_test {
+ name: "FrameworksServicesTestsRavenwood_Uri",
+ defaults: ["FrameworksServicesTestsRavenwood-defaults"],
+ team: "trendy_team_ravenwood",
+ static_libs: [
+ "services.core",
+ ],
srcs: [
"src/com/android/server/uri/**/*.java",
],
- auto_gen_config: true,
+}
+
+// Unit tests for compat-framework.
+// Compat-framework does support Ravenwood, and it uses the ravenwood anottations,
+// so we link "services.core.ravenwood".
+android_ravenwood_test {
+ name: "FrameworksServicesTestsRavenwood_Compat",
+ defaults: ["FrameworksServicesTestsRavenwood-defaults"],
+ team: "trendy_team_ravenwood",
+ static_libs: [
+ "services.core.ravenwood",
+ ],
+ srcs: [
+ "src/com/android/server/compat/**/*.java",
+ ],
}
java_library {
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java
index c4b3c149bd8d..5d7ffe91e67d 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.app.PropertyInvalidatedCache;
import android.companion.virtual.VirtualDeviceManager;
import android.content.Context;
import android.os.FileUtils;
@@ -69,6 +70,7 @@ public class AppOpsRecentAccessPersistenceTest {
@Before
public void setUp() {
+ PropertyInvalidatedCache.disableForTestMode();
when(mAppOpCheckingService.addAppOpsModeChangedListener(any())).thenReturn(true);
LocalServices.addService(AppOpsCheckingServiceInterface.class, mAppOpCheckingService);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index c9e9f00985f1..c418151b9946 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.audio;
import static android.media.AudioDeviceInfo.TYPE_BUILTIN_MIC;
+import static android.media.AudioManager.GET_DEVICES_INPUTS;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,7 +35,9 @@ import android.media.AudioSystem;
import android.os.Looper;
import android.os.PermissionEnforcer;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.platform.test.annotations.EnableFlags;
+import android.util.IntArray;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -80,12 +83,15 @@ public class AudioServiceTest {
private static boolean sLooperPrepared = false;
+ private TestLooper mTestLooper;
+
@Before
public void setUp() throws Exception {
if (!sLooperPrepared) {
Looper.prepare();
sLooperPrepared = true;
}
+ mTestLooper = new TestLooper();
mContext = InstrumentationRegistry.getTargetContext();
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSettingsAdapter = new NoOpSettingsAdapter();
@@ -93,8 +99,11 @@ public class AudioServiceTest {
when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSpySystemServer,
- mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy, null,
- mMockAppOpsManager, mMockPermissionEnforcer, mMockPermissionProvider, r -> r.run());
+ mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy,
+ mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer,
+ mMockPermissionProvider, r -> r.run());
+
+ mTestLooper.dispatchAll();
}
/**
@@ -216,7 +225,19 @@ public class AudioServiceTest {
public void testInputGainIndex() throws Exception {
Log.i(TAG, "running testInputGainIndex");
Assert.assertNotNull(mAudioService);
- Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // wait for full AudioService initialization
+
+ IntArray internalDeviceTypes = new IntArray();
+ int status = AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS, internalDeviceTypes);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, "AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS) failed. status:"
+ + status);
+ }
+
+ // Make sure TYPE_BUILTIN_MIC, aka DEVICE_IN_BUILTIN_MIC in terms of internal device type,
+ // is supported.
+ if (!internalDeviceTypes.contains(AudioSystem.DEVICE_IN_BUILTIN_MIC)) {
+ return;
+ }
AudioDeviceAttributes ada =
new AudioDeviceAttributes(
@@ -229,6 +250,8 @@ public class AudioServiceTest {
int inputGainIndex = 20;
mAudioService.setInputGainIndex(ada, inputGainIndex);
+ mTestLooper.dispatchAll();
+
Assert.assertEquals(
"input gain index reporting wrong value",
inputGainIndex,
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 36b163ec84b6..3d695a68f1f9 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -18,12 +18,12 @@ package com.android.server.compat;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
import android.app.compat.ChangeIdStateCache;
import android.app.compat.PackageOverride;
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 1d075401832d..95d601f69344 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -18,6 +18,7 @@ package com.android.server.compat;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
@@ -26,9 +27,9 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;
-import static org.testng.Assert.assertThrows;
import android.compat.Compatibility.ChangeConfig;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -39,6 +40,8 @@ import android.os.Process;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.os.PermissionEnforcer;
+import android.permission.PermissionCheckerManager;
import androidx.test.runner.AndroidJUnit4;
@@ -90,6 +93,22 @@ public class PlatformCompatTest {
.thenReturn(-1);
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
+
+ var allGrantingPermissionEnforcer = new PermissionEnforcer() {
+ @Override
+ protected int checkPermission(String permission, AttributionSource source) {
+ return PermissionCheckerManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ protected int checkPermission(String permission, int pid, int uid) {
+ return PermissionCheckerManager.PERMISSION_GRANTED;
+ }
+ };
+
+ when(mContext.getSystemService(eq(Context.PERMISSION_ENFORCER_SERVICE)))
+ .thenReturn(allGrantingPermissionEnforcer);
+
mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
mPlatformCompat =
new PlatformCompat(mContext, mCompatConfig, mBuildClassifier, mChangeReporter);
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 24abc183cad1..f5494534716a 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -61,7 +61,6 @@ import android.os.Process;
import android.os.UserHandle;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
-import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArraySet;
import org.junit.Before;
@@ -77,9 +76,6 @@ import java.util.Set;
@RunWith(Parameterized.class)
public class UriGrantsManagerServiceTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
/**
* Why this class needs to test all combinations of
* {@link android.security.Flags#FLAG_CONTENT_URI_PERMISSION_APIS}:
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
index 611c51463246..fe66f738487d 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
@@ -37,18 +37,13 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.os.SystemClock;
-import android.platform.test.ravenwood.RavenwoodRule;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class UriPermissionTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
@Mock
private UriGrantsManagerInternal mService;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 22a4f85758eb..0b89c11a11f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -34,10 +34,12 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
import static android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.android.server.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS;
+import static com.android.server.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS;
import static com.android.server.notification.GroupHelper.AGGREGATE_GROUP_KEY;
import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY;
import static com.android.server.notification.GroupHelper.BASE_FLAGS;
@@ -2217,6 +2219,7 @@ public class GroupHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+ @DisableFlags(FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION)
public void testMoveAggregateGroups_updateChannel_multipleChannels() {
final String pkg = "package";
final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
@@ -2265,16 +2268,17 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel1,
notificationList);
- // Check that channel1's notifications are moved to the silent section group
- // But not enough to auto-group => remove override group key
- verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- anyString(), anyInt(), any());
- verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ // Check that the override group key was cleared
for (NotificationRecord record: notificationList) {
if (record.getChannel().getId().equals(channel1.getId())) {
assertThat(record.getSbn().getOverrideGroupKey()).isNull();
}
}
+ // Check that channel1's notifications are moved to the silent section group
+ // and a group summary is created + notifications are added to the group
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), anyString(),
+ anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
// Check that the alerting section group is not removed, only updated
expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
@@ -2287,6 +2291,357 @@ public class GroupHelperTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testMoveAggregateGroups_updateChannel_multipleChannels_regroupOnClassifEnabled() {
+ final String pkg = "package";
+ final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
+ int numNotificationChannel1 = 0;
+ final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
+ "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
+ final NotificationChannel channel2 = new NotificationChannel("TEST_CHANNEL_ID2",
+ "TEST_CHANNEL_ID2", IMPORTANCE_DEFAULT);
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ // Post notifications with different channels that autogroup within the same section
+ NotificationRecord r;
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ if (i % 2 == 0) {
+ r = getNotificationRecord(pkg, i, String.valueOf(i),
+ UserHandle.SYSTEM, "testGrp " + i, false, channel1);
+ numNotificationChannel1++;
+ } else {
+ r = getNotificationRecord(pkg, i, String.valueOf(i),
+ UserHandle.SYSTEM, "testGrp " + i, false, channel2);
+ }
+ notificationList.add(r);
+ mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
+ }
+ NotificationAttributes expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
+ mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ "TEST_CHANNEL_ID1");
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_alerting), anyInt(), eq(expectedSummaryAttr));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_alerting), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ Mockito.reset(mCallback);
+
+ // Update channel1's importance
+ final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
+ channel1.setImportance(IMPORTANCE_LOW);
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())) {
+ record.updateNotificationChannel(channel1);
+ }
+ }
+ mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel1,
+ notificationList);
+
+ // Check that the override group key was cleared
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())) {
+ assertThat(record.getSbn().getOverrideGroupKey()).isNull();
+ }
+ }
+ // Check that channel1's notifications are moved to the silent section group
+ // and a group summary is created + notifications are added to the group
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_silent), anyInt(), any());
+ verify(mCallback, times(numNotificationChannel1)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_silent), anyBoolean());
+
+ // Check that the alerting section group is not removed, only updated
+ expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
+ mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ "TEST_CHANNEL_ID2");
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_alerting));
+ verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_alerting), eq(expectedSummaryAttr));
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testMoveSections_notificationBundled() {
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final String pkg = "package";
+ final int summaryId = 0;
+ final int numChildNotif = 4;
+
+ // Create an app-provided group: summary + child notifications
+ final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
+ "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId,
+ String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId,
+ true, channel1);
+ notificationList.add(summary);
+ final String originalAppGroupKey = summary.getGroupKey();
+ for (int i = 0; i < numChildNotif; i++) {
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp " + summaryId, false, channel1);
+ notificationList.add(child);
+ }
+
+ // Classify/bundle child notifications
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.SOCIAL_MEDIA_ID);
+ final NotificationChannel newsChannel = new NotificationChannel(
+ NotificationChannel.NEWS_ID, NotificationChannel.NEWS_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_news = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "NewsSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_news = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.NEWS_ID);
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())
+ && record.getSbn().getId() % 2 == 0) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ if (record.getChannel().getId().equals(channel1.getId())
+ && record.getSbn().getId() % 2 != 0) {
+ record.updateNotificationChannel(newsChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that 2 autogroup summaries were created for the news & social sections
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_news), anyInt(), eq(expectedSummaryAttr_news));
+ // Check that half of the child notifications were grouped in each new section
+ verify(mCallback, times(numChildNotif / 2)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_news), eq(true));
+ verify(mCallback, times(numChildNotif / 2)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_social), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, times(numChildNotif / 2)).updateAutogroupSummary(anyInt(), anyString(),
+ anyString(), any());
+ verify(mCallback, times(numChildNotif)).removeAppProvidedSummaryOnClassification(
+ anyString(), eq(originalAppGroupKey));
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testCacheAndCancelAppSummary_notificationBundled() {
+ // check that the original app summary is canceled & cached on classification regrouping
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final String pkg = "package";
+ final int summaryId = 0;
+ final int numChildNotif = 4;
+
+ // Create an app-provided group: summary + child notifications
+ final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
+ "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId,
+ String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId,
+ true, channel1);
+ notificationList.add(summary);
+ final String originalAppGroupKey = summary.getGroupKey();
+ final String originalAppGroupName = summary.getNotification().getGroup();
+ for (int i = 0; i < numChildNotif; i++) {
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp " + summaryId, false, channel1);
+ notificationList.add(child);
+ }
+
+ // Last regrouped notification will trigger summary cancellation in NMS
+ when(mCallback.removeAppProvidedSummaryOnClassification(anyString(),
+ eq(originalAppGroupKey))).thenReturn(summary);
+
+ // Classify/bundle child notifications
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that the original app summary was cached
+ CachedSummary cachedSummary = mGroupHelper.findCanceledSummary(pkg,
+ String.valueOf(summaryId), summaryId, UserHandle.SYSTEM.getIdentifier());
+ assertThat(cachedSummary.originalGroupKey()).isEqualTo(originalAppGroupName);
+ assertThat(cachedSummary.key()).isEqualTo(summary.getKey());
+
+ // App cancels the original summary
+ reset(mCallback);
+ mGroupHelper.maybeCancelGroupChildrenForCanceledSummary(pkg, String.valueOf(summaryId),
+ summaryId, UserHandle.SYSTEM.getIdentifier(), REASON_APP_CANCEL);
+ // Check that child notifications are removed and cache is cleared
+ verify(mCallback, times(1)).removeNotificationFromCanceledGroup(
+ eq(UserHandle.SYSTEM.getIdentifier()), eq(pkg), eq(originalAppGroupName),
+ eq(REASON_APP_CANCEL));
+ cachedSummary = mGroupHelper.findCanceledSummary(pkg, String.valueOf(summaryId), summaryId,
+ UserHandle.SYSTEM.getIdentifier());
+ assertThat(cachedSummary).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
+ public void testSingletonGroupsRegrouped_notificationBundledBeforeDelayTimeout() {
+ // Check that singleton group notifications are regrouped if classification is done
+ // before onNotificationPostedWithDelay
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ // Post singleton groups, above forced group limit
+ for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
+ NotificationRecord summary = getNotificationRecord(pkg, i,
+ String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
+ notificationList.add(summary);
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp " + i, false);
+ notificationList.add(child);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ }
+
+ // Classify/bundle child notifications
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.SOCIAL_MEDIA_ID);
+ for (NotificationRecord record: notificationList) {
+ if (record.getOriginalGroupKey().contains("testGrp")) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that notifications are forced grouped and app-provided summaries are canceled
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_social), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ verify(mCallback, times(2)).removeAppProvidedSummaryOnClassification(
+ anyString(), anyString());
+
+ // Adjust group key and cancel summaries
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupSummary()) {
+ record.isCanceled = true;
+ } else {
+ record.setOverrideGroupKey(expectedGroupKey_social);
+ }
+ }
+
+ // Check that after onNotificationPostedWithDelay there is no change in the grouping
+ reset(mCallback);
+ for (NotificationRecord record: notificationList) {
+ mGroupHelper.onNotificationPostedWithDelay(record, notificationList, summaryByGroup);
+ }
+
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
+ public void testSingletonGroupsRegrouped_notificationBundledAfterDelayTimeout() {
+ // Check that singleton group notifications are regrouped if classification is done
+ // after onNotificationPostedWithDelay
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+ final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
+ String expectedTriggeringKey = null;
+ // Post singleton groups, above forced group limit
+ for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
+ NotificationRecord summary = getNotificationRecord(pkg, i,
+ String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
+ notificationList.add(summary);
+ NotificationRecord child = getNotificationRecord(pkg, i + 42,
+ String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false);
+ notificationList.add(child);
+ expectedTriggeringKey = child.getKey();
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
+ summary.isCanceled = true; // simulate removing the app summary
+ mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
+ }
+
+ // Check that notifications are forced grouped and app-provided summaries are canceled
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg),
+ eq(expectedTriggeringKey), eq(expectedGroupKey_alerting), anyInt(),
+ eq(getNotificationAttributes(BASE_FLAGS)));
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_alerting), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).removeAppProvidedSummary(
+ anyString());
+ assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(0), 0,
+ UserHandle.SYSTEM.getIdentifier())).isNotNull();
+ assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(1), 1,
+ UserHandle.SYSTEM.getIdentifier())).isNotNull();
+
+ // Classify/bundle child notifications
+ reset(mCallback);
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.SOCIAL_MEDIA_ID);
+ for (NotificationRecord record: notificationList) {
+ if (record.getOriginalGroupKey().contains("testGrp")) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that all notifications are moved to the social section group
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_social), eq(true));
+ // Check that the alerting section group is removed
+ verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_alerting));
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).updateAutogroupSummary(anyInt(),
+ anyString(), anyString(), any());
+ }
+
+ @Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
public void testMoveAggregateGroups_updateChannel_groupsUngrouped() {
final String pkg = "package";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index b5724b5c0cc8..48bc9d7c51a1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -21,10 +21,8 @@ import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
-import static com.android.server.notification.Flags.FLAG_NOTIFICATION_NLS_REBIND;
import static com.android.server.notification.ManagedServices.APPROVAL_BY_COMPONENT;
import static com.android.server.notification.ManagedServices.APPROVAL_BY_PACKAGE;
import static com.android.server.notification.NotificationManagerService.privateSpaceFlagsEnabled;
@@ -65,14 +63,11 @@ import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
-import android.testing.TestableLooper;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -87,9 +82,7 @@ import com.android.server.UiServiceTestCase;
import com.google.android.collect.Lists;
-import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -110,10 +103,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
-
public class ManagedServicesTest extends UiServiceTestCase {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@Mock
private IPackageManager mIpm;
@@ -125,7 +115,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
private ManagedServices.UserProfiles mUserProfiles;
@Mock private DevicePolicyManager mDpm;
Object mLock = new Object();
- private TestableLooper mTestableLooper;
UserInfo mZero = new UserInfo(0, "zero", 0);
UserInfo mTen = new UserInfo(10, "ten", 0);
@@ -153,7 +142,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mTestableLooper = new TestableLooper(Looper.getMainLooper());
mContext.setMockPackageManager(mPm);
mContext.addMockSystemService(Context.USER_SERVICE, mUm);
@@ -211,11 +199,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
mIpm, APPROVAL_BY_COMPONENT);
}
- @After
- public void tearDown() throws Exception {
- mTestableLooper.destroy();
- }
-
@Test
public void testBackupAndRestore_migration() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
@@ -905,7 +888,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a", 0, true);
service.reregisterService(cn, 0);
@@ -936,7 +919,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a", 0, false);
service.reregisterService(cn, 0);
@@ -967,7 +950,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a/a", 0, true);
service.reregisterService(cn, 0);
@@ -998,7 +981,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a/a", 0, false);
service.reregisterService(cn, 0);
@@ -1070,78 +1053,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
- public void registerService_bindingDied_rebindIsClearedOnUserSwitch() throws Exception {
- Context context = mock(Context.class);
- PackageManager pm = mock(PackageManager.class);
- ApplicationInfo ai = new ApplicationInfo();
- ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-
- when(context.getPackageName()).thenReturn(mPkg);
- when(context.getUserId()).thenReturn(mUser.getIdentifier());
- when(context.getPackageManager()).thenReturn(pm);
- when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
-
- ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
- APPROVAL_BY_PACKAGE);
- service = spy(service);
- ComponentName cn = ComponentName.unflattenFromString("a/a");
-
- // Trigger onBindingDied for component when registering
- // => will schedule a rebind in 10 seconds
- when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
- Object[] args = invocation.getArguments();
- ServiceConnection sc = (ServiceConnection) args[1];
- sc.onBindingDied(cn);
- return true;
- });
- service.registerService(cn, 0);
- assertThat(service.isBound(cn, 0)).isFalse();
-
- // Switch to user 10
- service.onUserSwitched(10);
-
- // Check that the scheduled rebind for user 0 was cleared
- mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
- mTestableLooper.processAllMessages();
- verify(service, never()).reregisterService(any(), anyInt());
- }
-
- @Test
- public void registerService_bindingDied_rebindIsExecutedAfterTimeout() throws Exception {
- Context context = mock(Context.class);
- PackageManager pm = mock(PackageManager.class);
- ApplicationInfo ai = new ApplicationInfo();
- ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-
- when(context.getPackageName()).thenReturn(mPkg);
- when(context.getUserId()).thenReturn(mUser.getIdentifier());
- when(context.getPackageManager()).thenReturn(pm);
- when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
-
- ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
- APPROVAL_BY_PACKAGE);
- service = spy(service);
- ComponentName cn = ComponentName.unflattenFromString("a/a");
-
- // Trigger onBindingDied for component when registering
- // => will schedule a rebind in 10 seconds
- when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
- Object[] args = invocation.getArguments();
- ServiceConnection sc = (ServiceConnection) args[1];
- sc.onBindingDied(cn);
- return true;
- });
- service.registerService(cn, 0);
- assertThat(service.isBound(cn, 0)).isFalse();
-
- // Check that the scheduled rebind is run
- mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
- mTestableLooper.processAllMessages();
- verify(service, times(1)).reregisterService(eq(cn), eq(0));
- }
-
- @Test
public void testPackageUninstall_packageNoLongerInApprovedList() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1300,65 +1211,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
- public void testUpgradeAppNoIntentFilterNoRebind() throws Exception {
- Context context = spy(getContext());
- doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
-
- ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
- mIpm, APPROVAL_BY_COMPONENT);
-
- List<String> packages = new ArrayList<>();
- packages.add("package");
- addExpectedServices(service, packages, 0);
-
- final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
- final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
-
- // Both components are approved initially
- mExpectedPrimaryComponentNames.clear();
- mExpectedPrimaryPackages.clear();
- mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
- mExpectedSecondaryComponentNames.clear();
- mExpectedSecondaryPackages.clear();
-
- loadXml(service);
-
- //Component package/C1 loses serviceInterface intent filter
- ManagedServices.Config config = service.getConfig();
- when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
- .thenAnswer(new Answer<List<ResolveInfo>>() {
- @Override
- public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
- throws Throwable {
- Object[] args = invocationOnMock.getArguments();
- Intent invocationIntent = (Intent) args[0];
- if (invocationIntent != null) {
- if (invocationIntent.getAction().equals(config.serviceInterface)
- && packages.contains(invocationIntent.getPackage())) {
- List<ResolveInfo> dummyServices = new ArrayList<>();
- ResolveInfo resolveInfo = new ResolveInfo();
- ServiceInfo serviceInfo = new ServiceInfo();
- serviceInfo.packageName = invocationIntent.getPackage();
- serviceInfo.name = approvedComponent.getClassName();
- serviceInfo.permission = service.getConfig().bindPermission;
- resolveInfo.serviceInfo = serviceInfo;
- dummyServices.add(resolveInfo);
- return dummyServices;
- }
- }
- return new ArrayList<>();
- }
- });
-
- // Trigger package update
- service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
-
- assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
- assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
- }
-
- @Test
public void testSetPackageOrComponentEnabled() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1371,21 +1223,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
"user10package1/K", "user10.3/Component", "user10package2/L",
"user10.4/Component"}));
- // mock permissions for services
- PackageManager pm = mock(PackageManager.class);
- when(getContext().getPackageManager()).thenReturn(pm);
- List<ComponentName> enabledComponents = List.of(
- ComponentName.unflattenFromString("package/Comp"),
- ComponentName.unflattenFromString("package/C2"),
- ComponentName.unflattenFromString("again/M4"),
- ComponentName.unflattenFromString("user10package/B"),
- ComponentName.unflattenFromString("user10/Component"),
- ComponentName.unflattenFromString("user10package1/K"),
- ComponentName.unflattenFromString("user10.3/Component"),
- ComponentName.unflattenFromString("user10package2/L"),
- ComponentName.unflattenFromString("user10.4/Component"));
- mockServiceInfoWithMetaData(enabledComponents, service, pm, new ArrayMap<>());
-
for (int userId : expectedEnabled.keySet()) {
ArrayList<String> expectedForUser = expectedEnabled.get(userId);
for (int i = 0; i < expectedForUser.size(); i++) {
@@ -1447,90 +1284,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
- public void testSetPackageOrComponentEnabled_pkgInstalledAfterEnabling() throws Exception {
- ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
- mIpm, APPROVAL_BY_COMPONENT);
-
- final int userId = 0;
- final String validComponent = "again/M4";
- ArrayList<String> expectedEnabled = Lists.newArrayList("package/Comp", "package/C2",
- validComponent);
-
- PackageManager pm = mock(PackageManager.class);
- when(getContext().getPackageManager()).thenReturn(pm);
- service = spy(service);
-
- // Component again/M4 is a valid service and the package is available
- doReturn(true).when(service)
- .isValidService(ComponentName.unflattenFromString(validComponent), userId);
- when(pm.isPackageAvailable("again")).thenReturn(true);
-
- // "package" is not available and its services are not valid
- doReturn(false).when(service)
- .isValidService(ComponentName.unflattenFromString("package/Comp"), userId);
- doReturn(false).when(service)
- .isValidService(ComponentName.unflattenFromString("package/C2"), userId);
- when(pm.isPackageAvailable("package")).thenReturn(false);
-
- // Enable all components
- for (String component: expectedEnabled) {
- service.setPackageOrComponentEnabled(component, userId, true, true);
- }
-
- // Verify everything added is approved
- for (String component: expectedEnabled) {
- assertTrue("Not allowed: user: " + userId + " entry: " + component
- + " for approval level " + APPROVAL_BY_COMPONENT,
- service.isPackageOrComponentAllowed(component, userId));
- }
-
- // Add missing package "package"
- service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
-
- // Check that component of "package" are not enabled
- assertFalse(service.isComponentEnabledForCurrentProfiles(
- ComponentName.unflattenFromString("package/Comp")));
- assertFalse(service.isPackageOrComponentAllowed("package/Comp", userId));
-
- assertFalse(service.isComponentEnabledForCurrentProfiles(
- ComponentName.unflattenFromString("package/C2")));
- assertFalse(service.isPackageOrComponentAllowed("package/C2", userId));
-
- // Check that the valid components are still enabled
- assertTrue(service.isComponentEnabledForCurrentProfiles(
- ComponentName.unflattenFromString(validComponent)));
- assertTrue(service.isPackageOrComponentAllowed(validComponent, userId));
- }
-
- @Test
- @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
- public void testSetPackageOrComponentEnabled_invalidComponent() throws Exception {
- ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
- mIpm, APPROVAL_BY_COMPONENT);
-
- final int userId = 0;
- final String invalidComponent = "package/Comp";
-
- PackageManager pm = mock(PackageManager.class);
- when(getContext().getPackageManager()).thenReturn(pm);
- service = spy(service);
-
- // Component is an invalid service and the package is available
- doReturn(false).when(service)
- .isValidService(ComponentName.unflattenFromString(invalidComponent), userId);
- when(pm.isPackageAvailable("package")).thenReturn(true);
- service.setPackageOrComponentEnabled(invalidComponent, userId, true, true);
-
- // Verify that the component was not enabled
- assertFalse("Not allowed: user: " + userId + " entry: " + invalidComponent
- + " for approval level " + APPROVAL_BY_COMPONENT,
- service.isPackageOrComponentAllowed(invalidComponent, userId));
- assertFalse(service.isComponentEnabledForCurrentProfiles(
- ComponentName.unflattenFromString(invalidComponent)));
- }
-
- @Test
public void testGetAllowedPackages_byUser() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -2191,7 +1944,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
metaDataAutobindAllow.putBoolean(META_DATA_DEFAULT_AUTOBIND, true);
metaDatas.put(cn_allowed, metaDataAutobindAllow);
- mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, metaDatas);
service.addApprovedList(cn_allowed.flattenToString(), 0, true);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2236,7 +1989,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
- mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, metaDatas);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2275,7 +2028,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
- mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, metaDatas);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2346,8 +2099,8 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
- ManagedServices service, PackageManager packageManager,
- ArrayMap<ComponentName, Bundle> metaDatas) throws RemoteException {
+ ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
+ throws RemoteException {
when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
(Answer<ServiceInfo>) invocation -> {
ComponentName invocationCn = invocation.getArgument(0);
@@ -2362,39 +2115,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
return null;
}
);
-
- // add components to queryIntentServicesAsUser response
- final List<String> packages = new ArrayList<>();
- for (ComponentName cn: componentNames) {
- packages.add(cn.getPackageName());
- }
- ManagedServices.Config config = service.getConfig();
- when(packageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
- thenAnswer(new Answer<List<ResolveInfo>>() {
- @Override
- public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
- throws Throwable {
- Object[] args = invocationOnMock.getArguments();
- Intent invocationIntent = (Intent) args[0];
- if (invocationIntent != null) {
- if (invocationIntent.getAction().equals(config.serviceInterface)
- && packages.contains(invocationIntent.getPackage())) {
- List<ResolveInfo> dummyServices = new ArrayList<>();
- for (ComponentName cn: componentNames) {
- ResolveInfo resolveInfo = new ResolveInfo();
- ServiceInfo serviceInfo = new ServiceInfo();
- serviceInfo.packageName = invocationIntent.getPackage();
- serviceInfo.name = cn.getClassName();
- serviceInfo.permission = service.getConfig().bindPermission;
- resolveInfo.serviceInfo = serviceInfo;
- dummyServices.add(resolveInfo);
- }
- return dummyServices;
- }
- }
- return new ArrayList<>();
- }
- });
}
private void resetComponentsAndPackages() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 2c645e0ca353..6eb2f718a0e9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -17,6 +17,9 @@ package com.android.server.notification;
import static android.os.UserHandle.USER_ALL;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENTS;
@@ -28,7 +31,6 @@ import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
-
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
@@ -58,6 +60,8 @@ import android.testing.TestableContext;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.Log;
+import android.util.Slog;
import android.util.Xml;
import androidx.test.runner.AndroidJUnit4;
@@ -198,8 +202,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testWriteXml_userTurnedOffNAS() throws Exception {
int userId = ActivityManager.getCurrentUser();
- doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
-
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
@@ -435,10 +437,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
-
- doReturn(true).when(mAssistants).isValidService(eq(component1), eq(mZero.id));
- doReturn(true).when(mAssistants).isValidService(eq(component2), eq(mZero.id));
-
mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true,
true, true);
verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal(
@@ -584,7 +582,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testSetAdjustmentTypeSupportedState() throws Exception {
int userId = ActivityManager.getCurrentUser();
- doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
true, true);
@@ -608,7 +605,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testSetAdjustmentTypeSupportedState_readWriteXml_entries() throws Exception {
int userId = ActivityManager.getCurrentUser();
- doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
true, true);
@@ -632,7 +628,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testSetAdjustmentTypeSupportedState_readWriteXml_empty() throws Exception {
int userId = ActivityManager.getCurrentUser();
- doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
true, true);
@@ -690,4 +685,47 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
assertThat(mAssistants.getAllowedAssistantAdjustments())
.containsExactlyElementsIn(DEFAULT_ALLOWED_ADJUSTMENTS);
}
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testSetAssistantAdjustmentKeyTypeState_allow() {
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+ .containsExactly(TYPE_PROMOTION);
+
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
+
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+ .containsExactlyElementsIn(List.of(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION));
+ }
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testSetAssistantAdjustmentKeyTypeState_disallow() {
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testDisallowAdjustmentKeyType_readWriteXml() throws Exception {
+ mAssistants.loadDefaultsFromConfig(true);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
+
+ writeXmlAndReload(USER_ALL);
+
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+ .containsExactlyElementsIn(List.of(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION));
+ }
+
+ @Test
+ public void testDefaultAllowedKeyAdjustments_readWriteXml() throws Exception {
+ mAssistants.loadDefaultsFromConfig(true);
+
+ writeXmlAndReload(USER_ALL);
+
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+ .containsExactly(TYPE_PROMOTION);
+ }
} \ No newline at end of file
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 cbfdc5f61e3f..48308a409036 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -43,8 +43,8 @@ import static android.app.Notification.FLAG_PROMOTED_ONGOING;
import static android.app.Notification.FLAG_USER_INITIATED_JOB;
import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.VISIBILITY_PRIVATE;
-import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.PROMOTIONS_ID;
import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
@@ -78,7 +78,6 @@ import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
-import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.PackageManager.FEATURE_TELECOM;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -114,6 +113,7 @@ import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
import static android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION;
import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
@@ -336,12 +336,12 @@ import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import com.google.android.collect.Lists;
-import com.google.common.collect.ImmutableList;
-
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+import com.google.android.collect.Lists;
+import com.google.common.collect.ImmutableList;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -366,7 +366,6 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.OutputStream;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
@@ -2685,6 +2684,41 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testAggregateGroups_RemoveAppSummary_onClassification() throws Exception {
+ final String originalGroupName = "originalGroup";
+ final int summaryId = 0;
+ final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 1, originalGroupName, false);
+ mService.addNotification(r1);
+ final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 2, originalGroupName, false);
+ mService.addNotification(r2);
+ final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel,
+ summaryId, originalGroupName, true);
+ mService.addNotification(summary);
+ final String originalGroupKey = summary.getGroupKey();
+ assertThat(mService.mSummaryByGroupKey).containsEntry(originalGroupKey, summary);
+
+ // Regroup first child notification
+ r1.setOverrideGroupKey("newGroup");
+ // Check that removeAppProvidedSummaryOnClassificationLocked is null
+ // => there is still one child left in the original group
+ assertThat(mService.removeAppProvidedSummaryOnClassificationLocked(r1.getKey(),
+ originalGroupKey)).isNull();
+
+ // Regroup last child notification
+ r2.setOverrideGroupKey("newGroup");
+ // Check that removeAppProvidedSummaryOnClassificationLocked returns the original summary
+ // and that the original app-provided summary is canceled
+ assertThat(mService.removeAppProvidedSummaryOnClassificationLocked(r2.getKey(),
+ originalGroupKey)).isEqualTo(summary);
+ waitForIdle();
+ verify(mWorkerHandler, times(1)).scheduleCancelNotification(any(), eq(summaryId));
+ assertThat(mService.mSummaryByGroupKey).doesNotContainKey(originalGroupKey);
+ }
+
+ @Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
public void testUngroupingAggregateSummary() throws Exception {
final String originalGroupName = "originalGroup";
@@ -7480,6 +7514,63 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testClassificationChannelAdjustmentsLogged() throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+
+ // Set up notifications that will be adjusted
+ final NotificationRecord r1 = spy(generateNotificationRecord(
+ mTestNotificationChannel, 1, null, true));
+ when(r1.getLifespanMs(anyLong())).thenReturn(234);
+
+ r1.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ // Enqueues the notification to be posted, so hasPosted will be false.
+ mService.addEnqueuedNotification(r1);
+
+ // Test an adjustment for an enqueued notification
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment1 = new Adjustment(
+ r1.getSbn().getPackageName(), r1.getKey(), signals, "",
+ r1.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment1);
+ assertTrue(mService.checkLastClassificationChannelLog(false /*hasPosted*/,
+ true /*isAlerting*/, 3 /*TYPE_NEWS*/, 234));
+
+ // Set up notifications that will be adjusted
+ // This notification starts on a low importance channel, so isAlerting is false.
+ NotificationChannel mLowImportanceNotificationChannel = new NotificationChannel(
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_LOW);
+ final NotificationRecord r2 = spy(generateNotificationRecord(
+ mLowImportanceNotificationChannel, 1, null, true));
+ when(r2.getLifespanMs(anyLong())).thenReturn(345);
+
+ r2.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ // Adds the notification as already posted, so hasPosted will be true.
+ mService.addNotification(r2);
+ // The signal is removed when used so it has to be readded.
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment2 = new Adjustment(
+ r2.getSbn().getPackageName(), r2.getKey(), signals, "",
+ r2.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment2);
+ assertTrue(mService.checkLastClassificationChannelLog(true /*hasPosted*/,
+ false /*isAlerting*/, 3 /*TYPE_NEWS*/, 345)); // currently failing
+
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_PROMOTION);
+ Adjustment adjustment3 = new Adjustment(
+ r2.getSbn().getPackageName(), r2.getKey(), signals, "",
+ r2.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment3);
+ assertTrue(mService.checkLastClassificationChannelLog(true /*hasPosted*/,
+ false /*isAlerting*/, 1 /*TYPE_PROMOTION*/, 345));
+ }
+
+ @Test
public void testAdjustmentToImportanceNone_cancelsNotification() throws Exception {
NotificationManagerService.WorkerHandler handler = mock(
NotificationManagerService.WorkerHandler.class);
@@ -14365,9 +14456,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
}
- private NotificationRecord createBigPictureRecord(boolean isBigPictureStyle, boolean hasImage,
- boolean isImageBitmap, boolean isExpired) {
- Notification.Builder builder = new Notification.Builder(mContext);
+ private Notification createBigPictureNotification(boolean isBigPictureStyle, boolean hasImage,
+ boolean isImageBitmap) {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
Notification.BigPictureStyle style = new Notification.BigPictureStyle();
if (isBigPictureStyle && hasImage) {
@@ -14383,12 +14475,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification notification = builder.setChannelId(TEST_CHANNEL_ID).build();
+ return notification;
+ }
+
+ private NotificationRecord createBigPictureRecord(boolean isBigPictureStyle, boolean hasImage,
+ boolean isImageBitmap, boolean isExpired) {
long timePostedMs = System.currentTimeMillis();
if (isExpired) {
timePostedMs -= BITMAP_DURATION.toMillis();
}
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
- notification, UserHandle.getUserHandleForUid(mUid), null, timePostedMs);
+ createBigPictureNotification(isBigPictureStyle, hasImage, isImageBitmap),
+ UserHandle.getUserHandleForUid(mUid), null, timePostedMs);
return new NotificationRecord(mContext, sbn, mTestNotificationChannel);
}
@@ -14400,6 +14498,33 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testRemoveBitmaps_canRemoveRevokedDelegate() throws Exception {
+ Notification n = createBigPictureNotification(true, true, true);
+ long timePostedMs = System.currentTimeMillis();
+ timePostedMs -= BITMAP_DURATION.toMillis();
+
+ when(mPermissionHelper.hasPermission(UID_O)).thenReturn(true);
+ when(mPackageManagerInternal.isSameApp(PKG_O, UID_O, UserHandle.getUserId(UID_O)))
+ .thenReturn(true);
+ mService.mPreferencesHelper.createNotificationChannel(PKG_O, UID_O,
+ mTestNotificationChannel, true /* fromTargetApp */, false, UID_O,
+ false);
+ mBinderService.createNotificationChannels(PKG_O, new ParceledListSlice(
+ Arrays.asList(mTestNotificationChannel, mSilentChannel, mMinChannel)));
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG_O, "old.delegate", 8, "tag",
+ UID_O, 0, n, UserHandle.getUserHandleForUid(UID_O), null, timePostedMs);
+
+ mService.addNotification(new NotificationRecord(mContext, sbn, mTestNotificationChannel));
+ mInternalService.removeBitmaps();
+
+ waitForIdle();
+
+ verify(mWorkerHandler, times(1))
+ .post(any(NotificationManagerService.EnqueueNotificationRunnable.class));
+ }
+
+ @Test
public void testRemoveBitmaps_notBigPicture_noRepost() {
addRecordAndRemoveBitmaps(
createBigPictureRecord(
@@ -17067,6 +17192,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationManagerService.WorkerHandler handler = mock(
NotificationManagerService.WorkerHandler.class);
mService.setHandler(handler);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
Bundle signals = new Bundle();
signals.putInt(KEY_TYPE, TYPE_NEWS);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index 07d25dfd814e..ba91ca2323af 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -52,6 +52,14 @@ public class TestableNotificationManagerService extends NotificationManagerServi
}
public SensitiveLog lastSensitiveLog = null;
+ private static class ClassificationChannelLog {
+ public boolean hasPosted;
+ public boolean isAlerting;
+ public long classification;
+ public long lifetime;
+ }
+ public ClassificationChannelLog lastClassificationChannelLog = null;
+
TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
InstanceIdSequence notificationInstanceIdSequence) {
super(context, logger, notificationInstanceIdSequence);
@@ -211,4 +219,29 @@ public class TestableNotificationManagerService extends NotificationManagerServi
public interface ComponentPermissionChecker {
int check(String permission, int uid, int owningUid, boolean exported);
}
+
+ @Override
+ protected void logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting,
+ int classification, int lifetimeMs) {
+ lastClassificationChannelLog = new ClassificationChannelLog();
+ lastClassificationChannelLog.hasPosted = hasPosted;
+ lastClassificationChannelLog.isAlerting = isAlerting;
+ lastClassificationChannelLog.classification = classification;
+ lastClassificationChannelLog.lifetime = lifetimeMs;
+ }
+
+ /**
+ * Returns true if the last recorded classification channel log has all the values specified.
+ */
+ public boolean checkLastClassificationChannelLog(boolean hasPosted, boolean isAlerting,
+ int classification, int lifetime) {
+ if (lastClassificationChannelLog == null) {
+ return false;
+ }
+
+ return hasPosted == lastClassificationChannelLog.hasPosted
+ && isAlerting == lastClassificationChannelLog.isAlerting
+ && classification == lastClassificationChannelLog.classification
+ && lifetime == lastClassificationChannelLog.lifetime;
+ }
}
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 0019b3e45e7b..4b94e103b9f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -493,6 +493,22 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
+ public void testZenOn_RepeatCallers_CallTypesBlocked() {
+ mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelper.setPriorityOnlyDndExemptPackages(new String[]{PKG_O});
+ // Any call allowed but no repeat callers
+ mZenModeHelper.mConsolidatedPolicy = new Policy(PRIORITY_CATEGORY_CALLS,
+ PRIORITY_SENDERS_ANY, 0, 0, 0);
+ mZenModeHelper.applyRestrictions();
+
+ verifyApplyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
+ verifyApplyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST);
+ }
+
+
+ @Test
public void testZenOn_StarredCallers_CallTypesBlocked() {
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelper.setPriorityOnlyDndExemptPackages(new String[]{PKG_O});
@@ -501,7 +517,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
| PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
| PRIORITY_CATEGORY_CONVERSATIONS | PRIORITY_CATEGORY_CALLS
| PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_EVENTS | PRIORITY_CATEGORY_REMINDERS
- | PRIORITY_CATEGORY_SYSTEM,
+ | PRIORITY_CATEGORY_SYSTEM | PRIORITY_CATEGORY_REPEAT_CALLERS,
PRIORITY_SENDERS_STARRED,
PRIORITY_SENDERS_ANY, 0, CONVERSATION_SENDERS_ANYONE);
mZenModeHelper.applyRestrictions();
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 25a8db6e6b9c..1e9038ee1769 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -397,7 +397,7 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
}
@Test
- @EnableFlags(Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL)
+ @EnableFlags(Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
@DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
public void testToggleTalkbackPress() {
testShortcutInternal("Meta + Alt + T -> Toggle talkback",
@@ -745,7 +745,7 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
}
@Test
- @EnableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL)
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
public void testKeyGestureToggleTalkback() {
Assert.assertTrue(
sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK));
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
index 30cc002b4144..bfce3d276a2d 100644
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -168,32 +168,28 @@ public class BatteryUsageStatsPerfTest {
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(123)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10100)
- .setConsumedPower(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 10300)
- .setUsageDurationMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
+ .addConsumedPower(123)
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100)
+ .addConsumedPower(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
+ .addUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, 10300)
+ .addUsageDurationMillis(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
for (int i = 0; i < 1000; i++) {
final UidBatteryConsumer.Builder consumerBuilder =
builder.getOrCreateUidBatteryConsumerBuilder(i)
.setPackageWithHighestDrain("example.packagename" + i)
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, i * 2000)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
+ .setTimeInProcessStateMs(UidBatteryConsumer.STATE_FOREGROUND, i * 2000)
+ .setTimeInProcessStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
componentId++) {
- consumerBuilder.setConsumedPower(componentId, componentId * 123.0,
+ consumerBuilder.addConsumedPower(componentId, componentId * 123.0,
BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- consumerBuilder.setUsageDurationMillis(componentId, componentId * 1000);
+ consumerBuilder.addUsageDurationMillis(componentId, componentId * 1000);
}
consumerBuilder
- .setConsumedPower(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
- .setUsageDurationMillis(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
+ .addConsumedPower(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
+ .addUsageDurationMillis(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
}
return builder.build();
}
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 6c9f764bbdee..1574d1b7ce6f 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -1260,24 +1260,102 @@ class KeyGestureControllerTests {
testKeyGestureInternal(test)
}
+ class TouchpadTestData(
+ val name: String,
+ val touchpadGestureType: Int,
+ val expectedKeyGestureType: Int,
+ val expectedAction: Int,
+ val expectedAppLaunchData: AppLaunchData? = null,
+ ) {
+ override fun toString(): String = name
+ }
+
+ @Keep
+ private fun customTouchpadGesturesTestArguments(): Array<TouchpadTestData> {
+ return arrayOf(
+ TouchpadTestData(
+ "3 Finger Tap -> Go Home",
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ ),
+ TouchpadTestData(
+ "3 Finger Tap -> Launch app",
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
+ ),
+ )
+ }
+
+ @Test
+ @Parameters(method = "customTouchpadGesturesTestArguments")
+ fun testCustomTouchpadGesture(test: TouchpadTestData) {
+ setupKeyGestureController()
+ val builder = InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
+ if (test.expectedAppLaunchData != null) {
+ builder.setAppLaunchData(test.expectedAppLaunchData)
+ }
+ val inputGestureData = builder.build()
+
+ keyGestureController.addCustomInputGesture(0, inputGestureData.aidlData)
+
+ val handledEvents = mutableListOf<KeyGestureEvent>()
+ val handler = KeyGestureHandler { event, _ ->
+ handledEvents.add(KeyGestureEvent(event))
+ true
+ }
+ keyGestureController.registerKeyGestureHandler(handler, 0)
+ handledEvents.clear()
+
+ keyGestureController.handleTouchpadGesture(test.touchpadGestureType)
+
+ assertEquals(
+ "Test: $test doesn't produce correct number of key gesture events",
+ 1,
+ handledEvents.size
+ )
+ val event = handledEvents[0]
+ assertEquals(
+ "Test: $test doesn't produce correct key gesture type",
+ test.expectedKeyGestureType,
+ event.keyGestureType
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct key gesture action",
+ test.expectedAction,
+ event.action
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct app launch data",
+ test.expectedAppLaunchData,
+ event.appLaunchData
+ )
+
+ keyGestureController.unregisterKeyGestureHandler(handler, 0)
+ }
+
private fun testKeyGestureInternal(test: TestData) {
- var handleEvents = mutableListOf<KeyGestureEvent>()
+ val handledEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
- handleEvents.add(KeyGestureEvent(event))
+ handledEvents.add(KeyGestureEvent(event))
true
}
keyGestureController.registerKeyGestureHandler(handler, 0)
- handleEvents.clear()
+ handledEvents.clear()
sendKeys(test.keys)
assertEquals(
"Test: $test doesn't produce correct number of key gesture events",
test.expectedActions.size,
- handleEvents.size
+ handledEvents.size
)
- for (i in handleEvents.indices) {
- val event = handleEvents[i]
+ for (i in handledEvents.indices) {
+ val event = handledEvents[i]
assertArrayEquals(
"Test: $test doesn't produce correct key gesture keycodes",
test.expectedKeys,
@@ -1309,16 +1387,16 @@ class KeyGestureControllerTests {
}
private fun testKeyGestureNotProduced(testName: String, testKeys: IntArray) {
- var handleEvents = mutableListOf<KeyGestureEvent>()
+ var handledEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
- handleEvents.add(KeyGestureEvent(event))
+ handledEvents.add(KeyGestureEvent(event))
true
}
keyGestureController.registerKeyGestureHandler(handler, 0)
- handleEvents.clear()
+ handledEvents.clear()
sendKeys(testKeys)
- assertEquals("Test: $testName should not produce Key gesture", 0, handleEvents.size)
+ assertEquals("Test: $testName should not produce Key gesture", 0, handledEvents.size)
}
private fun sendKeys(testKeys: IntArray, assertNotSentToApps: Boolean = false) {
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index be5c84c0353c..ac96ef28f501 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -53,6 +53,7 @@ public class TestableLooper {
private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
private static final Field MESSAGE_NEXT_FIELD;
private static final Field MESSAGE_WHEN_FIELD;
+ private static Field MESSAGE_QUEUE_USE_CONCURRENT_FIELD = null;
private Looper mLooper;
private MessageQueue mQueue;
@@ -63,6 +64,14 @@ public class TestableLooper {
static {
try {
+ MESSAGE_QUEUE_USE_CONCURRENT_FIELD =
+ MessageQueue.class.getDeclaredField("mUseConcurrent");
+ MESSAGE_QUEUE_USE_CONCURRENT_FIELD.setAccessible(true);
+ } catch (NoSuchFieldException ignored) {
+ // Ignore - maybe this is not CombinedMessageQueue?
+ }
+
+ try {
MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
@@ -146,6 +155,15 @@ public class TestableLooper {
mLooper = l;
mQueue = mLooper.getQueue();
mHandler = new Handler(mLooper);
+
+ // If we are using CombinedMessageQueue, we need to disable concurrent mode for testing.
+ if (MESSAGE_QUEUE_USE_CONCURRENT_FIELD != null) {
+ try {
+ MESSAGE_QUEUE_USE_CONCURRENT_FIELD.set(mQueue, false);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
/**
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index e6eabd804294..1bcfaf60857d 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -90,20 +90,6 @@ public class TestLooper {
* and call {@link #dispatchAll()}.
*/
public TestLooper(Clock clock) {
- Field messageQueueUseConcurrentField = null;
- boolean previousUseConcurrentValue = false;
- try {
- messageQueueUseConcurrentField = MessageQueue.class.getDeclaredField("sUseConcurrent");
- messageQueueUseConcurrentField.setAccessible(true);
- previousUseConcurrentValue = messageQueueUseConcurrentField.getBoolean(null);
- // If we are using CombinedMessageQueue, we need to disable concurrent mode for testing.
- messageQueueUseConcurrentField.set(null, false);
- } catch (NoSuchFieldException e) {
- // Ignore - maybe this is not CombinedMessageQueue?
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Reflection error constructing or accessing looper", e);
- }
-
try {
mLooper = LOOPER_CONSTRUCTOR.newInstance(false);
@@ -114,15 +100,19 @@ public class TestLooper {
throw new RuntimeException("Reflection error constructing or accessing looper", e);
}
- mClock = clock;
-
- if (messageQueueUseConcurrentField != null) {
- try {
- messageQueueUseConcurrentField.set(null, previousUseConcurrentValue);
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Reflection error constructing or accessing looper", e);
- }
+ // If we are using CombinedMessageQueue, we need to disable concurrent mode for testing.
+ try {
+ Field messageQueueUseConcurrentField =
+ MessageQueue.class.getDeclaredField("mUseConcurrent");
+ messageQueueUseConcurrentField.setAccessible(true);
+ messageQueueUseConcurrentField.set(mLooper.getQueue(), false);
+ } catch (NoSuchFieldException e) {
+ // Ignore - maybe this is not CombinedMessageQueue?
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Reflection error constructing or accessing looper", e);
}
+
+ mClock = clock;
}
public Looper getLooper() {