summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp10
-rw-r--r--api/StubLibraries.bp1
-rwxr-xr-xcmds/am/am.sh3
-rw-r--r--cmds/uinput/README.md10
-rw-r--r--core/api/system-current.txt13
-rw-r--r--core/api/test-current.txt9
-rw-r--r--core/java/android/app/DreamManager.java15
-rw-r--r--core/java/android/app/INotificationManager.aidl4
-rw-r--r--core/java/android/app/NotificationManager.java6
-rw-r--r--core/java/android/app/supervision/ISupervisionAppService.aidl2
-rw-r--r--core/java/android/app/supervision/SupervisionAppService.java19
-rw-r--r--core/java/android/app/supervision/SupervisionManager.java29
-rw-r--r--core/java/android/app/wallpaper/WallpaperDescription.java8
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java5
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java2
-rw-r--r--core/java/android/content/res/AssetManager.java55
-rw-r--r--core/java/android/credentials/flags.aconfig10
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java11
-rw-r--r--core/java/android/hardware/input/KeyGestureEvent.java34
-rw-r--r--core/java/android/os/PowerManagerInternal.java6
-rw-r--r--core/java/android/provider/Settings.java13
-rw-r--r--core/java/android/security/flags.aconfig9
-rw-r--r--core/java/android/service/dreams/IDreamManager.aidl2
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java5
-rw-r--r--core/java/android/timezone/TelephonyNetworkFinder.java16
-rw-r--r--core/java/android/view/IWindowManager.aidl3
-rw-r--r--core/java/android/view/SurfaceControl.java7
-rw-r--r--core/java/android/view/SurfaceControlRegistry.java10
-rw-r--r--core/java/android/view/ViewRootImpl.java12
-rw-r--r--core/java/android/view/contentcapture/ChildContentCaptureSession.java4
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java13
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java5
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java136
-rw-r--r--core/java/android/view/contentcapture/flags/content_capture_flags.aconfig12
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java1
-rw-r--r--core/java/android/widget/Magnifier.java5
-rw-r--r--core/java/android/window/TransitionInfo.java2
-rw-r--r--core/java/com/android/internal/app/IntentForwarderActivity.java15
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java7
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java10
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressBar.java17
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp38
-rw-r--r--core/jni/fd_utils.cpp3
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--core/res/res/layout/preference_list_fragment.xml1
-rw-r--r--core/res/res/layout/preference_list_fragment_material.xml1
-rw-r--r--core/res/res/values/config.xml8
-rw-r--r--core/res/res/values/config_telephony.xml2
-rw-r--r--core/res/res/values/public-staging.xml2
-rw-r--r--core/res/res/values/symbols.xml6
-rw-r--r--core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java2
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java2
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java61
-rw-r--r--graphics/java/android/graphics/BLASTBufferQueue.java6
-rw-r--r--graphics/java/android/graphics/OWNERS4
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/common/TestShellExecutor.kt (renamed from libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.kt)4
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml3
-rw-r--r--libs/WindowManager/Shell/shared/Android.bp1
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java4
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleAnythingFlagHelper.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt86
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt91
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt97
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayTestUtil.kt45
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt59
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java20
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt146
-rw-r--r--libs/androidfw/ApkParsing.cpp10
-rw-r--r--libs/androidfw/include/androidfw/ApkParsing.h2
-rw-r--r--libs/androidfw/tests/ApkParsing_test.cpp26
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp9
-rw-r--r--media/java/android/media/quality/MediaQualityManager.java36
-rw-r--r--packages/SettingsLib/Android.bp3
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt7
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt35
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt9
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt3
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt14
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt2
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt2
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreDelegate.kt52
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt16
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt30
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt32
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt53
-rw-r--r--packages/SettingsLib/Preference/testutils/Android.bp1
-rw-r--r--packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt8
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml1
-rw-r--r--packages/SettingsLib/ZeroStatePreference/Android.bp1
-rw-r--r--packages/SettingsLib/ZeroStatePreference/res/layout/settingslib_expressive_preference_zerostate.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt37
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java41
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig10
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt27
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/dream/ui/composable/DreamScene.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt46
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt26
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt15
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt11
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt31
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt13
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt3
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt6
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt2
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt99
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt136
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt95
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt123
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt3
-rw-r--r--packages/SystemUI/proguard_common.flags5
-rw-r--r--packages/SystemUI/res/drawable/ic_media_connecting_button_container.xml45
-rw-r--r--packages/SystemUI/res/drawable/ic_media_connecting_status_container.xml199
-rw-r--r--packages/SystemUI/res/drawable/touchpad_tutorial_apps_icon.xml25
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml13
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl9
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/OWNERS2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt80
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OWNERS6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt195
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/SwitchAppsGestureTutorialScreen.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizer.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureRecognizerProvider.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureScreenViewModel.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java39
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt)2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java112
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java32
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java133
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java1966
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java4
-rw-r--r--services/core/java/com/android/server/am/UserController.java3
-rw-r--r--services/core/java/com/android/server/am/UserSwitchingDialog.java46
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java50
-rw-r--r--services/core/java/com/android/server/audio/HardeningEnforcer.java11
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java1
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java5
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java17
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java72
-rw-r--r--services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java16
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java33
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java567
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java177
-rw-r--r--services/core/java/com/android/server/notification/ZenLog.java36
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java32
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java25
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java2
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java48
-rw-r--r--services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java13
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java15
-rw-r--r--services/core/java/com/android/server/wm/AppCompatController.java10
-rw-r--r--services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java4
-rw-r--r--services/core/java/com/android/server/wm/AppWarnings.java14
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicy.java23
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java23
-rw-r--r--services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java5
-rw-r--r--services/core/java/com/android/server/wm/PageSizeMismatchDialog.java12
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java8
-rw-r--r--services/core/java/com/android/server/wm/StrictModeFlash.java5
-rw-r--r--services/core/java/com/android/server/wm/Task.java48
-rw-r--r--services/core/java/com/android/server/wm/Watermark.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java76
-rw-r--r--services/credentials/java/com/android/server/credentials/RequestSession.java72
-rw-r--r--services/java/com/android/server/SystemServer.java4
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java22
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java53
-rw-r--r--services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java256
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java57
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java206
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java29
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java53
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java33
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/utils/VirtualDisplayTestRule.java92
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java7
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java62
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt72
-rw-r--r--tests/NetworkSecurityConfigTest/Android.bp3
-rw-r--r--tests/NetworkSecurityConfigTest/TEST_MAPPING7
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp2
312 files changed, 6775 insertions, 3106 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index f249884cb1a0..458d1dfeadb8 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -115,6 +115,7 @@ aconfig_declarations_group {
"framework-jobscheduler-job.flags-aconfig-java",
"framework_graphics_flags_java_lib",
"hwui_flags_java_lib",
+ "icu_exported_aconfig_flags_lib",
"interaction_jank_monitor_flags_lib",
"keystore2_flags_java-framework",
"libcore_exported_aconfig_flags_lib",
@@ -163,6 +164,14 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// ICU
+java_aconfig_library {
+ name: "icu_exported_aconfig_flags_lib",
+ aconfig_declarations: "icu_aconfig_flags",
+ mode: "exported",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Camera
java_aconfig_library {
name: "camera_platform_flags_core_java_lib",
@@ -1409,6 +1418,7 @@ java_aconfig_library {
// Content Capture
aconfig_declarations {
name: "android.view.contentcapture.flags-aconfig",
+ exportable: true,
package: "android.view.contentcapture.flags",
container: "system",
srcs: ["core/java/android/view/contentcapture/flags/*.aconfig"],
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index a949ff5a331b..787fdee6ee16 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -1023,7 +1023,6 @@ stubs_defaults {
api_levels_annotations_enabled: true,
api_levels_annotations_dirs: [
"sdk-dir",
- "api-versions-jars-dir",
],
}
diff --git a/cmds/am/am.sh b/cmds/am/am.sh
index 54c2d394be2c..76ec214cb446 100755
--- a/cmds/am/am.sh
+++ b/cmds/am/am.sh
@@ -1,5 +1,8 @@
#!/system/bin/sh
+# set to top-app process group
+settaskprofile $$ SCHED_SP_TOP_APP >/dev/null 2>&1 || true
+
if [ "$1" != "instrument" ] ; then
cmd activity "$@"
else
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md
index 6138388b30c7..5734c847be87 100644
--- a/cmds/uinput/README.md
+++ b/cmds/uinput/README.md
@@ -59,7 +59,7 @@ Register a new uinput device
| `name` | string | Device name |
| `vid` | 16-bit integer | Vendor ID |
| `pid` | 16-bit integer | Product ID |
-| `bus` | string | Bus that device should use |
+| `bus` | string | The bus to report |
| `port` | string | `phys` value to report |
| `configuration` | object array | uinput device configuration|
| `ff_effects_max` | integer | `ff_effects_max` value |
@@ -68,8 +68,11 @@ Register a new uinput device
`id` is used for matching the subsequent commands to a specific device to avoid ambiguity when
multiple devices are registered.
-`bus` is used to determine how the uinput device is connected to the host. The options are `"usb"`
-and `"bluetooth"`.
+`bus` specifies the bus that the kernel should report the device as being connected to. The most
+common values are `"usb"` and `"bluetooth"`, but any bus with a `BUS_…` constant in the [Linux
+kernel's input.h][input.h] can be specified using the part of its identifier after `BUS_`. For
+example, to specify the SPI bus type (`BUS_SPI` in the kernel header), use `"spi"` (or `"SPI"`,
+since it's case-insensitive).
Device configuration is used to configure the uinput device. The `type` field provides a `UI_SET_*`
control code as an integer value or a string label (e.g. `"UI_SET_EVBIT"`), and data is a vector of
@@ -137,6 +140,7 @@ Example:
}
```
+[input.h]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/third_party/kernel/upstream/include/uapi/linux/input.h?q=BUS_
[struct input_absinfo]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/kernel/uapi/linux/input.h?q=%22struct%20input_absinfo%22
##### Waiting for registration
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f44448a8c311..15ae79e34061 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3165,6 +3165,11 @@ package android.app.wallpaper {
method @NonNull public android.util.SparseArray<android.graphics.Rect> getCropHints();
}
+ public static final class WallpaperDescription.Builder {
+ method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setCropHints(@NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>);
+ method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setCropHints(@NonNull android.util.SparseArray<android.graphics.Rect>);
+ }
+
}
package android.app.wallpapereffectsgeneration {
@@ -3457,7 +3462,7 @@ package android.companion.virtual {
method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
method public void setDevicePolicy(int, int);
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void setDevicePolicy(int, int, int);
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public void setDisplayImePolicy(int, int);
+ method public void setDisplayImePolicy(int, int);
method public void setShowPointerIcon(boolean);
method public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public void wakeUp();
@@ -3476,7 +3481,7 @@ package android.companion.virtual {
method public int getDevicePolicy(int);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getDimDuration();
method @Nullable public android.content.ComponentName getHomeComponent();
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @Nullable public android.content.ComponentName getInputMethodComponent();
+ method @Nullable public android.content.ComponentName getInputMethodComponent();
method public int getLockState();
method @Nullable public String getName();
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getScreenOffTimeout();
@@ -3515,7 +3520,7 @@ package android.companion.virtual {
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDimDuration(@NonNull java.time.Duration);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setInputMethodComponent(@Nullable android.content.ComponentName);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setInputMethodComponent(@Nullable android.content.ComponentName);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setScreenOffTimeout(@NonNull java.time.Duration);
@@ -19038,7 +19043,7 @@ package android.view.displayhash {
package android.view.inputmethod {
public final class InputMethodInfo implements android.os.Parcelable {
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public boolean isVirtualDeviceOnly();
+ method public boolean isVirtualDeviceOnly();
}
public final class InputMethodManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index eb483c82b450..0126db70296c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -881,15 +881,6 @@ package android.app.usage {
}
-package android.app.wallpaper {
-
- public static final class WallpaperDescription.Builder {
- method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setCropHints(@NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>);
- method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setCropHints(@NonNull android.util.SparseArray<android.graphics.Rect>);
- }
-
-}
-
package android.appwidget {
public class AppWidgetManager {
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
index 4ac40a1f77b2..c597a9dcae7e 100644
--- a/core/java/android/app/DreamManager.java
+++ b/core/java/android/app/DreamManager.java
@@ -234,4 +234,19 @@ public class DreamManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Notifies dream manager of device postured state, which may affect dream enablement.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_DREAM_WHEN_POSTURED)
+ @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+ public void setDevicePostured(boolean isPostured) {
+ try {
+ mService.setDevicePostured(isPostured);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 1738a92b7672..4a78d01783f8 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -271,6 +271,6 @@ interface INotificationManager
int[] getAllowedAdjustmentKeyTypes();
void setAssistantAdjustmentKeyTypeState(int type, boolean enabled);
- int[] getAllowedAdjustmentKeyTypesForPackage(String pkg);
- void setAssistantAdjustmentKeyTypeStateForPackage(String pkg, int type, boolean enabled);
+ String[] getTypeAdjustmentDeniedPackages();
+ void setTypeAdjustmentForPackageState(String pkg, boolean enabled);
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 24f2495d8f09..e5d80de24f2b 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -2163,12 +2163,10 @@ public class NotificationManager {
* @hide
*/
@FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- public void setAssistantAdjustmentKeyTypeStateForPackage(@NonNull String pkg,
- @Adjustment.Types int type,
- boolean enabled) {
+ public void setTypeAdjustmentForPackageState(@NonNull String pkg, boolean enabled) {
INotificationManager service = service();
try {
- service.setAssistantAdjustmentKeyTypeStateForPackage(pkg, type, enabled);
+ service.setTypeAdjustmentForPackageState(pkg, enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/supervision/ISupervisionAppService.aidl b/core/java/android/app/supervision/ISupervisionAppService.aidl
index 033998fc4a5b..207fab90d015 100644
--- a/core/java/android/app/supervision/ISupervisionAppService.aidl
+++ b/core/java/android/app/supervision/ISupervisionAppService.aidl
@@ -20,4 +20,6 @@ package android.app.supervision;
* @hide
*/
interface ISupervisionAppService {
+ void onEnabled();
+ void onDisabled();
}
diff --git a/core/java/android/app/supervision/SupervisionAppService.java b/core/java/android/app/supervision/SupervisionAppService.java
index 4468c78cbd34..4530be5c270a 100644
--- a/core/java/android/app/supervision/SupervisionAppService.java
+++ b/core/java/android/app/supervision/SupervisionAppService.java
@@ -28,10 +28,29 @@ import android.os.IBinder;
*/
public class SupervisionAppService extends Service {
private final ISupervisionAppService mBinder = new ISupervisionAppService.Stub() {
+ @Override
+ public void onEnabled() {
+ SupervisionAppService.this.onEnabled();
+ }
+
+ @Override
+ public void onDisabled() {
+ SupervisionAppService.this.onDisabled();
+ }
};
@Override
public final IBinder onBind(Intent intent) {
return mBinder.asBinder();
}
+
+ /**
+ * Called when supervision is enabled.
+ */
+ public void onEnabled() {}
+
+ /**
+ * Called when supervision is disabled.
+ */
+ public void onDisabled() {}
}
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index a5b58f968c27..92241f3634e8 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -34,6 +34,35 @@ public class SupervisionManager {
private final Context mContext;
private final ISupervisionManager mService;
+ /**
+ * Activity action: ask the human user to enable supervision for this user. Only the app that
+ * holds the {@code SYSTEM_SUPERVISION} role can launch this intent.
+ *
+ * <p>The intent must be invoked via {@link Activity#startActivityForResult} to receive the
+ * result of whether or not the user approved the action. If approved, the result will be {@link
+ * Activity#RESULT_OK}.
+ *
+ * <p>If supervision is already enabled, the operation will return a failure result.
+ *
+ * @hide
+ */
+ public static final String ACTION_ENABLE_SUPERVISION = "android.app.action.ENABLE_SUPERVISION";
+
+ /**
+ * Activity action: ask the human user to disable supervision for this user. Only the app that
+ * holds the {@code SYSTEM_SUPERVISION} role can launch this intent.
+ *
+ * <p>The intent must be invoked via {@link Activity#startActivityForResult} to receive the
+ * result of whether or not the user approved the action. If approved, the result will be {@link
+ * Activity#RESULT_OK}.
+ *
+ * <p>If supervision is not enabled, the operation will return a failure result.
+ *
+ * @hide
+ */
+ public static final String ACTION_DISABLE_SUPERVISION =
+ "android.app.action.DISABLE_SUPERVISION";
+
/** @hide */
@UnsupportedAppUsage
public SupervisionManager(Context context, ISupervisionManager service) {
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index 999a5da870ad..a13af7f1ddcd 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -19,9 +19,7 @@ package android.app.wallpaper;
import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
import android.annotation.FlaggedApi;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
import android.app.WallpaperManager.ScreenOrientation;
@@ -514,8 +512,7 @@ public final class WallpaperDescription implements Parcelable {
* @hide
*/
@NonNull
- @TestApi
- @SuppressLint("MissingGetterMatchingBuilder")
+ @SystemApi
public Builder setCropHints(@NonNull Map<Point, Rect> cropHints) {
mCropHints = new SparseArray<>();
cropHints.forEach(
@@ -531,8 +528,7 @@ public final class WallpaperDescription implements Parcelable {
* @hide
*/
@NonNull
- @TestApi
- @SuppressLint("MissingGetterMatchingBuilder")
+ @SystemApi
public Builder setCropHints(@NonNull SparseArray<Rect> cropHints) {
mCropHints = cropHints;
return this;
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 99794d7f49fe..252db824c69f 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -1116,11 +1116,8 @@ public final class VirtualDeviceManager {
* @throws SecurityException if the display is not owned by this device or is not
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED trusted}
*/
- @FlaggedApi(Flags.FLAG_VDM_CUSTOM_IME)
public void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
- if (Flags.vdmCustomIme()) {
- mVirtualDeviceInternal.setDisplayImePolicy(displayId, policy);
- }
+ mVirtualDeviceInternal.setDisplayImePolicy(displayId, policy);
}
/**
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 761e75bd9076..95dee9b72a88 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -440,7 +440,6 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @see Builder#setInputMethodComponent
*/
- @FlaggedApi(Flags.FLAG_VDM_CUSTOM_IME)
@Nullable
public ComponentName getInputMethodComponent() {
return mInputMethodComponent;
@@ -945,7 +944,6 @@ public final class VirtualDeviceParams implements Parcelable {
* @attr ref android.R.styleable#InputMethod_isVirtualDeviceOnly
* @attr ref android.R.styleable#InputMethod_showInInputMethodPicker
*/
- @FlaggedApi(Flags.FLAG_VDM_CUSTOM_IME)
@NonNull
public Builder setInputMethodComponent(@Nullable ComponentName inputMethodComponent) {
mInputMethodComponent = inputMethodComponent;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index bbfae8117b16..7cd2d31ac974 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -62,6 +62,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Provides access to an application's raw asset files; see {@link Resources}
@@ -133,7 +134,7 @@ public final class AssetManager implements AutoCloseable {
// Debug/reference counting implementation.
@GuardedBy("this") private boolean mOpen = true;
- @GuardedBy("this") private int mNumRefs = 1;
+ private AtomicInteger mNumRefs = new AtomicInteger(1);
@GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
private ResourcesLoader[] mLoaders;
@@ -244,7 +245,7 @@ public final class AssetManager implements AutoCloseable {
mObject = nativeCreate();
if (DEBUG_REFS) {
- mNumRefs = 0;
+ mNumRefs.set(0);
incRefsLocked(hashCode());
}
@@ -260,7 +261,7 @@ public final class AssetManager implements AutoCloseable {
private AssetManager(boolean sentinel) {
mObject = nativeCreate();
if (DEBUG_REFS) {
- mNumRefs = 0;
+ mNumRefs.set(0);
incRefsLocked(hashCode());
}
}
@@ -324,7 +325,7 @@ public final class AssetManager implements AutoCloseable {
}
mOpen = false;
- decRefsLocked(hashCode());
+ decRefs(hashCode());
}
}
@@ -1235,9 +1236,7 @@ public final class AssetManager implements AutoCloseable {
}
void xmlBlockGone(int id) {
- synchronized (this) {
- decRefsLocked(id);
- }
+ decRefs(id);
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1308,9 +1307,7 @@ public final class AssetManager implements AutoCloseable {
}
void releaseTheme(long themePtr) {
- synchronized (this) {
- decRefsLocked(themePtr);
- }
+ decRefs(themePtr);
}
static long getThemeFreeFunction() {
@@ -1332,7 +1329,7 @@ public final class AssetManager implements AutoCloseable {
if (this != newAssetManager) {
synchronized (this) {
ensureValidLocked();
- decRefsLocked(themePtr);
+ decRefs(themePtr);
}
synchronized (newAssetManager) {
newAssetManager.ensureValidLocked();
@@ -1364,8 +1361,8 @@ public final class AssetManager implements AutoCloseable {
@Override
protected void finalize() throws Throwable {
- if (DEBUG_REFS && mNumRefs != 0) {
- Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
+ if (DEBUG_REFS && mNumRefs.get() != 0) {
+ Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs.get());
if (mRefStacks != null) {
for (RuntimeException e : mRefStacks.values()) {
Log.w(TAG, "Reference from here", e);
@@ -1473,9 +1470,7 @@ public final class AssetManager implements AutoCloseable {
nativeAssetDestroy(mAssetNativePtr);
mAssetNativePtr = 0;
- synchronized (AssetManager.this) {
- decRefsLocked(hashCode());
- }
+ decRefs(hashCode());
}
}
@@ -1680,19 +1675,25 @@ public final class AssetManager implements AutoCloseable {
RuntimeException ex = new RuntimeException();
mRefStacks.put(id, ex);
}
- mNumRefs++;
+ mNumRefs.incrementAndGet();
}
- @GuardedBy("this")
- private void decRefsLocked(long id) {
- if (DEBUG_REFS && mRefStacks != null) {
- mRefStacks.remove(id);
- }
- mNumRefs--;
- if (mNumRefs == 0 && mObject != 0) {
- nativeDestroy(mObject);
- mObject = 0;
- mApkAssets = sEmptyApkAssets;
+ private void decRefs(long id) {
+ if (DEBUG_REFS) {
+ synchronized (this) {
+ if (mRefStacks != null) {
+ mRefStacks.remove(id);
+ }
+ }
+ }
+ if (mNumRefs.decrementAndGet() == 0) {
+ synchronized (this) {
+ if (mNumRefs.get() == 0 && mObject != 0) {
+ nativeDestroy(mObject);
+ mObject = 0;
+ mApkAssets = sEmptyApkAssets;
+ }
+ }
}
}
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 2161e10337c9..430ed2b68342 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -144,3 +144,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "credential_manager"
+ name: "fix_metric_duplication_emits"
+ description: "Fixes duplicate emits in the original metric emit system."
+ bug: "362994633"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} \ No newline at end of file
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 88d69b665c87..030c883924a7 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -182,17 +182,10 @@ public abstract class SQLiteOpenHelper implements AutoCloseable {
setOpenParamsBuilder(openParamsBuilder);
Object lock = null;
- if (mName == null || !Flags.concurrentOpenHelper()) {
+ if (!Flags.concurrentOpenHelper() || mName == null) {
lock = new Object();
} else {
- try {
- final String path = mContext.getDatabasePath(mName).getCanonicalPath();
- lock = sDbLock.computeIfAbsent(path, (String k) -> new Object());
- } catch (IOException e) {
- Log.d(TAG, "failed to construct db path for " + mName);
- // Ensure the lock is not null.
- lock = new Object();
- }
+ lock = sDbLock.computeIfAbsent(mName, (String k) -> new Object());
}
mLock = lock;
}
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 4025242fd208..1a712d2b3f31 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -122,17 +122,11 @@ public final class KeyGestureEvent {
public static final int KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW = 69;
public static final int KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW = 70;
public static final int KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW = 71;
- public static final int KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN = 72;
- public static final int KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT = 73;
- public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 74;
- public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75;
- public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 76;
- public static final int KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB = 77;
- public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT = 78;
- public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT = 79;
- public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP = 80;
- public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN = 81;
- public static final int KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS = 82;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 72;
+ public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 73;
+ public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 74;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB = 75;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS = 76;
public static final int FLAG_CANCELLED = 1;
@@ -220,16 +214,10 @@ public final class KeyGestureEvent {
KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW,
KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
- KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN,
- KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT,
KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK,
KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
- KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT,
- KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT,
- KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP,
- KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN,
KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS,
})
@Retention(RetentionPolicy.SOURCE)
@@ -815,10 +803,6 @@ public final class KeyGestureEvent {
return "KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW";
case KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW:
return "KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW";
- case KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN:
- return "KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN";
- case KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT:
- return "KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT";
case KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION:
return "KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION";
case KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK:
@@ -827,14 +811,6 @@ public final class KeyGestureEvent {
return "KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW";
case KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
return "KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB";
- case KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT:
- return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT";
- case KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT:
- return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT";
- case KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP:
- return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP";
- case KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN:
- return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN";
case KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS:
return "KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS";
default:
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 9435f4d59a2c..77b6d70339ab 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -351,4 +351,10 @@ public abstract class PowerManagerInternal {
* return false if ambient display is not available.
*/
public abstract boolean isAmbientDisplaySuppressed();
+
+ /**
+ * Notifies PowerManager that the device has entered a postured state (stationary + upright).
+ * This may affect dream eligibility.
+ */
+ public abstract void setDevicePostured(boolean isPostured);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0cfec2cc7314..73d1e1701eec 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12869,6 +12869,19 @@ public final class Settings {
*/
public static final String DISABLE_SECURE_WINDOWS = "disable_secure_windows";
+ /**
+ * Controls if the adaptive authentication feature should be disabled, which
+ * will attempt to lock the device after a number of consecutive authentication
+ * attempts fail.
+ *
+ * This can only be disabled on debuggable builds. Set to 1 to disable or 0 for the
+ * normal behavior.
+ *
+ * @hide
+ */
+ public static final String DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK =
+ "disable_adaptive_auth_limit_lock";
+
/** @hide */
public static final int PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK = 0;
/** @hide */
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 4a9e945e62a9..a5586227cbb3 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -155,4 +155,11 @@ flag {
description: "Feature flag to add the privileged flag to the SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE permission"
bug: "380120712"
is_fixed_read_only: true
-} \ No newline at end of file
+}
+
+flag {
+ name: "disable_adaptive_auth_counter_lock"
+ namespace: "biometrics"
+ description: "Flag to allow an adb secure setting to disable the adaptive auth lock"
+ bug: "371057865"
+}
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 1c0a2c021bca..3ca9d937b0ca 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -53,6 +53,8 @@ interface IDreamManager {
void startDreamActivity(in Intent intent);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
oneway void setDreamIsObscured(in boolean isObscured);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
+ oneway void setDevicePostured(in boolean isPostured);
oneway void startDozingOneway(in IBinder token, int screenState, int reason,
float screenBrightnessFloat, int screenBrightnessInt,
boolean useNormalBrightnessForDoze);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index eeb56d4d6c05..464756842caf 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -2424,9 +2424,10 @@ public abstract class WallpaperService extends Service {
Surface ret = null;
if (mBlastBufferQueue == null) {
- mBlastBufferQueue = new BLASTBufferQueue("Wallpaper", mBbqSurfaceControl,
- width, height, format);
+ mBlastBufferQueue = new BLASTBufferQueue("Wallpaper",
+ true /* updateDestinationFrame */);
mBlastBufferQueue.setApplyToken(mBbqApplyToken);
+ mBlastBufferQueue.update(mBbqSurfaceControl, width, height, format);
// We only return the Surface the first time, as otherwise
// it hasn't changed and there is no need to update.
ret = mBlastBufferQueue.createSurface();
diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java
index c69ddf86d3f8..fb4a19b9dcac 100644
--- a/core/java/android/timezone/TelephonyNetworkFinder.java
+++ b/core/java/android/timezone/TelephonyNetworkFinder.java
@@ -19,6 +19,9 @@ package android.timezone;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import com.android.i18n.timezone.MobileCountries;
+import com.android.icu.Flags;
+
import java.util.Objects;
/**
@@ -50,4 +53,17 @@ public final class TelephonyNetworkFinder {
return telephonyNetworkDelegate != null
? new TelephonyNetwork(telephonyNetworkDelegate) : null;
}
+
+ /**
+ * Returns the countries where a given MCC is in use.
+ */
+ @Nullable
+ public MobileCountries findCountriesByMcc(@NonNull String mcc) {
+ Objects.requireNonNull(mcc);
+
+ if (!Flags.telephonyLookupMccExtension()) {
+ return null;
+ }
+ return mDelegate.findCountriesByMcc(mcc);
+ }
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 028043340732..06eb0428bfcf 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -227,9 +227,6 @@ interface IWindowManager
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void endProlongedAnimations();
- void startFreezingScreen(int exitAnim, int enterAnim);
- void stopFreezingScreen();
-
// these require DISABLE_KEYGUARD permission
/** @deprecated use Activity.setShowWhenLocked instead. */
void disableKeyguard(IBinder token, String tag, int userId);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d7cf3e827695..311fbee2fc4b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3026,6 +3026,7 @@ public final class SurfaceControl implements Parcelable {
// Only non-null if the SurfaceControlRegistry is enabled. This list tracks the set of calls
// made through this transaction object, and is dumped (and cleared) when the transaction is
// later applied.
+ @Nullable
ArrayList<String> mCalls;
Runnable mFreeNativeResources;
@@ -4898,8 +4899,10 @@ public final class SurfaceControl implements Parcelable {
SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
"merge", this, null, "otherTx=" + other.getId());
if (mCalls != null) {
- mCalls.addAll(other.mCalls);
- other.mCalls.clear();
+ if (other.mCalls != null) {
+ mCalls.addAll(other.mCalls);
+ other.mCalls.clear();
+ }
}
}
mResizedSurfaces.putAll(other.mResizedSurfaces);
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 121c01be7294..0b528bffe5c5 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -334,13 +334,17 @@ public class SurfaceControlRegistry {
if (call == APPLY) {
// Log the apply and dump the calls on that transaction
Log.e(TAG, msg, new Throwable());
- for (int i = 0; i < tx.mCalls.size(); i++) {
- Log.d(TAG, " " + tx.mCalls.get(i));
+ if (tx.mCalls != null) {
+ for (int i = 0; i < tx.mCalls.size(); i++) {
+ Log.d(TAG, " " + tx.mCalls.get(i));
+ }
}
} else if (matchesForCallStackDebugging(sc != null ? sc.getName() : null, call)) {
// Otherwise log this call to the transaction if it matches the tracked calls
Log.e(TAG, msg, new Throwable());
- tx.mCalls.add(msg);
+ if (tx.mCalls != null) {
+ tx.mCalls.add(msg);
+ }
}
} else {
// Log this call if it matches the tracked calls
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a657acd261d4..64e7becb1ed4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2759,14 +2759,15 @@ public final class ViewRootImpl implements ViewParent,
if (mBlastBufferQueue != null) {
mBlastBufferQueue.destroy();
}
- mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
- mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
- mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
- mBlastBufferQueue.setWaitForBufferReleaseCallback(mChoreographer::onWaitForBufferRelease);
+ mBlastBufferQueue = new BLASTBufferQueue(mTag, true /* updateDestinationFrame */);
// If we create and destroy BBQ without recreating the SurfaceControl, we can end up
// queuing buffers on multiple apply tokens causing out of order buffer submissions. We
// fix this by setting the same apply token on all BBQs created by this VRI.
mBlastBufferQueue.setApplyToken(mBbqApplyToken);
+ mBlastBufferQueue.update(mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y,
+ mWindowAttributes.format);
+ mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
+ mBlastBufferQueue.setWaitForBufferReleaseCallback(mChoreographer::onWaitForBufferRelease);
Surface blastSurface;
if (addSchandleToVriSurface()) {
blastSurface = mBlastBufferQueue.createSurfaceWithHandle();
@@ -5551,6 +5552,9 @@ public final class ViewRootImpl implements ViewParent,
if (mAttachInfo.mContentCaptureManager != null) {
ContentCaptureSession session =
mAttachInfo.mContentCaptureManager.getMainContentCaptureSession();
+ if (android.view.contentcapture.flags.Flags.postCreateAndroidBgThread()) {
+ session.performStart();
+ }
session.notifyWindowBoundsChanged(session.getId(),
getConfiguration().windowConfiguration.getBounds());
}
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 8baa55f8e377..6e2e1009fd40 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -74,8 +74,8 @@ final class ChildContentCaptureSession extends ContentCaptureSession {
}
@Override
- void flush(@FlushReason int reason) {
- mParent.flush(reason);
+ void internalFlush(@FlushReason int reason) {
+ mParent.internalFlush(reason);
}
@Override
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 724e8fa830af..b7a77d701045 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -52,7 +52,6 @@ import android.view.contentcapture.ContentCaptureSession.FlushReason;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.RingBuffer;
import com.android.internal.util.SyncResultReceiver;
@@ -605,7 +604,6 @@ public final class ContentCaptureManager {
mContext,
this,
prepareUiHandler(),
- prepareContentCaptureHandler(),
mService
);
if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
@@ -616,15 +614,6 @@ public final class ContentCaptureManager {
@NonNull
@GuardedBy("mLock")
- private Handler prepareContentCaptureHandler() {
- if (mContentCaptureHandler == null) {
- mContentCaptureHandler = BackgroundThread.getHandler();
- }
- return mContentCaptureHandler;
- }
-
- @NonNull
- @GuardedBy("mLock")
private Handler prepareUiHandler() {
if (mUiHandler == null) {
mUiHandler = Handler.createAsync(Looper.getMainLooper());
@@ -674,7 +663,7 @@ public final class ContentCaptureManager {
@UiThread
public void flush(@FlushReason int reason) {
if (mOptions.lite) return;
- getMainContentCaptureSession().flush(reason);
+ getMainContentCaptureSession().internalFlush(reason);
}
/**
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 9aeec20ec9b7..791a6f4254ec 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -286,6 +286,9 @@ public abstract class ContentCaptureSession implements AutoCloseable {
abstract void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
@NonNull ComponentName component, int flags);
+ /** @hide */
+ public void performStart() {}
+
abstract boolean isDisabled();
/**
@@ -339,7 +342,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
/**
* Flushes the buffered events to the service.
*/
- abstract void flush(@FlushReason int reason);
+ abstract void internalFlush(@FlushReason int reason);
/**
* Sets the {@link ContentCaptureContext} associated with the session.
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 2fb78c038ca2..29cae857098d 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -57,10 +57,12 @@ import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import android.view.contentcapture.flags.Flags;
import android.view.contentprotection.ContentProtectionEventProcessor;
import android.view.inputmethod.BaseInputConnection;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.IResultReceiver;
import com.android.modules.expresslog.Counter;
@@ -107,8 +109,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
@NonNull
private final Handler mUiHandler;
- @NonNull
- private final Handler mContentCaptureHandler;
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @Nullable
+ public Handler mContentCaptureHandler;
/**
* Interface to the system_server binder object - it's only used to start the session (and
@@ -187,6 +191,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
@Nullable
public ContentProtectionEventProcessor mContentProtectionEventProcessor;
+ /**
+ * A runnable object to perform the start of this session.
+ */
+ @Nullable
+ private Runnable mStartRunnable = null;
+
private static class SessionStateReceiver extends IResultReceiver.Stub {
private final WeakReference<MainContentCaptureSession> mMainSession;
@@ -198,7 +208,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
public void send(int resultCode, Bundle resultData) {
final MainContentCaptureSession mainSession = mMainSession.get();
if (mainSession == null) {
- Log.w(TAG, "received result after mina session released");
+ Log.w(TAG, "received result after main session released");
return;
}
final IBinder binder;
@@ -213,6 +223,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
binder = resultData.getBinder(EXTRA_BINDER);
if (binder == null) {
Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
+ // explicitly init the bg thread
+ mainSession.mContentCaptureHandler = mainSession.prepareContentCaptureHandler();
mainSession.runOnContentCaptureThread(() -> mainSession.resetSession(
STATE_DISABLED | STATE_INTERNAL_ERROR));
return;
@@ -220,23 +232,45 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
} else {
binder = null;
}
+ // explicitly init the bg thread
+ mainSession.mContentCaptureHandler = mainSession.prepareContentCaptureHandler();
mainSession.runOnContentCaptureThread(() ->
mainSession.onSessionStarted(resultCode, binder));
}
}
+ /**
+ * Prepares the content capture handler(i.e. the background thread).
+ *
+ * This is expected to be called from the {@link SessionStateReceiver#send} callback, after the
+ * session {@link performStart}. This is expected to be executed in a binder thread, instead
+ * of the UI thread.
+ */
+ @NonNull
+ private Handler prepareContentCaptureHandler() {
+ if (mContentCaptureHandler == null) {
+ try {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareContentCaptureHandler");
+ }
+ mContentCaptureHandler = BackgroundThread.getHandler();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+ return mContentCaptureHandler;
+ }
+
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
public MainContentCaptureSession(
@NonNull ContentCaptureManager.StrippedContext context,
@NonNull ContentCaptureManager manager,
@NonNull Handler uiHandler,
- @NonNull Handler contentCaptureHandler,
@NonNull IContentCaptureManager systemServerInterface) {
mContext = context;
mManager = manager;
mUiHandler = uiHandler;
- mContentCaptureHandler = contentCaptureHandler;
mSystemServerInterface = systemServerInterface;
final int logHistorySize = mManager.mOptions.logHistorySize;
@@ -260,18 +294,49 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
/**
- * Starts this session.
+ * Performs the start of the session.
+ *
+ * This is expected to be called from the UI thread, when the activity finishes its first frame.
+ * This is a no-op if the session has already been started.
+ *
+ * See {@link #start(IBinder, IBinder, ComponentName, int)} for more details.
+ *
+ * @hide */
+ @Override
+ public void performStart() {
+ if (!hasStarted() && mStartRunnable != null) {
+ mStartRunnable.run();
+ }
+ }
+
+ /**
+ * Creates a runnable to start this session.
+ *
+ * For performance reasons, it is better to only create a task to start the session
+ * during the creation of the activity and perform the actual start when the activity
+ * finishes it's first frame.
*/
@Override
void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
@NonNull ComponentName component, int flags) {
- runOnContentCaptureThread(
- () -> startImpl(token, shareableActivityToken, component, flags));
+ if (Flags.postCreateAndroidBgThread()) {
+ mStartRunnable = () -> {
+ try {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "cc session startImpl");
+ }
+ startImpl(token, shareableActivityToken, component, flags);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ };
+ } else {
+ startImpl(token, shareableActivityToken, component, flags);
+ }
}
private void startImpl(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
@NonNull ComponentName component, int flags) {
- checkOnContentCaptureThread();
if (!isContentCaptureEnabled()) return;
if (sVerbose) {
@@ -305,11 +370,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
}
}
+
@Override
void onDestroy() {
clearAndRunOnContentCaptureThread(() -> {
try {
- flush(FLUSH_REASON_SESSION_FINISHED);
+ internalFlush(FLUSH_REASON_SESSION_FINISHED);
} finally {
destroySession();
}
@@ -557,11 +623,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
flushReason = forceFlush ? FLUSH_REASON_FORCE_FLUSH : FLUSH_REASON_FULL;
}
- flush(flushReason);
+ internalFlush(flushReason);
}
private boolean hasStarted() {
- checkOnContentCaptureThread();
return mState != UNKNOWN_STATE;
}
@@ -575,6 +640,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
if (sVerbose) Log.v(TAG, "handleScheduleFlush(): session not started yet");
return;
}
+ if (mContentCaptureHandler == null) {
+ Log.w(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): content capture "
+ + "thread not ready");
+ return;
+ }
if (mDisabled.get()) {
// Should not be called on this state, as handleSendEvent checks.
@@ -617,15 +687,18 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
if (sVerbose) Log.v(TAG, "Nothing to flush");
return;
}
- flush(reason);
+ internalFlush(reason);
}
- /** @hide */
+ /**
+ * Internal API to flush the buffered events to the service.
+ *
+ * Do not confuse this with the public API {@link #flush()}.
+ *
+ * @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@Override
- public void flush(@FlushReason int reason) {
- // TODO: b/380381249 renaming the internal APIs to prevent confusions between this and the
- // public API.
+ public void internalFlush(@FlushReason int reason) {
runOnContentCaptureThread(() -> flushImpl(reason));
}
@@ -647,6 +720,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
if (!isContentCaptureReceiverEnabled()) {
return;
}
+ if (mContentCaptureHandler == null) {
+ Log.w(TAG, "handleForceFlush(" + getDebugState(reason) + "): content capture thread"
+ + "not ready");
+ return;
+ }
if (mDirectServiceInterface == null) {
if (sVerbose) {
@@ -763,7 +841,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
mDirectServiceInterface = null;
mContentProtectionEventProcessor = null;
- mContentCaptureHandler.removeMessages(MSG_FLUSH);
+ if (mContentCaptureHandler != null) {
+ mContentCaptureHandler.removeMessages(MSG_FLUSH);
+ }
}
@Override
@@ -917,6 +997,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
* clear the buffer events then starting sending out current event.
*/
private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) {
+ if (mContentCaptureHandler == null) {
+ mEventProcessQueue.offer(event);
+ return;
+ }
if (forceFlush || mEventProcessQueue.size() >= mManager.mOptions.maxBufferSize - 1) {
// The buffer events are cleared in the same thread first to prevent new events
// being added during the time of context switch. This would disrupt the sequence
@@ -1119,6 +1203,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
* always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
*/
private void checkOnContentCaptureThread() {
+ if (mContentCaptureHandler == null) {
+ Log.e(TAG, "content capture thread is not initiallized!");
+ return;
+ }
final boolean onContentCaptureThread = mContentCaptureHandler.getLooper().isCurrentThread();
if (!onContentCaptureThread) {
mWrongThreadCount.incrementAndGet();
@@ -1139,6 +1227,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
* </p>
*/
private void runOnContentCaptureThread(@NonNull Runnable r) {
+ if (mContentCaptureHandler == null) {
+ Log.e(TAG, "content capture thread is not initiallized!");
+ // fall back to UI thread
+ runOnUiThread(r);
+ return;
+ }
if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
mContentCaptureHandler.post(r);
} else {
@@ -1147,6 +1241,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
private void clearAndRunOnContentCaptureThread(@NonNull Runnable r, int what) {
+ if (mContentCaptureHandler == null) {
+ Log.e(TAG, "content capture thread is not initiallized!");
+ // fall back to UI thread
+ runOnUiThread(r);
+ return;
+ }
if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
mContentCaptureHandler.removeMessages(what);
mContentCaptureHandler.post(r);
diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
index f709ed7f57cd..9df835098268 100644
--- a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
+++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
@@ -13,4 +13,16 @@ flag {
namespace: "machine_learning"
description: "Feature flag for baklava content capture API"
bug: "380381249"
+ is_exported: true
+}
+
+flag {
+ name: "post_create_android_bg_thread"
+ namespace: "pixel_state_server"
+ description: "Feature flag to post create the bg thread when an app is in the allowlist"
+ bug: "376468525"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 0a83bdc35b1f..8fef2d726b2c 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -766,7 +766,6 @@ public final class InputMethodInfo implements Parcelable {
* Returns true if IME supports only virtual devices.
* @hide
*/
- @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_IME)
@SystemApi
public boolean isVirtualDeviceOnly() {
return mIsVirtualDeviceOnly;
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 33890b80869d..f70bf9737636 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1021,8 +1021,9 @@ public final class Magnifier {
.setCallsite("InternalPopupWindow")
.build();
- mBBQ = new BLASTBufferQueue("magnifier surface", mBbqSurfaceControl,
- surfaceWidth, surfaceHeight, PixelFormat.TRANSLUCENT);
+ mBBQ = new BLASTBufferQueue("magnifier surface", /*updateDestinationFrame*/ true);
+ mBBQ.update(mBbqSurfaceControl,
+ surfaceWidth, surfaceHeight, PixelFormat.TRANSLUCENT);
mSurface = mBBQ.createSurface();
// Setup the RenderNode tree. The root has two children, one containing the bitmap
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 2c21417fb790..ddbf9e49bb8d 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -211,7 +211,7 @@ public final class TransitionInfo implements Parcelable {
FLAG_CONFIG_AT_END,
FLAG_IS_TASK_DISPLAY_AREA,
FLAG_FIRST_CUSTOM
- }, flag = true)
+ })
public @interface ChangeFlags {}
private final @TransitionType int mType;
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 644d69919998..9d7bedc4d0c3 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -689,14 +689,15 @@ public class IntentForwarderActivity extends Activity {
}
private void setMiniresolverPadding() {
- Insets systemWindowInsets =
- getWindowManager().getCurrentWindowMetrics().getWindowInsets().getInsets(
- WindowInsets.Type.systemBars());
-
View buttonContainer = findViewById(R.id.button_bar_container);
- buttonContainer.setPadding(0, 0, 0,
- systemWindowInsets.bottom + getResources().getDimensionPixelOffset(
- R.dimen.resolver_button_bar_spacing));
+ if (buttonContainer != null) {
+ Insets systemWindowInsets =
+ getWindowManager().getCurrentWindowMetrics().getWindowInsets().getInsets(
+ WindowInsets.Type.systemBars());
+ buttonContainer.setPadding(0, 0, 0,
+ systemWindowInsets.bottom + getResources().getDimensionPixelOffset(
+ R.dimen.resolver_button_bar_spacing));
+ }
}
@VisibleForTesting
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index a1945352ae09..db65d31f59da 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -929,8 +929,11 @@ public class ResolverActivity extends Activity implements
if (shouldUseMiniResolver()) {
View buttonContainer = findViewById(R.id.button_bar_container);
- buttonContainer.setPadding(0, 0, 0, mSystemWindowInsets.bottom
- + getResources().getDimensionPixelOffset(R.dimen.resolver_button_bar_spacing));
+ if (buttonContainer != null) {
+ buttonContainer.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+ + getResources().getDimensionPixelOffset(
+ R.dimen.resolver_button_bar_spacing));
+ }
}
// Need extra padding so the list can fully scroll up
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 6ad7fef95357..e170d6652863 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -172,8 +172,7 @@ public class NativeLibraryHelper {
private static native long nativeOpenApkFd(FileDescriptor fd, String debugPath);
private static native void nativeClose(long handle);
- private static native long nativeSumNativeBinaries(long handle, String cpuAbi,
- boolean debuggable);
+ private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
String abiToCopy, boolean extractNativeLibs, boolean debuggable);
@@ -188,7 +187,7 @@ public class NativeLibraryHelper {
private static long sumNativeBinaries(Handle handle, String abi) {
long sum = 0;
for (long apkHandle : handle.apkHandles) {
- sum += nativeSumNativeBinaries(apkHandle, abi, handle.debuggable);
+ sum += nativeSumNativeBinaries(apkHandle, abi);
}
return sum;
}
@@ -222,7 +221,7 @@ public class NativeLibraryHelper {
public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
int finalRes = NO_NATIVE_LIBRARIES;
for (long apkHandle : handle.apkHandles) {
- final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable);
+ final int res = nativeFindSupportedAbi(apkHandle, supportedAbis);
if (res == NO_NATIVE_LIBRARIES) {
// No native code, keep looking through all APKs.
} else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
@@ -244,8 +243,7 @@ public class NativeLibraryHelper {
return finalRes;
}
- private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis,
- boolean debuggable);
+ private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis);
// Convenience method to call removeNativeBinariesFromDirLI(File)
public static void removeNativeBinariesLI(String nativeLibraryPath) {
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 1b770207f2cb..0d3c470b0e8a 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -817,12 +817,13 @@ public final class NotificationProgressBar extends ProgressBar implements
if (part instanceof Segment segment) {
final float segWidth = segment.mFraction * totalWidth;
// Advance the start position to account for a point immediately prior.
- final float startOffset = getSegStartOffset(prevPart, pointRadius, segPointGap, x);
+ final float startOffset = getSegStartOffset(prevPart, pointRadius, segPointGap,
+ iPart == 1);
final float start = x + startOffset;
// Retract the end position to account for the padding and a point immediately
// after.
final float endOffset = getSegEndOffset(segment, nextPart, pointRadius, segPointGap,
- segSegGap, x + segWidth, totalWidth, hasTrackerIcon);
+ segSegGap, iPart == nParts - 2, totalWidth, hasTrackerIcon);
final float end = x + segWidth - endOffset;
drawableParts.add(new DrawableSegment(start, end, segment.mColor, segment.mFaded));
@@ -840,10 +841,10 @@ public final class NotificationProgressBar extends ProgressBar implements
// Only shift the points right at the start/end.
// For the points close to the start/end, the segment minimum width requirement
// would take care of shifting them to be within the bounds.
- if (x == 0) {
+ if (iPart == 0) {
start = 0;
end = pointWidth;
- } else if (x == totalWidth) {
+ } else if (iPart == nParts - 1) {
start = totalWidth - pointWidth;
end = totalWidth;
}
@@ -856,14 +857,14 @@ public final class NotificationProgressBar extends ProgressBar implements
}
private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap,
- float startX) {
+ boolean isSecondPart) {
if (!(prevPart instanceof Point)) return 0F;
- final float pointOffset = (startX == 0) ? pointRadius : 0;
+ final float pointOffset = isSecondPart ? pointRadius : 0;
return pointOffset + pointRadius + segPointGap;
}
private static float getSegEndOffset(Segment seg, Part nextPart, float pointRadius,
- float segPointGap, float segSegGap, float endX, float totalWidth,
+ float segPointGap, float segSegGap, boolean isSecondToLastPart, float totalWidth,
boolean hasTrackerIcon) {
if (nextPart == null) return 0F;
if (nextPart instanceof Segment nextSeg) {
@@ -874,7 +875,7 @@ public final class NotificationProgressBar extends ProgressBar implements
return segSegGap;
}
- final float pointOffset = (endX == totalWidth) ? pointRadius : 0;
+ final float pointOffset = isSecondToLastPart ? pointRadius : 0;
return segPointGap + pointRadius + pointOffset;
}
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 3108f1f2c7c5..e78c5247d8a7 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -349,21 +349,19 @@ static install_status_t copyFileIfChanged(JNIEnv* env, void* arg, ZipFileRO* zip
* satisfied :
*
* - The entry is under the lib/ directory.
- * - The entry name ends with ".so" and the entry name starts with "lib",
- * an exception is made for debuggable apps.
* - The entry filename is "safe" (as determined by isFilenameSafe).
*
*/
class NativeLibrariesIterator {
private:
- NativeLibrariesIterator(ZipFileRO* zipFile, bool debuggable, void* cookie)
- : mZipFile(zipFile), mDebuggable(debuggable), mCookie(cookie), mLastSlash(nullptr) {
+ NativeLibrariesIterator(ZipFileRO* zipFile, void* cookie)
+ : mZipFile(zipFile), mCookie(cookie), mLastSlash(nullptr) {
fileName[0] = '\0';
}
public:
static base::expected<std::unique_ptr<NativeLibrariesIterator>, int32_t> create(
- ZipFileRO* zipFile, bool debuggable) {
+ ZipFileRO* zipFile) {
// Do not specify a suffix to find both .so files and gdbserver.
auto result = zipFile->startIterationOrError(APK_LIB.data(), nullptr /* suffix */);
if (!result.ok()) {
@@ -371,7 +369,7 @@ public:
}
return std::unique_ptr<NativeLibrariesIterator>(
- new NativeLibrariesIterator(zipFile, debuggable, result.value()));
+ new NativeLibrariesIterator(zipFile, result.value()));
}
base::expected<ZipEntryRO, int32_t> next() {
@@ -390,7 +388,7 @@ public:
continue;
}
- const char* lastSlash = util::ValidLibraryPathLastSlash(fileName, false, mDebuggable);
+ const char* lastSlash = util::ValidLibraryPathLastSlash(fileName, false);
if (lastSlash) {
mLastSlash = lastSlash;
break;
@@ -415,20 +413,19 @@ private:
char fileName[PATH_MAX];
ZipFileRO* const mZipFile;
- const bool mDebuggable;
void* mCookie;
const char* mLastSlash;
};
static install_status_t
iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi,
- jboolean debuggable, iterFunc callFunc, void* callArg) {
+ iterFunc callFunc, void* callArg) {
ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
if (zipFile == nullptr) {
return INSTALL_FAILED_INVALID_APK;
}
- auto result = NativeLibrariesIterator::create(zipFile, debuggable);
+ auto result = NativeLibrariesIterator::create(zipFile);
if (!result.ok()) {
return INSTALL_FAILED_INVALID_APK;
}
@@ -470,14 +467,13 @@ iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi,
return INSTALL_SUCCEEDED;
}
-static int findSupportedAbi(JNIEnv* env, jlong apkHandle, jobjectArray supportedAbisArray,
- jboolean debuggable) {
+static int findSupportedAbi(JNIEnv* env, jlong apkHandle, jobjectArray supportedAbisArray) {
ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
if (zipFile == nullptr) {
return INSTALL_FAILED_INVALID_APK;
}
- auto result = NativeLibrariesIterator::create(zipFile, debuggable);
+ auto result = NativeLibrariesIterator::create(zipFile);
if (!result.ok()) {
return INSTALL_FAILED_INVALID_APK;
}
@@ -548,26 +544,26 @@ com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env,
{
jboolean app_compat_16kb = app_compat_16kb_enabled();
void* args[] = { &javaNativeLibPath, &extractNativeLibs, &debuggable, &app_compat_16kb };
- return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable,
+ return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi,
copyFileIfChanged, reinterpret_cast<void*>(args));
}
static jlong
com_android_internal_content_NativeLibraryHelper_sumNativeBinaries(JNIEnv *env, jclass clazz,
- jlong apkHandle, jstring javaCpuAbi, jboolean debuggable)
+ jlong apkHandle, jstring javaCpuAbi)
{
size_t totalSize = 0;
- iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable, sumFiles, &totalSize);
+ iterateOverNativeFiles(env, apkHandle, javaCpuAbi, sumFiles, &totalSize);
return totalSize;
}
static jint
com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz,
- jlong apkHandle, jobjectArray javaCpuAbisToSearch, jboolean debuggable)
+ jlong apkHandle, jobjectArray javaCpuAbisToSearch)
{
- return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch, debuggable);
+ return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch);
}
enum bitcode_scan_result_t {
@@ -748,7 +744,7 @@ static jint com_android_internal_content_NativeLibraryHelper_checkApkAlignment(
return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
}
- auto result = NativeLibrariesIterator::create(zipFile, debuggable);
+ auto result = NativeLibrariesIterator::create(zipFile);
if (!result.ok()) {
ALOGE("Can't iterate over native libs for file:%s", zipFile->getZipFileName());
return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
@@ -810,9 +806,9 @@ static const JNINativeMethod gMethods[] = {
{"nativeClose", "(J)V", (void*)com_android_internal_content_NativeLibraryHelper_close},
{"nativeCopyNativeBinaries", "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
(void*)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
- {"nativeSumNativeBinaries", "(JLjava/lang/String;Z)J",
+ {"nativeSumNativeBinaries", "(JLjava/lang/String;)J",
(void*)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
- {"nativeFindSupportedAbi", "(J[Ljava/lang/String;Z)I",
+ {"nativeFindSupportedAbi", "(J[Ljava/lang/String;)I",
(void*)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
{"hasRenderscriptBitcode", "(J)I",
(void*)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode},
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 0eb7c4aee287..5225ce878310 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -51,7 +51,8 @@ static const char* kPathAllowlist[] = {
"/dev/blkio/tasks",
"/metadata/aconfig/maps/system.package.map",
"/metadata/aconfig/maps/system.flag.map",
- "/metadata/aconfig/boot/system.val"
+ "/metadata/aconfig/boot/system.val",
+ "/metadata/libprocessgroup/memcg_v2_max_activation_depth" // TODO Revert after go/android-memcgv2-exp b/386797433
};
static const char kFdPath[] = "/proc/self/fd";
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 445080215017..73279700ecb1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -180,6 +180,7 @@
<protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL" />
<protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST" />
<protected-broadcast android:name="android.bluetooth.device.action.KEY_MISSING" />
+ <protected-broadcast android:name="android.bluetooth.device.action.ENCRYPTION_CHANGE" />
<protected-broadcast android:name="android.bluetooth.device.action.SDP_RECORD" />
<protected-broadcast android:name="android.bluetooth.device.action.BATTERY_LEVEL_CHANGED" />
<protected-broadcast android:name="android.bluetooth.device.action.REMOTE_ISSUE_OCCURRED" />
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index 44a5df9b60be..c43975e4ad3c 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -19,7 +19,6 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
- android:fitsSystemWindows="true"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:background="@android:color/transparent"
diff --git a/core/res/res/layout/preference_list_fragment_material.xml b/core/res/res/layout/preference_list_fragment_material.xml
index 4df76029e606..db2fe7d038e0 100644
--- a/core/res/res/layout/preference_list_fragment_material.xml
+++ b/core/res/res/layout/preference_list_fragment_material.xml
@@ -19,7 +19,6 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
- android:fitsSystemWindows="true"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:background="@android:color/transparent"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 416e0aeb776c..ec1be83dcae6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5235,10 +5235,6 @@
<!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
<bool name="config_swipe_up_gesture_setting_available">true</bool>
- <!-- Applications which are disabled unless matching a particular sku -->
- <string-array name="config_disableApksUnlessMatchedSku_apk_list" translatable="false" />
- <string-array name="config_disableApkUnlessMatchedSku_skus_list" translatable="false" />
-
<!-- Whether or not we should show the option to show battery percentage -->
<bool name="config_battery_percentage_setting_available">true</bool>
@@ -7323,4 +7319,8 @@
<!-- Whether the device supports Wi-Fi USD feature. -->
<bool name="config_deviceSupportsWifiUsd">false</bool>
+
+ <!-- Array containing the notification assistant service adjustments that are not supported by
+ default on this device-->
+ <string-array translatable="false" name="config_notificationDefaultUnsupportedAdjustments" />
</resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 666f1cf39fe3..965c69d16d79 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -84,7 +84,7 @@
CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
If 0, the device always switch to the higher score SIM.
If < 0, the network type and signal strength based auto switch is disabled. -->
- <integer name="auto_data_switch_score_tolerance">-1</integer>
+ <integer name="auto_data_switch_score_tolerance">4000</integer>
<java-symbol type="integer" name="auto_data_switch_score_tolerance" />
<!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 3f657541eb28..7baaa6d590f2 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -116,7 +116,7 @@
<public name="adServiceTypes" />
<!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") -->
<public name="languageSettingsActivity"/>
- <!-- @FlaggedApi("android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM") -->
+ <!-- @FlaggedApi(android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM) -->
<public name="dreamCategory"/>
<!-- @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled")
@hide @SystemApi -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 84d51f0b8ad8..77cc6868bd58 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4366,10 +4366,6 @@
<java-symbol type="integer" name="config_unfoldTransitionHalfFoldedTimeout" />
<java-symbol type="array" name="config_perDeviceStateRotationLockDefaults" />
-
- <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
- <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
-
<java-symbol type="string" name="config_misprovisionedDeviceModel" />
<java-symbol type="string" name="config_misprovisionedBrandValue" />
@@ -5842,4 +5838,6 @@
<!-- Whether the device supports Wi-Fi USD feature. -->
<java-symbol type="bool" name="config_deviceSupportsWifiUsd" />
+ <java-symbol type="array" name="config_notificationDefaultUnsupportedAdjustments" />
+
</resources>
diff --git a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
index 44b2d90decf2..dfe7d0306905 100644
--- a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -26,7 +26,6 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.os.Parcel;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -124,7 +123,6 @@ public class InputMethodInfoTest {
}
@Test
- @EnableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_IME)
public void testIsVirtualDeviceOnly() throws Exception {
final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_virtual_device_only);
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index f87b6994900f..ee8d428d8370 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -224,7 +224,7 @@ public class ContentCaptureSessionTest {
}
@Override
- void flush(int reason) {
+ void internalFlush(int reason) {
throw new UnsupportedOperationException("should not have been called");
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index b42bcee77c67..a1d7f87614e4 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -263,7 +263,7 @@ public class MainContentCaptureSessionTest {
session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
session.mDirectServiceInterface = mMockContentCaptureDirectManager;
- session.flush(REASON);
+ session.internalFlush(REASON);
mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -280,7 +280,7 @@ public class MainContentCaptureSessionTest {
session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
session.mDirectServiceInterface = mMockContentCaptureDirectManager;
- session.flush(REASON);
+ session.internalFlush(REASON);
mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -298,7 +298,7 @@ public class MainContentCaptureSessionTest {
session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
session.mDirectServiceInterface = mMockContentCaptureDirectManager;
- session.flush(REASON);
+ session.internalFlush(REASON);
mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -316,7 +316,7 @@ public class MainContentCaptureSessionTest {
session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
session.mDirectServiceInterface = mMockContentCaptureDirectManager;
- session.flush(REASON);
+ session.internalFlush(REASON);
mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -499,6 +499,57 @@ public class MainContentCaptureSessionTest {
assertThat(session.mEventProcessQueue).hasSize(1);
}
+ @Test
+ public void notifyContentCaptureEvents_beforeSessionPerformStart() throws RemoteException {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ true);
+ MainContentCaptureSession session = createSession(options);
+ session.mContentCaptureHandler = null;
+ session.mDirectServiceInterface = null;
+
+ notifyContentCaptureEvents(session);
+ mTestableLooper.processAllMessages();
+
+ assertThat(session.mEvents).isNull();
+ assertThat(session.mEventProcessQueue).hasSize(7); // 5 view events + 2 view tree events
+ }
+
+ @Test
+ public void notifyViewAppeared_beforeSessionPerformStart() throws RemoteException {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ true);
+ MainContentCaptureSession session = createSession(options);
+ session.mContentCaptureHandler = null;
+ session.mDirectServiceInterface = null;
+
+ View view = prepareView(session);
+ session.notifyViewAppeared(session.newViewStructure(view));
+
+ assertThat(session.mEvents).isNull();
+ assertThat(session.mEventProcessQueue).hasSize(1);
+ }
+
+ @Test
+ public void flush_beforeSessionPerformStart() throws Exception {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ true);
+ MainContentCaptureSession session = createSession(options);
+ session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
+ session.mContentCaptureHandler = null;
+ session.mDirectServiceInterface = null;
+
+ session.internalFlush(REASON);
+
+ assertThat(session.mEvents).hasSize(1);
+ assertThat(session.mEventProcessQueue).isEmpty();
+ }
+
/** Simulates the regular content capture events sequence. */
private void notifyContentCaptureEvents(final MainContentCaptureSession session) {
final ArrayList<Object> events = new ArrayList<>(
@@ -561,8 +612,8 @@ public class MainContentCaptureSessionTest {
sStrippedContext,
manager,
testHandler,
- testHandler,
mMockSystemServerInterface);
+ session.mContentCaptureHandler = testHandler;
session.mComponentName = COMPONENT_NAME;
return session;
}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 906c71d9caca..1c34e0d54908 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -66,12 +66,6 @@ public final class BLASTBufferQueue {
}
/** Create a new connection with the surface flinger. */
- public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
- @PixelFormat.Format int format) {
- this(name, true /* updateDestinationFrame */);
- update(sc, width, height, format);
- }
-
public BLASTBufferQueue(String name, boolean updateDestinationFrame) {
mNativeObject = nativeCreate(name, updateDestinationFrame);
}
diff --git a/graphics/java/android/graphics/OWNERS b/graphics/java/android/graphics/OWNERS
index ef8d26cc65b9..1ea197658f93 100644
--- a/graphics/java/android/graphics/OWNERS
+++ b/graphics/java/android/graphics/OWNERS
@@ -2,10 +2,10 @@
romainguy@google.com
jreck@google.com
-njawad@google.com
sumir@google.com
djsollen@google.com
-scroggo@google.com
+alecmouri@google.com
+sallyqi@google.com
per-file BLASTBufferQueue.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file FontFamily.java = file:fonts/OWNERS
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
index c62d2a06bad5..90ea7d35015e 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
@@ -34,7 +34,6 @@ import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
import com.android.wm.shell.Flags
import com.android.wm.shell.ShellTaskOrganizer
-import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.properties.ProdBubbleProperties
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
@@ -44,6 +43,7 @@ import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.FloatingContentCoordinator
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.common.TestShellExecutor
import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.shared.TransactionPool
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index ab2e552c7a3d..a7eebd6159e4 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -36,10 +36,10 @@ import com.android.internal.protolog.ProtoLog
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.Flags
import com.android.wm.shell.R
-import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.common.FloatingContentCoordinator
+import com.android.wm.shell.common.TestShellExecutor
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
index 3043e2bcb0be..a83327bbadee 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -35,7 +35,6 @@ import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.ShellTaskOrganizer
-import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.properties.BubbleProperties
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.common.DisplayController
@@ -44,6 +43,7 @@ import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.FloatingContentCoordinator
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.common.TestShellExecutor
import com.android.wm.shell.shared.TransactionPool
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
index bcaa63bfad36..750178678785 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
@@ -22,9 +22,9 @@ import android.content.res.Resources
import android.view.LayoutInflater
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.wm.shell.R
-import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.BubbleViewInfoTask.BubbleViewInfo
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView
+import com.android.wm.shell.common.TestShellExecutor
import com.google.common.util.concurrent.MoreExecutors.directExecutor
/** Helper to create a [Bubble] instance */
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
index 117ede0d0ac8..9e58b5be9d0d 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
@@ -36,7 +36,6 @@ import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
-import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.BubbleExpandedViewManager
import com.android.wm.shell.bubbles.BubbleLogger
@@ -46,6 +45,7 @@ import com.android.wm.shell.bubbles.BubbleTaskView
import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.FakeBubbleFactory
+import com.android.wm.shell.common.TestShellExecutor
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
index bfc798bb9c79..fbbcff2dee92 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -33,7 +33,6 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
-import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.BubbleExpandedViewManager
import com.android.wm.shell.bubbles.BubbleLogger
@@ -44,6 +43,7 @@ import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.RegionSamplingProvider
import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
+import com.android.wm.shell.common.TestShellExecutor
import com.android.wm.shell.shared.handles.RegionSamplingHelper
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
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 9b1645e9534c..5c5dde7da351 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
@@ -36,7 +36,6 @@ import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
import com.android.wm.shell.R
import com.android.wm.shell.ShellTaskOrganizer
-import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.BubbleController
import com.android.wm.shell.bubbles.BubbleData
@@ -58,6 +57,7 @@ import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.FloatingContentCoordinator
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.common.TestShellExecutor
import com.android.wm.shell.shared.TransactionPool
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/common/TestShellExecutor.kt
index ef8e71c2590b..6b549b42cdcd 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/common/TestShellExecutor.kt
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell
-
-import com.android.wm.shell.common.ShellExecutor
+package com.android.wm.shell.common
/**
* Simple implementation of [ShellExecutor] that collects all runnables and executes them
diff --git a/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml
index e5fe1b5431eb..83a3959ee2f9 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml
@@ -22,5 +22,6 @@
android:viewportWidth="960">
<path
android:fillColor="@android:color/black"
- android:pathData="M160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L800,160Q833,160 856.5,183.5Q880,207 880,240L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800ZM160,720L800,720Q800,720 800,720Q800,720 800,720L800,320L160,320L160,720Q160,720 160,720Q160,720 160,720Z"/>
+ android:pathData="M 244.79 796.408 C 222.79 796.408 203.79 788.74 187.79 773.408 C 172.457 757.408 164.79 738.408 164.79 716.408 L 164.79 236.408 C 164.79 214.408 172.457 195.741 187.79 180.408 C 203.79 164.408 222.79 156.408 244.79 156.408 L 724.79 156.408 C 746.79 156.408 765.458 164.408 780.79 180.408 C 796.79 195.741 804.79 214.408 804.79 236.408 L 804.79 716.408 C 804.79 738.408 796.79 757.408 780.79 773.408 C 765.458 788.74 746.79 796.408 724.79 796.408 Z M 244.79 716.408 L 724.79 716.408 L 724.79 236.408 L 244.79 236.408 Z M 244.79 236.408 L 244.79 716.408 Z"
+ />
</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/Android.bp b/libs/WindowManager/Shell/shared/Android.bp
index c3ee0f76f550..d7669ed5cf34 100644
--- a/libs/WindowManager/Shell/shared/Android.bp
+++ b/libs/WindowManager/Shell/shared/Android.bp
@@ -56,6 +56,7 @@ android_library {
static_libs: [
"androidx.core_core-animation",
"androidx.dynamicanimation_dynamicanimation",
+ "com_android_wm_shell_flags_lib",
"jsr330",
],
kotlincflags: ["-Xjvm-default=all"],
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
index e033f673d07d..840de2c86f92 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
@@ -40,7 +40,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
-import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.util.ArrayMap;
@@ -57,6 +56,9 @@ public class TransitionUtil {
/** Flag applied to a transition change to identify it as a divider bar for animation. */
public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
+ /** Flag applied to a transition change to identify it as a desktop wallpaper activity. */
+ public static final int FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY = FLAG_FIRST_CUSTOM << 1;
+
/** @return true if the transition was triggered by opening something vs closing something */
public static boolean isOpeningType(@WindowManager.TransitionType int type) {
return type == TRANSIT_OPEN
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleAnythingFlagHelper.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleAnythingFlagHelper.java
new file mode 100644
index 000000000000..e1f1d0c32eb0
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleAnythingFlagHelper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.shared.bubbles;
+
+import com.android.wm.shell.Flags;
+
+/**
+ * Bubble anything has some dependent flags, this class simplifies the checks.
+ * (TODO: b/389737359 - remove this when the feature is launched).
+ */
+public class BubbleAnythingFlagHelper {
+
+ private BubbleAnythingFlagHelper() {}
+
+ /** Whether creating any bubble or the overall bubble anything feature is enabled. */
+ public static boolean enableCreateAnyBubble() {
+ return enableBubbleAnything() || Flags.enableCreateAnyBubble();
+ }
+
+ /**
+ * Whether creating any bubble and transforming to fullscreen, or the overall bubble anything
+ * feature is enabled.
+ */
+ public static boolean enableBubbleToFullscreen() {
+ return enableBubbleAnything()
+ || (Flags.enableBubbleToFullscreen()
+ && Flags.enableCreateAnyBubble());
+ }
+
+ /** Whether the overall bubble anything feature is enabled. */
+ public static boolean enableBubbleAnything() {
+ return Flags.enableBubbleAnything();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
index 67592e60e954..0d0bc9be72b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.common
import android.app.PendingIntent
+import android.app.TaskInfo
import android.content.ComponentName
import android.content.Intent
import com.android.wm.shell.ShellTaskOrganizer
@@ -34,7 +35,11 @@ object ComponentUtils {
/** Retrieves the package name from a [taskId]. */
@JvmStatic
fun getPackageName(taskId: Int, taskOrganizer: ShellTaskOrganizer): String? {
- val taskInfo = taskOrganizer.getRunningTaskInfo(taskId)
- return getPackageName(taskInfo?.baseIntent)
+ val taskInfo = taskOrganizer.getRunningTaskInfo(taskId) ?: return null
+ return getPackageName(taskInfo)
}
+
+ /** Retrieves the package name from a [TaskInfo]. */
+ @JvmStatic
+ fun getPackageName(taskInfo: TaskInfo): String? = getPackageName(taskInfo.baseIntent)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt
new file mode 100644
index 000000000000..a13ad20f8c05
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.common
+
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+
+/**
+ * Utility class for calculating bounds during multi-display drag operations.
+ *
+ * This class provides helper functions to perform bounds calculation during window drag.
+ */
+object MultiDisplayDragMoveBoundsCalculator {
+ /**
+ * Calculates the global DP bounds of a window being dragged across displays.
+ *
+ * @param startDisplayLayout The DisplayLayout object of the display where the drag started.
+ * @param repositionStartPoint The starting position of the drag (in pixels), relative to the
+ * display where the drag started.
+ * @param boundsAtDragStart The initial bounds of the window (in pixels), relative to the
+ * display where the drag started.
+ * @param currentDisplayLayout The DisplayLayout object of the display where the pointer is
+ * currently located.
+ * @param x The current x-coordinate of the drag pointer (in pixels).
+ * @param y The current y-coordinate of the drag pointer (in pixels).
+ * @return A RectF object representing the calculated global DP bounds of the window.
+ */
+ fun calculateGlobalDpBoundsForDrag(
+ startDisplayLayout: DisplayLayout,
+ repositionStartPoint: PointF,
+ boundsAtDragStart: Rect,
+ currentDisplayLayout: DisplayLayout,
+ x: Float,
+ y: Float,
+ ): RectF {
+ // Convert all pixel values to DP.
+ val startCursorDp =
+ startDisplayLayout.localPxToGlobalDp(repositionStartPoint.x, repositionStartPoint.y)
+ val currentCursorDp = currentDisplayLayout.localPxToGlobalDp(x, y)
+ val startLeftTopDp =
+ startDisplayLayout.localPxToGlobalDp(boundsAtDragStart.left, boundsAtDragStart.top)
+ val widthDp = startDisplayLayout.pxToDp(boundsAtDragStart.width())
+ val heightDp = startDisplayLayout.pxToDp(boundsAtDragStart.height())
+
+ // Calculate DP bounds based on pointer movement delta.
+ val currentLeftDp = startLeftTopDp.x + (currentCursorDp.x - startCursorDp.x)
+ val currentTopDp = startLeftTopDp.y + (currentCursorDp.y - startCursorDp.y)
+ val currentRightDp = currentLeftDp + widthDp
+ val currentBottomDp = currentTopDp + heightDp
+
+ return RectF(currentLeftDp, currentTopDp, currentRightDp, currentBottomDp)
+ }
+
+ /**
+ * Converts global DP bounds to local pixel bounds for a specific display.
+ *
+ * @param rectDp The global DP bounds to convert.
+ * @param displayLayout The DisplayLayout representing the display to convert the bounds to.
+ * @return A Rect object representing the local pixel bounds on the specified display.
+ */
+ fun convertGlobalDpToLocalPxForRect(rectDp: RectF, displayLayout: DisplayLayout): Rect {
+ val leftTopPxDisplay = displayLayout.globalDpToLocalPx(rectDp.left, rectDp.top)
+ val rightBottomPxDisplay = displayLayout.globalDpToLocalPx(rectDp.right, rectDp.bottom)
+ return Rect(
+ leftTopPxDisplay.x.toInt(),
+ leftTopPxDisplay.y.toInt(),
+ rightBottomPxDisplay.x.toInt(),
+ rightBottomPxDisplay.y.toInt(),
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index f38957e48dbf..91e06300f1b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -35,6 +35,7 @@ import android.graphics.PointF
import android.graphics.Rect
import android.graphics.Region
import android.os.Binder
+import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.os.SystemProperties
@@ -884,6 +885,36 @@ class DesktopTasksController(
}
/**
+ * Start an intent through a launch transition for starting tasks whose transition does not get
+ * handled by [handleRequest]
+ */
+ fun startLaunchIntentTransition(intent: Intent, options: Bundle, displayId: Int) {
+ val wct = WindowContainerTransaction()
+ val displayLayout = displayController.getDisplayLayout(displayId) ?: return
+ val bounds = calculateDefaultDesktopTaskBounds(displayLayout)
+ if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue) {
+ cascadeWindow(bounds, displayLayout, displayId)
+ }
+ val pendingIntent =
+ PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE,
+ )
+ val ops =
+ ActivityOptions.fromBundle(options).apply {
+ launchWindowingMode = WINDOWING_MODE_FREEFORM
+ pendingIntentBackgroundActivityStartMode =
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+ launchBounds = bounds
+ }
+
+ wct.sendPendingIntent(pendingIntent, intent, ops.toBundle())
+ startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+ }
+
+ /**
* Move [task] to display with [displayId].
*
* No-op if task is already on that display per [RunningTaskInfo.displayId].
@@ -2425,6 +2456,25 @@ class DesktopTasksController(
// Update task bounds so that the task position will match the position of its leash
val wct = WindowContainerTransaction()
wct.setBounds(taskInfo.token, destinationBounds)
+
+ // TODO: b/362720497 - reparent to a specific desk within the target display.
+ // Reparent task if it has been moved to a new display.
+ if (Flags.enableConnectedDisplaysWindowDrag()) {
+ val newDisplayId = motionEvent.getDisplayId()
+ if (newDisplayId != taskInfo.getDisplayId()) {
+ val displayAreaInfo =
+ rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(newDisplayId)
+ if (displayAreaInfo == null) {
+ logW(
+ "Task reparent cannot find DisplayAreaInfo for displayId=%d",
+ newDisplayId,
+ )
+ } else {
+ wct.reparent(taskInfo.token, displayAreaInfo.token, /* onTop= */ true)
+ }
+ }
+ }
+
transitions.startTransition(TRANSIT_CHANGE, wct, null)
releaseVisualIndicator()
@@ -2885,6 +2935,12 @@ class DesktopTasksController(
c.moveToNextDisplay(taskId)
}
}
+
+ override fun startLaunchIntentTransition(intent: Intent, options: Bundle, displayId: Int) {
+ executeRemoteCallWithTaskPermission(controller, "startLaunchIntentTransition") { c ->
+ c.startLaunchIntentTransition(intent, options, displayId)
+ }
+ }
}
private fun logV(msg: String, vararg arguments: Any?) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index fa383cb55118..54f031293486 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -17,6 +17,8 @@
package com.android.wm.shell.desktopmode;
import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Intent;
+import android.os.Bundle;
import android.window.RemoteTransition;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
@@ -61,4 +63,7 @@ interface IDesktopMode {
/** Move a task with given `taskId` to external display */
void moveToExternalDisplay(int taskId);
+
+ /** Start a transition when launching an intent in desktop mode */
+ void startLaunchIntentTransition(in Intent intent, in Bundle options, in int displayId);
} \ No newline at end of file
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 975b65023f20..0182588398a4 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
@@ -54,7 +54,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
-import com.android.launcher3.Flags;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
@@ -553,9 +552,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
groupedTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
- if (
- Flags.enableUseTopVisibleActivityForExcludeFromRecentTask()
- && isWallpaperTask(taskInfo)) {
+ if (isWallpaperTask(taskInfo)) {
// Don't add the wallpaper task as an entry in grouped tasks
continue;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index d5929f010e02..abfb41bb513a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -39,6 +39,7 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI
import static com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived;
import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
+import static com.android.wm.shell.shared.TransitionUtil.FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
@@ -84,6 +85,7 @@ import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes;
+import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.FocusTransitionListener;
@@ -795,6 +797,14 @@ public class Transitions implements RemoteCallable<Transitions>,
mReadyDuringSync.remove(active);
}
+ // If any of the changes are on DesktopWallpaperActivity, add the flag to the change.
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getTaskInfo() != null
+ && DesktopWallpaperActivity.isWallpaperTask(change.getTaskInfo())) {
+ change.setFlags(FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY);
+ }
+ }
+
final Track track = getOrCreateTrack(info.getTrack());
track.mReadyTransitions.add(active);
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 055bc8f5f092..d6d393f2500c 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
@@ -99,6 +99,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
import com.android.wm.shell.apptoweb.AssistContentRequester;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -1926,14 +1927,21 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
// instances, then refer to the list's size and reuse the list for Manage Windows menu.
final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
try {
+ // TODO(b/389184897): Move the following into a helper method of
+ // RecentsTasksController, similar to #findTaskInBackground.
+ final String packageName = ComponentUtils.getPackageName(info);
return activityTaskManager.getRecentTasks(Integer.MAX_VALUE,
ActivityManager.RECENT_WITH_EXCLUDED,
info.userId).getList().stream().filter(
- recentTaskInfo -> (recentTaskInfo.taskId != info.taskId
- && recentTaskInfo.baseActivity != null
- && recentTaskInfo.baseActivity.getPackageName()
- .equals(info.baseActivity.getPackageName())
- )
+ recentTaskInfo -> {
+ if (recentTaskInfo.taskId == info.taskId) {
+ return false;
+ }
+ final String recentTaskPackageName =
+ ComponentUtils.getPackageName(recentTaskInfo);
+ return packageName != null
+ && packageName.equals(recentTaskPackageName);
+ }
).toList().size();
} catch (RemoteException e) {
throw new RuntimeException(e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index 8dc921c986ce..07496eb0e526 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -31,6 +31,7 @@ import com.android.internal.jank.Cuj
import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import java.util.concurrent.TimeUnit
@@ -69,6 +70,7 @@ class MultiDisplayVeiledResizeTaskPositioner(
@DragPositioningCallback.CtrlType private var ctrlType = 0
private var isResizingOrAnimatingResize = false
@Surface.Rotation private var rotation = 0
+ private var startDisplayId = 0
constructor(
taskOrganizer: ShellTaskOrganizer,
@@ -95,6 +97,7 @@ class MultiDisplayVeiledResizeTaskPositioner(
override fun onDragPositioningStart(ctrlType: Int, displayId: Int, x: Float, y: Float): Rect {
this.ctrlType = ctrlType
+ startDisplayId = displayId
taskBoundsAtDragStart.set(
desktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.bounds
)
@@ -160,16 +163,47 @@ class MultiDisplayVeiledResizeTaskPositioner(
interactionJankMonitor.begin(
createLongTimeoutJankConfigBuilder(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
)
+
val t = transactionSupplier.get()
- DragPositioningCallbackUtility.setPositionOnDrag(
- desktopWindowDecoration,
- repositionTaskBounds,
- taskBoundsAtDragStart,
- repositionStartPoint,
- t,
- x,
- y,
- )
+ val startDisplayLayout = displayController.getDisplayLayout(startDisplayId)
+ val currentDisplayLayout = displayController.getDisplayLayout(displayId)
+
+ if (startDisplayLayout == null || currentDisplayLayout == null) {
+ // Fall back to single-display drag behavior if any display layout is unavailable.
+ DragPositioningCallbackUtility.setPositionOnDrag(
+ desktopWindowDecoration,
+ repositionTaskBounds,
+ taskBoundsAtDragStart,
+ repositionStartPoint,
+ t,
+ x,
+ y,
+ )
+ } else {
+ val boundsDp =
+ MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
+ startDisplayLayout,
+ repositionStartPoint,
+ taskBoundsAtDragStart,
+ currentDisplayLayout,
+ x,
+ y,
+ )
+ repositionTaskBounds.set(
+ MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
+ boundsDp,
+ startDisplayLayout,
+ )
+ )
+
+ // TODO(b/383069173): Render drag indicator(s)
+
+ t.setPosition(
+ desktopWindowDecoration.mTaskSurface,
+ repositionTaskBounds.left.toFloat(),
+ repositionTaskBounds.top.toFloat(),
+ )
+ }
t.setFrameTimeline(Choreographer.getInstance().vsyncId)
t.apply()
}
@@ -200,13 +234,38 @@ class MultiDisplayVeiledResizeTaskPositioner(
}
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
} else {
- DragPositioningCallbackUtility.updateTaskBounds(
- repositionTaskBounds,
- taskBoundsAtDragStart,
- repositionStartPoint,
- x,
- y,
- )
+ val startDisplayLayout = displayController.getDisplayLayout(startDisplayId)
+ val currentDisplayLayout = displayController.getDisplayLayout(displayId)
+
+ if (startDisplayLayout == null || currentDisplayLayout == null) {
+ // Fall back to single-display drag behavior if any display layout is unavailable.
+ DragPositioningCallbackUtility.updateTaskBounds(
+ repositionTaskBounds,
+ taskBoundsAtDragStart,
+ repositionStartPoint,
+ x,
+ y,
+ )
+ } else {
+ val boundsDp =
+ MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
+ startDisplayLayout,
+ repositionStartPoint,
+ taskBoundsAtDragStart,
+ currentDisplayLayout,
+ x,
+ y,
+ )
+ repositionTaskBounds.set(
+ MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
+ boundsDp,
+ currentDisplayLayout,
+ )
+ )
+
+ // TODO(b/383069173): Clear drag indicator(s)
+ }
+
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt
new file mode 100644
index 000000000000..bd924c2b47c1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.common
+
+import android.content.res.Configuration
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+import android.testing.TestableResources
+import com.android.wm.shell.ShellTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Tests for [MultiDisplayDragMoveBoundsCalculator].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:MultiDisplayDragMoveBoundsCalculatorTest
+ */
+class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() {
+ private lateinit var resources: TestableResources
+
+ @Before
+ fun setUp() {
+ resources = mContext.getOrCreateTestableResources()
+ val configuration = Configuration()
+ configuration.uiMode = 0
+ resources.overrideConfiguration(configuration)
+ }
+
+ @Test
+ fun testCalculateGlobalDpBoundsForDrag() {
+ val repositionStartPoint = PointF(20f, 40f)
+ val boundsAtDragStart = Rect(10, 20, 110, 120)
+ val x = 300f
+ val y = 400f
+ val displayLayout0 =
+ MultiDisplayTestUtil.createSpyDisplayLayout(
+ MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_0,
+ MultiDisplayTestUtil.DISPLAY_DPI_0,
+ resources.resources,
+ )
+ val displayLayout1 =
+ MultiDisplayTestUtil.createSpyDisplayLayout(
+ MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_1,
+ MultiDisplayTestUtil.DISPLAY_DPI_1,
+ resources.resources,
+ )
+
+ val actualBoundsDp =
+ MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
+ displayLayout0,
+ repositionStartPoint,
+ boundsAtDragStart,
+ displayLayout1,
+ x,
+ y,
+ )
+
+ val expectedBoundsDp = RectF(240f, -820f, 340f, -720f)
+ assertEquals(expectedBoundsDp, actualBoundsDp)
+ }
+
+ @Test
+ fun testConvertGlobalDpToLocalPxForRect() {
+ val displayLayout =
+ MultiDisplayTestUtil.createSpyDisplayLayout(
+ MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_1,
+ MultiDisplayTestUtil.DISPLAY_DPI_1,
+ resources.resources,
+ )
+ val rectDp = RectF(150f, -350f, 300f, -250f)
+
+ val actualBoundsPx =
+ MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
+ rectDp,
+ displayLayout,
+ )
+
+ val expectedBoundsPx = Rect(100, 1300, 400, 1500)
+ assertEquals(expectedBoundsPx, actualBoundsPx)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayTestUtil.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayTestUtil.kt
new file mode 100644
index 000000000000..c8bebf11a82c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayTestUtil.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common
+
+import android.content.res.Resources
+import android.graphics.RectF
+import android.util.DisplayMetrics
+import android.view.DisplayInfo
+import org.mockito.Mockito.spy
+
+/** Utility class for tests of [DesktopModeWindowDecorViewModel] */
+object MultiDisplayTestUtil {
+ // We have two displays, display#1 is placed on middle top of display#0:
+ // +---+
+ // | 1 |
+ // +-+---+-+
+ // | 0 |
+ // +-------+
+ val DISPLAY_GLOBAL_BOUNDS_0 = RectF(0f, 0f, 1200f, 800f)
+ val DISPLAY_GLOBAL_BOUNDS_1 = RectF(100f, -1000f, 1100f, 0f)
+ val DISPLAY_DPI_0 = DisplayMetrics.DENSITY_DEFAULT
+ val DISPLAY_DPI_1 = DisplayMetrics.DENSITY_DEFAULT * 2
+
+ fun createSpyDisplayLayout(globalBounds: RectF, dpi: Int, resources: Resources): DisplayLayout {
+ val displayInfo = DisplayInfo()
+ displayInfo.logicalDensityDpi = dpi
+ val displayLayout = spy(DisplayLayout(displayInfo, resources, true, true))
+ displayLayout.setGlobalBoundsDp(globalBounds)
+ return displayLayout
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 2b986d184c20..d13ff79b9518 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -257,6 +257,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
// Mock running tasks are registered here so we can get the list from mock shell task organizer
private val runningTasks = mutableListOf<RunningTaskInfo>()
+ private val SECONDARY_DISPLAY_ID = 1
private val DISPLAY_DIMENSION_SHORT = 1600
private val DISPLAY_DIMENSION_LONG = 2560
private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 75, 2240, 1275)
@@ -316,6 +317,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECONDARY_DISPLAY_ID))
+ .thenReturn(tda)
whenever(
mMockDesktopImmersiveController.exitImmersiveIfApplicable(
any(),
@@ -1143,6 +1146,21 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun launchIntent_taskInDesktopMode_transitionStarted() {
+ setUpLandscapeDisplay()
+ val freeformTask = setUpFreeformTask()
+
+ controller.startLaunchIntentTransition(
+ freeformTask.baseIntent,
+ Bundle.EMPTY,
+ DEFAULT_DISPLAY,
+ )
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ assertThat(wct.hierarchyOps).hasSize(1)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun addMoveToDesktopChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
setUpLandscapeDisplay()
@@ -3588,6 +3606,45 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
+ fun onDesktopDragEnd_noIndicatorAndMoveToNewDisplay_reparent() {
+ val task = setUpFreeformTask()
+ val spyController = spy(controller)
+ val mockSurface = mock(SurfaceControl::class.java)
+ val mockDisplayLayout = mock(DisplayLayout::class.java)
+ whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+ spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+
+ val currentDragBounds = Rect(100, 200, 500, 1000)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+ whenever(motionEvent.displayId).thenReturn(SECONDARY_DISPLAY_ID)
+
+ spyController.onDragPositioningEnd(
+ task,
+ mockSurface,
+ position = Point(100, 200),
+ inputCoordinate = PointF(200f, 300f),
+ currentDragBounds,
+ validDragArea = Rect(0, 50, 2000, 2000),
+ dragStartBounds = Rect(),
+ motionEvent,
+ desktopWindowDecoration,
+ )
+
+ verify(transitions)
+ .startTransition(
+ eq(TRANSIT_CHANGE),
+ Mockito.argThat { wct ->
+ return@argThat wct.hierarchyOps[0].isReparent
+ },
+ eq(null),
+ )
+ }
+
+ @Test
fun onDesktopDragEnd_fullscreenIndicator_dragToExitDesktop() {
val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100))
val spyController = spy(controller)
@@ -5162,7 +5219,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val arg: ArgumentCaptor<WindowContainerTransaction> =
ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(desktopMixedTransitionHandler)
- .startLaunchTransition(eq(type), capture(arg), anyInt(), anyOrNull(), anyOrNull())
+ .startLaunchTransition(eq(type), capture(arg), anyOrNull(), anyOrNull(), anyOrNull())
return arg.value
}
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 7e5d6ce38c5a..28f4ea0c7ada 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
@@ -22,13 +22,13 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.launcher3.Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -65,8 +65,8 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.SurfaceControl;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
@@ -253,7 +253,6 @@ public class RecentTasksControllerTest extends ShellTestCase {
t3.taskId, -1);
}
- @EnableFlags(FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
@Test
public void testGetRecentTasks_removesDesktopWallpaperActivity() {
RecentTaskInfo t1 = makeTaskInfo(1);
@@ -753,15 +752,9 @@ public class RecentTasksControllerTest extends ShellTestCase {
/**
* Helper to set the raw task list on the controller.
*/
- 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(),
+ private void setRawList(RecentTaskInfo... tasks) {
+ doReturn(Arrays.asList(tasks)).when(mActivityTaskManager).getRecentTasks(anyInt(), anyInt(),
anyInt());
- return rawList;
}
/**
@@ -794,8 +787,9 @@ public class RecentTasksControllerTest extends ShellTestCase {
assertNull(pair.getSplitBounds());
}
}
- assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
+ assertArrayEquals("Expected: " + Arrays.toString(expectedTaskIds)
+ " Received: " + Arrays.toString(flattenedTaskIds),
- Arrays.equals(flattenedTaskIds, expectedTaskIds));
+ flattenedTaskIds,
+ expectedTaskIds);
}
}
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 dd645fd968e4..0a19be4eb959 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
@@ -1781,6 +1781,7 @@ public class ShellTransitionTests extends ShellTestCase {
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
taskInfo.configuration.windowConfiguration.setActivityType(activityType);
taskInfo.token = mock(WindowContainerToken.class);
+ taskInfo.baseIntent = mock(Intent.class);
return taskInfo;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index f179cac32244..2207c705d7dc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -17,14 +17,14 @@ package com.android.wm.shell.windowdecor
import android.app.ActivityManager
import android.app.WindowConfiguration
-import android.content.Context
-import android.content.res.Resources
+import android.content.res.Configuration
import android.graphics.Point
import android.graphics.Rect
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.testing.AndroidTestingRunner
+import android.testing.TestableResources
import android.view.Display
import android.view.Surface.ROTATION_0
import android.view.Surface.ROTATION_270
@@ -41,6 +41,7 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.MultiDisplayTestUtil
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
@@ -55,6 +56,7 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.argThat
+import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -82,7 +84,6 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
@Mock private lateinit var taskBinder: IBinder
@Mock private lateinit var mockDisplayController: DisplayController
- @Mock private lateinit var mockDisplayLayout: DisplayLayout
@Mock private lateinit var mockDisplay: Display
@Mock private lateinit var mockTransactionFactory: Supplier<SurfaceControl.Transaction>
@Mock private lateinit var mockTransaction: SurfaceControl.Transaction
@@ -90,9 +91,11 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
@Mock private lateinit var mockTransitionInfo: TransitionInfo
@Mock private lateinit var mockFinishCallback: TransitionFinishCallback
@Mock private lateinit var mockTransitions: Transitions
- @Mock private lateinit var mockContext: Context
- @Mock private lateinit var mockResources: Resources
@Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+ private lateinit var resources: TestableResources
+ private lateinit var spyDisplayLayout0: DisplayLayout
+ private lateinit var spyDisplayLayout1: DisplayLayout
+
private val mainHandler = Handler(Looper.getMainLooper())
private lateinit var taskPositioner: MultiDisplayVeiledResizeTaskPositioner
@@ -101,24 +104,45 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
- mockDesktopWindowDecoration.mDisplay = mockDisplay
- mockDesktopWindowDecoration.mDecorWindowContext = mockContext
- whenever(mockContext.getResources()).thenReturn(mockResources)
whenever(taskToken.asBinder()).thenReturn(taskBinder)
- whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
- whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
- whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
- if (
- mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
- .displayRotation == ROTATION_90 ||
+ mockDesktopWindowDecoration.mDisplay = mockDisplay
+ mockDesktopWindowDecoration.mDecorWindowContext = mContext
+ resources = mContext.orCreateTestableResources
+ val resourceConfiguration = Configuration()
+ resourceConfiguration.uiMode = 0
+ resources.overrideConfiguration(resourceConfiguration)
+ spyDisplayLayout0 =
+ MultiDisplayTestUtil.createSpyDisplayLayout(
+ MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_0,
+ MultiDisplayTestUtil.DISPLAY_DPI_0,
+ resources.resources,
+ )
+ spyDisplayLayout1 =
+ MultiDisplayTestUtil.createSpyDisplayLayout(
+ MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_1,
+ MultiDisplayTestUtil.DISPLAY_DPI_1,
+ resources.resources,
+ )
+ whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID_0)).thenReturn(spyDisplayLayout0)
+ whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID_1)).thenReturn(spyDisplayLayout1)
+ whenever(spyDisplayLayout0.densityDpi()).thenReturn(DENSITY_DPI)
+ whenever(spyDisplayLayout1.densityDpi()).thenReturn(DENSITY_DPI)
+ doAnswer { i ->
+ val rect = i.getArgument<Rect>(0)
+ if (
mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
- .displayRotation == ROTATION_270
- ) {
- (i.arguments.first() as Rect).set(STABLE_BOUNDS_LANDSCAPE)
- } else {
- (i.arguments.first() as Rect).set(STABLE_BOUNDS_PORTRAIT)
+ .displayRotation == ROTATION_90 ||
+ mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_270
+ ) {
+ rect.set(STABLE_BOUNDS_LANDSCAPE)
+ } else {
+ rect.set(STABLE_BOUNDS_PORTRAIT)
+ }
+ null
}
- }
+ .`when`(spyDisplayLayout0)
+ .getStableBounds(any())
`when`(mockTransactionFactory.get()).thenReturn(mockTransaction)
mockDesktopWindowDecoration.mTaskInfo =
ActivityManager.RunningTaskInfo().apply {
@@ -127,14 +151,14 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
minWidth = MIN_WIDTH
minHeight = MIN_HEIGHT
defaultMinSize = DEFAULT_MIN
- displayId = DISPLAY_ID
+ displayId = DISPLAY_ID_0
configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
configuration.windowConfiguration.displayRotation = ROTATION_90
isResizeable = true
}
`when`(mockDesktopWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
mockDesktopWindowDecoration.mDisplay = mockDisplay
- whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
+ whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID_0 }
taskPositioner =
MultiDisplayVeiledResizeTaskPositioner(
@@ -153,14 +177,14 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_noMove_doesNotShowResizeVeil() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
verify(mockDesktopWindowDecoration, never()).showResizeVeil(STARTING_BOUNDS)
taskPositioner.onDragPositioningEnd(
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
@@ -185,13 +209,13 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_movesTask_doesNotShowResizeVeil() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED,
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
taskPositioner.onDragPositioningMove(
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat() + 60,
STARTING_BOUNDS.top.toFloat() + 100,
)
@@ -205,7 +229,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
val endBounds =
taskPositioner.onDragPositioningEnd(
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat() + 70,
STARTING_BOUNDS.top.toFloat() + 20,
)
@@ -221,16 +245,39 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
}
@Test
+ fun testDragResize_movesTaskToNewDisplay() = runOnUiThread {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_UNDEFINED,
+ DISPLAY_ID_0,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ taskPositioner.onDragPositioningMove(DISPLAY_ID_1, 200f, 1900f)
+
+ val rectAfterMove = Rect(200, -50, 300, 50)
+ verify(mockTransaction)
+ .setPosition(any(), eq(rectAfterMove.left.toFloat()), eq(rectAfterMove.top.toFloat()))
+
+ val endBounds = taskPositioner.onDragPositioningEnd(DISPLAY_ID_1, 300f, 450f)
+ val rectAfterEnd = Rect(300, 450, 500, 650)
+
+ verify(mockDesktopWindowDecoration, never()).showResizeVeil(any())
+ verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
+ Assert.assertEquals(rectAfterEnd, endBounds)
+ }
+
+ @Test
fun testDragResize_resize_boundsUpdateOnEnd() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
taskPositioner.onDragPositioningMove(
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.right.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10,
)
@@ -252,7 +299,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
)
taskPositioner.onDragPositioningEnd(
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.right.toFloat() + 20,
STARTING_BOUNDS.top.toFloat() + 20,
)
@@ -278,20 +325,20 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
@Test
fun testDragResize_noEffectiveMove_skipsTransactionOnEnd() = runOnUiThread {
taskPositioner.onDragPositioningStart(
- DISPLAY_ID,
+ DISPLAY_ID_0,
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
taskPositioner.onDragPositioningMove(
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
taskPositioner.onDragPositioningEnd(
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10,
)
@@ -326,16 +373,16 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
fun testDragResize_drag_setBoundsNotRunIfDragEndsInDisallowedEndArea() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // drag
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
val newX = STARTING_BOUNDS.left.toFloat() + 5
val newY = DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT.toFloat() - 1
- taskPositioner.onDragPositioningMove(DISPLAY_ID, newX, newY)
+ taskPositioner.onDragPositioningMove(DISPLAY_ID_0, newX, newY)
- taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID_0, newX, newY)
verify(mockShellTaskOrganizer, never())
.applyTransaction(
@@ -354,7 +401,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
mockDesktopWindowDecoration.mHasGlobalFocus = false
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT, // Resize right
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
@@ -375,7 +422,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
mockDesktopWindowDecoration.mHasGlobalFocus = true
taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT, // Resize right
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
@@ -396,7 +443,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
mockDesktopWindowDecoration.mHasGlobalFocus = false
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // drag
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
@@ -427,7 +474,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
rectAfterDrag.right += 2000
rectAfterDrag.bottom = STABLE_BOUNDS_LANDSCAPE.bottom
// First drag; we should fetch stable bounds.
- verify(mockDisplayLayout, times(1)).getStableBounds(any())
+ verify(spyDisplayLayout0, times(1)).getStableBounds(any())
verify(mockTransitions)
.startTransition(
eq(TRANSIT_CHANGE),
@@ -451,7 +498,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
)
// Display did not rotate; we should use previous stable bounds
- verify(mockDisplayLayout, times(1)).getStableBounds(any())
+ verify(spyDisplayLayout0, times(1)).getStableBounds(any())
// Rotate the screen to portrait
mockDesktopWindowDecoration.mTaskInfo.apply {
@@ -482,7 +529,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
eq(taskPositioner),
)
// Display has rotated; we expect a new stable bounds.
- verify(mockDisplayLayout, times(2)).getStableBounds(any())
+ verify(spyDisplayLayout0, times(2)).getStableBounds(any())
}
@Test
@@ -491,13 +538,13 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
taskPositioner.onDragPositioningMove(
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat() - 20,
STARTING_BOUNDS.top.toFloat() - 20,
)
@@ -507,7 +554,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
verify(mockDragEventListener, times(1)).onDragMove(eq(TASK_ID))
taskPositioner.onDragPositioningEnd(
- DISPLAY_ID,
+ DISPLAY_ID_0,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat(),
)
@@ -568,10 +615,10 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
}
private fun performDrag(startX: Float, startY: Float, endX: Float, endY: Float, ctrlType: Int) {
- taskPositioner.onDragPositioningStart(ctrlType, DISPLAY_ID, startX, startY)
- taskPositioner.onDragPositioningMove(DISPLAY_ID, endX, endY)
+ taskPositioner.onDragPositioningStart(ctrlType, DISPLAY_ID_0, startX, startY)
+ taskPositioner.onDragPositioningMove(DISPLAY_ID_0, endX, endY)
- taskPositioner.onDragPositioningEnd(DISPLAY_ID, endX, endY)
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID_0, endX, endY)
}
companion object {
@@ -580,7 +627,8 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
private const val MIN_HEIGHT = 10
private const val DENSITY_DPI = 20
private const val DEFAULT_MIN = 40
- private const val DISPLAY_ID = 1
+ private const val DISPLAY_ID_0 = 0
+ private const val DISPLAY_ID_1 = 1
private const val NAVBAR_HEIGHT = 50
private const val CAPTION_HEIGHT = 50
private const val DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT = 10
diff --git a/libs/androidfw/ApkParsing.cpp b/libs/androidfw/ApkParsing.cpp
index 7eedfdb5c921..b80c8755f23e 100644
--- a/libs/androidfw/ApkParsing.cpp
+++ b/libs/androidfw/ApkParsing.cpp
@@ -33,7 +33,7 @@ const size_t LIB_SUFFIX_LEN = LIB_SUFFIX.size();
static const std::array<std::string_view, 2> abis = {"arm64-v8a", "x86_64"};
namespace android::util {
-const char* ValidLibraryPathLastSlash(const char* fileName, bool suppress64Bit, bool debuggable) {
+const char* ValidLibraryPathLastSlash(const char* fileName, bool suppress64Bit) {
// Make sure the filename is at least to the minimum library name size.
const size_t fileNameLen = strlen(fileName);
static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
@@ -66,14 +66,6 @@ const char* ValidLibraryPathLastSlash(const char* fileName, bool suppress64Bit,
return nullptr;
}
- if (!debuggable) {
- // Make sure the filename starts with lib and ends with ".so".
- if (strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX.data(), LIB_SUFFIX_LEN) != 0
- || strncmp(lastSlash, LIB_PREFIX.data(), LIB_PREFIX_LEN) != 0) {
- return nullptr;
- }
- }
-
// Don't include 64 bit versions if they are suppressed
if (suppress64Bit && std::find(abis.begin(), abis.end(), std::string_view(
fileName + APK_LIB_LEN, lastSlash - fileName - APK_LIB_LEN)) != abis.end()) {
diff --git a/libs/androidfw/include/androidfw/ApkParsing.h b/libs/androidfw/include/androidfw/ApkParsing.h
index 194eaae8e12a..b288e152f383 100644
--- a/libs/androidfw/include/androidfw/ApkParsing.h
+++ b/libs/androidfw/include/androidfw/ApkParsing.h
@@ -24,7 +24,7 @@ extern const size_t APK_LIB_LEN;
namespace android::util {
// Checks if filename is a valid library path and returns a pointer to the last slash in the path
// if it is, nullptr otherwise
-const char* ValidLibraryPathLastSlash(const char* filename, bool suppress64Bit, bool debuggable);
+const char* ValidLibraryPathLastSlash(const char* filename, bool suppress64Bit);
// Equivalent to android.os.FileUtils.isFilenameSafe
bool isFilenameSafe(const char* filename);
diff --git a/libs/androidfw/tests/ApkParsing_test.cpp b/libs/androidfw/tests/ApkParsing_test.cpp
index ac1dc9b88463..f1f9d7166914 100644
--- a/libs/androidfw/tests/ApkParsing_test.cpp
+++ b/libs/androidfw/tests/ApkParsing_test.cpp
@@ -27,57 +27,45 @@ using ::testing::NotNull;
namespace android {
TEST(ApkParsingTest, ValidArm64Path) {
const char* path = "lib/arm64-v8a/library.so";
- auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false);
ASSERT_THAT(lastSlash, NotNull());
ASSERT_THAT(lastSlash, Eq(path + 13));
}
TEST(ApkParsingTest, ValidArm64PathButSuppressed) {
const char* path = "lib/arm64-v8a/library.so";
- auto lastSlash = util::ValidLibraryPathLastSlash(path, true, false);
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, true);
ASSERT_THAT(lastSlash, IsNull());
}
TEST(ApkParsingTest, ValidArm32Path) {
const char* path = "lib/armeabi-v7a/library.so";
- auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false);
ASSERT_THAT(lastSlash, NotNull());
ASSERT_THAT(lastSlash, Eq(path + 15));
}
-TEST(ApkParsingTest, InvalidMustStartWithLib) {
- const char* path = "lib/arm64-v8a/random.so";
- auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
- ASSERT_THAT(lastSlash, IsNull());
-}
-
-TEST(ApkParsingTest, InvalidMustEndInSo) {
- const char* path = "lib/arm64-v8a/library.txt";
- auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
- ASSERT_THAT(lastSlash, IsNull());
-}
-
TEST(ApkParsingTest, InvalidCharacter) {
const char* path = "lib/arm64-v8a/lib#.so";
- auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false);
ASSERT_THAT(lastSlash, IsNull());
}
TEST(ApkParsingTest, InvalidSubdirectories) {
const char* path = "lib/arm64-v8a/anything/library.so";
- auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false);
ASSERT_THAT(lastSlash, IsNull());
}
TEST(ApkParsingTest, InvalidFileAtRoot) {
const char* path = "lib/library.so";
- auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false);
ASSERT_THAT(lastSlash, IsNull());
}
TEST(ApkParsingTest, InvalidPrefix) {
const char* path = "assets/libhello.so";
- auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false);
ASSERT_THAT(lastSlash, IsNull());
}
} \ No newline at end of file
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 69fe40c755ea..6ab8e4e0e2ab 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -323,6 +323,7 @@ void RenderThread::initGrContextOptions(GrContextOptions& options) {
}
void RenderThread::destroyRenderingContext() {
+ ATRACE_CALL();
mFunctorManager.onContextDestroyed();
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
if (mEglManager->hasEglContext()) {
@@ -520,7 +521,10 @@ void RenderThread::preload() {
// EGL driver is always preloaded only if HWUI renders with GL.
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
if (Properties::earlyPreloadGlContext()) {
- queue().post([this]() { requireGlContext(); });
+ queue().post([this]() {
+ ATRACE_NAME("earlyPreloadGlContext");
+ requireGlContext();
+ });
} else {
std::thread eglInitThread([]() { eglGetDisplay(EGL_DEFAULT_DISPLAY); });
eglInitThread.detach();
@@ -528,9 +532,6 @@ void RenderThread::preload() {
} else {
requireVkContext();
}
- if (Properties::earlyPreloadGlContext()) {
- queue().post([]() { GraphicBufferAllocator::getInstance(); });
- }
HardwareBitmapUploader::initialize();
}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index aeb028ccd0a6..b7269256a449 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -18,6 +18,7 @@ package android.media.quality;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -35,6 +36,8 @@ import androidx.annotation.RequiresPermission;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -74,6 +77,39 @@ public final class MediaQualityManager {
*/
public static final String OPTION_INCLUDE_PARAMETERS = "include_parameters";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({AMBIENT_BACKLIGHT_EVENT_ENABLED, AMBIENT_BACKLIGHT_EVENT_DISABLED,
+ AMBIENT_BACKLIGHT_EVENT_METADATA,
+ AMBIENT_BACKLIGHT_EVENT_INTERRUPTED})
+ public @interface AmbientBacklightEventTypes {}
+
+ /**
+ * Event type for ambient backlight events. The ambient backlight is enabled.
+ * @hide
+ */
+ public static final int AMBIENT_BACKLIGHT_EVENT_ENABLED = 1;
+
+ /**
+ * Event type for ambient backlight events. The ambient backlight is disabled.
+ * @hide
+ */
+ public static final int AMBIENT_BACKLIGHT_EVENT_DISABLED = 2;
+
+ /**
+ * Event type for ambient backlight events. The ambient backlight metadata is
+ * available.
+ * @hide
+ */
+ public static final int AMBIENT_BACKLIGHT_EVENT_METADATA = 3;
+
+ /**
+ * Event type for ambient backlight events. The ambient backlight event is
+ * preempted by another application.
+ * @hide
+ */
+ public static final int AMBIENT_BACKLIGHT_EVENT_INTERRUPTED = 4;
+
/**
* @hide
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 933c512313ce..e5b58370e6dd 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -46,6 +46,8 @@ android_library {
"SettingsLibIntroPreference",
"SettingsLibLayoutPreference",
"SettingsLibMainSwitchPreference",
+ "SettingsLibMetadata",
+ "SettingsLibPreference",
"SettingsLibProfileSelector",
"SettingsLibProgressBar",
"SettingsLibRestrictedLockUtils",
@@ -77,6 +79,7 @@ android_library {
"src/**/*.kt",
"src/**/I*.aidl",
],
+ kotlincflags: ["-Xjvm-default=all"],
}
// defaults for lint option
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index 8b29b0044eea..ce66a360a99f 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -40,7 +40,6 @@ import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceProto.ActionTarget
import com.android.settingslib.graph.proto.PreferenceScreenProto
import com.android.settingslib.graph.proto.TextProto
-import com.android.settingslib.metadata.FloatPersistentPreference
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceHierarchy
@@ -410,13 +409,13 @@ fun PreferenceMetadata.toProto(
value = preferenceValueProto {
when (metadata) {
is RangeValue -> storage.getInt(metadata.key)?.let { intValue = it }
- is FloatPersistentPreference ->
- storage.getFloat(metadata.key)?.let { floatValue = it }
else -> {}
}
when (metadata.valueType) {
Boolean::class.javaObjectType ->
storage.getBoolean(metadata.key)?.let { booleanValue = it }
+ Float::class.javaObjectType ->
+ storage.getFloat(metadata.key)?.let { floatValue = it }
}
}
}
@@ -428,12 +427,12 @@ fun PreferenceMetadata.toProto(
max = metadata.getMaxValue(context)
step = metadata.getIncrementStep(context)
}
- is FloatPersistentPreference -> floatType = true
else -> {}
}
if (metadata is PersistentPreference<*>) {
when (metadata.valueType) {
Boolean::class.javaObjectType -> booleanType = true
+ Float::class.javaObjectType -> floatType = true
}
}
}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt
new file mode 100644
index 000000000000..7323488c5299
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.metadata
+
+/** Metrics logger for preference actions triggered by user interaction. */
+interface PreferenceUiActionMetricsLogger {
+
+ /**
+ * Logs preference value change due to user interaction.
+ *
+ * Note: Preference value changed by external Set is excluded.
+ */
+ fun logPreferenceValueChange(
+ screen: PreferenceScreenMetadata,
+ preference: PreferenceMetadata,
+ value: Any?,
+ ) {}
+}
+
+/** Metrics logger for preference remote operations (e.g. external get/set). */
+interface PreferenceRemoteOpMetricsLogger
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
index 4cc65815a78a..8e850465ef7d 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
@@ -219,12 +219,3 @@ interface RangeValue : ValueDescriptor {
override fun isValidValue(context: Context, index: Int) =
index in getMinValue(context)..getMaxValue(context)
}
-
-/** A persistent preference that has a boolean value. */
-interface BooleanPreference : PersistentPreference<Boolean> {
- override val valueType: Class<Boolean>
- get() = Boolean::class.javaObjectType
-}
-
-/** A persistent preference that has a float value. */
-interface FloatPersistentPreference : PersistentPreference<Float>
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
index 9fc21343b6a0..c74b3151abb2 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -32,6 +32,9 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
*/
var preferenceScreenMetadataFactories = FixedArrayMap<String, PreferenceScreenMetadataFactory>()
+ /** Metrics logger for preference actions triggered by user interaction. */
+ var preferenceUiActionMetricsLogger: PreferenceUiActionMetricsLogger? = null
+
private var readWritePermitProvider: ReadWritePermitProvider =
object : ReadWritePermitProvider {}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
index b79a0c4f6381..623ea22410e5 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
@@ -18,8 +18,20 @@ package com.android.settingslib.metadata
import androidx.annotation.StringRes
+/** A persistent preference that has a boolean value. */
+interface BooleanValuePreference : PersistentPreference<Boolean> {
+ override val valueType: Class<Boolean>
+ get() = Boolean::class.javaObjectType
+}
+
+/** A persistent preference that has a float value. */
+interface FloatValuePreference : PersistentPreference<Float> {
+ override val valueType: Class<Float>
+ get() = Float::class.javaObjectType
+}
+
/** Common base class for preferences that have two selectable states and save a boolean value. */
-interface TwoStatePreference : PreferenceMetadata, BooleanPreference
+interface TwoStatePreference : PreferenceMetadata, BooleanValuePreference
/** A preference that provides a two-state toggleable option. */
open class SwitchPreference
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 6b7be91c1903..c61c6a5c75fa 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.preference
import android.content.Context
+import androidx.annotation.CallSuper
import androidx.preference.DialogPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
@@ -59,6 +60,7 @@ interface PreferenceBinding {
* @param preference preference widget created by [createWidget]
* @param metadata metadata to apply
*/
+ @CallSuper
fun bind(preference: Preference, metadata: PreferenceMetadata) {
metadata.apply {
preference.key = key
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
index bd5d17cb2468..b82c554ea26a 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
@@ -45,7 +45,7 @@ interface PreferenceScreenBinding : PreferenceBinding {
context.getString(screenTitle)
} else {
screenMetadata.getScreenTitle(context)
- ?: (this as? PreferenceTitleProvider)?.getTitle(context)
+ ?: (screenMetadata as? PreferenceTitleProvider)?.getTitle(context)
}
}
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreDelegate.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreDelegate.kt
new file mode 100644
index 000000000000..482eaf9146e5
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreDelegate.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.preference
+
+import androidx.preference.PreferenceDataStore
+
+/** [PreferenceDataStore] delegate. */
+open class PreferenceDataStoreDelegate(internal val delegate: PreferenceDataStore) :
+ PreferenceDataStore() {
+
+ override fun getBoolean(key: String, defValue: Boolean): Boolean =
+ delegate.getBoolean(key, defValue)
+
+ override fun getFloat(key: String, defValue: Float): Float = delegate.getFloat(key, defValue)
+
+ override fun getInt(key: String, defValue: Int): Int = delegate.getInt(key, defValue)
+
+ override fun getLong(key: String, defValue: Long): Long = delegate.getLong(key, defValue)
+
+ override fun getString(key: String, defValue: String?): String? =
+ delegate.getString(key, defValue)
+
+ override fun getStringSet(key: String, defValues: Set<String>?): Set<String>? =
+ delegate.getStringSet(key, defValues)
+
+ override fun putBoolean(key: String, value: Boolean) = delegate.putBoolean(key, value)
+
+ override fun putFloat(key: String, value: Float) = delegate.putFloat(key, value)
+
+ override fun putInt(key: String, value: Int) = delegate.putInt(key, value)
+
+ override fun putLong(key: String, value: Long) = delegate.putLong(key, value)
+
+ override fun putString(key: String, value: String?) = delegate.putString(key, value)
+
+ override fun putStringSet(key: String, values: Set<String>?) =
+ delegate.putStringSet(key, values)
+}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
index e237a6a4cf14..ffe181d0c350 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -21,6 +21,7 @@ import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.annotation.XmlRes
+import androidx.lifecycle.Lifecycle
import androidx.preference.PreferenceScreen
import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
@@ -38,6 +39,11 @@ open class PreferenceFragment :
preferenceScreen = createPreferenceScreen()
}
+ override fun setPreferenceScreen(preferenceScreen: PreferenceScreen?) {
+ super.setPreferenceScreen(preferenceScreen)
+ updateActivityTitle()
+ }
+
fun createPreferenceScreen(): PreferenceScreen? =
createPreferenceScreen(PreferenceScreenFactory(this))
@@ -102,9 +108,19 @@ open class PreferenceFragment :
override fun onResume() {
super.onResume()
+ // Even when activity has several fragments with preference screen, this will keep activity
+ // title in sync when fragment manager pops back stack.
+ updateActivityTitle()
preferenceScreenBindingHelper?.onResume()
}
+ internal fun updateActivityTitle() {
+ if (!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) return
+ val activity = activity ?: return
+ val title = preferenceScreen?.title ?: return
+ if (activity.title != title) activity.title = title
+ }
+
override fun onPause() {
preferenceScreenBindingHelper?.onPause()
super.onPause()
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
index 657f69a738f2..f3f854c0dc47 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
@@ -18,15 +18,30 @@ package com.android.settingslib.preference
import androidx.preference.PreferenceDataStore
import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceScreen
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceHierarchy
+import com.android.settingslib.metadata.PreferenceScreenMetadata
/** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */
-fun PreferenceGroup.inflatePreferenceHierarchy(
+fun PreferenceScreen.inflatePreferenceHierarchy(
preferenceBindingFactory: PreferenceBindingFactory,
hierarchy: PreferenceHierarchy,
- storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(),
+) =
+ inflatePreferenceHierarchy(
+ hierarchy.metadata as PreferenceScreenMetadata,
+ preferenceBindingFactory,
+ hierarchy,
+ mutableMapOf(),
+ )
+
+/** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */
+private fun PreferenceGroup.inflatePreferenceHierarchy(
+ preferenceScreenMetadata: PreferenceScreenMetadata,
+ preferenceBindingFactory: PreferenceBindingFactory,
+ hierarchy: PreferenceHierarchy,
+ storages: MutableMap<KeyValueStore, PreferenceDataStore>,
) {
preferenceBindingFactory.bind(this, hierarchy)
hierarchy.forEach {
@@ -38,11 +53,18 @@ fun PreferenceGroup.inflatePreferenceHierarchy(
val preferenceGroup = widget as PreferenceGroup
// MUST add preference before binding, otherwise exception is raised when add child
addPreference(preferenceGroup)
- preferenceGroup.inflatePreferenceHierarchy(preferenceBindingFactory, it)
+ preferenceGroup.inflatePreferenceHierarchy(
+ preferenceScreenMetadata,
+ preferenceBindingFactory,
+ it,
+ storages,
+ )
} else {
(metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
widget.preferenceDataStore =
- storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
+ storages.getOrPut(storage) {
+ storage.toPreferenceDataStore(preferenceScreenMetadata, metadata)
+ }
}
preferenceBindingFactory.bind(widget, it, preferenceBinding)
// MUST add preference after binding for persistent preference to get initial value
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 8358ab921fb6..4a6a589cd3c9 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -38,6 +38,7 @@ import com.android.settingslib.metadata.PreferenceHierarchyNode
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceScreenMetadata
import com.android.settingslib.metadata.PreferenceScreenRegistry
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableMultimap
@@ -51,7 +52,7 @@ import com.google.common.collect.ImmutableMultimap
*/
class PreferenceScreenBindingHelper(
context: Context,
- fragment: PreferenceFragment,
+ private val fragment: PreferenceFragment,
private val preferenceBindingFactory: PreferenceBindingFactory,
private val preferenceScreen: PreferenceScreen,
private val preferenceHierarchy: PreferenceHierarchy,
@@ -70,9 +71,7 @@ class PreferenceScreenBindingHelper(
override fun <T : Any> requirePreference(key: String) = findPreference<T>(key)!!
override fun getKeyValueStore(key: String) =
- (findPreference<Preference>(key)?.preferenceDataStore
- as? PreferenceDataStoreAdapter)
- ?.keyValueStore
+ findPreference<Preference>(key)?.preferenceDataStore?.findKeyValueStore()
override fun notifyPreferenceChange(key: String) =
notifyChange(key, PreferenceChangeReason.STATE)
@@ -137,22 +136,29 @@ class PreferenceScreenBindingHelper(
addObserver(preferenceObserver, mainExecutor)
preferenceScreen.forEachRecursively {
- val preferenceDataStore = it.preferenceDataStore
- if (preferenceDataStore is PreferenceDataStoreAdapter) {
+ it.preferenceDataStore?.findKeyValueStore()?.let { keyValueStore ->
val key = it.key
- val keyValueStore = preferenceDataStore.keyValueStore
storages[key] = keyValueStore
keyValueStore.addObserver(key, storageObserver, mainExecutor)
}
}
}
+ private fun PreferenceDataStore.findKeyValueStore(): KeyValueStore? =
+ when (this) {
+ is PreferenceDataStoreAdapter -> keyValueStore
+ is PreferenceDataStoreDelegate -> delegate.findKeyValueStore()
+ else -> null
+ }
+
private fun onPreferenceChange(key: String?, reason: Int) {
if (key == null) return
// bind preference to update UI
preferenceScreen.findPreference<Preference>(key)?.let {
- preferences[key]?.let { node -> preferenceBindingFactory.bind(it, node) }
+ val node = preferences[key] ?: return@let
+ preferenceBindingFactory.bind(it, node)
+ if (it == preferenceScreen) fragment.updateActivityTitle()
}
// check reason to avoid potential infinite loop
@@ -239,17 +245,17 @@ class PreferenceScreenBindingHelper(
preferenceBindingFactory: PreferenceBindingFactory,
preferenceHierarchy: PreferenceHierarchy,
) {
+ val preferenceScreenMetadata = preferenceHierarchy.metadata as PreferenceScreenMetadata
val preferences = mutableMapOf<String, PreferenceHierarchyNode>()
- preferenceHierarchy.forEachRecursively {
- val metadata = it.metadata
- preferences[metadata.key] = it
- }
+ preferenceHierarchy.forEachRecursively { preferences[it.metadata.key] = it }
val storages = mutableMapOf<KeyValueStore, PreferenceDataStore>()
fun Preference.setPreferenceDataStore(metadata: PreferenceMetadata) {
(metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
preferenceDataStore =
- storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
+ storages.getOrPut(storage) {
+ storage.toPreferenceDataStore(preferenceScreenMetadata, metadata)
+ }
}
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt
index 2e7221bd4d7f..f5ab4b2e38d8 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt
@@ -17,7 +17,12 @@
package com.android.settingslib.preference
import androidx.preference.Preference
+import androidx.preference.PreferenceDataStore
import androidx.preference.PreferenceGroup
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceScreenMetadata
+import com.android.settingslib.metadata.PreferenceScreenRegistry
/** Traversals preference hierarchy recursively and applies an action. */
fun PreferenceGroup.forEachRecursively(action: (Preference) -> Unit) {
@@ -31,3 +36,51 @@ fun PreferenceGroup.forEachRecursively(action: (Preference) -> Unit) {
}
}
}
+
+/**
+ * Converts [KeyValueStore] to [PreferenceDataStore].
+ *
+ * [PreferenceScreenRegistry.preferenceUiActionMetricsLogger] is wrapped on top of
+ * [PreferenceDataStoreDelegate] to log metrics.
+ *
+ * Note: Only user interaction changes are logged.
+ */
+fun KeyValueStore.toPreferenceDataStore(
+ screen: PreferenceScreenMetadata,
+ preference: PreferenceMetadata,
+): PreferenceDataStore {
+ val preferenceDataStore: PreferenceDataStore = PreferenceDataStoreAdapter(this)
+ val metricsLogger =
+ PreferenceScreenRegistry.preferenceUiActionMetricsLogger ?: return preferenceDataStore
+ return object : PreferenceDataStoreDelegate(preferenceDataStore) {
+ override fun putBoolean(key: String, value: Boolean) {
+ super.putBoolean(key, value)
+ metricsLogger.logPreferenceValueChange(screen, preference, value)
+ }
+
+ override fun putFloat(key: String, value: Float) {
+ super.putFloat(key, value)
+ metricsLogger.logPreferenceValueChange(screen, preference, value)
+ }
+
+ override fun putInt(key: String, value: Int) {
+ super.putInt(key, value)
+ metricsLogger.logPreferenceValueChange(screen, preference, value)
+ }
+
+ override fun putLong(key: String, value: Long) {
+ super.putLong(key, value)
+ metricsLogger.logPreferenceValueChange(screen, preference, value)
+ }
+
+ override fun putString(key: String, value: String?) {
+ super.putString(key, value)
+ metricsLogger.logPreferenceValueChange(screen, preference, value)
+ }
+
+ override fun putStringSet(key: String, values: Set<String>?) {
+ super.putStringSet(key, values)
+ metricsLogger.logPreferenceValueChange(screen, preference, values)
+ }
+ }
+}
diff --git a/packages/SettingsLib/Preference/testutils/Android.bp b/packages/SettingsLib/Preference/testutils/Android.bp
index 5287d6ce93f1..68b724280ac1 100644
--- a/packages/SettingsLib/Preference/testutils/Android.bp
+++ b/packages/SettingsLib/Preference/testutils/Android.bp
@@ -27,6 +27,7 @@ android_library {
"androidx.test.core",
"androidx.test.ext.junit",
"flag-junit",
+ "mockito-kotlin2",
"truth",
],
}
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 220614bf064f..172c68a4bd37 100644
--- a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt
+++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt
@@ -22,6 +22,8 @@ import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceScreenMetadata
+import org.mockito.kotlin.mock
/** Creates [Preference] widget and binds with metadata. */
@Suppress("UNCHECKED_CAST")
@@ -29,13 +31,13 @@ import com.android.settingslib.metadata.PreferenceMetadata
fun <P : Preference> PreferenceMetadata.createAndBindWidget(
context: Context,
preferenceScreen: PreferenceScreen? = null,
+ preferenceScreenMetadata: PreferenceScreenMetadata = mock(),
): P {
val binding = PreferenceBindingFactory.defaultFactory.getPreferenceBinding(this)!!
return (binding.createWidget(context) as P).also {
if (this is PersistentPreference<*>) {
- storage(context).let { keyValueStore ->
- it.preferenceDataStore = PreferenceDataStoreAdapter(keyValueStore)
- }
+ it.preferenceDataStore =
+ storage(context).toPreferenceDataStore(preferenceScreenMetadata, this)
// Attach preference to preference screen, otherwise `Preference.performClick` does not
// interact with underlying datastore
(preferenceScreen ?: PreferenceScreenFactory(context).getOrCreatePreferenceScreen())
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
index 2776544e2948..7d7bec14ed78 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
@@ -48,6 +48,7 @@
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@android:id/title"
app:layout_constraintStart_toStartOf="parent"
+ android:paddingTop="@dimen/settingslib_expressive_space_extrasmall6"
android:textAlignment="viewStart"
android:clickable="true"
android:visibility="gone"
diff --git a/packages/SettingsLib/ZeroStatePreference/Android.bp b/packages/SettingsLib/ZeroStatePreference/Android.bp
index 4fc00bdbfee0..0949e2c75c55 100644
--- a/packages/SettingsLib/ZeroStatePreference/Android.bp
+++ b/packages/SettingsLib/ZeroStatePreference/Android.bp
@@ -29,5 +29,6 @@ android_library {
min_sdk_version: "28",
apex_available: [
"//apex_available:platform",
+ "com.android.healthfitness",
],
}
diff --git a/packages/SettingsLib/ZeroStatePreference/res/layout/settingslib_expressive_preference_zerostate.xml b/packages/SettingsLib/ZeroStatePreference/res/layout/settingslib_expressive_preference_zerostate.xml
index c0b195cc1f74..ae3f1dde8a3a 100644
--- a/packages/SettingsLib/ZeroStatePreference/res/layout/settingslib_expressive_preference_zerostate.xml
+++ b/packages/SettingsLib/ZeroStatePreference/res/layout/settingslib_expressive_preference_zerostate.xml
@@ -17,7 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
diff --git a/packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt b/packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt
new file mode 100644
index 000000000000..a64e8cc07b15
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("ktlint:standard:filename") // remove once we have more bindings
+
+package com.android.settingslib
+
+import android.content.Context
+import androidx.preference.Preference
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.preference.PreferenceBinding
+
+/** Preference binding for [PrimarySwitchPreference]. */
+interface PrimarySwitchPreferenceBinding : PreferenceBinding {
+
+ override fun createWidget(context: Context): Preference = PrimarySwitchPreference(context)
+
+ override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ super.bind(preference, metadata)
+ (preference as PrimarySwitchPreference).apply {
+ isChecked = preferenceDataStore!!.getBoolean(key, false)
+ isSwitchEnabled = isEnabled
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
index 5bcdcc09206b..d9e79fa7c9dc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -52,6 +52,14 @@ import java.util.concurrent.Executors;
* BluetoothLeBroadcastAssistant.Callback} to get the result callback.
*/
public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile {
+ /** A derived source state based on {@link BluetoothLeBroadcastReceiveState}. */
+ public enum LocalBluetoothLeBroadcastSourceState {
+ UNKNOWN,
+ STREAMING,
+ DECRYPTION_FAILED,
+ PAUSED,
+ }
+
private static final String TAG = "LocalBluetoothLeBroadcastAssistant";
private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
private static final boolean DEBUG = BluetoothUtils.D;
@@ -59,6 +67,13 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile
static final String NAME = "LE_AUDIO_BROADCAST_ASSISTANT";
// Order of this profile in device profiles list
private static final int ORDINAL = 1;
+ // Referring to Broadcast Audio Scan Service 1.0
+ // Table 3.9: Broadcast Receive State characteristic format
+ // 0x00000000: 0b0 = Not synchronized to BIS_index[x]
+ // 0xFFFFFFFF: Failed to sync to BIG
+ private static final long BIS_SYNC_NOT_SYNC_TO_BIS = 0x00000000L;
+ private static final long BIS_SYNC_FAILED_SYNC_TO_BIG = 0xFFFFFFFFL;
+ private static final String EMPTY_DEVICE_ADDRESS = "00:00:00:00:00:00";
private LocalBluetoothProfileManager mProfileManager;
private BluetoothLeBroadcastAssistant mService;
@@ -558,4 +573,30 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile
}
}
}
+
+ /** Checks the source connection status based on the provided broadcast receive state. */
+ public static LocalBluetoothLeBroadcastSourceState getLocalSourceState(
+ BluetoothLeBroadcastReceiveState state) {
+ // Source is actively streaming
+ if (state.getBisSyncState().stream()
+ .anyMatch(
+ bitmap ->
+ (bitmap != BIS_SYNC_NOT_SYNC_TO_BIS
+ && bitmap != BIS_SYNC_FAILED_SYNC_TO_BIG))) {
+ return LocalBluetoothLeBroadcastSourceState.STREAMING;
+ }
+ // Wrong password is used for the source
+ if (state.getPaSyncState() == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED
+ && state.getBigEncryptionState()
+ == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE) {
+ return LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED;
+ }
+ // Source in hysteresis mode
+ if (!state.getSourceDevice().getAddress().equals(EMPTY_DEVICE_ADDRESS)) {
+ // Referring to Broadcast Audio Scan Service 1.0
+ // All zero address means no source on the sink device
+ return LocalBluetoothLeBroadcastSourceState.PAUSED;
+ }
+ return LocalBluetoothLeBroadcastSourceState.UNKNOWN;
+ }
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b0309a8fa5a5..6681c014f2e0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -454,5 +454,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
new InclusiveIntegerRangeValidator(0, 1));
VALIDATORS.put(Secure.ADVANCED_PROTECTION_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index cbdb36fff98c..9aad5d5f8367 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -689,6 +689,7 @@ public class SettingsBackupTest {
Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD,
Settings.Secure.DEVICE_PAIRED,
Settings.Secure.DIALER_DEFAULT_APPLICATION,
+ Settings.Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK,
Settings.Secure.DISABLED_PRINT_SERVICES,
Settings.Secure.DISABLE_SECURE_WINDOWS,
Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2d68ab8ff451..9531bc38e797 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1930,3 +1930,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "magnetic_notification_horizontal_swipe"
+ namespace: "systemui"
+ description: "Add support for magnetic behavior on horizontal notification swipes."
+ bug: "390179908"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
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 8a57e8cbbb20..f36f0306d82b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -27,6 +27,7 @@ import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRAN
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.wm.shell.shared.TransitionUtil.FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY;
import static com.android.wm.shell.shared.TransitionUtil.isClosingMode;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode;
@@ -270,7 +271,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() != null && change.getTaskInfo().isFreeform()
+ if (change.getTaskInfo() != null && (change.getTaskInfo().isFreeform()
+ || change.hasFlags(FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY))
&& isClosingMode(change.getMode())) {
startTransaction.setAlpha(leashMap.get(launcherChange.getLeash()), 0f);
return;
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index 439968590dad..d43b596b32f1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -87,10 +87,10 @@ import androidx.compose.ui.unit.times
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformButton
import com.android.compose.animation.Easings
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
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.transitions
import com.android.compose.windowsizeclass.LocalWindowSizeClass
@@ -515,7 +515,7 @@ private fun FoldAware(
}
@Composable
-private fun SceneScope.FoldableScene(
+private fun ContentScope.FoldableScene(
aboveFold: @Composable BoxScope.() -> Unit,
belowFold: @Composable BoxScope.() -> Unit,
isSplit: Boolean,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 55b42931b1fa..fad8ae7e3ba2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -24,8 +24,8 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.bouncer.ui.BouncerDialogFactory
@@ -73,7 +73,7 @@ constructor(
}
@Composable
- override fun SceneScope.Content(modifier: Modifier) =
+ override fun ContentScope.Content(modifier: Modifier) =
BouncerScene(
viewModel = rememberViewModel("BouncerScene") { contentViewModelFactory.create() },
dialogFactory = dialogFactory,
@@ -82,7 +82,7 @@ constructor(
}
@Composable
-private fun SceneScope.BouncerScene(
+private fun ContentScope.BouncerScene(
viewModel: BouncerSceneContentViewModel,
dialogFactory: BouncerDialogFactory,
modifier: Modifier = Modifier,
@@ -96,8 +96,8 @@ private fun SceneScope.BouncerScene(
drawRect(color = backgroundColor)
}
- // Separate the bouncer content into a reusable composable that doesn't have any SceneScope
- // dependencies
+ // Separate the bouncer content into a reusable composable that doesn't have any
+ // ContentScope dependencies
BouncerContent(
viewModel,
dialogFactory,
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 a2a91fcd5d52..9b5ff7f9e7e8 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
@@ -33,13 +33,13 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
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.observableTransitionState
@@ -229,7 +229,7 @@ fun CommunalContainer(
/** Scene containing the glanceable hub UI. */
@Composable
-fun SceneScope.CommunalScene(
+fun ContentScope.CommunalScene(
backgroundType: CommunalBackgroundType,
colors: CommunalColors,
content: CommunalContent,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 0a0003ee9a8a..fea34921b853 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -31,7 +31,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
@@ -65,7 +65,7 @@ constructor(
) {
@Composable
- fun SceneScope.Content(modifier: Modifier = Modifier) {
+ fun ContentScope.Content(modifier: Modifier = Modifier) {
CommunalTouchableSurface(viewModel = viewModel, modifier = modifier) {
Layout(
modifier = Modifier.fillMaxSize(),
@@ -81,7 +81,7 @@ constructor(
dialogFactory = dialogFactory,
widgetSection = widgetSection,
modifier = Modifier.element(Communal.Elements.Grid),
- sceneScope = this@Content,
+ contentScope = this@Content,
)
}
if (communalSettingsInteractor.isV2FlagEnabled()) {
@@ -193,6 +193,7 @@ constructor(
companion object {
private val screensaverButtonSize: Dp = 64.dp
private val screensaverButtonPadding: Dp = 24.dp
+
// TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
// position are sorted.
private val lockIconSize: Dp = 54.dp
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 068df8e31ed0..70a74f064563 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -171,7 +171,7 @@ import androidx.compose.ui.zIndex
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.window.layout.WindowMetricsCalculator
import com.android.compose.animation.Easings.Emphasized
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.internal.R.dimen.system_app_widget_background_radius
@@ -217,7 +217,7 @@ fun CommunalHub(
widgetConfigurator: WidgetConfigurator? = null,
onOpenWidgetPicker: (() -> Unit)? = null,
onEditDone: (() -> Unit)? = null,
- sceneScope: SceneScope? = null,
+ contentScope: ContentScope? = null,
) {
val communalContent by
viewModel.communalContent.collectAsStateWithLifecycle(initialValue = emptyList())
@@ -437,7 +437,7 @@ fun CommunalHub(
widgetConfigurator = widgetConfigurator,
interactionHandler = interactionHandler,
widgetSection = widgetSection,
- sceneScope = sceneScope,
+ contentScope = contentScope,
)
}
}
@@ -827,7 +827,7 @@ private fun BoxScope.CommunalHubLazyGrid(
widgetConfigurator: WidgetConfigurator?,
interactionHandler: RemoteViews.InteractionHandler?,
widgetSection: CommunalAppWidgetSection,
- sceneScope: SceneScope?,
+ contentScope: ContentScope?,
) {
var gridModifier =
Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
@@ -1009,7 +1009,7 @@ private fun BoxScope.CommunalHubLazyGrid(
interactionHandler = interactionHandler,
widgetSection = widgetSection,
resizeableItemFrameViewModel = resizeableItemFrameViewModel,
- sceneScope = sceneScope,
+ contentScope = contentScope,
)
}
}
@@ -1261,7 +1261,7 @@ private fun CommunalContent(
interactionHandler: RemoteViews.InteractionHandler?,
widgetSection: CommunalAppWidgetSection,
resizeableItemFrameViewModel: ResizeableItemFrameViewModel,
- sceneScope: SceneScope? = null,
+ contentScope: ContentScope? = null,
) {
when (model) {
is CommunalContentModel.WidgetContent.Widget ->
@@ -1285,7 +1285,7 @@ private fun CommunalContent(
is CommunalContentModel.CtaTileInViewMode -> CtaTileInViewModeContent(viewModel, modifier)
is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
- is CommunalContentModel.Umo -> Umo(viewModel, sceneScope, modifier)
+ is CommunalContentModel.Umo -> Umo(viewModel, contentScope, modifier)
is CommunalContentModel.Spacer -> Box(Modifier.fillMaxSize())
}
}
@@ -1451,7 +1451,6 @@ private fun WidgetContent(
} else {
Modifier
}
-
Box(
modifier =
modifier
@@ -1539,7 +1538,10 @@ private fun WidgetContent(
with(widgetSection) {
Widget(
isFocusable = isFocusable,
- openWidgetEditor = { viewModel.onOpenWidgetEditor() },
+ openWidgetEditor = {
+ viewModel.setSelectedKey(model.key)
+ viewModel.onOpenWidgetEditor()
+ },
model = model,
size = size,
modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
@@ -1701,11 +1703,11 @@ private fun TutorialContent(modifier: Modifier = Modifier) {
@Composable
private fun Umo(
viewModel: BaseCommunalViewModel,
- sceneScope: SceneScope?,
+ contentScope: ContentScope?,
modifier: Modifier = Modifier,
) {
- if (SceneContainerFlag.isEnabled && sceneScope != null) {
- sceneScope.MediaCarousel(
+ if (SceneContainerFlag.isEnabled && contentScope != null) {
+ contentScope.MediaCarousel(
modifier = modifier.fillMaxSize(),
isVisible = true,
mediaHost = viewModel.mediaHost,
@@ -1788,6 +1790,7 @@ fun AccessibilityContainer(viewModel: BaseCommunalViewModel, content: @Composabl
CustomAccessibilityAction(
context.getString(R.string.accessibility_action_label_edit_widgets)
) {
+ viewModel.setSelectedKey(null)
viewModel.onOpenWidgetEditor()
true
},
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 88b651019c4a..143fbe4de550 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.communal.shared.model.CommunalBackgroundType
@@ -55,7 +55,7 @@ constructor(
}
@Composable
- override fun SceneScope.Content(modifier: Modifier) {
+ override fun ContentScope.Content(modifier: Modifier) {
val backgroundType by
contentViewModel.communalBackground.collectAsStateWithLifecycle(
initialValue = CommunalBackgroundType.ANIMATED
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt
index 3b335fa3141e..1b0ddcb13ee2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt
@@ -22,7 +22,7 @@ import android.widget.FrameLayout
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent
import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView
import com.android.systemui.communal.ui.compose.Communal
@@ -31,11 +31,9 @@ import javax.inject.Inject
class AmbientStatusBarSection
@Inject
-constructor(
- private val factory: AmbientStatusBarComponent.Factory,
-) {
+constructor(private val factory: AmbientStatusBarComponent.Factory) {
@Composable
- fun SceneScope.AmbientStatusBar(modifier: Modifier = Modifier) {
+ fun ContentScope.AmbientStatusBar(modifier: Modifier = Modifier) {
AndroidView(
factory = { context ->
(LayoutInflater.from(context)
@@ -49,7 +47,7 @@ constructor(
factory.create(this).getController().apply { init() }
}
},
- modifier = modifier.element(Communal.Elements.StatusBar)
+ modifier = modifier.element(Communal.Elements.StatusBar),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/dream/ui/composable/DreamScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/dream/ui/composable/DreamScene.kt
index f4374c6c9487..6cd0c5dfd15c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/dream/ui/composable/DreamScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/dream/ui/composable/DreamScene.kt
@@ -24,7 +24,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.dagger.SysUISingleton
@@ -54,7 +54,7 @@ constructor(private val actionsViewModelFactory: DreamUserActionsViewModel.Facto
}
@Composable
- override fun SceneScope.Content(modifier: Modifier) {
+ override fun ContentScope.Content(modifier: Modifier) {
Box(modifier = modifier.fillMaxSize()) {
// Render a sleep emoji to make the scene appear visible.
Text(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 5c5514aec03e..7b2f9dc76158 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -24,7 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
@@ -50,7 +50,7 @@ class LockscreenContent(
}
@Composable
- fun SceneScope.Content(modifier: Modifier = Modifier) {
+ fun ContentScope.Content(modifier: Modifier = Modifier) {
val viewModel =
rememberViewModel("LockscreenContent-viewModel") { viewModelFactory.create() }
val notificationLockscreenScrimViewModel =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index c7c29f9fdb7c..5e61af634bbc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.composable
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateContentFloatAsState
@@ -54,18 +54,13 @@ constructor(
}
@Composable
- override fun SceneScope.Content(
- modifier: Modifier,
- ) {
- LockscreenScene(
- lockscreenContent = lockscreenContent,
- modifier = modifier,
- )
+ override fun ContentScope.Content(modifier: Modifier) {
+ LockscreenScene(lockscreenContent = lockscreenContent, modifier = modifier)
}
}
@Composable
-private fun SceneScope.LockscreenScene(
+private fun ContentScope.LockscreenScene(
lockscreenContent: Lazy<LockscreenContent>,
modifier: Modifier = Modifier,
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
index adad4468b751..c365ec590a5f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
@@ -23,7 +23,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import dagger.Binds
@@ -37,14 +37,8 @@ class CommunalBlueprint @Inject constructor() : ComposableLockscreenSceneBluepri
override val id: String = "communal"
@Composable
- override fun SceneScope.Content(
- viewModel: LockscreenContentViewModel,
- modifier: Modifier,
- ) {
- LockscreenLongPress(
- viewModel = viewModel.touchHandling,
- modifier = modifier,
- ) { _ ->
+ override fun ContentScope.Content(viewModel: LockscreenContentViewModel, modifier: Modifier) {
+ LockscreenLongPress(viewModel = viewModel.touchHandling, modifier = modifier) { _ ->
Box(modifier.background(Color.Black)) {
Text(
text = "TODO(b/316211368): communal blueprint",
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt
index df36d0774f11..cfafb6214c45 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt
@@ -18,16 +18,12 @@ package com.android.systemui.keyguard.ui.composable.blueprint
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
/** Defines interface for classes that can render the content for a specific blueprint/layout. */
interface ComposableLockscreenSceneBlueprint : LockscreenSceneBlueprint {
/** Renders the content of this blueprint. */
- @Composable
- fun SceneScope.Content(
- viewModel: LockscreenContentViewModel,
- modifier: Modifier,
- )
+ @Composable fun ContentScope.Content(viewModel: LockscreenContentViewModel, modifier: Modifier)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 9643f192e066..c55a3fdfc6c0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -32,7 +32,7 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.padding
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
@@ -68,7 +68,7 @@ constructor(
override val id: String = "default"
@Composable
- override fun SceneScope.Content(viewModel: LockscreenContentViewModel, modifier: Modifier) {
+ override fun ContentScope.Content(viewModel: LockscreenContentViewModel, modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
val isShadeLayoutWide by viewModel.isShadeLayoutWide.collectAsStateWithLifecycle()
val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt
index af9a195c4575..99a7633e3cd3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt
@@ -18,9 +18,9 @@ package com.android.systemui.keyguard.ui.composable.section
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
/** Defines interface for classes that can render the ambient indication area. */
interface AmbientIndicationSection {
- @Composable fun SceneScope.AmbientIndication(modifier: Modifier)
+ @Composable fun ContentScope.AmbientIndication(modifier: Modifier)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 5e9ade163ac2..52ccab3b4d1e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -30,8 +30,8 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.res.ResourcesCompat
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.SceneScope
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
@@ -61,7 +61,7 @@ constructor(
* shortcut is placed along the edges of the display.
*/
@Composable
- fun SceneScope.Shortcut(
+ fun ContentScope.Shortcut(
isStart: Boolean,
applyPadding: Boolean,
modifier: Modifier = Modifier,
@@ -89,7 +89,7 @@ constructor(
}
@Composable
- fun SceneScope.IndicationArea(modifier: Modifier = Modifier) {
+ fun ContentScope.IndicationArea(modifier: Modifier = Modifier) {
Element(key = IndicationAreaElementKey, modifier = modifier.indicationAreaPadding()) {
content {
IndicationArea(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index fb01e7039edd..34c0bcaca997 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -32,7 +32,7 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.contains
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.padding
import com.android.systemui.customization.R
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
@@ -54,7 +54,7 @@ constructor(
private val aodBurnInViewModel: AodBurnInViewModel,
) {
@Composable
- fun SceneScope.SmallClock(
+ fun ContentScope.SmallClock(
burnInParams: BurnInParameters,
onTopChanged: (top: Float?) -> Unit,
modifier: Modifier = Modifier,
@@ -87,7 +87,7 @@ constructor(
}
@Composable
- fun SceneScope.LargeClock(burnInParams: BurnInParameters, modifier: Modifier = Modifier) {
+ fun ContentScope.LargeClock(burnInParams: BurnInParameters, modifier: Modifier = Modifier) {
val currentClock by viewModel.currentClock.collectAsStateWithLifecycle()
if (currentClock?.largeClock?.view == null) {
return
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 597cbf24729b..4795e7cef87c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -28,8 +28,8 @@ import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.SceneScope
import com.android.systemui.biometrics.AuthController
import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.qualifiers.Application
@@ -66,7 +66,7 @@ constructor(
@LongPressTouchLog private val logBuffer: LogBuffer,
) {
@Composable
- fun SceneScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) {
+ fun ContentScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) {
val context = LocalContext.current
AndroidView(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
index 4a9f44b74099..0ff567bf90ad 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
@@ -23,7 +23,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.keyguard.ui.viewmodel.KeyguardMediaViewModel
import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -42,7 +42,7 @@ constructor(
) {
@Composable
- fun SceneScope.KeyguardMediaCarousel(
+ fun ContentScope.KeyguardMediaCarousel(
isShadeLayoutWide: Boolean,
modifier: Modifier = Modifier,
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 0344ab8e0196..2bc392d386bf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -34,7 +34,7 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.thenIf
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
@@ -148,7 +148,7 @@ constructor(
}
@Composable
- fun SceneScope.HeadsUpNotifications() {
+ fun ContentScope.HeadsUpNotifications() {
SnoozeableHeadsUpNotificationSpace(
stackScrollView = stackScrollView.get(),
viewModel = rememberViewModel("HeadsUpNotifications") { viewModelFactory.create() },
@@ -160,7 +160,7 @@ constructor(
* adjustment
*/
@Composable
- fun SceneScope.Notifications(
+ fun ContentScope.Notifications(
areNotificationsVisible: Boolean,
isShadeLayoutWide: Boolean,
burnInParams: BurnInParameters?,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index 1cee4d67df3b..c3ba7ab2fd19 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -35,7 +35,7 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.padding
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
@@ -57,7 +57,7 @@ constructor(
private val aodBurnInViewModel: AodBurnInViewModel,
) {
@Composable
- fun SceneScope.SmartSpace(
+ fun ContentScope.SmartSpace(
burnInParams: BurnInParameters,
onTopChanged: (top: Float?) -> Unit,
smartSpacePaddingTop: (Resources) -> Int,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
index 0d8a47019a08..172c3f5a1135 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
@@ -26,7 +26,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.height
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
@@ -45,9 +45,10 @@ constructor(
private val notificationPanelView: Lazy<NotificationPanelView>,
) {
@Composable
- fun SceneScope.StatusBar(modifier: Modifier = Modifier) {
+ fun ContentScope.StatusBar(modifier: Modifier = Modifier) {
val context = LocalContext.current
val viewDisplayCutout = LocalDisplayCutout.current.viewDisplayCutoutKeyguardStatusBarView
+
@SuppressLint("InflateParams")
val view =
remember(context) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
index 73c4fab7b646..6250da379402 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -30,9 +30,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.padding
import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
@@ -50,15 +49,10 @@ constructor(
private val aodBurnInViewModel: AodBurnInViewModel,
) {
@Composable
- fun SceneScope.Time(
- clock: ClockController,
- burnInParams: BurnInParameters,
- ) {
+ fun ContentScope.Time(clock: ClockController, burnInParams: BurnInParameters) {
Row(
modifier =
- Modifier.padding(
- horizontal = dimensionResource(customR.dimen.clock_padding_start)
- )
+ Modifier.padding(horizontal = dimensionResource(customR.dimen.clock_padding_start))
.burnInAware(aodBurnInViewModel, burnInParams, isClock = true)
) {
WeatherElement(
@@ -70,10 +64,7 @@ constructor(
}
@Composable
- private fun SceneScope.Date(
- clock: ClockController,
- modifier: Modifier = Modifier,
- ) {
+ private fun ContentScope.Date(clock: ClockController, modifier: Modifier = Modifier) {
WeatherElement(
weatherClockElementViewId = customR.id.weather_clock_date,
clock = clock,
@@ -83,10 +74,7 @@ constructor(
}
@Composable
- private fun SceneScope.Weather(
- clock: ClockController,
- modifier: Modifier = Modifier,
- ) {
+ private fun ContentScope.Weather(clock: ClockController, modifier: Modifier = Modifier) {
WeatherElement(
weatherClockElementViewId = customR.id.weather_clock_weather_icon,
clock = clock,
@@ -96,10 +84,7 @@ constructor(
}
@Composable
- private fun SceneScope.DndAlarmStatus(
- clock: ClockController,
- modifier: Modifier = Modifier,
- ) {
+ private fun ContentScope.DndAlarmStatus(clock: ClockController, modifier: Modifier = Modifier) {
WeatherElement(
weatherClockElementViewId = customR.id.weather_clock_alarm_dnd,
clock = clock,
@@ -109,10 +94,7 @@ constructor(
}
@Composable
- private fun SceneScope.Temperature(
- clock: ClockController,
- modifier: Modifier = Modifier,
- ) {
+ private fun ContentScope.Temperature(clock: ClockController, modifier: Modifier = Modifier) {
WeatherElement(
weatherClockElementViewId = customR.id.weather_clock_temperature,
clock = clock,
@@ -122,7 +104,7 @@ constructor(
}
@Composable
- private fun SceneScope.WeatherElement(
+ private fun ContentScope.WeatherElement(
weatherClockElementViewId: Int,
clock: ClockController,
elementKey: ElementKey,
@@ -144,32 +126,28 @@ constructor(
}
},
update = {},
- modifier = modifier
+ modifier = modifier,
)
}
}
}
@Composable
- fun SceneScope.LargeClockSectionBelowSmartspace(
+ fun ContentScope.LargeClockSectionBelowSmartspace(
burnInParams: BurnInParameters,
clock: ClockController,
) {
Row(
modifier =
Modifier.height(IntrinsicSize.Max)
- .padding(
- horizontal = dimensionResource(customR.dimen.clock_padding_start)
- )
+ .padding(horizontal = dimensionResource(customR.dimen.clock_padding_start))
.burnInAware(aodBurnInViewModel, burnInParams, isClock = true)
) {
Date(clock = clock, modifier = Modifier.wrapContentSize())
Box(
modifier =
Modifier.fillMaxSize()
- .padding(
- start = dimensionResource(customR.dimen.clock_padding_start)
- )
+ .padding(start = dimensionResource(customR.dimen.clock_padding_start))
) {
Weather(clock = clock, modifier = Modifier.align(Alignment.TopStart))
Temperature(clock = clock, modifier = Modifier.align(Alignment.BottomEnd))
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index b5d78398028d..f5de7dca6d9d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -31,8 +31,8 @@ import androidx.compose.ui.layout.layout
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.MovableElementKey
-import com.android.compose.animation.scene.SceneScope
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.media.controls.ui.composable.MediaCarouselStateLoader.stateForMediaCarouselContent
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -52,7 +52,7 @@ object MediaCarousel {
}
@Composable
-fun SceneScope.MediaCarousel(
+fun ContentScope.MediaCarousel(
isVisible: Boolean,
mediaHost: MediaHost,
modifier: Modifier = Modifier,
@@ -136,6 +136,6 @@ private fun ViewGroup.setView(view: View) {
}
@Composable
-fun SceneScope.isLandscape(): Boolean {
+fun ContentScope.isLandscape(): Boolean {
return LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt
index bad74052b669..525284207744 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt
@@ -17,8 +17,8 @@
package com.android.systemui.media.controls.ui.composable
import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
@@ -50,6 +50,7 @@ object MediaCarouselStateLoader {
if (isSplitShade) MediaHierarchyManager.LOCATION_QS
else MediaHierarchyManager.LOCATION_QQS
}
+
Scenes.Lockscreen -> MediaHierarchyManager.LOCATION_LOCKSCREEN
Scenes.Communal -> MediaHierarchyManager.LOCATION_COMMUNAL_HUB
else -> MediaHierarchyManager.LOCATION_UNKNOWN
@@ -69,6 +70,7 @@ object MediaCarouselStateLoader {
/** State for media carousel. */
sealed interface State {
val transitionProgress: Float
+
// TODO b/368368388: implement media squishiness
val squishFraction: () -> Float
@MediaLocation val startLocation: Int
@@ -100,7 +102,7 @@ object MediaCarouselStateLoader {
}
/** Returns the state of media carousel */
- fun SceneScope.stateForMediaCarouselContent(isInSplitShade: Boolean): State {
+ fun ContentScope.stateForMediaCarouselContent(isInSplitShade: Boolean): State {
return when (val transitionState = layoutState.transitionState) {
is TransitionState.Idle -> {
if (MediaContentPicker.contents.contains(transitionState.currentScene)) {
@@ -109,6 +111,7 @@ object MediaCarouselStateLoader {
State.Gone
}
}
+
is TransitionState.Transition.ChangeScene ->
with(transitionState) {
if (
@@ -130,6 +133,7 @@ object MediaCarouselStateLoader {
State.Gone
}
}
+
is TransitionState.Transition.OverlayTransition ->
with(transitionState) {
if (
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 7f7273d710a1..25b673bee1cd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -72,7 +72,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.animation.Expandable
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.fadingBackground
import com.android.compose.theme.colorAttr
import com.android.systemui.Flags.notificationShadeBlur
@@ -91,7 +91,7 @@ import com.android.systemui.res.R
import kotlinx.coroutines.launch
@Composable
-fun SceneScope.FooterActionsWithAnimatedVisibility(
+fun ContentScope.FooterActionsWithAnimatedVisibility(
viewModel: FooterActionsViewModel,
isCustomizing: Boolean,
customizingAnimationDuration: Int,
@@ -256,6 +256,7 @@ private fun RowScope.ForegroundServicesButton(
} else {
NumberButton(
model.foregroundServicesCount,
+ contentDescription = model.text,
showNewDot = model.hasNewChanges,
onClick = model.onClick,
)
@@ -284,6 +285,7 @@ fun IconButton(model: FooterActionsButtonViewModel, modifier: Modifier = Modifie
@Composable
private fun NumberButton(
number: Int,
+ contentDescription: String,
showNewDot: Boolean,
onClick: (Expandable) -> Unit,
modifier: Modifier = Modifier,
@@ -308,14 +310,16 @@ private fun NumberButton(
) {
Box(Modifier.size(40.dp)) {
Box(
- Modifier
- .fillMaxSize()
+ Modifier.fillMaxSize()
.clip(CircleShape)
.indication(interactionSource, LocalIndication.current)
) {
Text(
number.toString(),
- modifier = Modifier.align(Alignment.Center),
+ modifier =
+ Modifier.align(Alignment.Center).semantics {
+ this.contentDescription = contentDescription
+ },
style = MaterialTheme.typography.bodyLarge,
color = colorAttr(R.attr.onShadeInactiveVariant),
// TODO(b/242040009): This should only use a standard text style instead and
@@ -337,9 +341,7 @@ private fun NewChangesDot(modifier: Modifier = Modifier) {
val contentDescription = stringResource(R.string.fgs_dot_content_description)
val color = MaterialTheme.colorScheme.tertiary
- Canvas(modifier
- .size(12.dp)
- .semantics { this.contentDescription = contentDescription }) {
+ Canvas(modifier.size(12.dp).semantics { this.contentDescription = contentDescription }) {
drawCircle(color)
}
}
@@ -368,9 +370,7 @@ private fun TextButton(
Modifier.padding(horizontal = dimensionResource(R.dimen.qs_footer_padding)),
verticalAlignment = Alignment.CenterVertically,
) {
- Icon(icon, Modifier
- .padding(end = 12.dp)
- .size(20.dp))
+ Icon(icon, Modifier.padding(end = 12.dp).size(20.dp))
Text(
text,
@@ -391,9 +391,7 @@ private fun TextButton(
Icon(
painterResource(com.android.internal.R.drawable.ic_chevron_end),
contentDescription = null,
- Modifier
- .padding(start = 8.dp)
- .size(20.dp),
+ Modifier.padding(start = 8.dp).size(20.dp),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 58336c2e9d41..b826187578e0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -36,10 +36,10 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.MovableElementContentPicker
import com.android.compose.animation.scene.MovableElementKey
-import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayoutState
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.content.state.TransitionState
@@ -98,7 +98,7 @@ object QuickSettings {
}
}
-private fun SceneScope.stateForQuickSettingsContent(
+private fun ContentScope.stateForQuickSettingsContent(
isSplitShade: Boolean,
squishiness: () -> Float = { QuickSettings.SharedValues.SquishinessValues.Default },
): QSSceneAdapter.State {
@@ -141,7 +141,7 @@ private fun SceneScope.stateForQuickSettingsContent(
/**
* This composable will show QuickSettingsContent in the correct state (as determined by its
- * [SceneScope]).
+ * [ContentScope]).
*
* If adding to scenes not in:
* * QuickSettingsScene
@@ -153,7 +153,7 @@ private fun SceneScope.stateForQuickSettingsContent(
* * this doc.
*/
@Composable
-fun SceneScope.QuickSettings(
+fun ContentScope.QuickSettings(
qsSceneAdapter: QSSceneAdapter,
heightProvider: () -> Int,
isSplitShade: Boolean,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 26cf7066124a..4bfbb3a908fa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -68,7 +68,7 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateSceneDpAsState
@@ -144,7 +144,7 @@ constructor(
}
@Composable
- override fun SceneScope.Content(modifier: Modifier) {
+ override fun ContentScope.Content(modifier: Modifier) {
QuickSettingsScene(
notificationStackScrollView = notificationStackScrollView.get(),
viewModelFactory = contentViewModelFactory,
@@ -164,7 +164,7 @@ constructor(
}
@Composable
-private fun SceneScope.QuickSettingsScene(
+private fun ContentScope.QuickSettingsScene(
notificationStackScrollView: NotificationScrollView,
viewModelFactory: QuickSettingsSceneContentViewModel.Factory,
notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 9ee25c3404ec..2175a59e8e4d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -25,7 +25,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateContentDpAsState
@@ -71,7 +71,7 @@ constructor(
}
@Composable
- override fun SceneScope.Content(modifier: Modifier) {
+ override fun ContentScope.Content(modifier: Modifier) {
val isIdleAndNotOccluded by remember {
derivedStateOf {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt
index 8d8ab8ee7949..6c80c69b7a34 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt
@@ -18,8 +18,8 @@ package com.android.systemui.scene.ui.composable
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.SceneScope
import com.android.systemui.lifecycle.Activatable
/**
@@ -35,5 +35,5 @@ interface Scene : Activatable, ActionableContent {
/** Uniquely-identifying key for this scene. The key must be unique within its container. */
val key: SceneKey
- @Composable fun SceneScope.Content(modifier: Modifier)
+ @Composable fun ContentScope.Content(modifier: Modifier)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index bfcde7dab6d2..3131b539c6af 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -58,9 +58,9 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
-import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateElementFloatAsState
import com.android.compose.animation.scene.content.state.TransitionState
@@ -122,7 +122,7 @@ object ShadeHeader {
}
@Composable
-fun SceneScope.CollapsedShadeHeader(
+fun ContentScope.CollapsedShadeHeader(
viewModelFactory: ShadeHeaderViewModel.Factory,
createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -264,7 +264,7 @@ fun SceneScope.CollapsedShadeHeader(
}
@Composable
-fun SceneScope.ExpandedShadeHeader(
+fun ContentScope.ExpandedShadeHeader(
viewModelFactory: ShadeHeaderViewModel.Factory,
createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -339,7 +339,7 @@ fun SceneScope.ExpandedShadeHeader(
}
@Composable
-private fun SceneScope.Clock(scale: Float, viewModel: ShadeHeaderViewModel, modifier: Modifier) {
+private fun ContentScope.Clock(scale: Float, viewModel: ShadeHeaderViewModel, modifier: Modifier) {
val layoutDirection = LocalLayoutDirection.current
Element(key = ShadeHeader.Elements.Clock, modifier = modifier) {
@@ -446,7 +446,7 @@ private fun ShadeCarrierGroup(viewModel: ShadeHeaderViewModel, modifier: Modifie
}
@Composable
-private fun SceneScope.StatusIcons(
+private fun ContentScope.StatusIcons(
viewModel: ShadeHeaderViewModel,
createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
statusBarIconController: StatusBarIconController,
@@ -548,7 +548,10 @@ private fun SystemIconContainer(
}
@Composable
-private fun SceneScope.PrivacyChip(viewModel: ShadeHeaderViewModel, modifier: Modifier = Modifier) {
+private fun ContentScope.PrivacyChip(
+ viewModel: ShadeHeaderViewModel,
+ modifier: Modifier = Modifier,
+) {
val privacyList by viewModel.privacyItems.collectAsStateWithLifecycle()
AndroidView(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 0d3bab24f68f..f829a0d6facf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -62,9 +62,9 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
-import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateContentDpAsState
@@ -160,7 +160,7 @@ constructor(
override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
@Composable
- override fun SceneScope.Content(modifier: Modifier) =
+ override fun ContentScope.Content(modifier: Modifier) =
ShadeScene(
notificationStackScrollView.get(),
viewModel =
@@ -193,7 +193,7 @@ constructor(
}
@Composable
-private fun SceneScope.ShadeScene(
+private fun ContentScope.ShadeScene(
notificationStackScrollView: NotificationScrollView,
viewModel: ShadeSceneContentViewModel,
notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
@@ -242,7 +242,7 @@ private fun SceneScope.ShadeScene(
}
@Composable
-private fun SceneScope.SingleShade(
+private fun ContentScope.SingleShade(
notificationStackScrollView: NotificationScrollView,
viewModel: ShadeSceneContentViewModel,
notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
@@ -410,7 +410,7 @@ private fun SceneScope.SingleShade(
}
@Composable
-private fun SceneScope.SplitShade(
+private fun ContentScope.SplitShade(
notificationStackScrollView: NotificationScrollView,
viewModel: ShadeSceneContentViewModel,
notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
@@ -632,7 +632,7 @@ private fun SceneScope.SplitShade(
}
@Composable
-private fun SceneScope.ShadeMediaCarousel(
+private fun ContentScope.ShadeMediaCarousel(
isVisible: Boolean,
isInRow: Boolean,
mediaHost: MediaHost,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index 25892c5a75cc..d9e8f02f005b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -19,7 +19,6 @@ package com.android.systemui.volume.panel.component.volume.ui.composable
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
-import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
@@ -70,8 +69,6 @@ private const val SHRINK_FRACTION = 0.55f
private const val SCALE_FRACTION = 0.9f
private const val EXPAND_BUTTON_SCALE = 0.8f
-/** Volume sliders laid out in a collapsable column */
-@OptIn(ExperimentalAnimationApi::class)
@Composable
fun ColumnVolumeSliders(
viewModels: List<SliderViewModel>,
@@ -144,8 +141,7 @@ fun ColumnVolumeSliders(
VolumeSlider(
modifier =
- Modifier.padding(top = 16.dp)
- .fillMaxWidth()
+ Modifier.fillMaxWidth()
.animateEnterExit(
enter =
enterTransition(
@@ -157,7 +153,10 @@ fun ColumnVolumeSliders(
index = index,
totalCount = viewModels.size,
),
- ),
+ )
+ .thenIf(!Flags.volumeRedesign()) {
+ Modifier.padding(top = 16.dp)
+ },
state = sliderState,
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 5f991fbb50df..bdd0da9ce4a4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -17,10 +17,12 @@
package com.android.systemui.volume.panel.component.volume.ui.composable
import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -33,6 +35,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Icon as MaterialIcon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
@@ -47,6 +50,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.clearAndSetSemantics
@@ -67,6 +71,7 @@ import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState
import kotlin.math.round
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -98,7 +103,7 @@ fun VolumeSlider(
}
val value by valueState(state)
- Column(modifier) {
+ Column(modifier = modifier.animateContentSize(), verticalArrangement = Arrangement.Top) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.fillMaxWidth().height(40.dp),
@@ -127,7 +132,7 @@ fun VolumeSlider(
enabled = state.isEnabled,
modifier =
Modifier.height(40.dp)
- .padding(vertical = 8.dp)
+ .padding(top = 4.dp, bottom = 12.dp)
.sysuiResTag(state.label)
.clearAndSetSemantics {
if (state.isEnabled) {
@@ -168,6 +173,28 @@ fun VolumeSlider(
}
},
)
+ state.disabledMessage?.let { disabledMessage ->
+ AnimatedVisibility(visible = !state.isEnabled) {
+ Row(
+ modifier = Modifier.padding(bottom = 12.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ MaterialIcon(
+ painter = painterResource(R.drawable.ic_error_outline),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ modifier = Modifier.size(16.dp),
+ )
+ Text(
+ text = disabledMessage,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.labelSmall,
+ modifier = Modifier.basicMarquee(),
+ )
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
index 6349c1406a12..bc3013239289 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
@@ -37,18 +37,17 @@ fun VolumePanelComposeScope.VerticalVolumePanelContent(
layout: ComponentsLayout,
modifier: Modifier = Modifier,
) {
- Column(
- modifier = modifier.verticalScroll(rememberScrollState()),
- verticalArrangement = Arrangement.spacedBy(20.dp),
- ) {
+ Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(20.dp)) {
for (component in layout.headerComponents) {
AnimatedVisibility(component.isVisible) {
with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
}
}
- for (component in layout.contentComponents) {
- AnimatedVisibility(component.isVisible) {
- with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
+ Column(Modifier.verticalScroll(rememberScrollState())) {
+ for (component in layout.contentComponents) {
+ AnimatedVisibility(component.isVisible) {
+ with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
+ }
}
}
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 ef11932867a0..a1117e1bc1db 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
@@ -264,9 +264,6 @@ interface BaseContentScope : ElementStateScope {
)
}
-@Deprecated("Use ContentScope instead", ReplaceWith("ContentScope"))
-typealias SceneScope = ContentScope
-
@Stable
@ElementDsl
interface ContentScope : BaseContentScope {
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 005146997813..b405fbe89302 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
@@ -2095,7 +2095,7 @@ class ElementTest {
val foo = ElementKey("Foo", placeAllCopies = true)
@Composable
- fun SceneScope.Foo(size: Dp, modifier: Modifier = Modifier) {
+ fun ContentScope.Foo(size: Dp, modifier: Modifier = Modifier) {
Box(modifier.element(foo).size(size))
}
@@ -2159,7 +2159,7 @@ class ElementTest {
// Foo is a simple element that does not move or resize during the transition.
@Composable
- fun SceneScope.Foo(modifier: Modifier = Modifier) {
+ fun ContentScope.Foo(modifier: Modifier = Modifier) {
Box(
modifier
.element(TestElements.Foo)
@@ -2211,7 +2211,7 @@ class ElementTest {
@Ignore("b/363964445")
fun interruption_considerPreviousUniqueState() {
@Composable
- fun SceneScope.Foo(modifier: Modifier = Modifier) {
+ fun ContentScope.Foo(modifier: Modifier = Modifier) {
Box(modifier.element(TestElements.Foo).size(50.dp))
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
index b4c8ad7c3327..9e1bae577ed2 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
@@ -373,7 +373,7 @@ class MovableElementTest {
val fooParentInOverlayTag = "fooParentTagInOverlay"
@Composable
- fun SceneScope.Foo(modifier: Modifier = Modifier) {
+ fun ContentScope.Foo(modifier: Modifier = Modifier) {
// Foo wraps its content, so there is no way for STL to know its size in advance.
MovableElement(foo, modifier) { content { Box(Modifier.size(fooSize)) } }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
index bad4c6298e6b..93fa51654ca1 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
@@ -547,7 +547,7 @@ class OverlayTest {
val sharedIntValueByContent = mutableMapOf<ContentKey, Int>()
@Composable
- fun SceneScope.animateContentInt(targetValue: Int) {
+ fun ContentScope.animateContentInt(targetValue: Int) {
val animatedValue = animateContentIntAsState(targetValue, sharedIntKey)
LaunchedEffect(animatedValue) {
try {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
index a65415509d38..a6ed37ead7ae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
@@ -37,10 +37,10 @@ import androidx.compose.ui.semantics.SemanticsNode
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.Scale
import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.isElement
@@ -270,7 +270,7 @@ class BouncerPredictiveBackTest : SysuiTestCase() {
override val userActions: Flow<Map<UserAction, UserActionResult>> = flowOf()
@Composable
- override fun SceneScope.Content(modifier: Modifier) {
+ override fun ContentScope.Content(modifier: Modifier) {
Box(modifier = modifier, contentAlignment = Alignment.Center) {
Text(text = "Fake Lockscreen")
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt
index 47cba0723804..030233625027 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt
@@ -367,8 +367,6 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
DeviceEntryRestrictionReason.DeviceNotUnlockedSinceReboot,
LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST to
DeviceEntryRestrictionReason.AdaptiveAuthRequest,
- LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to
- DeviceEntryRestrictionReason.BouncerLockedOut,
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
DeviceEntryRestrictionReason.SecurityTimeout,
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
@@ -403,8 +401,6 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
DeviceEntryRestrictionReason.DeviceNotUnlockedSinceReboot,
LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST to
DeviceEntryRestrictionReason.AdaptiveAuthRequest,
- LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to
- DeviceEntryRestrictionReason.BouncerLockedOut,
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
DeviceEntryRestrictionReason.SecurityTimeout,
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
@@ -440,8 +436,6 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
DeviceEntryRestrictionReason.DeviceNotUnlockedSinceReboot,
LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST to
DeviceEntryRestrictionReason.AdaptiveAuthRequest,
- LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to
- DeviceEntryRestrictionReason.BouncerLockedOut,
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
DeviceEntryRestrictionReason.SecurityTimeout,
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 605a5d261424..bafabe07d370 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -22,6 +22,7 @@ import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.view.IRemoteAnimationFinishedCallback
+import android.view.RemoteAnimationTarget
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -39,11 +40,11 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -230,12 +231,15 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
@Test
fun remoteAnimationInstantlyFinished_ifDismissTransitionNotStarted() {
val mockedCallback = mock<IRemoteAnimationFinishedCallback>()
- whenever(keyguardDismissTransitionInteractor.startDismissKeyguardTransition(any()))
- .thenReturn(false)
+
+ // Call the onAlreadyGone callback immediately.
+ doAnswer { invocation -> (invocation.getArgument(1) as (() -> Unit)).invoke() }
+ .whenever(keyguardDismissTransitionInteractor)
+ .startDismissKeyguardTransition(any(), any())
underTest.onKeyguardGoingAwayRemoteAnimationStart(
transit = 0,
- apps = emptyArray(),
+ apps = arrayOf(mock<RemoteAnimationTarget>()),
wallpapers = emptyArray(),
nonApps = emptyArray(),
finishedCallback = mockedCallback,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt
index ec0773f79328..5a350435002f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt
@@ -24,7 +24,6 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
-import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -35,7 +34,17 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class QSColumnsRepositoryTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos =
+ testKosmos().apply {
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_settings_dual_shade_num_columns,
+ 2,
+ )
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_settings_split_shade_num_columns,
+ 3,
+ )
+ }
private lateinit var underTest: QSColumnsRepository
@Before
@@ -63,7 +72,7 @@ class QSColumnsRepositoryTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.dualShadeColumns)
- assertThat(latest).isEqualTo(4)
+ assertThat(latest).isEqualTo(2)
}
}
@@ -72,9 +81,8 @@ class QSColumnsRepositoryTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
val latest by collectLastValue(underTest.splitShadeColumns)
- fakeShadeRepository.setShadeLayoutWide(true)
- assertThat(latest).isEqualTo(4)
+ assertThat(latest).isEqualTo(3)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index 9173ac969324..f005375a2ef9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -48,6 +48,7 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.settings.FakeSettings
@@ -123,7 +124,12 @@ class ModesTileTest : SysuiTestCase() {
)
userActionInteractor =
- ModesTileUserActionInteractor(inputHandler, dialogDelegate, kosmos.zenModeInteractor)
+ ModesTileUserActionInteractor(
+ inputHandler,
+ dialogDelegate,
+ kosmos.zenModeInteractor,
+ kosmos.modesDialogEventLogger,
+ )
underTest =
ModesTile(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
index c8b3aba9b846..89b8e9171076 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,7 +61,12 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() {
private val zenModeInteractor = kosmos.zenModeInteractor
private val underTest =
- ModesTileUserActionInteractor(inputHandler, mockDialogDelegate, zenModeInteractor)
+ ModesTileUserActionInteractor(
+ inputHandler,
+ mockDialogDelegate,
+ zenModeInteractor,
+ kosmos.modesDialogEventLogger,
+ )
@Test
fun handleClick_active_showsDialog() = runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index ac3089d9286b..b0af8b180cce 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -30,10 +30,12 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
@@ -64,7 +66,7 @@ class CallChipViewModelTest : SysuiTestCase() {
.thenReturn(chipBackgroundView)
}
- private val underTest = kosmos.callChipViewModel
+ private val underTest by lazy { kosmos.callChipViewModel }
@Test
fun chip_noCall_isHidden() =
@@ -219,28 +221,94 @@ class CallChipViewModelTest : SysuiTestCase() {
}
@Test
- fun chip_positiveStartTime_colorsAreThemed() =
+ fun chip_positiveStartTime_notPromoted_colorsAreThemed() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000))
+ repo.setOngoingCallState(inCallModel(startTimeMs = 1000, promotedContent = null))
assertThat((latest as OngoingActivityChipModel.Shown).colors)
.isEqualTo(ColorsModel.Themed)
}
@Test
- fun chip_zeroStartTime_colorsAreThemed() =
+ fun chip_zeroStartTime_notPromoted_colorsAreThemed() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 0))
+ repo.setOngoingCallState(inCallModel(startTimeMs = 0, promotedContent = null))
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors)
+ .isEqualTo(ColorsModel.Themed)
+ }
+
+ @Test
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun chip_positiveStartTime_promoted_notifChipsFlagOff_colorsAreThemed() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(
+ inCallModel(startTimeMs = 1000, promotedContent = PROMOTED_CONTENT_WITH_COLOR)
+ )
assertThat((latest as OngoingActivityChipModel.Shown).colors)
.isEqualTo(ColorsModel.Themed)
}
@Test
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun chip_zeroStartTime_promoted_notifChipsFlagOff_colorsAreThemed() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(
+ inCallModel(startTimeMs = 0, promotedContent = PROMOTED_CONTENT_WITH_COLOR)
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors)
+ .isEqualTo(ColorsModel.Themed)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun chip_positiveStartTime_promoted_notifChipsFlagOn_colorsAreCustom() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(
+ inCallModel(startTimeMs = 1000, promotedContent = PROMOTED_CONTENT_WITH_COLOR)
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors)
+ .isEqualTo(
+ ColorsModel.Custom(
+ backgroundColorInt = PROMOTED_BACKGROUND_COLOR,
+ primaryTextColorInt = PROMOTED_PRIMARY_TEXT_COLOR,
+ )
+ )
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun chip_zeroStartTime_promoted_notifChipsFlagOff_colorsAreCustom() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(
+ inCallModel(startTimeMs = 0, promotedContent = PROMOTED_CONTENT_WITH_COLOR)
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors)
+ .isEqualTo(
+ ColorsModel.Custom(
+ backgroundColorInt = PROMOTED_BACKGROUND_COLOR,
+ primaryTextColorInt = PROMOTED_PRIMARY_TEXT_COLOR,
+ )
+ )
+ }
+
+ @Test
fun chip_resetsCorrectly() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -275,7 +343,7 @@ class CallChipViewModelTest : SysuiTestCase() {
repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = null))
- assertThat((latest as OngoingActivityChipModel.Shown).onClickListener).isNull()
+ assertThat((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy).isNull()
}
@Test
@@ -285,7 +353,7 @@ class CallChipViewModelTest : SysuiTestCase() {
val pendingIntent = mock<PendingIntent>()
repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = pendingIntent))
- val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
+ val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListenerLegacy
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -302,7 +370,8 @@ class CallChipViewModelTest : SysuiTestCase() {
val pendingIntent = mock<PendingIntent>()
repo.setOngoingCallState(inCallModel(startTimeMs = 0, intent = pendingIntent))
- val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
+ val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListenerLegacy
+
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -319,5 +388,19 @@ class CallChipViewModelTest : SysuiTestCase() {
} else {
mock<StatusBarIconView>()
}
+
+ private val PROMOTED_CONTENT_WITH_COLOR =
+ PromotedNotificationContentModel.Builder("notif")
+ .apply {
+ this.colors =
+ PromotedNotificationContentModel.Colors(
+ backgroundColor = PROMOTED_BACKGROUND_COLOR,
+ primaryTextColor = PROMOTED_PRIMARY_TEXT_COLOR,
+ )
+ }
+ .build()
+
+ private const val PROMOTED_BACKGROUND_COLOR = 65
+ private const val PROMOTED_PRIMARY_TEXT_COLOR = 98
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index c511c433d92d..fcf8c834dc12 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel
import android.content.DialogInterface
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -25,6 +26,7 @@ import com.android.internal.jank.Cuj
import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.Expandable
import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -46,8 +48,10 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.policy.CastDevice
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -84,6 +88,8 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
)
.thenReturn(chipBackgroundView)
}
+ private val mockExpandable: Expandable =
+ mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
private val underTest = kosmos.castToOtherDeviceChipViewModel
@@ -263,7 +269,13 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
// WHEN the stop action on the dialog is clicked
val dialogStopAction =
- getStopActionFromDialog(latest, chipView, mockScreenCastDialog, kosmos)
+ getStopActionFromDialog(
+ latest,
+ chipView,
+ mockExpandable,
+ mockScreenCastDialog,
+ kosmos,
+ )
dialogStopAction.onClick(mock<DialogInterface>(), 0)
// THEN the chip is immediately hidden...
@@ -296,7 +308,13 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
// WHEN the stop action on the dialog is clicked
val dialogStopAction =
- getStopActionFromDialog(latest, chipView, mockGenericCastDialog, kosmos)
+ getStopActionFromDialog(
+ latest,
+ chipView,
+ mockExpandable,
+ mockGenericCastDialog,
+ kosmos,
+ )
dialogStopAction.onClick(mock<DialogInterface>(), 0)
// THEN the chip is immediately hidden...
@@ -416,13 +434,14 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_projectionStateEntireScreen_clickListenerShowsScreenCastDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
mediaProjectionRepo.mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -431,6 +450,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_projectionStateSingleTask_clickListenerShowsScreenCastDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -442,7 +462,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
createTask(taskId = 1),
)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -451,6 +471,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_routerStateCasting_clickListenerShowsGenericCastDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -466,7 +487,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
)
)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -480,13 +501,14 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_projectionStateCasting_clickListenerHasCuj() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
mediaProjectionRepo.mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
clickListener!!.onClick(chipView)
val cujCaptor = argumentCaptor<DialogCuj>()
@@ -499,6 +521,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_routerStateCasting_clickListenerHasCuj() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -514,7 +537,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
)
)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
clickListener!!.onClick(chipView)
val cujCaptor = argumentCaptor<DialogCuj>()
@@ -525,4 +548,103 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
.isEqualTo(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP)
assertThat(cujCaptor.firstValue.tag).contains("Cast")
}
+
+ @Test
+ @EnableFlags(StatusBarChipsModernization.FLAG_NAME)
+ fun chip_routerStateCasting_hasClickBehavior() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaRouterRepo.castDevices.value =
+ listOf(
+ CastDevice(
+ state = CastDevice.CastState.Connected,
+ id = "id",
+ name = "name",
+ description = "desc",
+ origin = CastDevice.CastOrigin.MediaRouter,
+ )
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).clickBehavior)
+ .isInstanceOf(OngoingActivityChipModel.ClickBehavior.ExpandAction::class.java)
+ }
+
+ @Test
+ @EnableFlags(StatusBarChipsModernization.FLAG_NAME)
+ fun chip_projectionStateCasting_hasClickBehavior() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
+
+ assertThat((latest as OngoingActivityChipModel.Shown).clickBehavior)
+ .isInstanceOf(OngoingActivityChipModel.ClickBehavior.ExpandAction::class.java)
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_projectionStateEntireScreen_clickBehaviorShowsScreenCastDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
+
+ val expandAction =
+ ((latest as OngoingActivityChipModel.Shown).clickBehavior
+ as OngoingActivityChipModel.ClickBehavior.ExpandAction)
+ expandAction.onClick(mockExpandable)
+
+ verify(kosmos.mockDialogTransitionAnimator)
+ .show(eq(mockScreenCastDialog), any(), anyBoolean())
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_projectionStateSingleTask_clickBehaviorShowsScreenCastDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ CAST_TO_OTHER_DEVICES_PACKAGE,
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val expandAction =
+ ((latest as OngoingActivityChipModel.Shown).clickBehavior
+ as OngoingActivityChipModel.ClickBehavior.ExpandAction)
+ expandAction.onClick(mockExpandable)
+
+ verify(kosmos.mockDialogTransitionAnimator)
+ .show(eq(mockScreenCastDialog), any(), anyBoolean())
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_routerStateCasting_clickBehaviorShowsGenericCastDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaRouterRepo.castDevices.value =
+ listOf(
+ CastDevice(
+ state = CastDevice.CastState.Connected,
+ id = "id",
+ name = "name",
+ description = "desc",
+ origin = CastDevice.CastOrigin.MediaRouter,
+ )
+ )
+
+ val expandAction =
+ ((latest as OngoingActivityChipModel.Shown).clickBehavior
+ as OngoingActivityChipModel.ClickBehavior.ExpandAction)
+ expandAction.onClick(mockExpandable)
+
+ verify(kosmos.mockDialogTransitionAnimator)
+ .show(eq(mockGenericCastDialog), any(), anyBoolean())
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
index ee4a52d35d68..e89c929a5827 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -178,6 +179,37 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
assertThat(latest!![1].statusBarChipIconView).isEqualTo(secondIcon)
}
+ /** Regression test for b/388521980. */
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun notificationChips_callNotifIsAlsoPromoted_callNotifExcluded() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "promotedNormal",
+ statusBarChipIcon = mock(),
+ promotedContent =
+ PromotedNotificationContentModel.Builder("promotedNormal").build(),
+ callType = CallType.None,
+ ),
+ activeNotificationModel(
+ key = "promotedCall",
+ statusBarChipIcon = mock(),
+ promotedContent =
+ PromotedNotificationContentModel.Builder("promotedCall").build(),
+ callType = CallType.Ongoing,
+ ),
+ )
+ )
+
+ // Verify the promoted call notification is not included
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].key).isEqualTo("promotedNormal")
+ }
+
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun notificationChips_notifUpdatesGoThrough() =
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 902db5e10589..eec23d3ffb1a 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
@@ -650,7 +650,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
)
val chip = latest!![0]
- chip.onClickListener!!.onClick(mock<View>())
+ chip.onClickListenerLegacy!!.onClick(mock<View>())
assertThat(latestChipTap).isEqualTo("clickTest")
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index 48d8add6b33a..1f82dcd9c308 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -17,12 +17,15 @@
package com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel
import android.content.DialogInterface
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.jank.Cuj
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.Expandable
import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
@@ -41,8 +44,10 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -77,6 +82,8 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
)
.thenReturn(chipBackgroundView)
}
+ private val mockExpandable: Expandable =
+ mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
private val underTest = kosmos.screenRecordChipViewModel
@@ -106,7 +113,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Countdown::class.java)
assertThat((latest as OngoingActivityChipModel.Shown).icon).isNull()
- assertThat((latest as OngoingActivityChipModel.Shown).onClickListener).isNull()
+ assertThat((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy).isNull()
}
// The millis we typically get from [ScreenRecordRepository] are around 2995, 1995, and 995.
@@ -177,7 +184,13 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
// WHEN the stop action on the dialog is clicked
val dialogStopAction =
- getStopActionFromDialog(latest, chipView, mockSystemUIDialog, kosmos)
+ getStopActionFromDialog(
+ latest,
+ chipView,
+ mockExpandable,
+ mockSystemUIDialog,
+ kosmos,
+ )
dialogStopAction.onClick(mock<DialogInterface>(), 0)
// THEN both the screen record chip and the share-to-app chip are immediately hidden...
@@ -263,13 +276,14 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_notProjecting_clickListenerShowsDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -279,6 +293,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_projectingEntireScreen_clickListenerShowsDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -286,7 +301,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
mediaProjectionRepo.mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen("host.package")
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -296,6 +311,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_projectingSingleTask_clickListenerShowsDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -307,7 +323,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
FakeActivityTaskManager.createTask(taskId = 1),
)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -317,22 +333,85 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
}
@Test
- fun chip_clickListenerHasCuj() =
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
+ fun chip_clickListenerHasCujLegacy() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionRepo.mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen("host.package")
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
clickListener!!.onClick(chipView)
val cujCaptor = argumentCaptor<DialogCuj>()
verify(kosmos.mockDialogTransitionAnimator)
.showFromView(any(), any(), cujCaptor.capture(), anyBoolean())
-
assertThat(cujCaptor.firstValue.cujType)
.isEqualTo(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP)
assertThat(cujCaptor.firstValue.tag).contains("Screen record")
}
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_recordingState_hasClickBehavior() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown).clickBehavior)
+ .isInstanceOf(OngoingActivityChipModel.ClickBehavior.ExpandAction::class.java)
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_notProjecting_expandActionBehaviorShowsDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+ val expandAction =
+ ((latest as OngoingActivityChipModel.Shown).clickBehavior
+ as OngoingActivityChipModel.ClickBehavior.ExpandAction)
+
+ expandAction.onClick(mockExpandable)
+ verify(kosmos.mockDialogTransitionAnimator).show(any(), any(), anyBoolean())
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_projectingEntireScreen_expandActionBehaviorShowsDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+
+ val expandAction =
+ ((latest as OngoingActivityChipModel.Shown).clickBehavior
+ as OngoingActivityChipModel.ClickBehavior.ExpandAction)
+
+ expandAction.onClick(mockExpandable)
+ verify(kosmos.mockDialogTransitionAnimator).show(any(), any(), anyBoolean())
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_projectingSingleTask_expandActionBehaviorShowsDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ "host.package",
+ hostDeviceName = null,
+ FakeActivityTaskManager.createTask(taskId = 1),
+ )
+
+ val expandAction =
+ ((latest as OngoingActivityChipModel.Shown).clickBehavior
+ as OngoingActivityChipModel.ClickBehavior.ExpandAction)
+
+ expandAction.onClick(mockExpandable)
+ verify(kosmos.mockDialogTransitionAnimator).show(any(), any(), anyBoolean())
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index b3dec2eaa1c6..36fc5aa16407 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel
import android.content.DialogInterface
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -25,6 +26,7 @@ import com.android.internal.jank.Cuj
import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.Expandable
import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -45,8 +47,10 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -81,6 +85,8 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
)
.thenReturn(chipBackgroundView)
}
+ private val mockExpandable: Expandable =
+ mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
private val underTest = kosmos.shareToAppChipViewModel
@@ -215,7 +221,13 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
// WHEN the stop action on the dialog is clicked
val dialogStopAction =
- getStopActionFromDialog(latest, chipView, mockScreenShareDialog, kosmos)
+ getStopActionFromDialog(
+ latest,
+ chipView,
+ mockExpandable,
+ mockScreenShareDialog,
+ kosmos,
+ )
dialogStopAction.onClick(mock<DialogInterface>(), 0)
// THEN the chip is immediately hidden...
@@ -268,13 +280,14 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_noScreen_clickListenerShowsGenericShareDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
mediaProjectionRepo.mediaProjectionState.value =
MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -288,13 +301,14 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_entireScreen_clickListenerShowsScreenShareDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
mediaProjectionRepo.mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -308,6 +322,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_singleTask_clickListenerShowsScreenShareDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -318,7 +333,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
createTask(taskId = 1),
)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
@@ -332,6 +347,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun chip_clickListenerHasCuj() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -342,7 +358,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
createTask(taskId = 1),
)
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
clickListener!!.onClick(chipView)
val cujCaptor = argumentCaptor<DialogCuj>()
@@ -353,4 +369,101 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
.isEqualTo(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP)
assertThat(cujCaptor.firstValue.tag).contains("Share")
}
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_noScreen_hasClickBehavior() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE)
+
+ assertThat((latest as OngoingActivityChipModel.Shown).clickBehavior)
+ .isInstanceOf(OngoingActivityChipModel.ClickBehavior.ExpandAction::class.java)
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_entireScreen_hasClickBehavior() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+ assertThat((latest as OngoingActivityChipModel.Shown).clickBehavior)
+ .isInstanceOf(OngoingActivityChipModel.ClickBehavior.ExpandAction::class.java)
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_singleTask_hasClickBehavior() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ NORMAL_PACKAGE,
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).clickBehavior)
+ .isInstanceOf(OngoingActivityChipModel.ClickBehavior.ExpandAction::class.java)
+ }
+
+ @Test
+ @EnableFlags(
+ FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME,
+ )
+ fun chip_noScreen_clickBehaviorShowsGenericShareDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE)
+
+ val expandAction =
+ ((latest as OngoingActivityChipModel.Shown).clickBehavior
+ as OngoingActivityChipModel.ClickBehavior.ExpandAction)
+ expandAction.onClick(mockExpandable)
+ verify(kosmos.mockDialogTransitionAnimator)
+ .show(eq(mockGenericShareDialog), any(), any())
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_entireScreen_clickBehaviorShowsScreenShareDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+ val expandAction =
+ ((latest as OngoingActivityChipModel.Shown).clickBehavior
+ as OngoingActivityChipModel.ClickBehavior.ExpandAction)
+ expandAction.onClick(mockExpandable)
+ verify(kosmos.mockDialogTransitionAnimator)
+ .show(eq(mockScreenShareDialog), any(), any())
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chip_singleTask_clickBehaviorShowsScreenShareDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ NORMAL_PACKAGE,
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val expandAction =
+ ((latest as OngoingActivityChipModel.Shown).clickBehavior
+ as OngoingActivityChipModel.ClickBehavior.ExpandAction)
+ expandAction.onClick(mockExpandable)
+
+ verify(kosmos.mockDialogTransitionAnimator)
+ .show(eq(mockScreenShareDialog), any(), any())
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
index 8d4c68de8c79..d099e70c9bc5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
@@ -57,7 +57,8 @@ class ChipTransitionHelperTest : SysuiTestCase() {
icon = createIcon(R.drawable.ic_cake),
colors = ColorsModel.Themed,
startTimeMs = 100L,
- onClickListener = null,
+ onClickListenerLegacy = null,
+ clickBehavior = OngoingActivityChipModel.ClickBehavior.None,
)
inputChipFlow.value = newChip
@@ -68,7 +69,8 @@ class ChipTransitionHelperTest : SysuiTestCase() {
OngoingActivityChipModel.Shown.IconOnly(
icon = createIcon(R.drawable.ic_hotspot),
colors = ColorsModel.Themed,
- onClickListener = null,
+ onClickListenerLegacy = null,
+ clickBehavior = OngoingActivityChipModel.ClickBehavior.None,
)
inputChipFlow.value = newerChip
@@ -89,7 +91,8 @@ class ChipTransitionHelperTest : SysuiTestCase() {
icon = createIcon(R.drawable.ic_cake),
colors = ColorsModel.Themed,
startTimeMs = 100L,
- onClickListener = null,
+ onClickListenerLegacy = null,
+ clickBehavior = OngoingActivityChipModel.ClickBehavior.None,
)
inputChipFlow.value = shownChip
@@ -129,7 +132,8 @@ class ChipTransitionHelperTest : SysuiTestCase() {
icon = createIcon(R.drawable.ic_cake),
colors = ColorsModel.Themed,
startTimeMs = 100L,
- onClickListener = null,
+ onClickListenerLegacy = null,
+ clickBehavior = OngoingActivityChipModel.ClickBehavior.None,
)
inputChipFlow.value = shownChip
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
index e3510f5ce280..fc3af11c30b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -23,14 +25,19 @@ import com.android.internal.jank.Cuj
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import kotlin.test.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
@@ -53,8 +60,11 @@ class OngoingActivityChipViewModelTest : SysuiTestCase() {
)
.thenReturn(chipBackgroundView)
}
+ private val mockExpandable: Expandable =
+ mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
@Test
+ @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
fun createDialogLaunchOnClickListener_showsDialogOnClick() {
val cuj = DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Test")
val clickListener =
@@ -68,11 +78,23 @@ class OngoingActivityChipViewModelTest : SysuiTestCase() {
clickListener.onClick(chipView)
verify(dialogTransitionAnimator)
- .showFromView(
- eq(mockSystemUIDialog),
- eq(chipBackgroundView),
- eq(cuj),
- anyBoolean(),
+ .showFromView(eq(mockSystemUIDialog), eq(chipBackgroundView), eq(cuj), anyBoolean())
+ }
+
+ @Test
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun createDialogLaunchOnClickCallback_showsDialogOnClick() {
+ val cuj = DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Test")
+ val clickCallback =
+ createDialogLaunchOnClickCallback(
+ dialogDelegate,
+ dialogTransitionAnimator,
+ cuj,
+ logcatLogBuffer("OngoingActivityChipViewModelTest"),
+ "tag",
)
+
+ clickCallback.invoke(mockExpandable)
+ verify(dialogTransitionAnimator).show(eq(mockSystemUIDialog), any(), anyBoolean())
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 42358cce59a2..a4b6a841d61b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -26,6 +26,7 @@ import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
@@ -44,6 +45,7 @@ import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
@@ -91,6 +93,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
)
.thenReturn(chipBackgroundView)
}
+ private val mockExpandable: Expandable =
+ mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
private val underTest = kosmos.ongoingActivityChipsViewModel
@@ -294,7 +298,13 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
// WHEN screen record gets stopped via dialog
val dialogStopAction =
- getStopActionFromDialog(latest, chipView, mockSystemUIDialog, kosmos)
+ getStopActionFromDialog(
+ latest,
+ chipView,
+ mockExpandable,
+ mockSystemUIDialog,
+ kosmos,
+ )
dialogStopAction.onClick(mock<DialogInterface>(), 0)
// THEN the chip is immediately hidden with no animation
@@ -315,7 +325,13 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
// WHEN media projection gets stopped via dialog
val dialogStopAction =
- getStopActionFromDialog(latest, chipView, mockSystemUIDialog, kosmos)
+ getStopActionFromDialog(
+ latest,
+ chipView,
+ mockExpandable,
+ mockSystemUIDialog,
+ kosmos,
+ )
dialogStopAction.onClick(mock<DialogInterface>(), 0)
// THEN the chip is immediately hidden with no animation
@@ -330,6 +346,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
fun getStopActionFromDialog(
latest: OngoingActivityChipModel?,
chipView: View,
+ expandable: Expandable,
dialog: SystemUIDialog,
kosmos: Kosmos,
): DialogInterface.OnClickListener {
@@ -349,9 +366,17 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
.create(any<SystemUIDialog.Delegate>())
whenever(kosmos.packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
.thenThrow(PackageManager.NameNotFoundException())
- // Click the chip so that we open the dialog and we fill in [dialogStopAction]
- val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
- clickListener!!.onClick(chipView)
+
+ if (StatusBarChipsModernization.isEnabled) {
+ val clickBehavior = (latest as OngoingActivityChipModel.Shown).clickBehavior
+ (clickBehavior as OngoingActivityChipModel.ClickBehavior.ExpandAction).onClick(
+ expandable
+ )
+ } else {
+ val clickListener =
+ ((latest as OngoingActivityChipModel.Shown).onClickListenerLegacy)
+ clickListener!!.onClick(chipView)
+ }
return dialogStopAction
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 0f42f29e76ee..28f360108e50 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -25,6 +25,7 @@ import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Expandable
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
@@ -104,6 +105,8 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
)
.thenReturn(chipBackgroundView)
}
+ private val mockExpandable: Expandable =
+ mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
private val underTest by lazy { kosmos.ongoingActivityChipsViewModel }
@@ -679,7 +682,13 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// WHEN screen record gets stopped via dialog
val dialogStopAction =
- getStopActionFromDialog(latest, chipView, mockSystemUIDialog, kosmos)
+ getStopActionFromDialog(
+ latest,
+ chipView,
+ mockExpandable,
+ mockSystemUIDialog,
+ kosmos,
+ )
dialogStopAction.onClick(mock<DialogInterface>(), 0)
// THEN the chip is immediately hidden with no animation
@@ -700,7 +709,13 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// WHEN media projection gets stopped via dialog
val dialogStopAction =
- getStopActionFromDialog(latest, chipView, mockSystemUIDialog, kosmos)
+ getStopActionFromDialog(
+ latest,
+ chipView,
+ mockExpandable,
+ mockSystemUIDialog,
+ kosmos,
+ )
dialogStopAction.onClick(mock<DialogInterface>(), 0)
// THEN the chip is immediately hidden with no animation
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
index 739a9c956178..9dfc922eb7d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.headsup
import android.app.Notification
+import android.app.Notification.FLAG_PROMOTED_ONGOING
import android.app.PendingIntent
import android.app.Person
import android.os.Handler
@@ -677,10 +678,12 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : SysuiTestCase() {
}
@Test
- fun testIsSticky_rowPinnedAndExpanded_true() {
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- val row = testHelper.createRow()
- row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testIsSticky_promotedAndExpanded_notifChipsFlagOff_true() {
+ val notif = Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+ notif.flags = FLAG_PROMOTED_ONGOING
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, notif)
+ val row = testHelper.createRow().apply { setPinnedStatus(PinnedStatus.PinnedBySystem) }
notifEntry.row = row
underTest.showNotification(notifEntry)
@@ -692,6 +695,23 @@ class HeadsUpManagerImplTest(flags: FlagsParameterization) : SysuiTestCase() {
}
@Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testIsSticky_promotedAndExpanded_notifChipsFlagOn_false() {
+ val notif = Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+ notif.flags = FLAG_PROMOTED_ONGOING
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, notif)
+ val row = testHelper.createRow().apply { setPinnedStatus(PinnedStatus.PinnedBySystem) }
+ notifEntry.row = row
+
+ underTest.showNotification(notifEntry)
+
+ val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.setExpanded(true)
+
+ assertThat(underTest.isSticky(notifEntry.key)).isFalse()
+ }
+
+ @Test
fun testIsSticky_remoteInputActive_true() {
val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 650fa7ce46de..58856d970711 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertFalse;
@@ -44,7 +46,9 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.animation.back.BackAnimationSpec;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.settings.FakeDisplayTracker;
import org.junit.Before;
import org.junit.Rule;
@@ -68,6 +72,7 @@ public class SystemUIDialogTest extends SysuiTestCase {
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
private SystemUIDialog.Delegate mDelegate;
+ private SysUiState mSysUiState;
// TODO(b/292141694): build out Ravenwood support for DeviceFlagsValueProvider
// Ravenwood already has solid support for SetFlagsRule, but CheckFlagsRule will be added soon
@@ -78,7 +83,9 @@ public class SystemUIDialogTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
-
+ KosmosJavaAdapter kosmos = new KosmosJavaAdapter(this);
+ FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
+ mSysUiState = new SysUiState(displayTracker, kosmos.getSceneContainerPlugin());
mDependency.injectTestDependency(BroadcastDispatcher.class, mBroadcastDispatcher);
when(mDelegate.getBackAnimationSpec(ArgumentMatchers.any()))
.thenReturn(mock(BackAnimationSpec.class));
@@ -173,6 +180,30 @@ public class SystemUIDialogTest extends SysuiTestCase {
assertThat(calledStop.get()).isTrue();
}
+ /** Regression test for b/386871258 */
+ @Test
+ public void sysuiStateUpdated() {
+ SystemUIDialog dialog1 =
+ createDialogWithDelegate(mContext, mDelegate, /* shouldAcsDismissDialog */ true);
+ SystemUIDialog dialog2 =
+ createDialogWithDelegate(mContext, mDelegate, /* shouldAcsDismissDialog */ true);
+
+ dialog1.show();
+ assertThat((mSysUiState.getFlags() & SYSUI_STATE_DIALOG_SHOWING) != 0).isTrue();
+
+ dialog2.show();
+ assertThat((mSysUiState.getFlags() & SYSUI_STATE_DIALOG_SHOWING) != 0).isTrue();
+
+ dialog2.dismiss();
+ // explicitly call onWindowFocusChanged to simulate dialog 1 regaining focus
+ dialog1.onWindowFocusChanged(/* hasFocus= */ true);
+ assertThat((mSysUiState.getFlags() & SYSUI_STATE_DIALOG_SHOWING) != 0).isTrue();
+
+ dialog1.dismiss();
+ assertThat((mSysUiState.getFlags() & SYSUI_STATE_DIALOG_SHOWING) != 0).isFalse();
+ }
+
+
@Test
public void delegateIsCalled_inCorrectOrder() {
Configuration configuration = new Configuration();
@@ -198,7 +229,7 @@ public class SystemUIDialogTest extends SysuiTestCase {
SystemUIDialog.Factory factory = new SystemUIDialog.Factory(
getContext(),
Dependency.get(SystemUIDialogManager.class),
- Dependency.get(SysUiState.class),
+ mSysUiState,
Dependency.get(BroadcastDispatcher.class),
Dependency.get(DialogTransitionAnimator.class)
);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 6da06a36f63d..02135f6a7836 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -169,6 +170,27 @@ class OngoingCallControllerTest : SysuiTestCase() {
}
@Test
+ fun interactorHasOngoingCallNotif_repoHasPromotedContent() =
+ testScope.runTest {
+ val promotedContent = PromotedNotificationContentModel.Builder("ongoingNotif").build()
+ setNotifOnRepo(
+ activeNotificationModel(
+ key = "ongoingNotif",
+ callType = CallType.Ongoing,
+ uid = CALL_UID,
+ statusBarChipIcon = mock(),
+ whenTime = 567,
+ promotedContent = promotedContent,
+ )
+ )
+
+ val repoState = ongoingCallRepository.ongoingCallState.value
+ assertThat(repoState).isInstanceOf(OngoingCallModel.InCall::class.java)
+ assertThat((repoState as OngoingCallModel.InCall).promotedContent)
+ .isEqualTo(promotedContent)
+ }
+
+ @Test
fun notifRepoHasOngoingCallNotif_isOngoingCallNotif_windowControllerUpdated() {
setCallNotifOnRepo()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
index 8fb95e843ec1..14263c4b1b9b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
@@ -69,33 +70,37 @@ class OngoingCallInteractorTest : SysuiTestCase() {
}
@Test
- fun ongoingCallNotification_setsNotificationIconAndIntent() =
+ fun ongoingCallNotification_setsAllFields() =
kosmos.runTest {
val latest by collectLastValue(underTest.ongoingCallState)
// Set up notification with icon view and intent
val testIconView: StatusBarIconView = mock()
val testIntent: PendingIntent = mock()
+ val testPromotedContent =
+ PromotedNotificationContentModel.Builder("promotedCall").build()
repository.activeNotifications.value =
ActiveNotificationsStore.Builder()
.apply {
addIndividualNotif(
activeNotificationModel(
- key = "notif1",
+ key = "promotedCall",
whenTime = 1000L,
callType = CallType.Ongoing,
statusBarChipIcon = testIconView,
contentIntent = testIntent,
+ promotedContent = testPromotedContent,
)
)
}
.build()
- // Verify model is InCall and has the correct icon and intent.
+ // Verify model is InCall and has the correct icon, intent, and promoted content.
assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
val model = latest as OngoingCallModel.InCall
assertThat(model.notificationIconView).isSameInstanceAs(testIconView)
assertThat(model.intent).isSameInstanceAs(testIntent)
+ assertThat(model.promotedContent).isSameInstanceAs(testPromotedContent)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
index b2378d2c3aae..2d6315014164 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
@@ -66,13 +66,14 @@ class ModesDialogDelegateTest : SysuiTestCase() {
whenever(
mockDialogTransitionAnimator.createActivityTransitionController(
any<SystemUIDialog>(),
- eq(null)
+ eq(null),
)
)
.thenReturn(mockAnimationController)
underTest =
ModesDialogDelegate(
+ context,
kosmos.systemUIDialogFactory,
mockDialogTransitionAnimator,
activityStarter,
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index 02b2bcf8e40d..e152c98a93e5 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -58,3 +58,8 @@
static *** v(...);
}
-maximumremovedandroidloglevel 2
+
+#Keep the R
+-keepclassmembers class com.android.systemui.customization.R$* {
+ public static <fields>;
+}
diff --git a/packages/SystemUI/res/drawable/ic_media_connecting_button_container.xml b/packages/SystemUI/res/drawable/ic_media_connecting_button_container.xml
new file mode 100644
index 000000000000..32aacf6522e7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_connecting_button_container.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="88dp"
+ android:height="56dp"
+ android:viewportHeight="56"
+ android:viewportWidth="88">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:scaleX="1.05905"
+ android:scaleY="1.0972"
+ android:translateX="43.528999999999996"
+ android:translateY="27.898" />
+ <group
+ android:name="_R_G_L_0_G"
+ android:pivotX="0.493"
+ android:pivotY="0.124"
+ android:scaleX="1.05905"
+ android:scaleY="1.0972"
+ android:translateX="43.528999999999996"
+ android:translateY="27.898">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#3d90ff"
+ android:fillType="nonZero"
+ android:pathData=" M34.49 -5.75 C34.49,-5.75 34.49,6 34.49,6 C34.49,14.84 27.32,22 18.49,22 C18.49,22 -17.5,22 -17.5,22 C-26.34,22 -33.5,14.84 -33.5,6 C-33.5,6 -33.5,-5.75 -33.5,-5.75 C-33.5,-14.59 -26.34,-21.75 -17.5,-21.75 C-17.5,-21.75 18.49,-21.75 18.49,-21.75 C27.32,-21.75 34.49,-14.59 34.49,-5.75c " />
+ </group>
+ </group>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_connecting_status_container.xml b/packages/SystemUI/res/drawable/ic_media_connecting_status_container.xml
deleted file mode 100644
index f8c0fa04cd39..000000000000
--- a/packages/SystemUI/res/drawable/ic_media_connecting_status_container.xml
+++ /dev/null
@@ -1,199 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2025 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <target android:name="_R_G_L_1_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleX"
- android:startOffset="1000"
- android:valueFrom="0.45561"
- android:valueTo="0.69699"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleY"
- android:startOffset="1000"
- android:valueFrom="0.6288400000000001"
- android:valueTo="0.81618"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="417"
- android:propertyName="scaleX"
- android:startOffset="1083"
- android:valueFrom="0.69699"
- android:valueTo="1.05905"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.1,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="417"
- android:propertyName="scaleY"
- android:startOffset="1083"
- android:valueFrom="0.81618"
- android:valueTo="1.0972"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.1,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="rotation"
- android:startOffset="0"
- android:valueFrom="90"
- android:valueTo="135"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="rotation"
- android:startOffset="500"
- android:valueFrom="135"
- android:valueTo="180"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleX"
- android:startOffset="1000"
- android:valueFrom="0.0434"
- android:valueTo="0.05063"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleY"
- android:startOffset="1000"
- android:valueFrom="0.0434"
- android:valueTo="0.042350000000000006"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="417"
- android:propertyName="scaleX"
- android:startOffset="1083"
- android:valueFrom="0.05063"
- android:valueTo="0.06146"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.1,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="417"
- android:propertyName="scaleY"
- android:startOffset="1083"
- android:valueFrom="0.042350000000000006"
- android:valueTo="0.040780000000000004"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.1,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="1017"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
- <aapt:attr name="android:drawable">
- <vector
- android:width="88dp"
- android:height="56dp"
- android:viewportHeight="56"
- android:viewportWidth="88">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_1_G"
- android:pivotX="0.493"
- android:pivotY="0.124"
- android:scaleX="1.05905"
- android:scaleY="1.0972"
- android:translateX="43.528999999999996"
- android:translateY="27.898">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#3d90ff"
- android:fillType="nonZero"
- android:pathData=" M34.47 0.63 C34.47,0.63 34.42,0.64 34.42,0.64 C33.93,12.88 24.69,21.84 13.06,21.97 C13.06,21.97 -12.54,21.97 -12.54,21.97 C-23.11,21.84 -33.38,13.11 -33.52,-0.27 C-33.52,-0.27 -33.52,-0.05 -33.52,-0.05 C-33.5,-13.21 -21.73,-21.76 -12.9,-21.76 C-12.9,-21.76 14.59,-21.76 14.59,-21.76 C24.81,-21.88 34.49,-10.58 34.47,0.63c " />
- </group>
- <group
- android:name="_R_G_L_0_G"
- android:rotation="0"
- android:scaleX="0.06146"
- android:scaleY="0.040780000000000004"
- android:translateX="44"
- android:translateY="28">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#3d90ff"
- android:fillType="nonZero"
- android:pathData=" M-0.65 -437.37 C-0.65,-437.37 8.33,-437.66 8.33,-437.66 C8.33,-437.66 17.31,-437.95 17.31,-437.95 C17.31,-437.95 26.25,-438.78 26.25,-438.78 C26.25,-438.78 35.16,-439.95 35.16,-439.95 C35.16,-439.95 44.07,-441.11 44.07,-441.11 C44.07,-441.11 52.85,-443 52.85,-443 C52.85,-443 61.6,-445.03 61.6,-445.03 C61.6,-445.03 70.35,-447.09 70.35,-447.09 C70.35,-447.09 78.91,-449.83 78.91,-449.83 C78.91,-449.83 87.43,-452.67 87.43,-452.67 C87.43,-452.67 95.79,-455.97 95.79,-455.97 C95.79,-455.97 104.11,-459.35 104.11,-459.35 C104.11,-459.35 112.36,-462.93 112.36,-462.93 C112.36,-462.93 120.6,-466.51 120.6,-466.51 C120.6,-466.51 128.84,-470.09 128.84,-470.09 C128.84,-470.09 137.09,-473.67 137.09,-473.67 C137.09,-473.67 145.49,-476.84 145.49,-476.84 C145.49,-476.84 153.9,-480.01 153.9,-480.01 C153.9,-480.01 162.31,-483.18 162.31,-483.18 C162.31,-483.18 170.98,-485.54 170.98,-485.54 C170.98,-485.54 179.66,-487.85 179.66,-487.85 C179.66,-487.85 188.35,-490.15 188.35,-490.15 C188.35,-490.15 197.22,-491.58 197.22,-491.58 C197.22,-491.58 206.09,-493.01 206.09,-493.01 C206.09,-493.01 214.98,-494.28 214.98,-494.28 C214.98,-494.28 223.95,-494.81 223.95,-494.81 C223.95,-494.81 232.93,-495.33 232.93,-495.33 C232.93,-495.33 241.9,-495.5 241.9,-495.5 C241.9,-495.5 250.88,-495.13 250.88,-495.13 C250.88,-495.13 259.86,-494.75 259.86,-494.75 C259.86,-494.75 268.78,-493.78 268.78,-493.78 C268.78,-493.78 277.68,-492.52 277.68,-492.52 C277.68,-492.52 286.57,-491.26 286.57,-491.26 C286.57,-491.26 295.31,-489.16 295.31,-489.16 C295.31,-489.16 304.04,-487.04 304.04,-487.04 C304.04,-487.04 312.7,-484.65 312.7,-484.65 C312.7,-484.65 321.19,-481.72 321.19,-481.72 C321.19,-481.72 329.68,-478.78 329.68,-478.78 C329.68,-478.78 337.96,-475.31 337.96,-475.31 C337.96,-475.31 346.14,-471.59 346.14,-471.59 C346.14,-471.59 354.3,-467.82 354.3,-467.82 C354.3,-467.82 362.11,-463.38 362.11,-463.38 C362.11,-463.38 369.92,-458.93 369.92,-458.93 C369.92,-458.93 377.53,-454.17 377.53,-454.17 C377.53,-454.17 384.91,-449.04 384.91,-449.04 C384.91,-449.04 392.29,-443.91 392.29,-443.91 C392.29,-443.91 399.26,-438.24 399.26,-438.24 C399.26,-438.24 406.15,-432.48 406.15,-432.48 C406.15,-432.48 412.92,-426.57 412.92,-426.57 C412.92,-426.57 419.27,-420.22 419.27,-420.22 C419.27,-420.22 425.62,-413.87 425.62,-413.87 C425.62,-413.87 431.61,-407.18 431.61,-407.18 C431.61,-407.18 437.38,-400.29 437.38,-400.29 C437.38,-400.29 443.14,-393.39 443.14,-393.39 C443.14,-393.39 448.27,-386.01 448.27,-386.01 C448.27,-386.01 453.4,-378.64 453.4,-378.64 C453.4,-378.64 458.26,-371.09 458.26,-371.09 C458.26,-371.09 462.71,-363.28 462.71,-363.28 C462.71,-363.28 467.16,-355.47 467.16,-355.47 C467.16,-355.47 471.03,-347.37 471.03,-347.37 C471.03,-347.37 474.75,-339.19 474.75,-339.19 C474.75,-339.19 478.34,-330.95 478.34,-330.95 C478.34,-330.95 481.28,-322.46 481.28,-322.46 C481.28,-322.46 484.21,-313.97 484.21,-313.97 C484.21,-313.97 486.72,-305.35 486.72,-305.35 C486.72,-305.35 488.84,-296.62 488.84,-296.62 C488.84,-296.62 490.96,-287.88 490.96,-287.88 C490.96,-287.88 492.33,-279.01 492.33,-279.01 C492.33,-279.01 493.59,-270.11 493.59,-270.11 C493.59,-270.11 494.69,-261.2 494.69,-261.2 C494.69,-261.2 495.07,-252.22 495.07,-252.22 C495.07,-252.22 495.44,-243.24 495.44,-243.24 C495.44,-243.24 495.41,-234.27 495.41,-234.27 C495.41,-234.27 494.88,-225.29 494.88,-225.29 C494.88,-225.29 494.35,-216.32 494.35,-216.32 C494.35,-216.32 493.22,-207.42 493.22,-207.42 C493.22,-207.42 491.79,-198.55 491.79,-198.55 C491.79,-198.55 490.36,-189.68 490.36,-189.68 C490.36,-189.68 488.19,-180.96 488.19,-180.96 C488.19,-180.96 485.88,-172.28 485.88,-172.28 C485.88,-172.28 483.56,-163.6 483.56,-163.6 C483.56,-163.6 480.48,-155.16 480.48,-155.16 C480.48,-155.16 477.31,-146.75 477.31,-146.75 C477.31,-146.75 474.14,-138.34 474.14,-138.34 C474.14,-138.34 470.62,-130.07 470.62,-130.07 C470.62,-130.07 467.04,-121.83 467.04,-121.83 C467.04,-121.83 463.46,-113.59 463.46,-113.59 C463.46,-113.59 459.88,-105.35 459.88,-105.35 C459.88,-105.35 456.54,-97.01 456.54,-97.01 C456.54,-97.01 453.37,-88.6 453.37,-88.6 C453.37,-88.6 450.21,-80.19 450.21,-80.19 C450.21,-80.19 447.68,-71.57 447.68,-71.57 C447.68,-71.57 445.36,-62.89 445.36,-62.89 C445.36,-62.89 443.04,-54.21 443.04,-54.21 C443.04,-54.21 441.54,-45.35 441.54,-45.35 C441.54,-45.35 440.09,-36.48 440.09,-36.48 C440.09,-36.48 438.78,-27.6 438.78,-27.6 C438.78,-27.6 438.19,-18.63 438.19,-18.63 C438.19,-18.63 437.61,-9.66 437.61,-9.66 C437.61,-9.66 437.36,-0.69 437.36,-0.69 C437.36,-0.69 437.65,8.29 437.65,8.29 C437.65,8.29 437.95,17.27 437.95,17.27 C437.95,17.27 438.77,26.21 438.77,26.21 C438.77,26.21 439.94,35.12 439.94,35.12 C439.94,35.12 441.11,44.03 441.11,44.03 C441.11,44.03 442.99,52.81 442.99,52.81 C442.99,52.81 445.02,61.57 445.02,61.57 C445.02,61.57 447.07,70.31 447.07,70.31 C447.07,70.31 449.82,78.87 449.82,78.87 C449.82,78.87 452.65,87.4 452.65,87.4 C452.65,87.4 455.96,95.75 455.96,95.75 C455.96,95.75 459.33,104.08 459.33,104.08 C459.33,104.08 462.91,112.32 462.91,112.32 C462.91,112.32 466.49,120.57 466.49,120.57 C466.49,120.57 470.07,128.81 470.07,128.81 C470.07,128.81 473.65,137.05 473.65,137.05 C473.65,137.05 476.82,145.46 476.82,145.46 C476.82,145.46 479.99,153.87 479.99,153.87 C479.99,153.87 483.17,162.28 483.17,162.28 C483.17,162.28 485.52,170.94 485.52,170.94 C485.52,170.94 487.84,179.63 487.84,179.63 C487.84,179.63 490.14,188.31 490.14,188.31 C490.14,188.31 491.57,197.18 491.57,197.18 C491.57,197.18 493,206.06 493,206.06 C493,206.06 494.27,214.95 494.27,214.95 C494.27,214.95 494.8,223.92 494.8,223.92 C494.8,223.92 495.33,232.89 495.33,232.89 C495.33,232.89 495.5,241.86 495.5,241.86 C495.5,241.86 495.12,250.84 495.12,250.84 C495.12,250.84 494.75,259.82 494.75,259.82 C494.75,259.82 493.78,268.74 493.78,268.74 C493.78,268.74 492.52,277.64 492.52,277.64 C492.52,277.64 491.27,286.54 491.27,286.54 C491.27,286.54 489.16,295.27 489.16,295.27 C489.16,295.27 487.05,304.01 487.05,304.01 C487.05,304.01 484.66,312.66 484.66,312.66 C484.66,312.66 481.73,321.16 481.73,321.16 C481.73,321.16 478.79,329.65 478.79,329.65 C478.79,329.65 475.32,337.93 475.32,337.93 C475.32,337.93 471.6,346.11 471.6,346.11 C471.6,346.11 467.84,354.27 467.84,354.27 C467.84,354.27 463.39,362.08 463.39,362.08 C463.39,362.08 458.94,369.89 458.94,369.89 C458.94,369.89 454.19,377.5 454.19,377.5 C454.19,377.5 449.06,384.88 449.06,384.88 C449.06,384.88 443.93,392.26 443.93,392.26 C443.93,392.26 438.26,399.23 438.26,399.23 C438.26,399.23 432.5,406.12 432.5,406.12 C432.5,406.12 426.6,412.89 426.6,412.89 C426.6,412.89 420.24,419.24 420.24,419.24 C420.24,419.24 413.89,425.6 413.89,425.6 C413.89,425.6 407.2,431.59 407.2,431.59 C407.2,431.59 400.31,437.36 400.31,437.36 C400.31,437.36 393.42,443.12 393.42,443.12 C393.42,443.12 386.04,448.25 386.04,448.25 C386.04,448.25 378.66,453.38 378.66,453.38 C378.66,453.38 371.11,458.24 371.11,458.24 C371.11,458.24 363.31,462.69 363.31,462.69 C363.31,462.69 355.5,467.14 355.5,467.14 C355.5,467.14 347.4,471.02 347.4,471.02 C347.4,471.02 339.22,474.73 339.22,474.73 C339.22,474.73 330.99,478.33 330.99,478.33 C330.99,478.33 322.49,481.27 322.49,481.27 C322.49,481.27 314,484.2 314,484.2 C314,484.2 305.38,486.71 305.38,486.71 C305.38,486.71 296.65,488.83 296.65,488.83 C296.65,488.83 287.91,490.95 287.91,490.95 C287.91,490.95 279.04,492.33 279.04,492.33 C279.04,492.33 270.14,493.59 270.14,493.59 C270.14,493.59 261.23,494.69 261.23,494.69 C261.23,494.69 252.25,495.07 252.25,495.07 C252.25,495.07 243.28,495.44 243.28,495.44 C243.28,495.44 234.3,495.41 234.3,495.41 C234.3,495.41 225.33,494.88 225.33,494.88 C225.33,494.88 216.36,494.35 216.36,494.35 C216.36,494.35 207.45,493.23 207.45,493.23 C207.45,493.23 198.58,491.8 198.58,491.8 C198.58,491.8 189.71,490.37 189.71,490.37 C189.71,490.37 180.99,488.21 180.99,488.21 C180.99,488.21 172.31,485.89 172.31,485.89 C172.31,485.89 163.63,483.57 163.63,483.57 C163.63,483.57 155.19,480.5 155.19,480.5 C155.19,480.5 146.78,477.32 146.78,477.32 C146.78,477.32 138.37,474.15 138.37,474.15 C138.37,474.15 130.11,470.63 130.11,470.63 C130.11,470.63 121.86,467.06 121.86,467.06 C121.86,467.06 113.62,463.48 113.62,463.48 C113.62,463.48 105.38,459.9 105.38,459.9 C105.38,459.9 97.04,456.56 97.04,456.56 C97.04,456.56 88.63,453.39 88.63,453.39 C88.63,453.39 80.22,450.22 80.22,450.22 C80.22,450.22 71.6,447.7 71.6,447.7 C71.6,447.7 62.92,445.37 62.92,445.37 C62.92,445.37 54.24,443.05 54.24,443.05 C54.24,443.05 45.38,441.55 45.38,441.55 C45.38,441.55 36.52,440.1 36.52,440.1 C36.52,440.1 27.63,438.78 27.63,438.78 C27.63,438.78 18.66,438.2 18.66,438.2 C18.66,438.2 9.7,437.61 9.7,437.61 C9.7,437.61 0.72,437.36 0.72,437.36 C0.72,437.36 -8.26,437.65 -8.26,437.65 C-8.26,437.65 -17.24,437.95 -17.24,437.95 C-17.24,437.95 -26.18,438.77 -26.18,438.77 C-26.18,438.77 -35.09,439.94 -35.09,439.94 C-35.09,439.94 -44,441.1 -44,441.1 C-44,441.1 -52.78,442.98 -52.78,442.98 C-52.78,442.98 -61.53,445.02 -61.53,445.02 C-61.53,445.02 -70.28,447.07 -70.28,447.07 C-70.28,447.07 -78.84,449.81 -78.84,449.81 C-78.84,449.81 -87.37,452.64 -87.37,452.64 C-87.37,452.64 -95.72,455.95 -95.72,455.95 C-95.72,455.95 -104.05,459.32 -104.05,459.32 C-104.05,459.32 -112.29,462.9 -112.29,462.9 C-112.29,462.9 -120.53,466.48 -120.53,466.48 C-120.53,466.48 -128.78,470.06 -128.78,470.06 C-128.78,470.06 -137.02,473.63 -137.02,473.63 C-137.02,473.63 -145.43,476.81 -145.43,476.81 C-145.43,476.81 -153.84,479.98 -153.84,479.98 C-153.84,479.98 -162.24,483.15 -162.24,483.15 C-162.24,483.15 -170.91,485.52 -170.91,485.52 C-170.91,485.52 -179.59,487.83 -179.59,487.83 C-179.59,487.83 -188.28,490.13 -188.28,490.13 C-188.28,490.13 -197.15,491.56 -197.15,491.56 C-197.15,491.56 -206.02,492.99 -206.02,492.99 C-206.02,492.99 -214.91,494.27 -214.91,494.27 C-214.91,494.27 -223.88,494.8 -223.88,494.8 C-223.88,494.8 -232.85,495.33 -232.85,495.33 C-232.85,495.33 -241.83,495.5 -241.83,495.5 C-241.83,495.5 -250.81,495.13 -250.81,495.13 C-250.81,495.13 -259.79,494.75 -259.79,494.75 C-259.79,494.75 -268.71,493.79 -268.71,493.79 C-268.71,493.79 -277.61,492.53 -277.61,492.53 C-277.61,492.53 -286.51,491.27 -286.51,491.27 C-286.51,491.27 -295.24,489.17 -295.24,489.17 C-295.24,489.17 -303.98,487.06 -303.98,487.06 C-303.98,487.06 -312.63,484.67 -312.63,484.67 C-312.63,484.67 -321.12,481.74 -321.12,481.74 C-321.12,481.74 -329.62,478.8 -329.62,478.8 C-329.62,478.8 -337.9,475.33 -337.9,475.33 C-337.9,475.33 -346.08,471.62 -346.08,471.62 C-346.08,471.62 -354.24,467.85 -354.24,467.85 C-354.24,467.85 -362.05,463.41 -362.05,463.41 C-362.05,463.41 -369.86,458.96 -369.86,458.96 C-369.86,458.96 -377.47,454.21 -377.47,454.21 C-377.47,454.21 -384.85,449.08 -384.85,449.08 C-384.85,449.08 -392.23,443.95 -392.23,443.95 C-392.23,443.95 -399.2,438.29 -399.2,438.29 C-399.2,438.29 -406.09,432.52 -406.09,432.52 C-406.09,432.52 -412.86,426.62 -412.86,426.62 C-412.86,426.62 -419.22,420.27 -419.22,420.27 C-419.22,420.27 -425.57,413.91 -425.57,413.91 C-425.57,413.91 -431.57,407.23 -431.57,407.23 C-431.57,407.23 -437.33,400.34 -437.33,400.34 C-437.33,400.34 -443.1,393.44 -443.1,393.44 C-443.1,393.44 -448.23,386.07 -448.23,386.07 C-448.23,386.07 -453.36,378.69 -453.36,378.69 C-453.36,378.69 -458.23,371.15 -458.23,371.15 C-458.23,371.15 -462.67,363.33 -462.67,363.33 C-462.67,363.33 -467.12,355.53 -467.12,355.53 C-467.12,355.53 -471,347.43 -471,347.43 C-471,347.43 -474.72,339.25 -474.72,339.25 C-474.72,339.25 -478.32,331.02 -478.32,331.02 C-478.32,331.02 -481.25,322.52 -481.25,322.52 C-481.25,322.52 -484.19,314.03 -484.19,314.03 C-484.19,314.03 -486.71,305.42 -486.71,305.42 C-486.71,305.42 -488.82,296.68 -488.82,296.68 C-488.82,296.68 -490.94,287.95 -490.94,287.95 C-490.94,287.95 -492.32,279.07 -492.32,279.07 C-492.32,279.07 -493.58,270.18 -493.58,270.18 C-493.58,270.18 -494.69,261.27 -494.69,261.27 C-494.69,261.27 -495.07,252.29 -495.07,252.29 C-495.07,252.29 -495.44,243.31 -495.44,243.31 C-495.44,243.31 -495.42,234.33 -495.42,234.33 C-495.42,234.33 -494.89,225.36 -494.89,225.36 C-494.89,225.36 -494.36,216.39 -494.36,216.39 C-494.36,216.39 -493.23,207.49 -493.23,207.49 C-493.23,207.49 -491.8,198.61 -491.8,198.61 C-491.8,198.61 -490.37,189.74 -490.37,189.74 C-490.37,189.74 -488.22,181.02 -488.22,181.02 C-488.22,181.02 -485.9,172.34 -485.9,172.34 C-485.9,172.34 -483.58,163.66 -483.58,163.66 C-483.58,163.66 -480.51,155.22 -480.51,155.22 C-480.51,155.22 -477.34,146.81 -477.34,146.81 C-477.34,146.81 -474.17,138.41 -474.17,138.41 C-474.17,138.41 -470.65,130.14 -470.65,130.14 C-470.65,130.14 -467.07,121.9 -467.07,121.9 C-467.07,121.9 -463.49,113.65 -463.49,113.65 C-463.49,113.65 -459.91,105.41 -459.91,105.41 C-459.91,105.41 -456.57,97.07 -456.57,97.07 C-456.57,97.07 -453.4,88.66 -453.4,88.66 C-453.4,88.66 -450.23,80.25 -450.23,80.25 C-450.23,80.25 -447.7,71.64 -447.7,71.64 C-447.7,71.64 -445.38,62.96 -445.38,62.96 C-445.38,62.96 -443.06,54.28 -443.06,54.28 C-443.06,54.28 -441.56,45.42 -441.56,45.42 C-441.56,45.42 -440.1,36.55 -440.1,36.55 C-440.1,36.55 -438.78,27.67 -438.78,27.67 C-438.78,27.67 -438.2,18.7 -438.2,18.7 C-438.2,18.7 -437.62,9.73 -437.62,9.73 C-437.62,9.73 -437.36,0.76 -437.36,0.76 C-437.36,0.76 -437.66,-8.22 -437.66,-8.22 C-437.66,-8.22 -437.95,-17.2 -437.95,-17.2 C-437.95,-17.2 -438.77,-26.14 -438.77,-26.14 C-438.77,-26.14 -439.93,-35.05 -439.93,-35.05 C-439.93,-35.05 -441.1,-43.96 -441.1,-43.96 C-441.1,-43.96 -442.98,-52.75 -442.98,-52.75 C-442.98,-52.75 -445.01,-61.5 -445.01,-61.5 C-445.01,-61.5 -447.06,-70.25 -447.06,-70.25 C-447.06,-70.25 -449.8,-78.81 -449.8,-78.81 C-449.8,-78.81 -452.63,-87.33 -452.63,-87.33 C-452.63,-87.33 -455.94,-95.69 -455.94,-95.69 C-455.94,-95.69 -459.31,-104.02 -459.31,-104.02 C-459.31,-104.02 -462.89,-112.26 -462.89,-112.26 C-462.89,-112.26 -466.47,-120.5 -466.47,-120.5 C-466.47,-120.5 -470.05,-128.74 -470.05,-128.74 C-470.05,-128.74 -473.68,-137.12 -473.68,-137.12 C-473.68,-137.12 -476.85,-145.53 -476.85,-145.53 C-476.85,-145.53 -480.03,-153.94 -480.03,-153.94 C-480.03,-153.94 -483.2,-162.34 -483.2,-162.34 C-483.2,-162.34 -485.55,-171.02 -485.55,-171.02 C-485.55,-171.02 -487.86,-179.7 -487.86,-179.7 C-487.86,-179.7 -490.15,-188.39 -490.15,-188.39 C-490.15,-188.39 -491.58,-197.26 -491.58,-197.26 C-491.58,-197.26 -493.01,-206.13 -493.01,-206.13 C-493.01,-206.13 -494.28,-215.02 -494.28,-215.02 C-494.28,-215.02 -494.81,-223.99 -494.81,-223.99 C-494.81,-223.99 -495.33,-232.96 -495.33,-232.96 C-495.33,-232.96 -495.5,-241.94 -495.5,-241.94 C-495.5,-241.94 -495.12,-250.92 -495.12,-250.92 C-495.12,-250.92 -494.75,-259.9 -494.75,-259.9 C-494.75,-259.9 -493.78,-268.82 -493.78,-268.82 C-493.78,-268.82 -492.52,-277.72 -492.52,-277.72 C-492.52,-277.72 -491.26,-286.61 -491.26,-286.61 C-491.26,-286.61 -489.15,-295.35 -489.15,-295.35 C-489.15,-295.35 -487.03,-304.08 -487.03,-304.08 C-487.03,-304.08 -484.64,-312.73 -484.64,-312.73 C-484.64,-312.73 -481.7,-321.23 -481.7,-321.23 C-481.7,-321.23 -478.77,-329.72 -478.77,-329.72 C-478.77,-329.72 -475.29,-338 -475.29,-338 C-475.29,-338 -471.57,-346.18 -471.57,-346.18 C-471.57,-346.18 -467.8,-354.33 -467.8,-354.33 C-467.8,-354.33 -463.36,-362.14 -463.36,-362.14 C-463.36,-362.14 -458.91,-369.95 -458.91,-369.95 C-458.91,-369.95 -454.15,-377.56 -454.15,-377.56 C-454.15,-377.56 -449.02,-384.94 -449.02,-384.94 C-449.02,-384.94 -443.88,-392.32 -443.88,-392.32 C-443.88,-392.32 -438.22,-399.28 -438.22,-399.28 C-438.22,-399.28 -432.45,-406.18 -432.45,-406.18 C-432.45,-406.18 -426.55,-412.94 -426.55,-412.94 C-426.55,-412.94 -420.19,-419.3 -420.19,-419.3 C-420.19,-419.3 -413.84,-425.65 -413.84,-425.65 C-413.84,-425.65 -407.15,-431.64 -407.15,-431.64 C-407.15,-431.64 -400.26,-437.41 -400.26,-437.41 C-400.26,-437.41 -393.36,-443.16 -393.36,-443.16 C-393.36,-443.16 -385.98,-448.29 -385.98,-448.29 C-385.98,-448.29 -378.6,-453.43 -378.6,-453.43 C-378.6,-453.43 -371.05,-458.28 -371.05,-458.28 C-371.05,-458.28 -363.24,-462.73 -363.24,-462.73 C-363.24,-462.73 -355.43,-467.18 -355.43,-467.18 C-355.43,-467.18 -347.33,-471.05 -347.33,-471.05 C-347.33,-471.05 -339.15,-474.76 -339.15,-474.76 C-339.15,-474.76 -330.92,-478.35 -330.92,-478.35 C-330.92,-478.35 -322.42,-481.29 -322.42,-481.29 C-322.42,-481.29 -313.93,-484.23 -313.93,-484.23 C-313.93,-484.23 -305.31,-486.73 -305.31,-486.73 C-305.31,-486.73 -296.58,-488.85 -296.58,-488.85 C-296.58,-488.85 -287.85,-490.97 -287.85,-490.97 C-287.85,-490.97 -278.97,-492.34 -278.97,-492.34 C-278.97,-492.34 -270.07,-493.6 -270.07,-493.6 C-270.07,-493.6 -261.16,-494.7 -261.16,-494.7 C-261.16,-494.7 -252.18,-495.07 -252.18,-495.07 C-252.18,-495.07 -243.2,-495.44 -243.2,-495.44 C-243.2,-495.44 -234.23,-495.41 -234.23,-495.41 C-234.23,-495.41 -225.26,-494.88 -225.26,-494.88 C-225.26,-494.88 -216.29,-494.35 -216.29,-494.35 C-216.29,-494.35 -207.38,-493.22 -207.38,-493.22 C-207.38,-493.22 -198.51,-491.79 -198.51,-491.79 C-198.51,-491.79 -189.64,-490.36 -189.64,-490.36 C-189.64,-490.36 -180.92,-488.19 -180.92,-488.19 C-180.92,-488.19 -172.24,-485.87 -172.24,-485.87 C-172.24,-485.87 -163.56,-483.56 -163.56,-483.56 C-163.56,-483.56 -155.12,-480.47 -155.12,-480.47 C-155.12,-480.47 -146.72,-477.3 -146.72,-477.3 C-146.72,-477.3 -138.31,-474.13 -138.31,-474.13 C-138.31,-474.13 -130.04,-470.61 -130.04,-470.61 C-130.04,-470.61 -121.8,-467.03 -121.8,-467.03 C-121.8,-467.03 -113.55,-463.45 -113.55,-463.45 C-113.55,-463.45 -105.31,-459.87 -105.31,-459.87 C-105.31,-459.87 -96.97,-456.53 -96.97,-456.53 C-96.97,-456.53 -88.56,-453.37 -88.56,-453.37 C-88.56,-453.37 -80.15,-450.2 -80.15,-450.2 C-80.15,-450.2 -71.53,-447.68 -71.53,-447.68 C-71.53,-447.68 -62.85,-445.36 -62.85,-445.36 C-62.85,-445.36 -54.17,-443.04 -54.17,-443.04 C-54.17,-443.04 -45.31,-441.54 -45.31,-441.54 C-45.31,-441.54 -36.44,-440.09 -36.44,-440.09 C-36.44,-440.09 -27.56,-438.78 -27.56,-438.78 C-27.56,-438.78 -18.59,-438.19 -18.59,-438.19 C-18.59,-438.19 -9.62,-437.61 -9.62,-437.61 C-9.62,-437.61 -0.65,-437.37 -0.65,-437.37c " />
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
-</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/touchpad_tutorial_apps_icon.xml b/packages/SystemUI/res/drawable/touchpad_tutorial_apps_icon.xml
new file mode 100644
index 000000000000..5f9d4212e440
--- /dev/null
+++ b/packages/SystemUI/res/drawable/touchpad_tutorial_apps_icon.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:autoMirrored="true"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M240,800q-33,0 -56.5,-23.5T160,720q0,-33 23.5,-56.5T240,640q33,0 56.5,23.5T320,720q0,33 -23.5,56.5T240,800ZM480,800q-33,0 -56.5,-23.5T400,720q0,-33 23.5,-56.5T480,640q33,0 56.5,23.5T560,720q0,33 -23.5,56.5T480,800ZM720,800q-33,0 -56.5,-23.5T640,720q0,-33 23.5,-56.5T720,640q33,0 56.5,23.5T800,720q0,33 -23.5,56.5T720,800ZM240,560q-33,0 -56.5,-23.5T160,480q0,-33 23.5,-56.5T240,400q33,0 56.5,23.5T320,480q0,33 -23.5,56.5T240,560ZM480,560q-33,0 -56.5,-23.5T400,480q0,-33 23.5,-56.5T480,400q33,0 56.5,23.5T560,480q0,33 -23.5,56.5T480,560ZM720,560q-33,0 -56.5,-23.5T640,480q0,-33 23.5,-56.5T720,400q33,0 56.5,23.5T800,480q0,33 -23.5,56.5T720,560ZM240,320q-33,0 -56.5,-23.5T160,240q0,-33 23.5,-56.5T240,160q33,0 56.5,23.5T320,240q0,33 -23.5,56.5T240,320ZM480,320q-33,0 -56.5,-23.5T400,240q0,-33 23.5,-56.5T480,160q33,0 56.5,23.5T560,240q0,33 -23.5,56.5T480,320ZM720,320q-33,0 -56.5,-23.5T640,240q0,-33 23.5,-56.5T720,160q33,0 56.5,23.5T800,240q0,33 -23.5,56.5T720,320Z"/>
+</vector>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index cf975d43293d..f9904e336f24 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1122,6 +1122,7 @@
<dimen name="smart_reply_button_corner_radius">8dp</dimen>
<dimen name="smart_action_button_icon_size">18dp</dimen>
<dimen name="smart_action_button_icon_padding">8dp</dimen>
+ <dimen name="smart_action_button_outline_stroke_width">2dp</dimen>
<!-- A reasonable upper bound for the height of the smart reply button. The measuring code
needs to start with a guess for the maximum size. Currently two-line smart reply buttons
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f2c648cb3ab0..414d3f1d17d5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3954,6 +3954,8 @@
<string name="touchpad_tutorial_home_gesture_button">Go home</string>
<!-- Label for button opening tutorial for "view recent apps" gesture on touchpad [CHAR LIMIT=NONE] -->
<string name="touchpad_tutorial_recent_apps_gesture_button">View recent apps</string>
+ <!-- Label for button opening tutorial for "switch apps" gesture on touchpad [CHAR LIMIT=NONE] -->
+ <string name="touchpad_tutorial_switch_apps_gesture_button">Switch apps</string>
<!-- Label for button finishing touchpad tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_tutorial_done_button">Done</string>
<!-- Screen title after gesture was not done correctly [CHAR LIMIT=NONE] -->
@@ -3991,6 +3993,17 @@
<string name="touchpad_recent_apps_gesture_success_body">You completed the view recent apps gesture.</string>
<!-- Text shown to the user after recent gesture was not done correctly [CHAR LIMIT=NONE] -->
<string name="touchpad_recent_gesture_error_body">To view recent apps, swipe up and hold using three fingers on your touchpad</string>
+ <!-- SWITCH APPS GESTURE -->
+ <!-- Touchpad switch apps gesture action name in tutorial [CHAR LIMIT=NONE] -->
+ <string name="touchpad_switch_apps_gesture_action_title">Switch apps</string>
+ <!-- Touchpad switch apps gesture guidance in gestures tutorial [CHAR LIMIT=NONE] -->
+ <string name="touchpad_switch_apps_gesture_guidance">Swipe left or right using four fingers on your touchpad</string>
+ <!-- Screen title after switch apps gesture was done successfully [CHAR LIMIT=NONE] -->
+ <string name="touchpad_switch_apps_gesture_success_title">Great job!</string>
+ <!-- Text shown to the user after they complete switch apps gesture tutorial [CHAR LIMIT=NONE] -->
+ <string name="touchpad_switch_apps_gesture_success_body">You completed the switch apps gesture.</string>
+ <!-- Text shown to the user after switch gesture was not done correctly [CHAR LIMIT=NONE] -->
+ <string name="touchpad_switch_gesture_error_body">Swipe left or right using four fingers on your touchpad to switch apps</string>
<!-- KEYBOARD TUTORIAL-->
<!-- Action key tutorial title [CHAR LIMIT=NONE] -->
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 83ca496dbef2..2b71c87bfa27 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
@@ -19,10 +19,11 @@ package com.android.systemui.shared.recents;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
+import android.os.IRemoteCallback;
import android.view.MotionEvent;
import com.android.systemui.shared.recents.ISystemUiProxy;
-// Next ID: 34
+// Next ID: 36
oneway interface IOverviewProxy {
void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
@@ -137,4 +138,10 @@ oneway interface IOverviewProxy {
* Sent when {@link TaskbarDelegate#appTransitionPending} is called.
*/
void appTransitionPending(boolean pending) = 34;
+
+ /**
+ * Sent right after OverviewProxy calls unbindService() on the TouchInteractionService.
+ * TouchInteractionService is expected to send the reply once it has finished cleaning up.
+ */
+ void onUnbind(IRemoteCallback reply) = 35;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index caf043a1b1be..b2f3df60c82b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -200,7 +200,15 @@ public class FullscreenMagnificationController implements ComponentCallbacks {
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(@NonNull Animator animation) {
- mHandler.post(() -> setState(ENABLED));
+ // This could be called when the animation ends or is canceled. Therefore, we need
+ // to check the state of fullscreen magnification for the following actions. We only
+ // update the state to ENABLED when the previous state is ENABLING which implies
+ // fullscreen magnification is experiencing an ongoing create border process.
+ mHandler.post(() -> {
+ if (getState() == ENABLING) {
+ setState(ENABLED);
+ }
+ });
}});
return valueAnimator;
}
@@ -221,7 +229,14 @@ public class FullscreenMagnificationController implements ComponentCallbacks {
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(@NonNull Animator animation) {
- mHandler.post(() -> cleanUpBorder());
+ // This could be called when the animation ends or is canceled. Therefore, we need
+ // to check the state of fullscreen magnification for the following actions. Border
+ // cleanup should only happens after a removal process.
+ mHandler.post(() -> {
+ if (getState() == DISABLING) {
+ cleanUpBorder();
+ }
+ });
}});
return valueAnimator;
}
@@ -250,6 +265,8 @@ public class FullscreenMagnificationController implements ComponentCallbacks {
// If there is an ongoing disable process or it is already disabled, return
return;
}
+ // The state should be updated as early as possible so others could check
+ // the ongoing process.
setState(DISABLING);
mShowHideBorderAnimator = createHideTargetAnimator(mFullscreenBorder);
mShowHideBorderAnimator.start();
@@ -297,10 +314,13 @@ public class FullscreenMagnificationController implements ComponentCallbacks {
// If there is an ongoing enable process or it is already enabled, return
return;
}
+ // The state should be updated as early as possible so others could check
+ // the ongoing process.
+ setState(ENABLING);
+
if (mShowHideBorderAnimator != null) {
mShowHideBorderAnimator.cancel();
}
- setState(ENABLING);
onConfigurationChanged(mContext.getResources().getConfiguration());
mContext.registerComponentCallbacks(this);
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index 6635d8b06a5d..a061d38d0c18 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -215,7 +215,8 @@ constructor(
override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
refreshingFlow(
- initialValue = false,
+ initialValue =
+ lockPatternUtils.isAutoPinConfirmEnabled(userRepository.getSelectedUserInfo().id),
getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled,
)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
index c05dcd5cea83..c59c6816a350 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
@@ -41,13 +41,12 @@ fun BouncerContainer(
Box {
Canvas(Modifier.fillMaxSize()) { drawRect(color = backgroundColor) }
- // Separate the bouncer content into a reusable composable that
- // doesn't have any SceneScope
- // dependencies
+ // Separate the bouncer content into a reusable composable that doesn't have any
+ // ContentScope dependencies
BouncerContent(
bouncerViewModel,
dialogFactory,
- Modifier.sysuiResTag(Bouncer.TestTags.Root).fillMaxSize()
+ Modifier.sysuiResTag(Bouncer.TestTags.Root).fillMaxSize(),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt
new file mode 100644
index 000000000000..c7b7050340a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal
+
+import android.annotation.SuppressLint
+import android.app.DreamManager
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+
+@SysUISingleton
+class DevicePosturingCommandListener
+@Inject
+constructor(private val commandRegistry: CommandRegistry, private val dreamManager: DreamManager) :
+ CoreStartable {
+ private val command = DevicePosturingCommand()
+
+ override fun start() {
+ commandRegistry.registerCommand(COMMAND_ROOT) { command }
+ }
+
+ internal inner class DevicePosturingCommand : Command {
+ @SuppressLint("MissingPermission")
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val arg = args.getOrNull(0)
+ if (arg == null || arg.lowercase() == "help") {
+ help(pw)
+ return
+ }
+
+ when (arg.lowercase()) {
+ "true" -> dreamManager.setDevicePostured(true)
+ "false" -> dreamManager.setDevicePostured(false)
+ else -> {
+ pw.println("Invalid argument!")
+ help(pw)
+ }
+ }
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: $ adb shell cmd statusbar device-postured <true|false>")
+ }
+ }
+
+ private companion object {
+ const val COMMAND_ROOT = "device-postured"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
index 2d19b026489c..e3443227685f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
@@ -22,6 +22,7 @@ import com.android.systemui.communal.CommunalDreamStartable
import com.android.systemui.communal.CommunalMetricsStartable
import com.android.systemui.communal.CommunalOngoingContentStartable
import com.android.systemui.communal.CommunalSceneStartable
+import com.android.systemui.communal.DevicePosturingCommandListener
import com.android.systemui.communal.log.CommunalLoggerStartable
import com.android.systemui.communal.widgets.CommunalAppWidgetHostStartable
import com.android.systemui.dagger.qualifiers.PerUser
@@ -67,4 +68,9 @@ interface CommunalStartableModule {
@IntoMap
@ClassKey(CommunalMetricsStartable::class)
fun bindCommunalMetricsStartable(impl: CommunalMetricsStartable): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(DevicePosturingCommandListener::class)
+ fun bindDevicePosturingCommandListener(impl: DevicePosturingCommandListener): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
index 7d684cab39f7..5e3b2ae0b59f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
@@ -132,8 +132,6 @@ constructor(
DeviceEntryRestrictionReason.UnattendedUpdate
authFlags.isPrimaryAuthRequiredAfterTimeout ->
DeviceEntryRestrictionReason.SecurityTimeout
- authFlags.isPrimaryAuthRequiredAfterLockout ->
- DeviceEntryRestrictionReason.BouncerLockedOut
isFingerprintLockedOut ->
DeviceEntryRestrictionReason.StrongBiometricsLockedOut
isFaceLockedOut && faceAuthInteractor.isFaceAuthStrong() ->
@@ -376,8 +374,7 @@ constructor(
private val interactor: DeviceUnlockedInteractor,
) : CoreStartable {
override fun start() {
- if (!SceneContainerFlag.isEnabled)
- return
+ if (!SceneContainerFlag.isEnabled) return
applicationScope.launch { interactor.activate() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
index 208a17c0a220..ebe603b7428c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
@@ -2,6 +2,8 @@ set noparent
# Bug component: 78010
+include /services/core/java/com/android/server/biometrics/OWNERS
+
amiko@google.com
beverlyt@google.com
bhinegardner@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index a74384f61469..58692746d1e0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard
import android.app.IActivityTaskManager
+import android.os.RemoteException
import android.util.Log
import android.view.IRemoteAnimationFinishedCallback
import android.view.RemoteAnimationTarget
@@ -174,25 +175,24 @@ constructor(
if (!isKeyguardGoingAway) {
// Since WM triggered this, we're likely not transitioning to GONE yet. See if we can
// start that transition.
- val startedDismiss =
- keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
- reason = "Going away remote animation started"
- )
-
- if (!startedDismiss) {
- // If the transition wasn't started, we're already GONE. This can happen with timing
- // issues, where the remote animation took a long time to start, and something else
- // caused us to unlock in the meantime. Since we're already GONE, simply end the
- // remote animatiom immediately.
- Log.d(
- TAG,
- "onKeyguardGoingAwayRemoteAnimationStart: " +
- "Dismiss transition was not started; we're already GONE. " +
- "Ending remote animation.",
- )
- finishedCallback.onAnimationFinished()
- return
- }
+ keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
+ reason = "Going away remote animation started",
+ onAlreadyGone = {
+ // Called if we're already GONE by the time the dismiss transition would have
+ // started. This can happen due to timing issues, where the remote animation
+ // took a long time to start, and something else caused us to unlock in the
+ // meantime. Since we're already GONE, simply end the remote animation
+ // immediately.
+ Log.d(
+ TAG,
+ "onKeyguardGoingAwayRemoteAnimationStart: " +
+ "Dismiss transition was not started; we're already GONE. " +
+ "Ending remote animation.",
+ )
+ finishedCallback.onAnimationFinished()
+ isKeyguardGoingAway = false
+ },
+ )
isKeyguardGoingAway = true
}
@@ -266,7 +266,11 @@ constructor(
if (enableNewKeyguardShellTransitions) {
startKeyguardTransition(lockscreenShowing, aodVisible)
} else {
- activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible)
+ try {
+ activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Remote exception", e)
+ }
}
this.isLockscreenShowing = lockscreenShowing
this.isAodVisible = aodVisible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt
index cc070b66917b..d3e2560d6a21 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.dagger
import android.content.res.Resources
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.ui.transitions.BlurConfig
@@ -34,7 +35,6 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransition
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToOccludedTransitionViewModel
import com.android.systemui.res.R
-import com.android.systemui.window.flag.WindowBlurFlag
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -56,7 +56,7 @@ interface PrimaryBouncerTransitionModule {
fun provideBlurConfig(@Main resources: Resources): BlurConfig {
val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
val maxBlurRadius =
- if (WindowBlurFlag.isEnabled) {
+ if (Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) {
resources.getDimensionPixelSize(R.dimen.max_shade_window_blur_radius)
} else {
resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
index 089e5dc42df3..c0a486c005ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
@@ -16,23 +16,31 @@
package com.android.systemui.keyguard.domain.interactor
+import android.animation.ValueAnimator
import android.util.Log
import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
@SysUISingleton
class KeyguardDismissTransitionInteractor
@Inject
constructor(
+ @Background private val scope: CoroutineScope,
private val repository: KeyguardTransitionRepository,
private val fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor,
private val fromPrimaryBouncerTransitionInteractor: FromPrimaryBouncerTransitionInteractor,
@@ -43,45 +51,63 @@ constructor(
) {
/**
- * Called to start a transition that will ultimately dismiss the keyguard from the current
- * state.
+ * Launches a coroutine to start a transition that will ultimately dismiss the keyguard from the
+ * current state.
*
* This is called exclusively by sources that can authoritatively say we should be unlocked,
* including KeyguardSecurityContainerController and WindowManager.
*
- * Returns [false] if the transition was not started, because we're already GONE or we don't
- * know how to dismiss keyguard from the current state.
+ * This is one of the few transitions that is started outside of the From*TransitionInteractor
+ * classes. This is because this is an external call that must be respected, so it doesn't
+ * matter what state we're in/coming from - we must transition from that state to GONE.
+ *
+ * Invokes [onAlreadyGone] if the transition was not started because we're already GONE by the
+ * time the coroutine runs.
*/
- fun startDismissKeyguardTransition(reason: String = ""): Boolean {
- if (SceneContainerFlag.isEnabled) return false
+ @JvmOverloads
+ fun startDismissKeyguardTransition(reason: String = "", onAlreadyGone: (() -> Unit)? = null) {
+ if (SceneContainerFlag.isEnabled) return
Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
- val startedState =
- if (transitionRaceCondition()) {
- repository.currentTransitionInfo.to
+
+ scope.launch {
+ val startedState =
+ if (transitionRaceCondition()) {
+ repository.currentTransitionInfo.to
+ } else {
+ repository.currentTransitionInfoInternal.value.to
+ }
+
+ val animator: ValueAnimator? =
+ when (startedState) {
+ LOCKSCREEN -> fromLockscreenTransitionInteractor
+ PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor
+ ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor
+ AOD -> fromAodTransitionInteractor
+ DOZING -> fromDozingTransitionInteractor
+ OCCLUDED -> fromOccludedTransitionInteractor
+ else -> null
+ }?.getDefaultAnimatorForTransitionsToState(KeyguardState.GONE)
+
+ if (startedState != KeyguardState.GONE && animator != null) {
+ repository.startTransition(
+ TransitionInfo(
+ "KeyguardDismissTransitionInteractor" +
+ if (reason.isNotBlank()) "($reason)" else "",
+ startedState,
+ KeyguardState.GONE,
+ animator,
+ TransitionModeOnCanceled.LAST_VALUE,
+ )
+ )
} else {
- repository.currentTransitionInfoInternal.value.to
- }
- when (startedState) {
- LOCKSCREEN -> fromLockscreenTransitionInteractor.dismissKeyguard()
- PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.dismissPrimaryBouncer()
- ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor.dismissAlternateBouncer()
- AOD -> fromAodTransitionInteractor.dismissAod()
- DOZING -> fromDozingTransitionInteractor.dismissFromDozing()
- KeyguardState.OCCLUDED -> fromOccludedTransitionInteractor.dismissFromOccluded()
- KeyguardState.GONE -> {
Log.i(
TAG,
- "Already transitioning to GONE; ignoring startDismissKeyguardTransition.",
+ "Can't transition to GONE from $startedState; " +
+ "ignoring startDismissKeyguardTransition.",
)
- return false
- }
- else -> {
- Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
- return false
+ onAlreadyGone?.invoke()
}
}
-
- return true
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index 213083db71c9..9c886b228ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -31,6 +31,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.flow.combine
object KeyguardSmartspaceViewBinder {
@JvmStatic
@@ -43,21 +44,25 @@ object KeyguardSmartspaceViewBinder {
return keyguardRootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch("$TAG#clockViewModel.hasCustomWeatherDataDisplay") {
- clockViewModel.hasCustomWeatherDataDisplay.collect { hasCustomWeatherDataDisplay
- ->
- updateDateWeatherToBurnInLayer(
- keyguardRootView,
- clockViewModel,
- smartspaceViewModel,
+ combine(
+ smartspaceViewModel.isWeatherVisible,
+ clockViewModel.hasCustomWeatherDataDisplay,
+ ::Pair,
)
- blueprintInteractor.refreshBlueprint(
- Config(
- Type.SmartspaceVisibility,
- checkPriority = false,
- terminatePrevious = false,
+ .collect {
+ updateDateWeatherToBurnInLayer(
+ keyguardRootView,
+ clockViewModel,
+ smartspaceViewModel,
)
- )
- }
+ blueprintInteractor.refreshBlueprint(
+ Config(
+ Type.SmartspaceVisibility,
+ checkPriority = false,
+ terminatePrevious = false,
+ )
+ )
+ }
}
launch("$TAG#smartspaceViewModel.bcSmartspaceVisibility") {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index cd038d799f42..9319bc890c6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -23,6 +23,8 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener
import androidx.constraintlayout.widget.Barrier
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.GONE
+import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
@@ -195,24 +197,13 @@ constructor(
smartspaceController.requestSmartspaceUpdate()
constraintSet.apply {
- val weatherVisibility =
- when (keyguardSmartspaceViewModel.isWeatherVisible.value) {
- true -> ConstraintSet.VISIBLE
- false -> ConstraintSet.GONE
- }
- setVisibility(sharedR.id.weather_smartspace_view, weatherVisibility)
- setAlpha(
- sharedR.id.weather_smartspace_view,
- if (weatherVisibility == View.VISIBLE) 1f else 0f,
- )
- val dateVisibility =
- if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) ConstraintSet.GONE
- else ConstraintSet.VISIBLE
- setVisibility(sharedR.id.date_smartspace_view, dateVisibility)
- setAlpha(
- sharedR.id.date_smartspace_view,
- if (dateVisibility == ConstraintSet.VISIBLE) 1f else 0f,
- )
+ val showWeather = keyguardSmartspaceViewModel.isWeatherVisible.value
+ setVisibility(sharedR.id.weather_smartspace_view, if (showWeather) VISIBLE else GONE)
+ setAlpha(sharedR.id.weather_smartspace_view, if (showWeather) 1f else 0f)
+
+ val showDateView = !keyguardClockViewModel.hasCustomWeatherDataDisplay.value
+ setVisibility(sharedR.id.date_smartspace_view, if (showDateView) VISIBLE else GONE)
+ setAlpha(sharedR.id.date_smartspace_view, if (showDateView) 1f else 0f)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index 92bb5e6029cb..733d7d71061e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -30,7 +30,6 @@ import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION
-import com.android.systemui.window.flag.WindowBlurFlag
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -73,7 +72,7 @@ constructor(
onStep = alphaForAnimationStep,
)
- val lockscreenAlpha: Flow<Float> = if (WindowBlurFlag.isEnabled) alphaFlow else emptyFlow()
+ val lockscreenAlpha: Flow<Float> = if (Flags.bouncerUiRevamp()) alphaFlow else emptyFlow()
val notificationAlpha: Flow<Float> =
if (Flags.bouncerUiRevamp()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
index 5ee80a7b7442..f8425c16c341 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
@@ -77,7 +77,7 @@ constructor(
isWeatherVisible(
clockIncludesCustomWeatherDisplay =
keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
- isWeatherEnabled = smartspaceInteractor.isWeatherEnabled.value,
+ isWeatherEnabled = isWeatherEnabled.value,
),
)
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 a6b9442b1270..71c8d1f5b4c7 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
@@ -130,7 +130,7 @@ constructor(
null, // no action to perform when clicked
context.getString(R.string.controls_media_button_connecting),
if (Flags.mediaControlsUiUpdate()) {
- context.getDrawable(R.drawable.ic_media_connecting_status_container)
+ context.getDrawable(R.drawable.ic_media_connecting_button_container)
} else {
context.getDrawable(R.drawable.ic_media_connecting_container)
},
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
index 9bf556cf07c2..5fef81f4596a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
@@ -71,7 +71,7 @@ fun createActionsFromState(
null, // no action to perform when clicked
context.getString(R.string.controls_media_button_connecting),
if (Flags.mediaControlsUiUpdate()) {
- context.getDrawable(R.drawable.ic_media_connecting_status_container)
+ context.getDrawable(R.drawable.ic_media_connecting_button_container)
} else {
context.getDrawable(R.drawable.ic_media_connecting_container)
},
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 65fba28c5465..b53685ee3cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -88,11 +88,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
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.content.state.TransitionState
import com.android.compose.animation.scene.transitions
@@ -580,7 +580,7 @@ constructor(
}
@Composable
- private fun SceneScope.QuickQuickSettingsElement() {
+ private fun ContentScope.QuickQuickSettingsElement() {
val qqsPadding = viewModel.qqsHeaderHeight
val bottomPadding = viewModel.qqsBottomPadding
DisposableEffect(Unit) {
@@ -664,7 +664,7 @@ constructor(
}
@Composable
- private fun SceneScope.QuickSettingsElement() {
+ private fun ContentScope.QuickSettingsElement() {
val qqsPadding = viewModel.qqsHeaderHeight
val qsExtraPadding = dimensionResource(R.dimen.qs_panel_padding_top)
Column(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
index a22eb3a8d517..185ea93387a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
@@ -18,7 +18,7 @@ package com.android.systemui.qs.panels.ui.compose
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.TileRow
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
@@ -27,7 +27,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
/** A layout of tiles, indicating how they should be composed when showing in QS or in edit mode. */
interface GridLayout {
- @Composable fun SceneScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier)
+ @Composable fun ContentScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier)
@Composable
fun EditTileGrid(
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 39408d3dee72..c72381f45239 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
@@ -40,7 +40,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.padding
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.development.ui.compose.BuildNumber
@@ -63,7 +63,7 @@ constructor(
@PaginatedBaseLayoutType private val delegateGridLayout: PaginatableGridLayout,
) : GridLayout by delegateGridLayout {
@Composable
- override fun SceneScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier) {
+ override fun ContentScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier) {
val viewModel =
rememberViewModel(traceName = "PaginatedGridLayout-TileGrid") {
viewModelFactory.create()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index 8fda23d54625..5cb30b999e13 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -27,7 +27,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.util.fastMap
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
import com.android.systemui.qs.composefragment.ui.GridAnchor
@@ -38,7 +38,7 @@ import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey
import com.android.systemui.res.R
@Composable
-fun SceneScope.QuickQuickSettings(
+fun ContentScope.QuickQuickSettings(
viewModel: QuickQuickSettingsViewModel,
modifier: Modifier = Modifier,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
index 6c1906bb906f..bcc44d397eb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
@@ -20,11 +20,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
@Composable
-fun SceneScope.TileGrid(viewModel: TileGridViewModel, modifier: Modifier = Modifier) {
+fun ContentScope.TileGrid(viewModel: TileGridViewModel, modifier: Modifier = Modifier) {
val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
val tiles by viewModel.tileViewModels.collectAsStateWithLifecycle(emptyList())
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 66961b6efe46..4432d336237f 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
@@ -26,7 +26,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.util.fastMap
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
@@ -58,7 +58,7 @@ constructor(
) : PaginatableGridLayout {
@Composable
- override fun SceneScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier) {
+ override fun ContentScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier) {
DisposableEffect(tiles) {
val token = Any()
tiles.forEach { it.startListening(token) }
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 e93cec875429..42b35c736d42 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,9 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
@Override
public boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) {
- handleClick(() ->
- callback.accept(new ScreenRecordDetailsViewModel())
+ handleClick(() -> executeWhenUnlockedKeyguard(
+ () -> callback.accept(new ScreenRecordDetailsViewModel(mController,
+ this::onStartRecordingClicked)))
);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
index 340cb68a83a4..6b5a22a4fc09 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
@@ -1506,15 +1506,17 @@ public class InternetDetailsContentController implements AccessPointController.A
Intent getConfiguratorQrCodeGeneratorIntentOrNull(WifiEntry wifiEntry) {
if (!mFeatureFlags.isEnabled(Flags.SHARE_WIFI_QS_BUTTON) || wifiEntry == null
- || mWifiManager == null || !wifiEntry.canShare()
- || wifiEntry.getWifiConfiguration() == null) {
+ || mWifiManager == null || !wifiEntry.canShare()) {
+ return null;
+ }
+ var wifiConfiguration = wifiEntry.getWifiConfiguration();
+ if (wifiConfiguration == null) {
return null;
}
Intent intent = new Intent();
intent.setAction(WifiDppIntentHelper.ACTION_CONFIGURATOR_AUTH_QR_CODE_GENERATOR);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- WifiDppIntentHelper.setConfiguratorIntentExtra(intent, mWifiManager,
- wifiEntry.getWifiConfiguration());
+ WifiDppIntentHelper.setConfiguratorIntentExtra(intent, mWifiManager, wifiConfiguration);
return intent;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt
index 42cb1248ccff..54e4a521c239 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt
@@ -17,27 +17,45 @@
package com.android.systemui.qs.tiles.dialog
import android.view.LayoutInflater
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.res.R
+import com.android.systemui.screenrecord.RecordingController
+import com.android.systemui.screenrecord.ScreenRecordPermissionViewBinder
/** The view model used for the screen record details view in the Quick Settings */
-class ScreenRecordDetailsViewModel() : TileDetailsViewModel() {
+class ScreenRecordDetailsViewModel(
+ private val recordingController: RecordingController,
+ private val onStartRecordingClicked: Runnable,
+) : TileDetailsViewModel() {
+
+ private var viewBinder: ScreenRecordPermissionViewBinder =
+ recordingController.createScreenRecordPermissionViewBinder(onStartRecordingClicked)
+
@Composable
override fun GetContentView() {
// TODO(b/378514312): Finish implementing this function.
+
+ if (recordingController.isScreenCaptureDisabled) {
+ // TODO(b/388345506): Show disabled page here.
+ return
+ }
+
AndroidView(
- modifier = Modifier.fillMaxWidth().heightIn(max = VIEW_MAX_HEIGHT),
+ modifier = Modifier.fillMaxWidth().fillMaxHeight(),
factory = { context ->
// Inflate with the existing dialog xml layout
- LayoutInflater.from(context).inflate(R.layout.screen_share_dialog, null)
+ val view = LayoutInflater.from(context).inflate(R.layout.screen_share_dialog, null)
+ viewBinder.bind(view)
+
+ view
+ // TODO(b/378514473): Revamp the details view according to the spec.
},
+ onRelease = { viewBinder.unbind() },
)
}
@@ -54,8 +72,4 @@ class ScreenRecordDetailsViewModel() : TileDetailsViewModel() {
// No sub-title in this tile.
return ""
}
-
- companion object {
- private val VIEW_MAX_HEIGHT: Dp = 320.dp
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
index 594394f68d48..5ce7f0d039c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
@@ -29,6 +29,7 @@ import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogEventLogger
import javax.inject.Inject
@SysUISingleton
@@ -39,6 +40,7 @@ constructor(
// TODO(b/353896370): The domain layer should not have to depend on the UI layer.
private val dialogDelegate: ModesDialogDelegate,
private val zenModeInteractor: ZenModeInteractor,
+ private val dialogEventLogger: ModesDialogEventLogger,
) : QSTileUserActionInteractor<ModesTileModel> {
val longClickIntent = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
@@ -78,7 +80,16 @@ constructor(
Log.wtf(TAG, "Triggered DND but it's null!?")
return
}
- zenModeInteractor.activateMode(dnd)
+
+ if (zenModeInteractor.shouldAskForZenDuration(dnd)) {
+ dialogEventLogger.logOpenDurationDialog(dnd)
+ // NOTE: The dialog handles turning on the mode itself.
+ val dialog = dialogDelegate.makeDndDurationDialog()
+ dialog.show()
+ } else {
+ dialogEventLogger.logModeOn(dnd)
+ zenModeInteractor.activateMode(dnd)
+ }
} else {
zenModeInteractor.deactivateAllModes()
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index e3cf41191384..adf9eb44e162 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -58,6 +58,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.PatternMatcher;
import android.os.Process;
@@ -146,7 +147,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public static final String TAG_OPS = "OverviewProxyService";
private static final long BACKOFF_MILLIS = 1000;
private static final long DEFERRED_CALLBACK_MILLIS = 5000;
-
// Max backoff caps at 5 mins
private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
@@ -183,6 +183,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private int mConnectionBackoffAttempts;
private boolean mBound;
private boolean mIsEnabled;
+ // This is set to false when the overview service is requested to be bound until it is notified
+ // that the previous service has been cleaned up in IOverviewProxy#onUnbind(). It is also set to
+ // true after a 1000ms timeout by mDeferredBindAfterTimedOutCleanup.
+ private boolean mIsPrevServiceCleanedUp = true;
private boolean mIsSystemOrVisibleBgUser;
private int mCurrentBoundedUserId = -1;
@@ -489,6 +493,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
retryConnectionWithBackoff();
};
+ private final Runnable mDeferredBindAfterTimedOutCleanup = () -> {
+ Log.w(TAG_OPS, "Timed out waiting for previous service to clean up, binding to new one");
+ mIsPrevServiceCleanedUp = true;
+ maybeBindService();
+ };
+
private final BroadcastReceiver mUserEventReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -859,6 +869,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
mShadeViewControllerLazy.get().cancelInputFocusTransfer();
});
}
+ mIsPrevServiceCleanedUp = true;
startConnectionToCurrentUser();
}
@@ -889,6 +900,19 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
mHandler.removeCallbacks(mConnectionRunnable);
+ maybeBindService();
+ }
+
+ private void maybeBindService() {
+ if (!mIsPrevServiceCleanedUp) {
+ Log.w(TAG_OPS, "Skipping connection to TouchInteractionService until previous"
+ + " instance is cleaned up.");
+ if (!mHandler.hasCallbacks(mDeferredConnectionCallback)) {
+ mHandler.postDelayed(mDeferredBindAfterTimedOutCleanup, BACKOFF_MILLIS);
+ }
+ return;
+ }
+
// Avoid creating TouchInteractionService because the System user in HSUM mode does not
// interact with UI elements
UserHandle currentUser = UserHandle.of(mUserTracker.getUserId());
@@ -907,6 +931,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
if (mBound) {
+ mIsPrevServiceCleanedUp = false;
// Ensure that connection has been established even if it thinks it is bound
mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);
} else {
@@ -960,6 +985,24 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
// Always unbind the service (ie. if called through onNullBinding or onBindingDied)
mContext.unbindService(mOverviewServiceConnection);
mBound = false;
+ if (mOverviewProxy != null) {
+ try {
+ mOverviewProxy.onUnbind(new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ // Received Launcher reply, try to bind anew.
+ mIsPrevServiceCleanedUp = true;
+ if (mHandler.hasCallbacks(mDeferredBindAfterTimedOutCleanup)) {
+ mHandler.removeCallbacks(mDeferredBindAfterTimedOutCleanup);
+ maybeBindService();
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Log.w(TAG_OPS, "disconnectFromLauncherService failed to notify Launcher");
+ mIsPrevServiceCleanedUp = true;
+ }
+ }
}
if (mOverviewProxy != null) {
@@ -1189,6 +1232,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis);
pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
pw.print(" mNavBarMode="); pw.println(mNavBarMode);
+ pw.print(" mIsPrevServiceCleanedUp="); pw.println(mIsPrevServiceCleanedUp);
mSysUiState.dump(pw, args);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 9ee99e45ceeb..140fbf340f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -70,6 +70,8 @@ public class RecordingController
private final ScreenCaptureDisabledDialogDelegate mScreenCaptureDisabledDialogDelegate;
private final ScreenRecordPermissionDialogDelegate.Factory
mScreenRecordPermissionDialogDelegateFactory;
+ private final ScreenRecordPermissionViewBinder.Factory
+ mScreenRecordPermissionViewBinderFactory;
protected static final String INTENT_UPDATE_STATE =
"com.android.systemui.screenrecord.UPDATE_STATE";
@@ -118,7 +120,8 @@ public class RecordingController
MediaProjectionMetricsLogger mediaProjectionMetricsLogger,
ScreenCaptureDisabledDialogDelegate screenCaptureDisabledDialogDelegate,
ScreenRecordPermissionDialogDelegate.Factory
- screenRecordPermissionDialogDelegateFactory) {
+ screenRecordPermissionDialogDelegateFactory,
+ ScreenRecordPermissionViewBinder.Factory screenRecordPermissionViewBinderFactory) {
mMainExecutor = mainExecutor;
mDevicePolicyResolver = devicePolicyResolver;
mBroadcastDispatcher = broadcastDispatcher;
@@ -127,6 +130,7 @@ public class RecordingController
mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
mScreenCaptureDisabledDialogDelegate = screenCaptureDisabledDialogDelegate;
mScreenRecordPermissionDialogDelegateFactory = screenRecordPermissionDialogDelegateFactory;
+ mScreenRecordPermissionViewBinderFactory = screenRecordPermissionViewBinderFactory;
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
@@ -151,8 +155,7 @@ public class RecordingController
* If screen capturing is currently not allowed it will return a dialog
* that warns users about it. */
public Dialog createScreenRecordDialog(@Nullable Runnable onStartRecordingClicked) {
- if (mDevicePolicyResolver.get()
- .isScreenCaptureCompletelyDisabled(getHostUserHandle())) {
+ if (isScreenCaptureDisabled()) {
return mScreenCaptureDisabledDialogDelegate.createSysUIDialog();
}
@@ -165,6 +168,27 @@ public class RecordingController
}
/**
+ * Create a view binder that controls the logic of views inside the screen record permission
+ * view.
+ * @param onStartRecordingClicked the callback that is run when the start button is clicked.
+ */
+ public ScreenRecordPermissionViewBinder createScreenRecordPermissionViewBinder(
+ @Nullable Runnable onStartRecordingClicked
+ ) {
+ return mScreenRecordPermissionViewBinderFactory
+ .create(getHostUserHandle(), getHostUid(), this,
+ onStartRecordingClicked);
+ }
+
+ /**
+ * Check if screen capture is currently disabled for this device and user.
+ */
+ public boolean isScreenCaptureDisabled() {
+ return mDevicePolicyResolver.get()
+ .isScreenCaptureCompletelyDisabled(getHostUserHandle());
+ }
+
+ /**
* Start counting down in preparation to start a recording
* @param ms Total time in ms to wait before starting
* @param interval Time in ms per countdown step
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
index 9fcb3dfc0ad3..23df1c5441bf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
@@ -49,6 +49,9 @@ import com.android.systemui.mediaprojection.permission.ScreenShareOption
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
class ScreenRecordPermissionViewBinder(
private val hostUserHandle: UserHandle,
@@ -68,6 +71,38 @@ class ScreenRecordPermissionViewBinder(
mediaProjectionMetricsLogger,
defaultSelectedMode,
) {
+ @AssistedInject
+ constructor(
+ @Assisted hostUserHandle: UserHandle,
+ @Assisted hostUid: Int,
+ mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+ displayManager: DisplayManager,
+ @Assisted controller: RecordingController,
+ activityStarter: ActivityStarter,
+ userContextProvider: UserContextProvider,
+ @Assisted onStartRecordingClicked: Runnable?,
+ ) : this(
+ hostUserHandle,
+ hostUid,
+ mediaProjectionMetricsLogger,
+ defaultSelectedMode = SINGLE_APP,
+ displayManager,
+ controller,
+ activityStarter,
+ userContextProvider,
+ onStartRecordingClicked,
+ )
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ hostUserHandle: UserHandle,
+ hostUid: Int,
+ recordingController: RecordingController,
+ onStartRecordingClicked: Runnable?,
+ ): ScreenRecordPermissionViewBinder
+ }
+
private lateinit var tapsSwitch: Switch
private lateinit var audioSwitch: Switch
private lateinit var tapsView: View
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 0650f8606ba9..9a1ffcbab8d1 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -35,7 +35,6 @@ import android.view.animation.DecelerateInterpolator;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
-import com.android.systemui.window.flag.WindowBlurFlag;
/**
* Drawable used on SysUI scrims.
@@ -214,10 +213,6 @@ public class ScrimDrawable extends Drawable {
public void draw(@NonNull Canvas canvas) {
mPaint.setColor(mMainColor);
mPaint.setAlpha(mAlpha);
- if (WindowBlurFlag.isEnabled()) {
- // TODO (b/381263600), wire this at ScrimController, move it to PrimaryBouncerTransition
- mPaint.setAlpha((int) (0.5f * mAlpha));
- }
if (mConcaveInfo != null) {
drawConcave(canvas);
} else if (mCornerRadiusEnabled && mCornerRadius > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 03a8d17847f9..49f3cfc4ceaf 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -39,10 +39,8 @@ import androidx.core.graphics.ColorUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
-import com.android.systemui.res.R;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.util.LargeScreenUtils;
-import com.android.systemui.window.flag.WindowBlurFlag;
import java.util.concurrent.Executor;
@@ -252,13 +250,6 @@ public class ScrimView extends View {
if (mBlendWithMainColor) {
mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount);
}
- if (WindowBlurFlag.isEnabled()) {
- int layerAbove = ColorUtils.setAlphaComponent(
- getResources().getColor(R.color.shade_panel, null),
- (int) (0.4f * 255));
- int layerBelow = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.1f * 255));
- mainTinted = ColorUtils.compositeColors(layerAbove, layerBelow);
- }
drawable.setColor(mainTinted, animated);
} else {
boolean hasAlpha = Color.alpha(mTintColor) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 38f7c39203f0..ca2fbdd1cdd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -34,6 +34,7 @@ import androidx.dynamicanimation.animation.SpringForce
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.TrackTracer
import com.android.systemui.Dumpable
+import com.android.systemui.Flags
import com.android.systemui.Flags.spatialModelAppPushback
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.dagger.SysUISingleton
@@ -52,8 +53,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.WallpaperController
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
-import com.android.systemui.window.flag.WindowBlurFlag
import com.android.wm.shell.appzoomout.AppZoomOut
+
import java.io.PrintWriter
import java.util.Optional
import javax.inject.Inject
@@ -230,7 +231,7 @@ constructor(
val zoomOut = blurRadiusToZoomOut(blurRadius = shadeRadius)
// Make blur be 0 if it is necessary to stop blur effect.
if (scrimsVisible) {
- if (!WindowBlurFlag.isEnabled) {
+ if (!Flags.notificationShadeBlur()) {
blur = 0
}
}
@@ -258,7 +259,9 @@ constructor(
}
private val shouldBlurBeOpaque: Boolean
- get() = if (WindowBlurFlag.isEnabled) false else scrimsVisible && !blursDisabledForAppLaunch
+ get() =
+ if (Flags.notificationShadeBlur()) false
+ else scrimsVisible && !blursDisabledForAppLaunch
/** Callback that updates the window blur value and is called only once per frame. */
@VisibleForTesting
@@ -388,7 +391,7 @@ constructor(
}
private fun initBlurListeners() {
- if (!WindowBlurFlag.isEnabled) return
+ if (!Flags.bouncerUiRevamp()) return
applicationScope.launch {
Log.d(TAG, "Starting coroutines for window root view blur")
@@ -523,7 +526,7 @@ constructor(
private fun scheduleUpdate() {
val (blur, zoomOutFromShadeRadius) = computeBlurAndZoomOut()
zoomOutCalculatedFromShadeRadius = zoomOutFromShadeRadius
- if (WindowBlurFlag.isEnabled) {
+ if (Flags.bouncerUiRevamp()) {
updateScheduled =
windowRootViewBlurInteractor.requestBlurForShade(blur, shouldBlurBeOpaque)
return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 72b03bfa20c3..b2764e1a2302 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -16,6 +16,12 @@ per-file *Keyguard* = set noparent
per-file *Keyguard* = file:../keyguard/OWNERS
# Not setting noparent here, since *Notification* also matches some status bar notification chips files (statusbar/chips/notification) which should be owned by the status bar team.
per-file *Notification* = file:notification/OWNERS
+# Files that control blur effects on shade
+per-file *NotificationShadeDepth* = set noparent
+per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com
+per-file *NotificationShadeDepth* = file:../keyguard/OWNERS
+per-file *Blur* = set noparent
+per-file *Blur* = shanh@google.com, rahulbanerjee@google.com
# Not setting noparent here, since *Mode* matches many other classes (e.g., *ViewModel*)
per-file *Mode* = file:notification/OWNERS
per-file *RemoteInput* = set noparent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 86954d569199..108d737e7658 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -30,7 +30,9 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel.Companion.toCustomColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
@@ -76,14 +78,24 @@ constructor(
OngoingActivityChipModel.ChipIcon.SingleColorIcon(phoneIcon)
}
+ val colors =
+ if (StatusBarNotifChips.isEnabled && state.promotedContent != null) {
+ state.promotedContent.toCustomColorsModel()
+ } else {
+ ColorsModel.Themed
+ }
+
// This block mimics OngoingCallController#updateChip.
if (state.startTimeMs <= 0L) {
// If the start time is invalid, don't show a timer and show just an
// icon. See b/192379214.
OngoingActivityChipModel.Shown.IconOnly(
icon = icon,
- colors = ColorsModel.Themed,
- getOnClickListener(state),
+ colors = colors,
+ onClickListenerLegacy = getOnClickListener(state),
+ // TODO(b/372657935): Add click support for the call chip when
+ // StatusBarChipModernization is enabled.
+ clickBehavior = OngoingActivityChipModel.ClickBehavior.None,
)
} else {
val startTimeInElapsedRealtime =
@@ -91,9 +103,12 @@ constructor(
systemClock.elapsedRealtime()
OngoingActivityChipModel.Shown.Timer(
icon = icon,
- colors = ColorsModel.Themed,
+ colors = colors,
startTimeMs = startTimeInElapsedRealtime,
- getOnClickListener(state),
+ onClickListenerLegacy = getOnClickListener(state),
+ // TODO(b/372657935): Add click support for the call chip when
+ // StatusBarChipModernization is enabled.
+ clickBehavior = OngoingActivityChipModel.ClickBehavior.None,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
index 3422337523f9..baa8eec5f767 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.ChipTransitionHelper
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -204,13 +205,25 @@ constructor(
colors = ColorsModel.Red,
// TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
startTimeMs = systemClock.elapsedRealtime(),
- createDialogLaunchOnClickListener(
- createCastScreenToOtherDeviceDialogDelegate(state),
- dialogTransitionAnimator,
- DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Cast to other device"),
- logger,
- TAG,
- ),
+ onClickListenerLegacy =
+ createDialogLaunchOnClickListener(
+ createCastScreenToOtherDeviceDialogDelegate(state),
+ dialogTransitionAnimator,
+ DIALOG_CUJ,
+ logger,
+ TAG,
+ ),
+ clickBehavior =
+ OngoingActivityChipModel.ClickBehavior.ExpandAction(
+ onClick =
+ createDialogLaunchOnClickCallback(
+ createCastScreenToOtherDeviceDialogDelegate(state),
+ dialogTransitionAnimator,
+ DIALOG_CUJ,
+ logger,
+ TAG,
+ )
+ ),
)
}
@@ -225,16 +238,24 @@ constructor(
)
),
colors = ColorsModel.Red,
- createDialogLaunchOnClickListener(
- createGenericCastToOtherDeviceDialogDelegate(deviceName),
- dialogTransitionAnimator,
- DialogCuj(
- Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP,
- tag = "Cast to other device audio only",
+ onClickListenerLegacy =
+ createDialogLaunchOnClickListener(
+ createGenericCastToOtherDeviceDialogDelegate(deviceName),
+ dialogTransitionAnimator,
+ DIALOG_CUJ_AUDIO_ONLY,
+ logger,
+ TAG,
+ ),
+ clickBehavior =
+ OngoingActivityChipModel.ClickBehavior.ExpandAction(
+ createDialogLaunchOnClickCallback(
+ createGenericCastToOtherDeviceDialogDelegate(deviceName),
+ dialogTransitionAnimator,
+ DIALOG_CUJ_AUDIO_ONLY,
+ logger,
+ TAG,
+ )
),
- logger,
- TAG,
- ),
)
}
@@ -256,6 +277,13 @@ constructor(
companion object {
@DrawableRes val CAST_TO_OTHER_DEVICE_ICON = R.drawable.ic_cast_connected
+ private val DIALOG_CUJ =
+ DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Cast to other device")
+ private val DIALOG_CUJ_AUDIO_ONLY =
+ DialogCuj(
+ Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP,
+ tag = "Cast to other device audio only",
+ )
private val TAG = "CastToOtherVM".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
index 2121f94caced..4fad01d9f448 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
@@ -28,6 +28,7 @@ import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor.Companion.isOngoingCallNotification
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -41,6 +42,7 @@ import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
/** An interactor for the notification chips shown in the status bar. */
@SysUISingleton
@@ -88,45 +90,56 @@ constructor(
private val promotedNotificationInteractors =
MutableStateFlow<List<SingleNotificationChipInteractor>>(emptyList())
+ /**
+ * The notifications that are promoted and ongoing.
+ *
+ * Explicitly does *not* include any ongoing call notifications, even if the call notifications
+ * meet the promotion criteria. Those call notifications will be handled by
+ * [com.android.systemui.statusbar.chips.call.domain.CallChipInteractor] instead. See
+ * b/388521980.
+ */
+ private val promotedOngoingNotifications =
+ activeNotificationsInteractor.promotedOngoingNotifications.map { notifs ->
+ notifs.filterNot { it.isOngoingCallNotification() }
+ }
+
override fun start() {
if (!StatusBarNotifChips.isEnabled) {
return
}
backgroundScope.launch("StatusBarNotificationChipsInteractor") {
- activeNotificationsInteractor.promotedOngoingNotifications
- .pairwise(initialValue = emptyList())
- .collect { (oldNotifs, currentNotifs) ->
- val removedNotifKeys =
- oldNotifs.map { it.key }.minus(currentNotifs.map { it.key }.toSet())
- removedNotifKeys.forEach { removedNotifKey ->
- val wasRemoved = promotedNotificationInteractorMap.remove(removedNotifKey)
- if (wasRemoved == null) {
- logger.w({
- "Attempted to remove $str1 from interactor map but it wasn't present"
- }) {
- str1 = removedNotifKey
- }
+ promotedOngoingNotifications.pairwise(initialValue = emptyList()).collect {
+ (oldNotifs, currentNotifs) ->
+ val removedNotifKeys =
+ oldNotifs.map { it.key }.minus(currentNotifs.map { it.key }.toSet())
+ removedNotifKeys.forEach { removedNotifKey ->
+ val wasRemoved = promotedNotificationInteractorMap.remove(removedNotifKey)
+ if (wasRemoved == null) {
+ logger.w({
+ "Attempted to remove $str1 from interactor map but it wasn't present"
+ }) {
+ str1 = removedNotifKey
}
}
+ }
- currentNotifs.forEach { notif ->
- val interactor =
- promotedNotificationInteractorMap.computeIfAbsent(notif.key) {
- singleNotificationChipInteractorFactory.create(
- notif,
- creationTime = systemClock.currentTimeMillis(),
- )
- }
- interactor.setNotification(notif)
- }
- logger.d({ "Interactors: $str1" }) {
- str1 =
- promotedNotificationInteractorMap.keys.joinToString(separator = " /// ")
- }
- promotedNotificationInteractors.value =
- promotedNotificationInteractorMap.values.toList()
+ currentNotifs.forEach { notif ->
+ val interactor =
+ promotedNotificationInteractorMap.computeIfAbsent(notif.key) {
+ singleNotificationChipInteractorFactory.create(
+ notif,
+ creationTime = systemClock.currentTimeMillis(),
+ )
+ }
+ interactor.setNotification(notif)
}
+ logger.d({ "Interactors: $str1" }) {
+ str1 = promotedNotificationInteractorMap.keys.joinToString(separator = " /// ")
+ }
+ promotedNotificationInteractors.value =
+ promotedNotificationInteractorMap.values.toList()
+ }
}
}
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 ec3a5b271e35..b7cad625b7b8 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
@@ -23,7 +23,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
-import com.android.systemui.statusbar.chips.ui.model.ColorsModel
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel.Companion.toCustomColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
@@ -72,11 +72,7 @@ constructor(
StatusBarConnectedDisplays.assertInNewMode()
OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(this.key)
}
- val colors =
- ColorsModel.Custom(
- backgroundColorInt = this.promotedContent.colors.backgroundColor,
- primaryTextColorInt = this.promotedContent.colors.primaryTextColor,
- )
+ val colors = this.promotedContent.toCustomColorsModel()
val onClickListener =
View.OnClickListener {
// The notification pipeline needs everything to run on the main thread, so keep
@@ -87,6 +83,7 @@ constructor(
)
}
}
+ val clickBehavior = OngoingActivityChipModel.ClickBehavior.None
val isShowingHeadsUpFromChipTap =
headsUpState is TopPinnedState.Pinned &&
@@ -95,7 +92,12 @@ constructor(
if (isShowingHeadsUpFromChipTap) {
// If the user tapped this chip to show the HUN, we want to just show the icon because
// the HUN will show the rest of the information.
- return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+ return OngoingActivityChipModel.Shown.IconOnly(
+ icon,
+ colors,
+ onClickListener,
+ clickBehavior,
+ )
}
if (this.promotedContent.shortCriticalText != null) {
@@ -104,6 +106,7 @@ constructor(
colors,
this.promotedContent.shortCriticalText,
onClickListener,
+ clickBehavior,
)
}
@@ -115,11 +118,21 @@ constructor(
// notification will likely just be set to the current time, which would cause the chip
// to always show "now". We don't want early testers to get that experience since it's
// not what will happen at launch, so just don't show any time.
- return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+ return OngoingActivityChipModel.Shown.IconOnly(
+ icon,
+ colors,
+ onClickListener,
+ clickBehavior,
+ )
}
if (this.promotedContent.time == null) {
- return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+ return OngoingActivityChipModel.Shown.IconOnly(
+ icon,
+ colors,
+ onClickListener,
+ clickBehavior,
+ )
}
when (this.promotedContent.time.mode) {
PromotedNotificationContentModel.When.Mode.BasicTime -> {
@@ -128,6 +141,7 @@ constructor(
colors,
time = this.promotedContent.time.time,
onClickListener,
+ clickBehavior,
)
}
PromotedNotificationContentModel.When.Mode.CountUp -> {
@@ -136,6 +150,7 @@ constructor(
colors,
startTimeMs = this.promotedContent.time.time,
onClickListener,
+ clickBehavior,
)
}
PromotedNotificationContentModel.When.Mode.CountDown -> {
@@ -145,6 +160,7 @@ constructor(
colors,
startTimeMs = this.promotedContent.time.time,
onClickListener,
+ clickBehavior,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
index 0065593c7b73..7f2327a742e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.ChipTransitionHelper
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.time.SystemClock
@@ -91,16 +92,24 @@ constructor(
),
colors = ColorsModel.Red,
startTimeMs = systemClock.elapsedRealtime(),
- createDialogLaunchOnClickListener(
- createDelegate(state.recordedTask),
- dialogTransitionAnimator,
- DialogCuj(
- Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP,
- tag = "Screen record",
+ onClickListenerLegacy =
+ createDialogLaunchOnClickListener(
+ createDelegate(state.recordedTask),
+ dialogTransitionAnimator,
+ DIALOG_CUJ,
+ logger,
+ TAG,
+ ),
+ clickBehavior =
+ OngoingActivityChipModel.ClickBehavior.ExpandAction(
+ createDialogLaunchOnClickCallback(
+ dialogDelegate = createDelegate(state.recordedTask),
+ dialogTransitionAnimator = dialogTransitionAnimator,
+ DIALOG_CUJ,
+ logger,
+ TAG,
+ )
),
- logger,
- TAG,
- ),
)
}
}
@@ -154,6 +163,8 @@ constructor(
companion object {
@DrawableRes val ICON = R.drawable.ic_screenrecord
+ private val DIALOG_CUJ =
+ DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Screen record")
private val TAG = "ScreenRecordVM".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index 2af86a51cf70..6654d4a8f104 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -39,6 +39,7 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.ChipTransitionHelper
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -128,13 +129,25 @@ constructor(
colors = ColorsModel.Red,
// TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
startTimeMs = systemClock.elapsedRealtime(),
- createDialogLaunchOnClickListener(
- createShareScreenToAppDialogDelegate(state),
- dialogTransitionAnimator,
- DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Share to app"),
- logger,
- TAG,
- ),
+ onClickListenerLegacy =
+ createDialogLaunchOnClickListener(
+ createShareScreenToAppDialogDelegate(state),
+ dialogTransitionAnimator,
+ DIALOG_CUJ,
+ logger,
+ TAG,
+ ),
+ clickBehavior =
+ OngoingActivityChipModel.ClickBehavior.ExpandAction(
+ onClick =
+ createDialogLaunchOnClickCallback(
+ createShareScreenToAppDialogDelegate(state),
+ dialogTransitionAnimator,
+ DIALOG_CUJ,
+ logger,
+ TAG,
+ )
+ ),
)
}
@@ -150,16 +163,24 @@ constructor(
)
),
colors = ColorsModel.Red,
- createDialogLaunchOnClickListener(
- createGenericShareToAppDialogDelegate(),
- dialogTransitionAnimator,
- DialogCuj(
- Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP,
- tag = "Share to app audio only",
+ onClickListenerLegacy =
+ createDialogLaunchOnClickListener(
+ createGenericShareToAppDialogDelegate(),
+ dialogTransitionAnimator,
+ DIALOG_CUJ_AUDIO_ONLY,
+ logger,
+ TAG,
+ ),
+ clickBehavior =
+ OngoingActivityChipModel.ClickBehavior.ExpandAction(
+ createDialogLaunchOnClickCallback(
+ createGenericShareToAppDialogDelegate(),
+ dialogTransitionAnimator,
+ DIALOG_CUJ_AUDIO_ONLY,
+ logger,
+ TAG,
+ )
),
- logger,
- TAG,
- ),
)
}
@@ -180,6 +201,10 @@ constructor(
companion object {
@DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_present_to_all
+ private val DIALOG_CUJ =
+ DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Share to app")
+ private val DIALOG_CUJ_AUDIO_ONLY =
+ DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Share to app audio only")
private val TAG = "ShareToAppVM".pad()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index b0fa9d842480..d46638fac46c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -56,7 +56,8 @@ object OngoingActivityChipBinder {
// Data
setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView, iconViewStore)
setChipMainContent(chipModel, chipTextView, chipTimeView, chipShortTimeDeltaView)
- viewBinding.rootView.setOnClickListener(chipModel.onClickListener)
+
+ viewBinding.rootView.setOnClickListener(chipModel.onClickListenerLegacy)
updateChipPadding(
chipModel,
chipBackgroundView,
@@ -424,7 +425,7 @@ object OngoingActivityChipBinder {
// Clickable chips need to be a minimum size for accessibility purposes, but let
// non-clickable chips be smaller.
val minimumWidth =
- if (chipModel.onClickListener != null) {
+ if (chipModel.onClickListenerLegacy != null) {
chipBackgroundView.context.resources.getDimensionPixelSize(
R.dimen.min_clickable_item_size
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 1be5842bceeb..6ce3228531d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -35,10 +35,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.Expandable
+import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.compose.modifiers.neverDecreaseWidth
@@ -47,23 +50,42 @@ import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
@Composable
fun OngoingActivityChip(model: OngoingActivityChipModel.Shown, modifier: Modifier = Modifier) {
+ when (val clickBehavior = model.clickBehavior) {
+ is OngoingActivityChipModel.ClickBehavior.ExpandAction -> {
+ // Wrap the chip in an Expandable so we can animate the expand transition.
+ ExpandableChip(
+ color = { Color.Transparent },
+ shape =
+ RoundedCornerShape(
+ dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius)
+ ),
+ modifier = modifier,
+ ) { expandable ->
+ ChipBody(model, onClick = { clickBehavior.onClick(expandable) })
+ }
+ }
+
+ is OngoingActivityChipModel.ClickBehavior.None -> {
+ ChipBody(model, modifier = modifier)
+ }
+ }
+}
+
+@Composable
+private fun ChipBody(
+ model: OngoingActivityChipModel.Shown,
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit = {},
+) {
val context = LocalContext.current
- val isClickable = model.onClickListener != null
+ val isClickable = onClick != {}
val hasEmbeddedIcon = model.icon is OngoingActivityChipModel.ChipIcon.StatusBarView
// Use a Box with `fillMaxHeight` to create a larger click surface for the chip. The visible
// height of the chip is determined by the height of the background of the Row below.
Box(
contentAlignment = Alignment.Center,
- modifier =
- modifier
- .fillMaxHeight()
- .clickable(
- enabled = isClickable,
- onClick = {
- // TODO(b/372657935): Implement click actions.
- },
- ),
+ modifier = modifier.fillMaxHeight().clickable(enabled = isClickable, onClick = onClick),
) {
Row(
horizontalArrangement = Arrangement.Center,
@@ -206,3 +228,13 @@ private fun ChipContent(viewModel: OngoingActivityChipModel.Shown, modifier: Mod
}
}
}
+
+@Composable
+private fun ExpandableChip(
+ color: () -> Color,
+ shape: Shape,
+ modifier: Modifier = Modifier,
+ content: @Composable (Expandable) -> Unit,
+) {
+ Expandable(color = color(), shape = shape, modifier = modifier.clip(shape)) { content(it) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
index cac25d04f1a5..25f90f9a0065 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
@@ -21,6 +21,7 @@ import android.content.res.ColorStateList
import androidx.annotation.ColorInt
import com.android.settingslib.Utils
import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
/** Model representing how the chip in the status bar should be colored. */
sealed interface ColorsModel {
@@ -55,4 +56,14 @@ sealed interface ColorsModel {
override fun text(context: Context) = context.getColor(android.R.color.white)
}
+
+ companion object {
+ /** Converts the promoted notification colors to a [Custom] colors model. */
+ fun PromotedNotificationContentModel.toCustomColorsModel(): Custom {
+ return Custom(
+ backgroundColorInt = this.colors.backgroundColor,
+ primaryTextColorInt = this.colors.primaryTextColor,
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 956d99e46766..68c8f8cb4254 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.chips.ui.model
import android.view.View
+import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
@@ -46,17 +47,20 @@ sealed class OngoingActivityChipModel {
open val colors: ColorsModel,
/**
* Listener method to invoke when this chip is clicked. If null, the chip won't be
- * clickable.
+ * clickable. Will be deprecated after [StatusBarChipsModernization] is enabled.
*/
- open val onClickListener: View.OnClickListener?,
+ open val onClickListenerLegacy: View.OnClickListener?,
+ /** Data class that determines how clicks on the chip should be handled. */
+ open val clickBehavior: ClickBehavior,
) : OngoingActivityChipModel() {
/** This chip shows only an icon and nothing else. */
data class IconOnly(
override val icon: ChipIcon,
override val colors: ColorsModel,
- override val onClickListener: View.OnClickListener?,
- ) : Shown(icon, colors, onClickListener) {
+ override val onClickListenerLegacy: View.OnClickListener?,
+ override val clickBehavior: ClickBehavior,
+ ) : Shown(icon, colors, onClickListenerLegacy, clickBehavior) {
override val logName = "Shown.Icon"
}
@@ -74,8 +78,9 @@ sealed class OngoingActivityChipModel {
* [android.widget.Chronometer.setBase].
*/
val startTimeMs: Long,
- override val onClickListener: View.OnClickListener?,
- ) : Shown(icon, colors, onClickListener) {
+ override val onClickListenerLegacy: View.OnClickListener?,
+ override val clickBehavior: ClickBehavior,
+ ) : Shown(icon, colors, onClickListenerLegacy, clickBehavior) {
override val logName = "Shown.Timer"
}
@@ -88,8 +93,9 @@ sealed class OngoingActivityChipModel {
override val colors: ColorsModel,
/** The time of the event that this chip represents. */
val time: Long,
- override val onClickListener: View.OnClickListener?,
- ) : Shown(icon, colors, onClickListener) {
+ override val onClickListenerLegacy: View.OnClickListener?,
+ override val clickBehavior: ClickBehavior,
+ ) : Shown(icon, colors, onClickListenerLegacy, clickBehavior) {
init {
StatusBarNotifChips.assertInNewMode()
}
@@ -105,7 +111,13 @@ sealed class OngoingActivityChipModel {
override val colors: ColorsModel,
/** The number of seconds until an event is started. */
val secondsUntilStarted: Long,
- ) : Shown(icon = null, colors, onClickListener = null) {
+ ) :
+ Shown(
+ icon = null,
+ colors,
+ onClickListenerLegacy = null,
+ clickBehavior = ClickBehavior.None,
+ ) {
override val logName = "Shown.Countdown"
}
@@ -115,8 +127,9 @@ sealed class OngoingActivityChipModel {
override val colors: ColorsModel,
// TODO(b/361346412): Enforce a max length requirement?
val text: String,
- override val onClickListener: View.OnClickListener? = null,
- ) : Shown(icon, colors, onClickListener) {
+ override val onClickListenerLegacy: View.OnClickListener? = null,
+ override val clickBehavior: ClickBehavior,
+ ) : Shown(icon, colors, onClickListenerLegacy, clickBehavior) {
override val logName = "Shown.Text"
}
}
@@ -149,4 +162,13 @@ sealed class OngoingActivityChipModel {
*/
data class SingleColorIcon(val impl: Icon) : ChipIcon
}
+
+ /** Defines the behavior of the chip when it is clicked. */
+ sealed interface ClickBehavior {
+ /** No specific click behavior. */
+ data object None : ClickBehavior
+
+ /** The chip expands into a dialog or activity on click. */
+ data class ExpandAction(val onClick: (Expandable) -> Unit) : ClickBehavior
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt
index 2fc366b7f078..a978c04d2a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.chips.ui.viewmodel
import android.view.View
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.res.R
@@ -26,6 +27,7 @@ import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import kotlinx.coroutines.flow.StateFlow
/**
@@ -46,6 +48,7 @@ interface OngoingActivityChipViewModel {
tag: String,
): View.OnClickListener {
return View.OnClickListener { view ->
+ StatusBarChipsModernization.assertInLegacyMode()
logger.log(tag, LogLevel.INFO, {}, { "Chip clicked" })
val dialog = dialogDelegate.createDialog()
val launchableView =
@@ -55,5 +58,28 @@ interface OngoingActivityChipViewModel {
dialogTransitionAnimator.showFromView(dialog, launchableView, cuj)
}
}
+
+ /**
+ * Creates a chip click callback with an [Expandable] parameter that launches a dialog
+ * created by [dialogDelegate].
+ */
+ fun createDialogLaunchOnClickCallback(
+ dialogDelegate: SystemUIDialog.Delegate,
+ dialogTransitionAnimator: DialogTransitionAnimator,
+ cuj: DialogCuj,
+ @StatusBarChipsLog logger: LogBuffer,
+ tag: String,
+ ): (Expandable) -> Unit {
+ return { expandable ->
+ StatusBarChipsModernization.assertInNewMode()
+ logger.log(tag, LogLevel.INFO, {}, { "Chip clicked" })
+ val dialog = dialogDelegate.createDialog()
+
+ val controller = expandable.dialogTransitionController(cuj)
+ if (controller != null) {
+ dialogTransitionAnimator.show(dialog, controller)
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index c38b84b710bc..417e57d2205f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -34,6 +34,8 @@ import static com.android.systemui.statusbar.notification.stack.NotificationPrio
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
+import android.app.Flags;
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
@@ -1091,6 +1093,14 @@ public final class NotificationEntry extends ListEntry {
}
/**
+ * Returns whether the NotificationEntry is promoted ongoing.
+ */
+ @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+ public boolean isPromotedOngoing() {
+ return PromotedNotificationContentModel.isPromotedForStatusBarChip(mSbn.getNotification());
+ }
+
+ /**
* Sets the content needed to render this notification as a promoted notification on various
* surfaces (like status bar chips and AOD).
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 0c040c855368..502fb51ba5c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -76,11 +76,14 @@ constructor(
val allNotificationsCountValue: Int
get() = repository.activeNotifications.value.individuals.size
- /** The notifications that are promoted and ongoing. Sorted by priority order. */
+ /**
+ * The notifications that are promoted and ongoing.
+ *
+ * This *may* include ongoing call notifications if the call notification also meets promotion
+ * criteria.
+ */
val promotedOngoingNotifications: Flow<List<ActiveNotificationModel>> =
if (StatusBarNotifChips.isEnabled) {
- // TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow
- // instead of being separate.
topLevelRepresentativeNotifications
.map { notifs -> notifs.filter { it.promotedContent != null } }
.distinctUntilChanged()
@@ -98,10 +101,10 @@ constructor(
val ongoingCallNotification: Flow<ActiveNotificationModel?> =
allRepresentativeNotifications
.map { notifMap ->
- // Once a call has started, its `whenTime` should stay the same, so we can use it as
- // a stable sort value.
notifMap.values
- .filter { it.callType == CallType.Ongoing }
+ .filter { it.isOngoingCallNotification() }
+ // Once a call has started, its `whenTime` should stay the same, so we can use
+ // it as a stable sort value.
.minByOrNull { it.whenTime }
}
.distinctUntilChanged()
@@ -153,4 +156,8 @@ constructor(
fun setNotifStats(notifStats: NotifStats) {
repository.notifStats.value = notifStats
}
+
+ companion object {
+ fun ActiveNotificationModel.isOngoingCallNotification() = this.callType == CallType.Ongoing
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index c88dd7af6b24..d401283aa84e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -154,10 +154,12 @@ public class FooterView extends StackScrollerDecorView {
DumpUtilsKt.withIncreasedIndent(pw, () -> {
// TODO: b/375010573 - update dumps for redesign
pw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility()));
- pw.println("manageButton visibility: "
- + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
- pw.println("dismissButton visibility: "
- + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
+ if (mManageOrHistoryButton != null)
+ pw.println("mManageOrHistoryButton visibility: "
+ + DumpUtilsKt.visibilityString(mManageOrHistoryButton.getVisibility()));
+ if (mClearAllButton != null)
+ pw.println("mClearAllButton visibility: "
+ + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
index d02e17cab534..0171fb72e158 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
@@ -1425,7 +1425,12 @@ public class HeadsUpManagerImpl
}
}
- return (mEntry.isRowPinned() && mExpanded)
+ // Promoted notifications are always shown as expanded, and we don't want them to ever
+ // be sticky.
+ boolean isStickyDueToExpansion =
+ mEntry.isRowPinned() && mExpanded && !mEntry.isPromotedOngoing();
+
+ return isStickyDueToExpansion
|| mRemoteInputActive
|| hasFullScreenIntent(mEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index 258d80c60cd7..a175f90c384a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -147,6 +147,7 @@ data class PromotedNotificationContentModel(
* Returns true if the given notification should be considered promoted when deciding
* whether or not to show the status bar chip UI.
*/
+ @JvmStatic
fun isPromotedForStatusBarChip(notification: Notification): Boolean {
// Notification.isPromotedOngoing checks the ui_rich_ongoing flag, but we want the
// status bar chip to be ready before all the features behind the ui_rich_ongoing flag
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
index e27ff7d6746b..793b3b8b1e42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
@@ -17,39 +17,210 @@
package com.android.systemui.statusbar.notification.row
import android.content.Context
+import android.graphics.BlendMode
import android.graphics.Canvas
+import android.graphics.Color
import android.graphics.ColorFilter
+import android.graphics.LinearGradient
import android.graphics.Paint
+import android.graphics.Path
import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.graphics.RuntimeShader
+import android.graphics.Shader
import android.graphics.drawable.Drawable
+import android.widget.FrameLayout
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.internal.graphics.ColorUtils
+import com.android.systemui.res.R
+import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
+import kotlin.math.max
+import kotlin.math.roundToInt
/**
- * A background style for smarter-smart-actions.
- *
- * TODO(b/383567383) implement final UX
+ * A background style for smarter-smart-actions. The style is composed by a simplex3d noise,
+ * overlaid with sparkles.
*/
-class MagicActionBackgroundDrawable(context: Context) : Drawable() {
+class MagicActionBackgroundDrawable(
+ context: Context,
+ primaryContainer: Int? = null,
+ private val seed: Float = 0f,
+) : Drawable() {
+
+ private val pixelDensity = context.resources.displayMetrics.density
+ private val cornerRadius =
+ context.resources.getDimensionPixelSize(R.dimen.smart_reply_button_corner_radius).toFloat()
+ private val outlineStrokeWidth =
+ context.resources
+ .getDimensionPixelSize(R.dimen.smart_action_button_outline_stroke_width)
+ .toFloat()
+ private val buttonShape = Path()
+ private val paddingVertical =
+ context.resources
+ .getDimensionPixelSize(R.dimen.smart_reply_button_padding_vertical)
+ .toFloat()
+
+ /** The color of the button background. */
+ private val mainColor =
+ primaryContainer
+ ?: context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
- private var _alpha: Int = 255
- private var _colorFilter: ColorFilter? = null
- private val paint =
- Paint().apply {
- color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
+ /** Slightly dimmed down version of [mainColor] used on the simplex noise. */
+ private val dimColor: Int
+ get() {
+ val labColor = arrayOf(0.0, 0.0, 0.0).toDoubleArray()
+ ColorUtils.colorToLAB(mainColor, labColor)
+ val camColor = ColorUtils.colorToCAM(mainColor)
+ return ColorUtils.CAMToColor(
+ camColor.hue,
+ camColor.chroma,
+ max(0f, (labColor[0] - 20).toFloat()),
+ )
}
+ private val bgShader = MagicActionBackgroundShader()
+ private val bgPaint = Paint()
+ private val outlinePaint = Paint()
+
+ init {
+ bgShader.setColorUniform("in_color", mainColor)
+ bgShader.setColorUniform("in_dimColor", dimColor)
+ bgPaint.shader = bgShader
+ outlinePaint.style = Paint.Style.STROKE
+ // Stroke is doubled in width and then clipped, to avoid anti-aliasing artifacts at the edge
+ // of the rectangle.
+ outlinePaint.strokeWidth = outlineStrokeWidth * 2
+ outlinePaint.blendMode = BlendMode.SCREEN
+ outlinePaint.alpha = (255 * 0.32f).roundToInt()
+ }
+
override fun draw(canvas: Canvas) {
- canvas.drawRect(bounds, paint)
+ // We clip instead of drawing 2 rounded rects, otherwise there will be artifacts where
+ // around the button background and the outline.
+ canvas.clipPath(buttonShape)
+
+ canvas.drawRect(bounds, bgPaint)
+ canvas.drawRoundRect(
+ bounds.left.toFloat(),
+ bounds.top + paddingVertical,
+ bounds.right.toFloat(),
+ bounds.bottom - paddingVertical,
+ cornerRadius,
+ cornerRadius,
+ outlinePaint,
+ )
+ }
+
+ override fun onBoundsChange(bounds: Rect) {
+ super.onBoundsChange(bounds)
+
+ val width = bounds.width().toFloat()
+ val height = bounds.height() - paddingVertical * 2
+ if (width == 0f || height == 0f) return
+
+ bgShader.setFloatUniform("in_gridNum", NOISE_SIZE)
+ bgShader.setFloatUniform("in_spkarkleAlpha", SPARKLE_ALPHA)
+ bgShader.setFloatUniform("in_noiseMove", 0f, 0f, 0f)
+ bgShader.setFloatUniform("in_size", width, height)
+ bgShader.setFloatUniform("in_aspectRatio", width / height)
+ bgShader.setFloatUniform("in_time", seed)
+ bgShader.setFloatUniform("in_pixelDensity", pixelDensity)
+
+ buttonShape.reset()
+ buttonShape.addRoundRect(
+ bounds.left.toFloat(),
+ bounds.top + paddingVertical,
+ bounds.right.toFloat(),
+ bounds.bottom - paddingVertical,
+ cornerRadius,
+ cornerRadius,
+ Path.Direction.CW,
+ )
+
+ val outlineGradient =
+ LinearGradient(
+ bounds.left.toFloat(),
+ 0f,
+ bounds.right.toFloat(),
+ 0f,
+ mainColor,
+ ColorUtils.setAlphaComponent(mainColor, 0),
+ Shader.TileMode.CLAMP,
+ )
+ outlinePaint.shader = outlineGradient
}
override fun setAlpha(alpha: Int) {
- _alpha = alpha
+ bgPaint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
- _colorFilter = colorFilter
+ bgPaint.colorFilter = colorFilter
invalidateSelf()
}
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
+
+ companion object {
+ /** Smoothness of the turbulence. Larger numbers yield more detail. */
+ private const val NOISE_SIZE = 0.7f
+
+ /** Strength of the sparkles overlaid on the turbulence. */
+ private const val SPARKLE_ALPHA = 0.15f
+ }
+}
+
+private class MagicActionBackgroundShader : RuntimeShader(SHADER) {
+
+ // language=AGSL
+ companion object {
+ private const val UNIFORMS =
+ """
+ uniform float in_gridNum;
+ uniform vec3 in_noiseMove;
+ uniform vec2 in_size;
+ uniform float in_aspectRatio;
+ uniform half in_time;
+ uniform half in_pixelDensity;
+ uniform float in_spkarkleAlpha;
+ layout(color) uniform vec4 in_color;
+ layout(color) uniform vec4 in_dimColor;
+ """
+ private const val MAIN_SHADER =
+ """vec4 main(vec2 p) {
+ vec2 uv = p / in_size.xy;
+ uv.x *= in_aspectRatio;
+ vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
+ half luma = 1.0 - getLuminosity(half3(simplex3d(noiseP)));
+ half4 turbulenceColor = mix(in_color, in_dimColor, luma);
+ float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time);
+ sparkle = min(sparkle * in_spkarkleAlpha, in_spkarkleAlpha);
+ return turbulenceColor + half4(half3(sparkle), 1.0);
+ }
+ """
+ private const val SHADER = UNIFORMS + ShaderUtilLibrary.SHADER_LIB + MAIN_SHADER
+ }
+}
+
+// @Preview
+@Composable
+fun DrawablePreview() {
+ AndroidView(
+ factory = { context ->
+ FrameLayout(context).apply {
+ background =
+ MagicActionBackgroundDrawable(
+ context = context,
+ primaryContainer = Color.parseColor("#c5eae2"),
+ seed = 0f,
+ )
+ }
+ },
+ modifier = Modifier.size(100.dp, 50.dp),
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index c47ed1722bb4..fcc3af6a2134 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -397,6 +397,13 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
mDelegate.onWindowFocusChanged(this, hasFocus);
+ if (hasFocus) {
+ // Update SysUI state to reflect that a dialog is showing. This ensures the state is
+ // correct when this dialog regains focus after another dialog was closed.
+ // See b/386871258
+ mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, true)
+ .commitUpdate(mContext.getDisplayId());
+ }
}
public void setShowForAllUsers(boolean show) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 4eb69babfadb..a29934fa3a16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -39,6 +39,7 @@ import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
@@ -159,6 +160,7 @@ constructor(
notificationIconView = currentInfo.notificationIconView,
intent = currentInfo.intent,
notificationKey = currentInfo.key,
+ promotedContent = currentInfo.promotedContent,
)
} else {
return OngoingCallModel.NoCall
@@ -215,6 +217,7 @@ constructor(
notifModel.statusBarChipIconView,
notifModel.contentIntent,
notifModel.uid,
+ notifModel.promotedContent,
isOngoing = true,
statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false,
)
@@ -334,6 +337,11 @@ constructor(
val notificationIconView: StatusBarIconView?,
val intent: PendingIntent?,
val uid: Int,
+ /**
+ * If the call notification also meets promoted notification criteria, this field is filled
+ * in with the content related to promotion. Otherwise null.
+ */
+ val promotedContent: PromotedNotificationContentModel?,
/** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */
val isOngoing: Boolean,
/** True if the user has swiped away the status bar while in this phone call. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
index 2ab2b68bbb2e..9f8b45578903 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
@@ -1,18 +1,18 @@
/*
-* 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.
-*/
+ * 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.phone.ongoingcall
import com.android.systemui.Flags
@@ -44,9 +44,16 @@ object StatusBarChipsModernization {
RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
/**
+ * Called to ensure code is only run when the flag is enabled. This will throw an exception if
+ * the flag is not enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+ /**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
* the flag is enabled to ensure that the refactor author catches issues in testing.
*/
@JvmStatic
inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
index 2bfbf4826053..99141f592a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
@@ -165,6 +165,7 @@ constructor(
notificationIconView = model.statusBarChipIconView,
intent = model.contentIntent,
notificationKey = model.key,
+ promotedContent = model.promotedContent,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
index 1a5dcc16f3db..7d00e9d58e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone.ongoingcall.shared.model
import android.app.PendingIntent
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
/** Represents the state of any ongoing calls. */
sealed interface OngoingCallModel {
@@ -25,8 +26,8 @@ sealed interface OngoingCallModel {
data object NoCall : OngoingCallModel
/**
- * There is an ongoing call but the call app is currently visible, so we don't need to show
- * the chip.
+ * There is an ongoing call but the call app is currently visible, so we don't need to show the
+ * chip.
*/
data object InCallWithVisibleApp : OngoingCallModel
@@ -41,11 +42,14 @@ sealed interface OngoingCallModel {
* @property notificationIconView the [android.app.Notification.getSmallIcon] that's set on the
* call notification. We may use this icon in the chip instead of the default phone icon.
* @property intent the intent associated with the call notification.
+ * @property promotedContent if the call notification also meets promoted notification criteria,
+ * this field is filled in with the content related to promotion. Otherwise null.
*/
data class InCall(
val startTimeMs: Long,
val notificationIconView: StatusBarIconView?,
val intent: PendingIntent?,
val notificationKey: String,
+ val promotedContent: PromotedNotificationContentModel?,
) : OngoingCallModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
index b32037992501..dacb859967e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
@@ -111,7 +111,12 @@ constructor(
pw.println("Carrier configs by subId")
configs.keyIterator().forEach {
pw.println(" subId=$it")
- pw.println(" config=${configs.get(it).toStringConsideringDefaults()}")
+ val config = configs.get(it)
+ if (config == null) {
+ pw.println(" config=null (config was removed during dump)")
+ } else {
+ pw.println(" config=${config.toStringConsideringDefaults()}")
+ }
}
// Finally, print the default config
pw.println("Default config:")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
index cb26679434ef..520c5632370b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
@@ -408,6 +408,11 @@ constructor(
action.extras.getBoolean(Notification.Action.EXTRA_IS_MAGIC, false)
) {
background = MagicActionBackgroundDrawable(parent.context)
+ val textColor =
+ parent.context.getColor(
+ com.android.internal.R.color.materialColorOnPrimaryContainer
+ )
+ setTextColor(textColor)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
index 9ff0d18f0e2b..c23508f8ae68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy.ui.dialog
+import android.app.Dialog
+import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.util.Log
@@ -36,6 +38,7 @@ import com.android.compose.PlatformOutlinedButton
import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
+import com.android.settingslib.notification.modes.EnableZenModeDialog
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
@@ -43,6 +46,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dialog.ui.composable.AlertDialogContent
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
@@ -61,6 +65,7 @@ import kotlinx.coroutines.withContext
class ModesDialogDelegate
@Inject
constructor(
+ val context: Context,
private val sysuiDialogFactory: SystemUIDialogFactory,
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val activityStarter: ActivityStarter,
@@ -72,6 +77,7 @@ constructor(
) : SystemUIDialog.Delegate {
// NOTE: This should only be accessed/written from the main thread.
@VisibleForTesting var currentDialog: ComponentSystemUIDialog? = null
+ private val zenDialogMetricsLogger by lazy { QSZenModeDialogMetricsLogger(context) }
override fun createDialog(): SystemUIDialog {
Assert.isMainThread()
@@ -195,6 +201,26 @@ constructor(
activityStarter.startActivity(intent, /* dismissShade= */ true, animationController)
}
+ /**
+ * Special dialog to ask the user for the duration of DND. Not to be confused with the modes
+ * dialog itself.
+ */
+ fun makeDndDurationDialog(): Dialog {
+ val dialog =
+ EnableZenModeDialog(
+ context,
+ R.style.Theme_SystemUI_Dialog,
+ /* cancelIsNeutral= */ true,
+ zenDialogMetricsLogger,
+ )
+ .createDialog()
+ SystemUIDialog.applyFlags(dialog)
+ SystemUIDialog.setShowForAllUsers(dialog, true)
+ SystemUIDialog.registerDismissListener(dialog)
+ SystemUIDialog.setDialogSize(dialog)
+ return dialog
+ }
+
companion object {
private const val TAG = "ModesDialogDelegate"
private val ZEN_MODE_SETTINGS_INTENT = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
index 1c13a833ef30..07f1c3470c83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
@@ -16,20 +16,16 @@
package com.android.systemui.statusbar.policy.ui.dialog.viewmodel
-import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.provider.Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS
import android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID
-import com.android.settingslib.notification.modes.EnableZenModeDialog
import com.android.settingslib.notification.modes.ZenMode
import com.android.settingslib.notification.modes.ZenModeDescriptions
import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger
import com.android.systemui.res.R
-import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogEventLogger
@@ -54,7 +50,6 @@ constructor(
private val dialogDelegate: ModesDialogDelegate,
private val dialogEventLogger: ModesDialogEventLogger,
) {
- private val zenDialogMetricsLogger = QSZenModeDialogMetricsLogger(context)
private val zenModeDescriptions = ZenModeDescriptions(context)
// Modes that should be displayed in the dialog
@@ -112,7 +107,7 @@ constructor(
if (zenModeInteractor.shouldAskForZenDuration(mode)) {
dialogEventLogger.logOpenDurationDialog(mode)
// NOTE: The dialog handles turning on the mode itself.
- val dialog = makeZenModeDialog()
+ val dialog = dialogDelegate.makeDndDurationDialog()
dialog.show()
} else {
dialogEventLogger.logModeOn(mode)
@@ -173,20 +168,4 @@ constructor(
modeDescription ?: context.getString(R.string.zen_mode_off)
}
}
-
- private fun makeZenModeDialog(): Dialog {
- val dialog =
- EnableZenModeDialog(
- context,
- R.style.Theme_SystemUI_Dialog,
- /* cancelIsNeutral= */ true,
- zenDialogMetricsLogger,
- )
- .createDialog()
- SystemUIDialog.applyFlags(dialog)
- SystemUIDialog.setShowForAllUsers(dialog, true)
- SystemUIDialog.registerDismissListener(dialog)
- SystemUIDialog.setDialogSize(dialog)
- return dialog
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
index c43f31beb5bc..a2125c8f0955 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
@@ -39,6 +39,8 @@ import com.android.systemui.touchpad.tutorial.ui.viewmodel.HomeGestureRecognizer
import com.android.systemui.touchpad.tutorial.ui.viewmodel.HomeGestureScreenViewModel
import com.android.systemui.touchpad.tutorial.ui.viewmodel.RecentAppsGestureRecognizerProvider
import com.android.systemui.touchpad.tutorial.ui.viewmodel.RecentAppsGestureScreenViewModel
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.SwitchAppsGestureRecognizerProvider
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.SwitchAppsGestureScreenViewModel
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -69,6 +71,14 @@ interface TouchpadTutorialModule {
}
@Provides
+ fun switchAppsViewModel(
+ recognizerProvider: SwitchAppsGestureRecognizerProvider,
+ adapterFactory: GestureRecognizerAdapter.Factory,
+ ): SwitchAppsGestureScreenViewModel {
+ return SwitchAppsGestureScreenViewModel(adapterFactory.create(recognizerProvider))
+ }
+
+ @Provides
fun recentAppsViewModel(
recognizerProvider: RecentAppsGestureRecognizerProvider,
adapterFactory: GestureRecognizerAdapter.Factory,
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/SwitchAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/SwitchAppsGestureTutorialScreen.kt
new file mode 100644
index 000000000000..3bb0dd779613
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/SwitchAppsGestureTutorialScreen.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.composable
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import com.airbnb.lottie.compose.rememberLottieDynamicProperties
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
+import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty
+import com.android.systemui.res.R
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.EasterEggGestureViewModel
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.SwitchAppsGestureScreenViewModel
+
+@Composable
+fun SwitchAppsGestureTutorialScreen(
+ viewModel: SwitchAppsGestureScreenViewModel,
+ easterEggGestureViewModel: EasterEggGestureViewModel,
+ onDoneButtonClicked: () -> Unit,
+ onBack: () -> Unit,
+) {
+ val screenConfig =
+ TutorialScreenConfig(
+ colors = rememberScreenColors(),
+ strings =
+ TutorialScreenConfig.Strings(
+ titleResId = R.string.touchpad_switch_apps_gesture_action_title,
+ bodyResId = R.string.touchpad_switch_apps_gesture_guidance,
+ titleSuccessResId = R.string.touchpad_switch_apps_gesture_success_title,
+ bodySuccessResId = R.string.touchpad_switch_apps_gesture_success_body,
+ titleErrorResId = R.string.gesture_error_title,
+ bodyErrorResId = R.string.touchpad_switch_gesture_error_body,
+ ),
+ // TODO: replace animation
+ animations = TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_back_edu),
+ )
+ GestureTutorialScreen(
+ screenConfig = screenConfig,
+ tutorialStateFlow = viewModel.tutorialState,
+ motionEventConsumer = {
+ easterEggGestureViewModel.accept(it)
+ viewModel.handleEvent(it)
+ },
+ easterEggTriggeredFlow = easterEggGestureViewModel.easterEggTriggered,
+ onEasterEggFinished = easterEggGestureViewModel::onEasterEggFinished,
+ onDoneButtonClicked = onDoneButtonClicked,
+ onBack = onBack,
+ )
+}
+
+@Composable
+private fun rememberScreenColors(): TutorialScreenConfig.Colors {
+ val onTertiary = MaterialTheme.colorScheme.onTertiary
+ val onTertiaryFixed = LocalAndroidColorScheme.current.onTertiaryFixed
+ val onTertiaryFixedVariant = LocalAndroidColorScheme.current.onTertiaryFixedVariant
+ val tertiaryFixedDim = LocalAndroidColorScheme.current.tertiaryFixedDim
+ val dynamicProperties =
+ rememberLottieDynamicProperties(
+ rememberColorFilterProperty(".tertiaryFixedDim", tertiaryFixedDim),
+ rememberColorFilterProperty(".onTertiaryFixed", onTertiaryFixed),
+ rememberColorFilterProperty(".onTertiary", onTertiary),
+ rememberColorFilterProperty(".onTertiaryFixedVariant", onTertiaryFixedVariant),
+ )
+ val screenColors =
+ remember(dynamicProperties) {
+ TutorialScreenConfig.Colors(
+ background = onTertiaryFixed,
+ title = tertiaryFixedDim,
+ animationColors = dynamicProperties,
+ )
+ }
+ return screenColors
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index c8a58400069e..69b7e892a380 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -61,6 +61,7 @@ fun TutorialSelectionScreen(
onBackTutorialClicked: () -> Unit,
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
+ onSwitchAppsTutorialClicked: () -> Unit,
onDoneButtonClicked: () -> Unit,
lastSelectedScreen: Screen,
) {
@@ -86,6 +87,7 @@ fun TutorialSelectionScreen(
onBackTutorialClicked = onBackTutorialClicked,
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
modifier = Modifier.weight(1f).padding(60.dp),
lastSelectedScreen,
)
@@ -95,6 +97,7 @@ fun TutorialSelectionScreen(
onBackTutorialClicked = onBackTutorialClicked,
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
modifier = Modifier.weight(1f).padding(60.dp),
lastSelectedScreen,
)
@@ -113,6 +116,7 @@ private fun HorizontalSelectionButtons(
onBackTutorialClicked: () -> Unit,
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
+ onSwitchAppsTutorialClicked: () -> Unit,
modifier: Modifier = Modifier,
lastSelectedScreen: Screen,
) {
@@ -121,10 +125,11 @@ private fun HorizontalSelectionButtons(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier,
) {
- ThreeTutorialButtons(
+ FourTutorialButtons(
onBackTutorialClicked,
onHomeTutorialClicked,
onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked,
modifier = Modifier.weight(1f).fillMaxSize(),
lastSelectedScreen,
)
@@ -136,6 +141,7 @@ private fun VerticalSelectionButtons(
onBackTutorialClicked: () -> Unit,
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
+ onSwitchAppsTutorialClicked: () -> Unit,
modifier: Modifier = Modifier,
lastSelectedScreen: Screen,
) {
@@ -144,10 +150,11 @@ private fun VerticalSelectionButtons(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier,
) {
- ThreeTutorialButtons(
+ FourTutorialButtons(
onBackTutorialClicked,
onHomeTutorialClicked,
onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked,
modifier = Modifier.weight(1f).fillMaxSize(),
lastSelectedScreen,
)
@@ -155,21 +162,24 @@ private fun VerticalSelectionButtons(
}
@Composable
-private fun ThreeTutorialButtons(
+private fun FourTutorialButtons(
onBackTutorialClicked: () -> Unit,
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
+ onSwitchAppsTutorialClicked: () -> Unit,
modifier: Modifier = Modifier,
lastSelectedScreen: Screen,
) {
val homeFocusRequester = remember { FocusRequester() }
val backFocusRequester = remember { FocusRequester() }
val recentAppsFocusRequester = remember { FocusRequester() }
+ val switchAppsFocusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
when (lastSelectedScreen) {
Screen.HOME_GESTURE -> homeFocusRequester.requestFocus()
Screen.BACK_GESTURE -> backFocusRequester.requestFocus()
Screen.RECENT_APPS_GESTURE -> recentAppsFocusRequester.requestFocus()
+ Screen.SWITCH_APPS_GESTURE -> switchAppsFocusRequester.requestFocus()
else -> {} // No-Op.
}
}
@@ -197,6 +207,14 @@ private fun ThreeTutorialButtons(
backgroundColor = MaterialTheme.colorScheme.secondary,
modifier = modifier.focusRequester(recentAppsFocusRequester).focusable(),
)
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_switch_apps_gesture_button),
+ icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_apps_icon),
+ iconColor = MaterialTheme.colorScheme.primary,
+ onClick = onSwitchAppsTutorialClicked,
+ backgroundColor = MaterialTheme.colorScheme.onPrimary,
+ modifier = modifier.focusRequester(switchAppsFocusRequester).focusable(),
+ )
}
@Composable
@@ -227,7 +245,7 @@ private fun TutorialButton(
tint = iconColor,
)
Spacer(modifier = Modifier.height(16.dp))
- Text(text = text, style = MaterialTheme.typography.headlineLarge)
+ Text(text = text, style = MaterialTheme.typography.headlineLarge, color = iconColor)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizer.kt
new file mode 100644
index 000000000000..470048bd3b20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizer.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+
+// TODO: javadoc
+class SwitchAppsGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer {
+
+ private val distanceTracker = DistanceTracker()
+ private var gestureStateChangedCallback: (GestureState) -> Unit = {}
+
+ override fun addGestureStateCallback(callback: (GestureState) -> Unit) {
+ gestureStateChangedCallback = callback
+ }
+
+ override fun clearGestureStateCallback() {
+ gestureStateChangedCallback = {}
+ }
+
+ // TODO: recognizer logic
+ override fun accept(event: MotionEvent) {
+ if (!isMultifingerTouchpadSwipe(event)) return
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
index 3264300ed908..0a139125afa2 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
@@ -37,6 +37,7 @@ import com.android.systemui.res.R
import com.android.systemui.touchpad.tutorial.ui.composable.BackGestureTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.HomeGestureTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.RecentAppsGestureTutorialScreen
+import com.android.systemui.touchpad.tutorial.ui.composable.SwitchAppsGestureTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.TutorialSelectionScreen
import com.android.systemui.touchpad.tutorial.ui.viewmodel.BackGestureScreenViewModel
import com.android.systemui.touchpad.tutorial.ui.viewmodel.EasterEggGestureViewModel
@@ -45,7 +46,9 @@ import com.android.systemui.touchpad.tutorial.ui.viewmodel.RecentAppsGestureScre
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.BACK_GESTURE
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.HOME_GESTURE
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.RECENT_APPS_GESTURE
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.SWITCH_APPS_GESTURE
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.TUTORIAL_SELECTION
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.SwitchAppsGestureScreenViewModel
import com.android.systemui.touchpad.tutorial.ui.viewmodel.TouchpadTutorialViewModel
import javax.inject.Inject
@@ -58,6 +61,7 @@ constructor(
private val backGestureViewModel: BackGestureScreenViewModel,
private val homeGestureViewModel: HomeGestureScreenViewModel,
private val recentAppsGestureViewModel: RecentAppsGestureScreenViewModel,
+ private val switchAppsGestureScreenViewModel: SwitchAppsGestureScreenViewModel,
private val easterEggGestureViewModel: EasterEggGestureViewModel,
) : ComponentActivity() {
@@ -75,6 +79,7 @@ constructor(
backGestureViewModel,
homeGestureViewModel,
recentAppsGestureViewModel,
+ switchAppsGestureScreenViewModel,
easterEggGestureViewModel,
closeTutorial = ::finishTutorial,
)
@@ -108,6 +113,7 @@ fun TouchpadTutorialScreen(
backGestureViewModel: BackGestureScreenViewModel,
homeGestureViewModel: HomeGestureScreenViewModel,
recentAppsGestureViewModel: RecentAppsGestureScreenViewModel,
+ switchAppsGestureScreenViewModel: SwitchAppsGestureScreenViewModel,
easterEggGestureViewModel: EasterEggGestureViewModel,
closeTutorial: () -> Unit,
) {
@@ -128,6 +134,10 @@ fun TouchpadTutorialScreen(
lastSelectedScreen = RECENT_APPS_GESTURE
vm.goTo(RECENT_APPS_GESTURE)
},
+ onSwitchAppsTutorialClicked = {
+ lastSelectedScreen = SWITCH_APPS_GESTURE
+ vm.goTo(SWITCH_APPS_GESTURE)
+ },
onDoneButtonClicked = closeTutorial,
lastSelectedScreen,
)
@@ -152,5 +162,12 @@ fun TouchpadTutorialScreen(
onDoneButtonClicked = { vm.goTo(TUTORIAL_SELECTION) },
onBack = { vm.goTo(TUTORIAL_SELECTION) },
)
+ SWITCH_APPS_GESTURE ->
+ SwitchAppsGestureTutorialScreen(
+ switchAppsGestureScreenViewModel,
+ easterEggGestureViewModel,
+ onDoneButtonClicked = { vm.goTo(SWITCH_APPS_GESTURE) },
+ onBack = { vm.goTo(TUTORIAL_SELECTION) },
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureRecognizerProvider.kt
new file mode 100644
index 000000000000..b1e163b55377
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureRecognizerProvider.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.viewmodel
+
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.SwitchAppsGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class SwitchAppsGestureRecognizerProvider
+@Inject
+constructor(val resources: TouchpadGestureResources, val velocityTracker: VelocityTracker) :
+ GestureRecognizerProvider {
+
+ override val recognizer: Flow<GestureRecognizer> =
+ resources.distanceThreshold().map {
+ SwitchAppsGestureRecognizer(gestureDistanceThresholdPx = it)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureScreenViewModel.kt
new file mode 100644
index 000000000000..6593db49745d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureScreenViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.viewmodel
+
+import android.view.MotionEvent
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.res.R
+import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class SwitchAppsGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) :
+ TouchpadTutorialScreenViewModel {
+
+ // TODO: replace with correct markers and resource
+ override val tutorialState: Flow<TutorialActionState> =
+ gestureRecognizer.gestureState
+ .map {
+ it to
+ TutorialAnimationProperties(
+ progressStartMarker = "drag with gesture",
+ progressEndMarker = "onPause",
+ successAnimation = R.raw.trackpad_recent_apps_success,
+ )
+ }
+ .mapToTutorialState()
+
+ override fun handleEvent(event: MotionEvent): Boolean {
+ return gestureRecognizer.handleTouchpadMotionEvent(event)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
index c56dcf3bf062..c6d5e7ad0a71 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
@@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.StateFlow
class TouchpadTutorialViewModel(
private val gesturesInteractor: TouchpadGesturesInteractor,
- private val logger: InputDeviceTutorialLogger
+ private val logger: InputDeviceTutorialLogger,
) : ViewModel() {
private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION)
@@ -50,7 +50,7 @@ class TouchpadTutorialViewModel(
@Inject
constructor(
private val gesturesInteractor: TouchpadGesturesInteractor,
- private val logger: InputDeviceTutorialLogger
+ private val logger: InputDeviceTutorialLogger,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@@ -65,4 +65,5 @@ enum class Screen {
BACK_GESTURE,
HOME_GESTURE,
RECENT_APPS_GESTURE,
+ SWITCH_APPS_GESTURE,
}
diff --git a/packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt b/packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt
deleted file mode 100644
index 8b6c8601f5d2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/window/flag/WindowBlurFlag.kt
+++ /dev/null
@@ -1,32 +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.window.flag
-
-import com.android.systemui.Flags
-
-/**
- * Flag that controls whether the background surface is blurred or not while on the
- * lockscreen/shade/bouncer. This makes the background of scrim, bouncer and few other opaque
- * surfaces transparent so that we can see the blur effect on the background surface (wallpaper).
- */
-object WindowBlurFlag {
- /** Whether the blur is enabled or not */
- @JvmStatic
- val isEnabled
- // Add flags here that require scrims/background surfaces to be transparent.
- get() = Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
index 2491ca7565c7..d2069cfdfdc6 100644
--- a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
@@ -19,12 +19,12 @@ package com.android.systemui.window.ui
import android.util.Log
import android.view.Choreographer
import android.view.Choreographer.FrameCallback
+import com.android.systemui.Flags
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.statusbar.BlurUtils
-import com.android.systemui.window.flag.WindowBlurFlag
import com.android.systemui.window.ui.viewmodel.WindowRootViewModel
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.filter
@@ -43,7 +43,7 @@ object WindowRootViewBinder {
blurUtils: BlurUtils?,
choreographer: Choreographer?,
) {
- if (!WindowBlurFlag.isEnabled) return
+ if (!Flags.bouncerUiRevamp()) return
if (blurUtils == null || choreographer == null) return
view.repeatWhenAttached {
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 a17f100904be..e5376d26840d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -88,8 +88,13 @@ public class RecordingControllerTest extends SysuiTestCase {
private ScreenRecordPermissionDialogDelegate.Factory
mScreenRecordPermissionDialogDelegateFactory;
@Mock
+ private ScreenRecordPermissionViewBinder.Factory
+ mScreenRecordPermissionViewBinderFactory;
+ @Mock
private ScreenRecordPermissionDialogDelegate mScreenRecordPermissionDialogDelegate;
@Mock
+ private ScreenRecordPermissionViewBinder mScreenRecordPermissionViewBinder;
+ @Mock
private SystemUIDialog mScreenRecordSystemUIDialog;
private RecordingController mController;
@@ -106,6 +111,8 @@ public class RecordingControllerTest extends SysuiTestCase {
.thenReturn(mScreenCaptureDisabledDialog);
when(mScreenRecordPermissionDialogDelegateFactory.create(any(), any(), anyInt(), any()))
.thenReturn(mScreenRecordPermissionDialogDelegate);
+ when(mScreenRecordPermissionViewBinderFactory.create(any(), anyInt(), any(), any()))
+ .thenReturn(mScreenRecordPermissionViewBinder);
when(mScreenRecordPermissionDialogDelegate.createDialog())
.thenReturn(mScreenRecordSystemUIDialog);
mController = new RecordingController(
@@ -116,7 +123,8 @@ public class RecordingControllerTest extends SysuiTestCase {
new RecordingControllerLogger(logcatLogBuffer("RecordingControllerTest")),
mMediaProjectionMetricsLogger,
mScreenCaptureDisabledDialogDelegate,
- mScreenRecordPermissionDialogDelegateFactory
+ mScreenRecordPermissionDialogDelegateFactory,
+ mScreenRecordPermissionViewBinderFactory
);
mController.addCallback(mCallback);
}
@@ -238,6 +246,26 @@ public class RecordingControllerTest extends SysuiTestCase {
}
@Test
+ public void testCreateScreenRecordPermissionViewBinder() {
+ ScreenRecordPermissionViewBinder viewBinder =
+ mController.createScreenRecordPermissionViewBinder(
+ /* onStartRecordingClicked= */ null);
+ assertThat(viewBinder).isEqualTo(mScreenRecordPermissionViewBinder);
+ }
+
+ @Test
+ public void testScreenCapturingAllowed_returnsFalseIsScreenCaptureDisabled() {
+ when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
+ assertFalse(mController.isScreenCaptureDisabled());
+ }
+
+ @Test
+ public void testScreenCapturingNotAllowed_returnsTrueIsScreenCaptureDisabled() {
+ when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
+ assertTrue(mController.isScreenCaptureDisabled());
+ }
+
+ @Test
public void testScreenCapturingAllowed_logsProjectionInitiated() {
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 72d1db3affe8..281ce16b539f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -22,6 +22,7 @@ import static android.app.Notification.CATEGORY_EVENT;
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
+import static android.app.Notification.FLAG_PROMOTED_ONGOING;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
@@ -43,6 +44,8 @@ import android.graphics.drawable.Icon;
import android.media.session.MediaSession;
import android.os.Bundle;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
@@ -54,6 +57,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -280,6 +285,40 @@ public class NotificationEntryTest extends SysuiTestCase {
}
@Test
+ @EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
+ public void isPromotedOngoing_noFlagOnNotif_false() {
+ mEntry.getSbn().getNotification().flags &= ~FLAG_PROMOTED_ONGOING;
+
+ assertFalse(mEntry.isPromotedOngoing());
+ }
+
+ @Test
+ @DisableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
+ public void isPromotedOngoing_statusBarNotifChipsFlagAndUiFlagOff_false() {
+ mEntry.getSbn().getNotification().flags |= FLAG_PROMOTED_ONGOING;
+
+ assertFalse(mEntry.isPromotedOngoing());
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ public void isPromotedOngoing_uiFlagOnAndNotifHasFlag_true() {
+ mEntry.getSbn().getNotification().flags |= FLAG_PROMOTED_ONGOING;
+
+ assertTrue(mEntry.isPromotedOngoing());
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME)
+ public void isPromotedOngoing_statusBarNotifChipsFlagOnAndNotifHasFlag_true() {
+ mEntry.getSbn().getNotification().flags |= FLAG_PROMOTED_ONGOING;
+
+ assertTrue(mEntry.isPromotedOngoing());
+ }
+
+ @Test
public void testIsNotificationVisibilityPrivate_true() {
assertTrue(mEntry.isNotificationVisibilityPrivate());
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
index 82a5311269f0..56a7b4db5455 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
@@ -18,10 +18,12 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
val Kosmos.keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor by
Kosmos.Fixture {
KeyguardDismissTransitionInteractor(
+ scope = testScope,
repository = keyguardTransitionRepository,
fromLockscreenTransitionInteractor = fromLockscreenTransitionInteractor,
fromPrimaryBouncerTransitionInteractor = fromPrimaryBouncerTransitionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
index 3c37101cfe66..13cbddff8803 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
@@ -20,6 +20,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.modesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
import javax.inject.Provider
val Kosmos.modesTileUserActionInteractor: ModesTileUserActionInteractor by
@@ -28,5 +29,6 @@ val Kosmos.modesTileUserActionInteractor: ModesTileUserActionInteractor by
qsTileIntentUserInputHandler,
Provider { modesDialogDelegate }.get(),
zenModeInteractor,
+ modesDialogEventLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
index 766b280334c8..f4e74fe0e6bb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone.ongoingcall.shared.model
import android.app.PendingIntent
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
/** Helper for building [OngoingCallModel.InCall] instances in tests. */
fun inCallModel(
@@ -25,4 +26,5 @@ fun inCallModel(
notificationIcon: StatusBarIconView? = null,
intent: PendingIntent? = null,
notificationKey: String = "test",
-) = OngoingCallModel.InCall(startTimeMs, notificationIcon, intent, notificationKey)
+ promotedContent: PromotedNotificationContentModel? = null,
+) = OngoingCallModel.InCall(startTimeMs, notificationIcon, intent, notificationKey, promotedContent)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
index 6c98d19db5d7..ef043e0177a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy.ui.dialog
+import android.content.mockedContext
import com.android.systemui.animation.dialogTransitionAnimator
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.mainCoroutineContext
@@ -30,6 +31,7 @@ val Kosmos.mockModesDialogDelegate by Kosmos.Fixture { mock<ModesDialogDelegate>
var Kosmos.modesDialogDelegate: ModesDialogDelegate by
Kosmos.Fixture {
ModesDialogDelegate(
+ mockedContext,
systemUIDialogFactory,
dialogTransitionAnimator,
activityStarter,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 79888b051c54..70c4c1311fc9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -21,14 +21,14 @@ import static android.view.MotionEvent.ACTION_SCROLL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
+
import android.accessibilityservice.AccessibilityTrace;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
import android.hardware.input.InputManager;
-import android.hardware.input.KeyGestureEvent;
-import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -46,15 +46,13 @@ import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import android.view.accessibility.AccessibilityEvent;
-import androidx.annotation.Nullable;
-
import com.android.server.LocalServices;
import com.android.server.accessibility.gestures.TouchExplorer;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
import com.android.server.accessibility.magnification.FullScreenMagnificationVibrationHelper;
-import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
+import com.android.server.accessibility.magnification.MagnificationKeyHandler;
import com.android.server.accessibility.magnification.MouseEventHandler;
import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
@@ -209,6 +207,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private MouseKeysInterceptor mMouseKeysInterceptor;
+ private MagnificationKeyHandler mMagnificationKeyHandler;
+
private boolean mInstalled;
private int mUserId;
@@ -235,74 +235,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
*/
private MotionEvent mLastActiveDeviceMotionEvent = null;
- private boolean mKeyGestureEventHandlerInstalled = false;
- private InputManager.KeyGestureEventHandler mKeyGestureEventHandler =
- new InputManager.KeyGestureEventHandler() {
- @Override
- public boolean handleKeyGestureEvent(
- @NonNull KeyGestureEvent event,
- @Nullable IBinder focusedToken) {
- final boolean complete =
- event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
- && !event.isCancelled();
-
- // TODO(b/355499907): Receive and handle held key gestures, which can be used
- // for continuous scaling and panning. In addition, handle multiple pan gestures
- // at the same time (e.g. user may try to pan diagonally) reasonably, including
- // decreasing diagonal movement by sqrt(2) to make it appear the same speed
- // as non-diagonal movement.
-
- if (!complete) {
- return false;
- }
-
- final int gestureType = event.getKeyGestureType();
- final int displayId = isDisplayIdValid(event.getDisplayId())
- ? event.getDisplayId() : Display.DEFAULT_DISPLAY;
-
- switch (gestureType) {
- case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN:
- mAms.getMagnificationController().scaleMagnificationByStep(
- displayId, MagnificationController.ZOOM_DIRECTION_IN);
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT:
- mAms.getMagnificationController().scaleMagnificationByStep(
- displayId, MagnificationController.ZOOM_DIRECTION_OUT);
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT:
- mAms.getMagnificationController().panMagnificationByStep(
- displayId, MagnificationController.PAN_DIRECTION_LEFT);
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT:
- mAms.getMagnificationController().panMagnificationByStep(
- displayId, MagnificationController.PAN_DIRECTION_RIGHT);
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP:
- mAms.getMagnificationController().panMagnificationByStep(
- displayId, MagnificationController.PAN_DIRECTION_UP);
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN:
- mAms.getMagnificationController().panMagnificationByStep(
- displayId, MagnificationController.PAN_DIRECTION_DOWN);
- return true;
- }
- return false;
- }
-
- @Override
- public boolean isKeyGestureSupported(int gestureType) {
- return switch (gestureType) {
- case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN -> true;
- default -> false;
- };
- }
- };
-
private static MotionEvent cancelMotion(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT
@@ -787,20 +719,11 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
});
}
- if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
- || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0)
- || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0)
- || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
+ if (isAnyMagnificationEnabled()) {
final MagnificationGestureHandler magnificationGestureHandler =
createMagnificationGestureHandler(displayId, displayContext);
addFirstEventHandler(displayId, magnificationGestureHandler);
mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
-
- if (com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures()
- && !mKeyGestureEventHandlerInstalled) {
- mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler);
- mKeyGestureEventHandlerInstalled = true;
- }
}
if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
@@ -817,6 +740,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
+ // mKeyboardInterceptor does not forward KeyEvents to other EventStreamTransformations,
+ // so it must be the last EventStreamTransformation for key events in the list.
mKeyboardInterceptor = new KeyboardInterceptor(mAms,
LocalServices.getService(WindowManagerPolicy.class));
// Since the display id of KeyEvent always would be -1 and it would be dispatched to
@@ -832,6 +757,19 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
Display.DEFAULT_DISPLAY);
addFirstEventHandler(Display.DEFAULT_DISPLAY, mMouseKeysInterceptor);
}
+
+ if (enableTalkbackAndMagnifierKeyGestures() && isAnyMagnificationEnabled()) {
+ mMagnificationKeyHandler = new MagnificationKeyHandler(
+ mAms.getMagnificationController());
+ addFirstEventHandler(Display.DEFAULT_DISPLAY, mMagnificationKeyHandler);
+ }
+ }
+
+ private boolean isAnyMagnificationEnabled() {
+ return (mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
+ || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0)
+ || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0)
+ || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0);
}
/**
@@ -921,9 +859,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mMouseKeysInterceptor = null;
}
- if (mKeyGestureEventHandlerInstalled) {
- mInputManager.unregisterKeyGestureEventHandler(mKeyGestureEventHandler);
- mKeyGestureEventHandlerInstalled = false;
+ if (mMagnificationKeyHandler != null) {
+ mMagnificationKeyHandler.onDestroy();
+ mMagnificationKeyHandler = null;
}
}
@@ -1365,6 +1303,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
joiner.add("AutoclickController");
} else if (next instanceof MotionEventInjector) {
joiner.add("MotionEventInjector");
+ } else if (next instanceof MagnificationKeyHandler) {
+ joiner.add("MagnificationKeyHandler");
}
next = next.getNext();
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 2e131b696afc..75ec8ea88ace 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -84,7 +84,7 @@ import java.util.concurrent.Executor;
* is done and before invoking {@link TransitionCallBack#onResult}.
*/
public class MagnificationController implements MagnificationConnectionManager.Callback,
- MagnificationGestureHandler.Callback,
+ MagnificationGestureHandler.Callback, MagnificationKeyHandler.Callback,
FullScreenMagnificationController.MagnificationInfoChangedCallback,
WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
@@ -347,6 +347,36 @@ public class MagnificationController implements MagnificationConnectionManager.C
handleUserInteractionChanged(displayId, mode);
}
+ @Override
+ public void onPanMagnificationStart(int displayId,
+ @MagnificationController.PanDirection int direction) {
+ // TODO(b/355499907): Handle multiple pan gestures at the same time (e.g. user may try to
+ // pan diagonally) by decreasing diagonal movement by sqrt(2) to make it appear the same
+ // speed as non-diagonal movement.
+ panMagnificationByStep(displayId, direction);
+ }
+
+ @Override
+ public void onPanMagnificationStop(int displayId,
+ @MagnificationController.PanDirection int direction) {
+ // TODO(b/388847283): Handle held key gestures, which can be used
+ // for continuous scaling and panning, until they are released.
+
+ }
+
+ @Override
+ public void onScaleMagnificationStart(int displayId,
+ @MagnificationController.ZoomDirection int direction) {
+ scaleMagnificationByStep(displayId, direction);
+ }
+
+ @Override
+ public void onScaleMagnificationStop(int displayId,
+ @MagnificationController.ZoomDirection int direction) {
+ // TODO(b/388847283): Handle held key gestures, which can be used
+ // for continuous scaling and panning, until they are released.
+ }
+
private void handleUserInteractionChanged(int displayId, int mode) {
if (mMagnificationCapabilities != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
return;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java
new file mode 100644
index 000000000000..a65580c82124
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import android.view.Display;
+import android.view.KeyEvent;
+
+import com.android.server.accessibility.BaseEventStreamTransformation;
+
+/*
+ * A class that listens to key presses used to control magnification.
+ */
+public class MagnificationKeyHandler extends BaseEventStreamTransformation {
+
+ /** Callback interface to report that a user is intending to interact with Magnification. */
+ public interface Callback {
+ /**
+ * Called when a keyboard shortcut to pan magnification in direction {@code direction} is
+ * pressed by a user. Note that this can be called for multiple directions if multiple
+ * arrows are pressed at the same time (e.g. diagonal panning).
+ *
+ * @param displayId The logical display ID
+ * @param direction The direction to start panning
+ */
+ void onPanMagnificationStart(int displayId,
+ @MagnificationController.PanDirection int direction);
+
+ /**
+ * Called when a keyboard shortcut to pan magnification in direction {@code direction} is
+ * unpressed by a user. Note that this can be called for multiple directions if multiple
+ * arrows had been pressed at the same time (e.g. diagonal panning).
+ *
+ * @param displayId The logical display ID
+ * @param direction The direction in which panning stopped
+ */
+ void onPanMagnificationStop(int displayId,
+ @MagnificationController.PanDirection int direction);
+
+ /**
+ * Called when a keyboard shortcut to scale magnification in direction `direction` is
+ * pressed by a user.
+ *
+ * @param displayId The logical display ID
+ * @param direction The direction in which scaling started
+ */
+ void onScaleMagnificationStart(int displayId,
+ @MagnificationController.ZoomDirection int direction);
+
+ /**
+ * Called when a keyboard shortcut to scale magnification in direction `direction` is
+ * unpressed by a user.
+ *
+ * @param displayId The logical display ID
+ * @param direction The direction in which scaling stopped
+ */
+ void onScaleMagnificationStop(int displayId,
+ @MagnificationController.ZoomDirection int direction);
+ }
+
+ protected final MagnificationKeyHandler.Callback mCallback;
+
+ public MagnificationKeyHandler(Callback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (!com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures()) {
+ // Send to the rest of the handlers.
+ super.onKeyEvent(event, policyFlags);
+ return;
+ }
+ boolean modifiersPressed = event.isAltPressed() && event.isMetaPressed();
+ if (!modifiersPressed) {
+ super.onKeyEvent(event, policyFlags);
+ return;
+ }
+ boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
+ int keyCode = event.getKeyCode();
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
+ || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+ int panDirection = switch(keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT -> MagnificationController.PAN_DIRECTION_LEFT;
+ case KeyEvent.KEYCODE_DPAD_RIGHT -> MagnificationController.PAN_DIRECTION_RIGHT;
+ case KeyEvent.KEYCODE_DPAD_UP -> MagnificationController.PAN_DIRECTION_UP;
+ default -> MagnificationController.PAN_DIRECTION_DOWN;
+ };
+ if (isDown) {
+ mCallback.onPanMagnificationStart(getDisplayId(event), panDirection);
+ } else {
+ mCallback.onPanMagnificationStop(getDisplayId(event), panDirection);
+ }
+ return;
+ } else if (keyCode == KeyEvent.KEYCODE_EQUALS || keyCode == KeyEvent.KEYCODE_MINUS) {
+ int zoomDirection = MagnificationController.ZOOM_DIRECTION_OUT;
+ if (keyCode == KeyEvent.KEYCODE_EQUALS) {
+ zoomDirection = MagnificationController.ZOOM_DIRECTION_IN;
+ }
+ if (isDown) {
+ mCallback.onScaleMagnificationStart(getDisplayId(event), zoomDirection);
+ } else {
+ mCallback.onScaleMagnificationStop(getDisplayId(event), zoomDirection);
+ }
+ return;
+ }
+
+ // Continue down the eventing chain if this was unused.
+ super.onKeyEvent(event, policyFlags);
+ }
+
+ private int getDisplayId(KeyEvent event) {
+ // Display ID may be invalid, e.g. for external keyboard attached to phone.
+ // In that case, use the default display.
+ if (event.getDisplayId() != Display.INVALID_DISPLAY) {
+ return event.getDisplayId();
+ }
+ return Display.DEFAULT_DISPLAY;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 5af2346650ed..2143aaaa4cd6 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -236,12 +236,13 @@ public class UserBackupManagerService {
// If an app is busy when we want to do a full-data backup, how long to defer the retry.
// This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
- private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
- private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
+ private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
+ private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
private static final String SERIAL_ID_FILE = "serial_id";
private final @UserIdInt int mUserId;
+ private final String mLogIdMsg; // Prepended to Logcat messages.
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final TransportManager mTransportManager;
@@ -259,13 +260,13 @@ public class UserBackupManagerService {
private final IBackupManager mBackupManagerBinder;
- private boolean mEnabled; // writes to this are synchronized on 'this'
+ private boolean mEnabled; // writes to this are synchronized on 'this'
private boolean mSetupComplete;
private boolean mAutoRestore;
private final PendingIntent mRunInitIntent;
- private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
+ private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
// map UIDs to the set of participating packages under that UID
private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
@@ -315,8 +316,7 @@ public class UserBackupManagerService {
private final File mBaseStateDir;
private final File mDataDir;
private final File mJournalDir;
- @Nullable
- private DataChangedJournal mJournal;
+ @Nullable private DataChangedJournal mJournal;
private final File mFullBackupScheduleFile;
// Keep a log of all the apps we've ever backed up.
@@ -337,7 +337,7 @@ public class UserBackupManagerService {
* includes setting up the directories where we keep our bookkeeping and transport management.
*
* @see #createAndInitializeService(int, Context, BackupManagerService, HandlerThread, File,
- * File, TransportManager)
+ * File, TransportManager)
*/
static UserBackupManagerService createAndInitializeService(
@UserIdInt int userId,
@@ -351,7 +351,7 @@ public class UserBackupManagerService {
currentTransport = null;
}
- Slog.d(TAG, addUserIdToLogMessage(userId, "Starting with transport " + currentTransport));
+ Slog.d(TAG, "Starting with transport " + currentTransport + " for user " + userId);
TransportManager transportManager =
new TransportManager(userId, context, transportWhitelist, currentTransport);
@@ -361,7 +361,7 @@ public class UserBackupManagerService {
HandlerThread userBackupThread =
new HandlerThread("backup-" + userId, Process.THREAD_PRIORITY_BACKGROUND);
userBackupThread.start();
- Slog.d(TAG, addUserIdToLogMessage(userId, "Started thread " + userBackupThread.getName()));
+ Slog.d(TAG, "Started thread " + userBackupThread.getName() + " for user " + userId);
return createAndInitializeService(
userId,
@@ -417,26 +417,32 @@ public class UserBackupManagerService {
*/
public static boolean getSetupCompleteSettingForUser(Context context, int userId) {
return Settings.Secure.getIntForUser(
- context.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE,
- 0,
- userId)
+ context.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 0,
+ userId)
!= 0;
}
@VisibleForTesting
- UserBackupManagerService(Context context, PackageManager packageManager,
- LifecycleOperationStorage operationStorage, TransportManager transportManager,
- BackupHandler backupHandler, BackupManagerConstants backupManagerConstants,
- IActivityManager activityManager, ActivityManagerInternal activityManagerInternal) {
+ UserBackupManagerService(
+ Context context,
+ PackageManager packageManager,
+ LifecycleOperationStorage operationStorage,
+ TransportManager transportManager,
+ BackupHandler backupHandler,
+ BackupManagerConstants backupManagerConstants,
+ IActivityManager activityManager,
+ ActivityManagerInternal activityManagerInternal) {
mContext = context;
mUserId = 0;
+ mLogIdMsg = "[UserID:" + mUserId + "] ";
mRegisterTransportsRequestedTime = 0;
mPackageManager = packageManager;
mOperationStorage = operationStorage;
- mBackupAgentConnectionManager = new BackupAgentConnectionManager(mOperationStorage,
- mPackageManager, this, mUserId);
+ mBackupAgentConnectionManager =
+ new BackupAgentConnectionManager(mOperationStorage, mPackageManager, this, mUserId);
mTransportManager = transportManager;
mFullBackupQueue = new ArrayList<>();
mBackupHandler = backupHandler;
@@ -470,13 +476,14 @@ public class UserBackupManagerService {
File dataDir,
TransportManager transportManager) {
mUserId = userId;
+ mLogIdMsg = "[UserID:" + mUserId + "] ";
mContext = Objects.requireNonNull(context, "context cannot be null");
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
mActivityManager = ActivityManager.getService();
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
- mScheduledBackupEligibility = getEligibilityRules(mPackageManager, userId, mContext,
- BackupDestination.CLOUD);
+ mScheduledBackupEligibility =
+ getEligibilityRules(mPackageManager, userId, mContext, BackupDestination.CLOUD);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -484,13 +491,13 @@ public class UserBackupManagerService {
Objects.requireNonNull(parent, "parent cannot be null");
mBackupManagerBinder = BackupManagerService.asInterface(parent.asBinder());
- mAgentTimeoutParameters = new
- BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+ mAgentTimeoutParameters =
+ new BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
mAgentTimeoutParameters.start();
mOperationStorage = new LifecycleOperationStorage(mUserId);
- mBackupAgentConnectionManager = new BackupAgentConnectionManager(mOperationStorage,
- mPackageManager, this, mUserId);
+ mBackupAgentConnectionManager =
+ new BackupAgentConnectionManager(mOperationStorage, mPackageManager, this, mUserId);
Objects.requireNonNull(userBackupThread, "userBackupThread cannot be null");
mBackupHandler = new BackupHandler(this, mOperationStorage, userBackupThread);
@@ -498,8 +505,10 @@ public class UserBackupManagerService {
// Set up our bookkeeping
final ContentResolver resolver = context.getContentResolver();
mSetupComplete = getSetupCompleteSettingForUser(context, userId);
- mAutoRestore = Settings.Secure.getIntForUser(resolver,
- Settings.Secure.BACKUP_AUTO_RESTORE, 1, userId) != 0;
+ mAutoRestore =
+ Settings.Secure.getIntForUser(
+ resolver, Settings.Secure.BACKUP_AUTO_RESTORE, 1, userId)
+ != 0;
mSetupObserver = new SetupObserver(this, mBackupHandler);
resolver.registerContentObserver(
@@ -514,10 +523,7 @@ public class UserBackupManagerService {
if (userId == UserHandle.USER_SYSTEM) {
mBaseStateDir.mkdirs();
if (!SELinux.restorecon(mBaseStateDir)) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- userId, "SELinux restorecon failed on " + mBaseStateDir));
+ Slog.w(TAG, mLogIdMsg + "SELinux restorecon failed on " + mBaseStateDir);
}
}
@@ -549,8 +555,8 @@ public class UserBackupManagerService {
// Set up the backup-request journaling
mJournalDir = new File(mBaseStateDir, "pending");
- mJournalDir.mkdirs(); // creates mBaseStateDir along the way
- mJournal = null; // will be created on first use
+ mJournalDir.mkdirs(); // creates mBaseStateDir along the way
+ mJournal = null; // will be created on first use
mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
// We are observing changes to the constants throughout the lifecycle of BMS. This is
@@ -580,14 +586,20 @@ public class UserBackupManagerService {
// if so delete expired events and do not print them to dumpsys
BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils =
new BackupManagerMonitorDumpsysUtils();
- mBackupHandler.postDelayed(backupManagerMonitorDumpsysUtils::deleteExpiredBMMEvents,
+ mBackupHandler.postDelayed(
+ backupManagerMonitorDumpsysUtils::deleteExpiredBMMEvents,
INITIALIZATION_DELAY_MILLIS);
mBackupPreferences = new UserBackupPreferences(mContext, mBaseStateDir);
// Power management
- mWakelock = new BackupWakeLock(mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "*backup*-" + userId + "-" + userBackupThread.getThreadId()), userId, mConstants);
+ mWakelock =
+ new BackupWakeLock(
+ mPowerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "*backup*-" + userId + "-" + userBackupThread.getThreadId()),
+ userId,
+ mConstants);
// Set up the various sorts of package tracking we do
mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
@@ -788,14 +800,13 @@ public class UserBackupManagerService {
mPendingInits.clear();
}
- public void setRunningFullBackupTask(
- PerformFullTransportBackupTask runningFullBackupTask) {
+ public void setRunningFullBackupTask(PerformFullTransportBackupTask runningFullBackupTask) {
mRunningFullBackupTask = runningFullBackupTask;
}
/**
- * Utility: build a new random integer token. The low bits are the ordinal of the operation for
- * near-time uniqueness, and the upper bits are random for app-side unpredictability.
+ * Utility: build a new random integer token. The low bits are the ordinal of the operation for
+ * near-time uniqueness, and the upper bits are random for app-side unpredictability.
*/
public int generateRandomIntegerToken() {
int token = mTokenGenerator.nextInt();
@@ -815,16 +826,14 @@ public class UserBackupManagerService {
public BackupAgent makeMetadataAgentWithEligibilityRules(
BackupEligibilityRules backupEligibilityRules) {
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager, mUserId,
- backupEligibilityRules);
+ PackageManagerBackupAgent pmAgent =
+ new PackageManagerBackupAgent(mPackageManager, mUserId, backupEligibilityRules);
pmAgent.attach(mContext);
pmAgent.onCreate(UserHandle.of(mUserId));
return pmAgent;
}
- /**
- * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
- */
+ /** Same as {@link #makeMetadataAgent()} but with explicit package-set configuration. */
public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
PackageManagerBackupAgent pmAgent =
new PackageManagerBackupAgent(mPackageManager, packages, mUserId);
@@ -834,12 +843,12 @@ public class UserBackupManagerService {
}
private void initPackageTracking() {
- if (DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "` tracking"));
+ if (DEBUG) Slog.v(TAG, mLogIdMsg + "` tracking");
// Remember our ancestral dataset
mTokenFile = new File(mBaseStateDir, "ancestral");
- try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
- new FileInputStream(mTokenFile)))) {
+ try (DataInputStream tokenStream =
+ new DataInputStream(new BufferedInputStream(new FileInputStream(mTokenFile)))) {
int version = tokenStream.readInt();
if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
mAncestralToken = tokenStream.readLong();
@@ -856,9 +865,9 @@ public class UserBackupManagerService {
}
} catch (FileNotFoundException fnf) {
// Probably innocuous
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "No ancestral data"));
+ Slog.d(TAG, mLogIdMsg + "No ancestral data");
} catch (IOException e) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId, "Unable to read token file"), e);
+ Slog.w(TAG, mLogIdMsg + "Unable to read token file", e);
}
mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
@@ -899,20 +908,20 @@ public class UserBackupManagerService {
boolean changed = false;
ArrayList<FullBackupEntry> schedule = null;
List<PackageInfo> apps =
- PackageManagerBackupAgent.getStorableApplications(mPackageManager, mUserId,
- mScheduledBackupEligibility);
+ PackageManagerBackupAgent.getStorableApplications(
+ mPackageManager, mUserId, mScheduledBackupEligibility);
if (mFullBackupScheduleFile.exists()) {
try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
- BufferedInputStream bufStream = new BufferedInputStream(fstream);
- DataInputStream in = new DataInputStream(bufStream)) {
+ BufferedInputStream bufStream = new BufferedInputStream(fstream);
+ DataInputStream in = new DataInputStream(bufStream)) {
int version = in.readInt();
if (version != SCHEDULE_FILE_VERSION) {
// The file version doesn't match the expected value.
// Since this is within a "try" block, this exception will be treated like
// any other exception, and caught below.
- throw new IllegalArgumentException("Unknown backup schedule version "
- + version);
+ throw new IllegalArgumentException(
+ "Unknown backup schedule version " + version);
}
final int numPackages = in.readInt();
@@ -935,12 +944,20 @@ public class UserBackupManagerService {
pkg.applicationInfo)) {
schedule.add(new FullBackupEntry(pkgName, lastBackup));
} else {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Package " + pkgName
- + " no longer eligible for full backup"));
+ Slog.i(
+ TAG,
+ mLogIdMsg
+ + "Package "
+ + pkgName
+ + " no longer eligible for full backup");
}
} catch (NameNotFoundException e) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Package " + pkgName
- + " not installed; dropping from full backup"));
+ Slog.i(
+ TAG,
+ mLogIdMsg
+ + "Package "
+ + pkgName
+ + " not installed; dropping from full backup");
}
}
@@ -954,11 +971,10 @@ public class UserBackupManagerService {
if (DEBUG) {
Slog.i(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "New full backup app "
- + app.packageName
- + " found"));
+ mLogIdMsg
+ + "New full backup app "
+ + app.packageName
+ + " found");
}
schedule.add(new FullBackupEntry(app.packageName, 0));
changed = true;
@@ -968,7 +984,7 @@ public class UserBackupManagerService {
Collections.sort(schedule);
} catch (Exception e) {
- Slog.e(TAG, addUserIdToLogMessage(mUserId, "Unable to read backup schedule"), e);
+ Slog.e(TAG, mLogIdMsg + "Unable to read backup schedule", e);
mFullBackupScheduleFile.delete();
schedule = null;
}
@@ -994,46 +1010,43 @@ public class UserBackupManagerService {
return schedule;
}
- private Runnable mFullBackupScheduleWriter = new Runnable() {
- @Override
- public void run() {
- synchronized (mQueueLock) {
- try {
- ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
- DataOutputStream bufOut = new DataOutputStream(bufStream);
- bufOut.writeInt(SCHEDULE_FILE_VERSION);
-
- // version 1:
- //
- // [int] # of packages in the queue = N
- // N * {
- // [utf8] package name
- // [long] last backup time for this package
- // }
- int numPackages = mFullBackupQueue.size();
- bufOut.writeInt(numPackages);
-
- for (int i = 0; i < numPackages; i++) {
- FullBackupEntry entry = mFullBackupQueue.get(i);
- bufOut.writeUTF(entry.packageName);
- bufOut.writeLong(entry.lastBackup);
+ private Runnable mFullBackupScheduleWriter =
+ new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mQueueLock) {
+ try {
+ ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
+ DataOutputStream bufOut = new DataOutputStream(bufStream);
+ bufOut.writeInt(SCHEDULE_FILE_VERSION);
+
+ // version 1:
+ //
+ // [int] # of packages in the queue = N
+ // N * {
+ // [utf8] package name
+ // [long] last backup time for this package
+ // }
+ int numPackages = mFullBackupQueue.size();
+ bufOut.writeInt(numPackages);
+
+ for (int i = 0; i < numPackages; i++) {
+ FullBackupEntry entry = mFullBackupQueue.get(i);
+ bufOut.writeUTF(entry.packageName);
+ bufOut.writeLong(entry.lastBackup);
+ }
+ bufOut.flush();
+
+ AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
+ FileOutputStream out = af.startWrite();
+ out.write(bufStream.toByteArray());
+ af.finishWrite(out);
+ } catch (Exception e) {
+ Slog.e(TAG, mLogIdMsg + "Unable to write backup schedule!", e);
+ }
}
- bufOut.flush();
-
- AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
- FileOutputStream out = af.startWrite();
- out.write(bufStream.toByteArray());
- af.finishWrite(out);
- } catch (Exception e) {
- Slog.e(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Unable to write backup schedule!"),
- e);
}
- }
- }
- };
+ };
private void writeFullBackupScheduleAsync() {
mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
@@ -1044,28 +1057,33 @@ public class UserBackupManagerService {
ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
journals.removeAll(Collections.singletonList(mJournal));
if (!journals.isEmpty()) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId,
- "Found " + journals.size() + " stale backup journal(s), scheduling."));
+ Slog.i(
+ TAG,
+ mLogIdMsg
+ + "Found "
+ + journals.size()
+ + " stale backup journal(s), scheduling.");
}
Set<String> packageNames = new LinkedHashSet<>();
for (DataChangedJournal journal : journals) {
try {
- journal.forEach(packageName -> {
- if (packageNames.add(packageName)) {
- dataChangedImpl(packageName);
- }
- });
+ journal.forEach(
+ packageName -> {
+ if (packageNames.add(packageName)) {
+ dataChangedImpl(packageName);
+ }
+ });
} catch (IOException e) {
- Slog.e(TAG, addUserIdToLogMessage(mUserId, "Can't read " + journal), e);
+ Slog.e(TAG, mLogIdMsg + "Can't read " + journal, e);
}
}
if (!packageNames.isEmpty()) {
- String msg = "Stale backup journals: Scheduled " + packageNames.size()
- + " package(s) total";
+ String msg =
+ "Stale backup journals: Scheduled " + packageNames.size() + " package(s) total";
if (DEBUG) {
msg += ": " + packageNames;
}
- Slog.i(TAG, addUserIdToLogMessage(mUserId, msg));
+ Slog.i(TAG, mLogIdMsg + msg);
}
}
@@ -1105,12 +1123,11 @@ public class UserBackupManagerService {
if (DEBUG) {
Slog.i(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "recordInitPending("
- + isPending
- + ") on transport "
- + transportName));
+ mLogIdMsg
+ + "recordInitPending("
+ + isPending
+ + ") on transport "
+ + transportName);
}
File stateDir = new File(mBaseStateDir, transportDirName);
@@ -1170,10 +1187,16 @@ public class UserBackupManagerService {
private void onTransportRegistered(String transportName, String transportDirName) {
long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "Transport " + transportName + " registered " + timeMs
- + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS
- + "ms)"));
+ Slog.d(
+ TAG,
+ mLogIdMsg
+ + "Transport "
+ + transportName
+ + " registered "
+ + timeMs
+ + "ms after first request (delay = "
+ + INITIALIZATION_DELAY_MILLIS
+ + "ms)");
File stateDir = new File(mBaseStateDir, transportDirName);
stateDir.mkdirs();
@@ -1185,8 +1208,10 @@ public class UserBackupManagerService {
// TODO: pick a better starting time than now + 1 minute
long delay = 1000 * 60; // one minute, in milliseconds
- mAlarmManager.set(AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + delay, mRunInitIntent);
+ mAlarmManager.set(
+ AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + delay,
+ mRunInitIntent);
}
}
}
@@ -1195,137 +1220,131 @@ public class UserBackupManagerService {
* A {@link BroadcastReceiver} tracking changes to packages and sd cards in order to update our
* internal bookkeeping.
*/
- private BroadcastReceiver mPackageTrackingReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Received broadcast " + intent));
- }
+ private BroadcastReceiver mPackageTrackingReceiver =
+ new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, mLogIdMsg + "Received broadcast " + intent);
+ }
- String action = intent.getAction();
- boolean replacing = false;
- boolean added = false;
- boolean changed = false;
- Bundle extras = intent.getExtras();
- String[] packageList = null;
+ String action = intent.getAction();
+ boolean replacing = false;
+ boolean added = false;
+ boolean changed = false;
+ Bundle extras = intent.getExtras();
+ String[] packageList = null;
+
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+ || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+ || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
- if (Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_REMOVED.equals(action)
- || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- Uri uri = intent.getData();
- if (uri == null) {
- return;
- }
+ String packageName = uri.getSchemeSpecificPart();
+ if (packageName != null) {
+ packageList = new String[] {packageName};
+ }
- String packageName = uri.getSchemeSpecificPart();
- if (packageName != null) {
- packageList = new String[] {packageName};
- }
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+ if (changed) {
+ // Look at new transport states for package changed events.
+ String[] components =
+ intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
- changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
- if (changed) {
- // Look at new transport states for package changed events.
- String[] components =
- intent.getStringArrayExtra(
- Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ if (DEBUG) {
+ Slog.i(TAG, mLogIdMsg + "Package " + packageName + " changed");
+ for (int i = 0; i < components.length; i++) {
+ Slog.i(TAG, mLogIdMsg + " * " + components[i]);
+ }
+ }
- if (DEBUG) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Package " + packageName + " changed"));
- for (int i = 0; i < components.length; i++) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, " * " + components[i]));
+ mBackupHandler.post(
+ () ->
+ mTransportManager.onPackageChanged(
+ packageName, components));
+ return;
}
- }
- mBackupHandler.post(
- () ->
- mTransportManager.onPackageChanged(
- packageName, components));
- return;
- }
+ added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ added = true;
+ packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ added = false;
+ packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ }
- added = Intent.ACTION_PACKAGE_ADDED.equals(action);
- replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- added = true;
- packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- added = false;
- packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- }
+ if (packageList == null || packageList.length == 0) {
+ return;
+ }
- if (packageList == null || packageList.length == 0) {
- return;
- }
+ int uid = extras.getInt(Intent.EXTRA_UID);
+ if (added) {
+ synchronized (mBackupParticipants) {
+ if (replacing) {
+ // Remove the entry under the old uid and fall through to re-add. If
+ // an app
+ // just opted into key/value backup, add it as a known participant.
+ removePackageParticipantsLocked(packageList, uid);
+ }
+ addPackageParticipantsLocked(packageList);
+ }
- int uid = extras.getInt(Intent.EXTRA_UID);
- if (added) {
- synchronized (mBackupParticipants) {
- if (replacing) {
- // Remove the entry under the old uid and fall through to re-add. If
- // an app
- // just opted into key/value backup, add it as a known participant.
- removePackageParticipantsLocked(packageList, uid);
- }
- addPackageParticipantsLocked(packageList);
- }
+ long now = System.currentTimeMillis();
+ for (String packageName : packageList) {
+ try {
+ PackageInfo app =
+ mPackageManager.getPackageInfoAsUser(
+ packageName, /* flags */ 0, mUserId);
+ if (mScheduledBackupEligibility.appGetsFullBackup(app)
+ && mScheduledBackupEligibility.appIsEligibleForBackup(
+ app.applicationInfo)) {
+ enqueueFullBackup(packageName, now);
+ scheduleNextFullBackupJob(0);
+ } else {
+ // The app might have just transitioned out of full-data into
+ // doing
+ // key/value backups, or might have just disabled backups
+ // entirely. Make
+ // sure it is no longer in the full-data queue.
+ synchronized (mQueueLock) {
+ dequeueFullBackupLocked(packageName);
+ }
+ writeFullBackupScheduleAsync();
+ }
- long now = System.currentTimeMillis();
- for (String packageName : packageList) {
- try {
- PackageInfo app =
- mPackageManager.getPackageInfoAsUser(
- packageName, /* flags */ 0, mUserId);
- if (mScheduledBackupEligibility.appGetsFullBackup(app)
- && mScheduledBackupEligibility.appIsEligibleForBackup(
- app.applicationInfo)) {
- enqueueFullBackup(packageName, now);
- scheduleNextFullBackupJob(0);
- } else {
- // The app might have just transitioned out of full-data into
- // doing
- // key/value backups, or might have just disabled backups
- // entirely. Make
- // sure it is no longer in the full-data queue.
- synchronized (mQueueLock) {
- dequeueFullBackupLocked(packageName);
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageAdded(packageName));
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, mLogIdMsg + "Can't resolve new app " + packageName);
}
- writeFullBackupScheduleAsync();
}
- mBackupHandler.post(
- () -> mTransportManager.onPackageAdded(packageName));
- } catch (NameNotFoundException e) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId,
- "Can't resolve new app " + packageName));
- }
- }
+ // Whenever a package is added or updated we need to update the package
+ // metadata
+ // bookkeeping.
+ dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
+ } else {
+ if (!replacing) {
+ // Outright removal. In the full-data case, the app will be dropped from
+ // the
+ // queue when its (now obsolete) name comes up again for backup.
+ synchronized (mBackupParticipants) {
+ removePackageParticipantsLocked(packageList, uid);
+ }
+ }
- // Whenever a package is added or updated we need to update the package
- // metadata
- // bookkeeping.
- dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
- } else {
- if (!replacing) {
- // Outright removal. In the full-data case, the app will be dropped from
- // the
- // queue when its (now obsolete) name comes up again for backup.
- synchronized (mBackupParticipants) {
- removePackageParticipantsLocked(packageList, uid);
+ for (String packageName : packageList) {
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageRemoved(packageName));
+ }
}
}
-
- for (String packageName : packageList) {
- mBackupHandler.post(
- () -> mTransportManager.onPackageRemoved(packageName));
- }
- }
- }
- };
+ };
// Add the backup agents in the given packages to our set of known backup participants.
// If 'packageNames' is null, adds all backup agents in the whole system.
@@ -1334,29 +1353,23 @@ public class UserBackupManagerService {
List<PackageInfo> targetApps = allAgentPackages();
if (packageNames != null) {
if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "addPackageParticipantsLocked: #" + packageNames.length));
+ Slog.v(TAG, mLogIdMsg + "addPackageParticipantsLocked: #" + packageNames.length);
}
for (String packageName : packageNames) {
addPackageParticipantsLockedInner(packageName, targetApps);
}
} else {
if (DEBUG) {
- Slog.v(TAG, addUserIdToLogMessage(mUserId, "addPackageParticipantsLocked: all"));
+ Slog.v(TAG, mLogIdMsg + "addPackageParticipantsLocked: all");
}
addPackageParticipantsLockedInner(null, targetApps);
}
}
- private void addPackageParticipantsLockedInner(String packageName,
- List<PackageInfo> targetPkgs) {
+ private void addPackageParticipantsLockedInner(
+ String packageName, List<PackageInfo> targetPkgs) {
if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Examining " + packageName + " for backup agent"));
+ Slog.v(TAG, mLogIdMsg + "Examining " + packageName + " for backup agent");
}
for (PackageInfo pkg : targetPkgs) {
@@ -1368,17 +1381,14 @@ public class UserBackupManagerService {
mBackupParticipants.put(uid, set);
}
set.add(pkg.packageName);
- if (DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "Agent found; added"));
+ if (DEBUG) Slog.v(TAG, mLogIdMsg + "Agent found; added");
// Schedule a backup for it on general principles
if (DEBUG) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Scheduling backup for new app " + pkg.packageName));
+ Slog.i(TAG, mLogIdMsg + "Scheduling backup for new app " + pkg.packageName);
}
- Message msg = mBackupHandler
- .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
+ Message msg =
+ mBackupHandler.obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
mBackupHandler.sendMessage(msg);
}
}
@@ -1387,19 +1397,18 @@ public class UserBackupManagerService {
// Remove the given packages' entries from our known active set.
private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
if (packageNames == null) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId, "removePackageParticipants with null list"));
+ Slog.w(TAG, mLogIdMsg + "removePackageParticipants with null list");
return;
}
if (DEBUG) {
Slog.v(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "removePackageParticipantsLocked: uid="
- + oldUid
- + " #"
- + packageNames.length));
+ mLogIdMsg
+ + "removePackageParticipantsLocked: uid="
+ + oldUid
+ + " #"
+ + packageNames.length);
}
for (String pkg : packageNames) {
// Known previous UID, so we know which package set to check
@@ -1408,10 +1417,7 @@ public class UserBackupManagerService {
removePackageFromSetLocked(set, pkg);
if (set.isEmpty()) {
if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, " last one of this uid; purging set"));
+ Slog.v(TAG, mLogIdMsg + " last one of this uid; purging set");
}
mBackupParticipants.remove(oldUid);
}
@@ -1419,8 +1425,7 @@ public class UserBackupManagerService {
}
}
- private void removePackageFromSetLocked(final HashSet<String> set,
- final String packageName) {
+ private void removePackageFromSetLocked(final HashSet<String> set, final String packageName) {
if (set.contains(packageName)) {
// Found it. Remove this one package from the bookkeeping, and
// if it's the last participating app under this uid we drop the
@@ -1429,9 +1434,7 @@ public class UserBackupManagerService {
// bookkeeping so that its current-dataset data will be retrieved
// if the app is subsequently reinstalled
if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(mUserId, " removing participant " + packageName));
+ Slog.v(TAG, mLogIdMsg + " removing participant " + packageName);
}
set.remove(packageName);
mPendingBackups.remove(packageName);
@@ -1456,8 +1459,11 @@ public class UserBackupManagerService {
// we will need the shared library path, so look that up and store it here.
// This is used implicitly when we pass the PackageInfo object off to
// the Activity Manager to launch the app for backup/restore purposes.
- app = mPackageManager.getApplicationInfoAsUser(pkg.packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES, mUserId);
+ app =
+ mPackageManager.getApplicationInfoAsUser(
+ pkg.packageName,
+ PackageManager.GET_SHARED_LIBRARY_FILES,
+ mUserId);
pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
}
@@ -1479,8 +1485,8 @@ public class UserBackupManagerService {
final Intent notification = new Intent();
notification.setAction(BACKUP_FINISHED_ACTION);
notification.setPackage(receiver);
- notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
- | Intent.FLAG_RECEIVER_FOREGROUND);
+ notification.addFlags(
+ Intent.FLAG_INCLUDE_STOPPED_PACKAGES | Intent.FLAG_RECEIVER_FOREGROUND);
notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
mContext.sendBroadcastAsUser(notification, UserHandle.of(mUserId));
}
@@ -1506,17 +1512,14 @@ public class UserBackupManagerService {
af.writeInt(-1);
} else {
af.writeInt(mAncestralPackages.size());
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Ancestral packages: " + mAncestralPackages.size()));
+ Slog.d(TAG, mLogIdMsg + "Ancestral packages: " + mAncestralPackages.size());
for (String pkgName : mAncestralPackages) {
af.writeUTF(pkgName);
- if (DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, " " + pkgName));
+ if (DEBUG) Slog.v(TAG, mLogIdMsg + " " + pkgName);
}
}
} catch (IOException e) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId, "Unable to write token file:"), e);
+ Slog.w(TAG, mLogIdMsg + "Unable to write token file:", e);
}
}
@@ -1539,45 +1542,39 @@ public class UserBackupManagerService {
/**
* Clear an application's data, blocking until the operation completes or times out.
*
- * @param checkFlagAllowClearUserDataOnFailedRestore if {@code true} uses
- * {@link ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE} to decide if
- * clearing data is allowed after a failed restore.
- *
+ * @param checkFlagAllowClearUserDataOnFailedRestore if {@code true} uses {@link
+ * ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE} to decide if
+ * clearing data is allowed after a failed restore.
* @param keepSystemState if {@code true}, we don't clear system state such as already restored
- * notification settings, permission grants, etc.
+ * notification settings, permission grants, etc.
*/
- private void clearApplicationDataSynchronous(String packageName,
- boolean checkFlagAllowClearUserDataOnFailedRestore, boolean keepSystemState) {
+ private void clearApplicationDataSynchronous(
+ String packageName,
+ boolean checkFlagAllowClearUserDataOnFailedRestore,
+ boolean keepSystemState) {
try {
- ApplicationInfo applicationInfo = mPackageManager.getPackageInfoAsUser(
- packageName, 0, mUserId).applicationInfo;
+ ApplicationInfo applicationInfo =
+ mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId).applicationInfo;
boolean shouldClearData;
if (checkFlagAllowClearUserDataOnFailedRestore
&& applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
- shouldClearData = (applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE) != 0;
+ int clearOnFailedRestoreFlag =
+ ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE;
+ shouldClearData = (applicationInfo.privateFlags & clearOnFailedRestoreFlag) != 0;
} else {
shouldClearData =
- (applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) != 0;
+ (applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) != 0;
}
if (!shouldClearData) {
if (DEBUG) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Clearing app data is not allowed so not wiping "
- + packageName));
+ Slog.i(TAG, mLogIdMsg + "Clearing app data is not allowed " + packageName);
}
return;
}
} catch (NameNotFoundException e) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Tried to clear data for " + packageName + " but not found"));
+ Slog.w(TAG, mLogIdMsg + "Tried to clear data for " + packageName + " but not found");
return;
}
@@ -1585,8 +1582,8 @@ public class UserBackupManagerService {
synchronized (mClearDataLock) {
mClearingData = true;
- mActivityManagerInternal.clearApplicationUserData(packageName, keepSystemState,
- /*isRestore=*/ true, observer, mUserId);
+ mActivityManagerInternal.clearApplicationUserData(
+ packageName, keepSystemState, /* isRestore= */ true, observer, mUserId);
// Only wait 30 seconds for the clear data to happen.
long timeoutMark = System.currentTimeMillis() + CLEAR_DATA_TIMEOUT_INTERVAL;
@@ -1598,20 +1595,16 @@ public class UserBackupManagerService {
mClearingData = false;
Slog.w(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Interrupted while waiting for "
- + packageName
- + " data to be cleared"),
+ mLogIdMsg
+ + "Interrupted while waiting for "
+ + packageName
+ + " data to be cleared",
e);
}
}
if (mClearingData) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Clearing app data for " + packageName + " timed out"));
+ Slog.w(TAG, mLogIdMsg + "Clearing app data for " + packageName + " timed out");
}
}
}
@@ -1632,23 +1625,20 @@ public class UserBackupManagerService {
* the active set if possible, else the ancestral one. Returns zero if none available.
*/
public long getAvailableRestoreToken(String packageName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getAvailableRestoreToken");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "getAvailableRestoreToken");
long token = mAncestralToken;
synchronized (mQueueLock) {
if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
if (DEBUG) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "App in ever-stored, so using current token"));
+ Slog.i(TAG, mLogIdMsg + "App in ever-stored, so using current token");
}
token = mCurrentToken;
}
}
if (DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "getAvailableRestoreToken() == " + token));
+ Slog.i(TAG, mLogIdMsg + "getAvailableRestoreToken() == " + token);
}
return token;
}
@@ -1666,35 +1656,40 @@ public class UserBackupManagerService {
* Requests a backup for the inputted {@code packages} with a specified {@link
* IBackupManagerMonitor} and {@link OperationType}.
*/
- public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags) {
- BackupManagerMonitorEventSender mBackupManagerMonitorEventSender =
+ public int requestBackup(
+ String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) {
+ BackupManagerMonitorEventSender mBackupManagerMonitorEventSender =
getBMMEventSender(monitor);
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
if (packages == null || packages.length < 1) {
- Slog.e(TAG, addUserIdToLogMessage(mUserId, "No packages named for backup request"));
+ Slog.e(TAG, mLogIdMsg + "No packages named for backup request");
BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
mBackupManagerMonitorEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
- null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+ BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES, null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
throw new IllegalArgumentException("No packages are provided for backup");
}
if (!mEnabled || !mSetupComplete) {
Slog.i(
TAG,
- addUserIdToLogMessage(mUserId, "Backup requested but enabled="
+ mLogIdMsg
+ + "Backup requested but enabled="
+ mEnabled
+ " setupComplete="
- + mSetupComplete));
- BackupObserverUtils.sendBackupFinished(observer,
- BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- final int logTag = mSetupComplete
- ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
- : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
- mBackupManagerMonitorEventSender.monitorEvent(logTag, null,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+ + mSetupComplete);
+ BackupObserverUtils.sendBackupFinished(
+ observer, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ final int logTag =
+ mSetupComplete
+ ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
+ : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
+ mBackupManagerMonitorEventSender.monitorEvent(
+ logTag,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ null);
return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
}
@@ -1708,31 +1703,45 @@ public class UserBackupManagerService {
transportConnection =
mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
backupDestination = getBackupDestinationFromTransport(transportConnection);
- } catch (TransportNotRegisteredException | TransportNotAvailableException
+ } catch (TransportNotRegisteredException
+ | TransportNotAvailableException
| RemoteException e) {
BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
mBackupManagerMonitorEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
- null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL, null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
return BackupManager.ERROR_TRANSPORT_ABORTED;
}
OnTaskFinishedListener listener =
caller -> mTransportManager.disposeOfTransportClient(transportConnection, caller);
- BackupEligibilityRules backupEligibilityRules = getEligibilityRulesForOperation(
- backupDestination);
+ BackupEligibilityRules backupEligibilityRules =
+ getEligibilityRulesForOperation(backupDestination);
Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
- msg.obj = getRequestBackupParams(packages, observer, monitor, flags, backupEligibilityRules,
- transportConnection, transportDirName, listener);
+ msg.obj =
+ getRequestBackupParams(
+ packages,
+ observer,
+ monitor,
+ flags,
+ backupEligibilityRules,
+ transportConnection,
+ transportDirName,
+ listener);
mBackupHandler.sendMessage(msg);
return BackupManager.SUCCESS;
}
@VisibleForTesting
- BackupParams getRequestBackupParams(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags, BackupEligibilityRules backupEligibilityRules,
- TransportConnection transportConnection, String transportDirName,
+ BackupParams getRequestBackupParams(
+ String[] packages,
+ IBackupObserver observer,
+ IBackupManagerMonitor monitor,
+ int flags,
+ BackupEligibilityRules backupEligibilityRules,
+ TransportConnection transportConnection,
+ String transportDirName,
OnTaskFinishedListener listener) {
ArrayList<String> fullBackupList = new ArrayList<>();
ArrayList<String> kvBackupList = new ArrayList<>();
@@ -1742,11 +1751,12 @@ public class UserBackupManagerService {
continue;
}
try {
- PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
+ PackageInfo packageInfo =
+ mPackageManager.getPackageInfoAsUser(
+ packageName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
if (!backupEligibilityRules.appIsEligibleForBackup(packageInfo.applicationInfo)) {
- BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
- BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ BackupObserverUtils.sendBackupOnPackageResult(
+ observer, packageName, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
}
if (backupEligibilityRules.appGetsFullBackup(packageInfo)) {
@@ -1755,31 +1765,41 @@ public class UserBackupManagerService {
kvBackupList.add(packageInfo.packageName);
}
} catch (NameNotFoundException e) {
- BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
- BackupManager.ERROR_PACKAGE_NOT_FOUND);
+ BackupObserverUtils.sendBackupOnPackageResult(
+ observer, packageName, BackupManager.ERROR_PACKAGE_NOT_FOUND);
}
}
- EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
+ EventLog.writeEvent(
+ EventLogTags.BACKUP_REQUESTED,
+ packages.length,
+ kvBackupList.size(),
fullBackupList.size());
if (DEBUG) {
Slog.i(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Backup requested for "
- + packages.length
- + " packages, of them: "
- + fullBackupList.size()
- + " full backups, "
- + kvBackupList.size()
- + " k/v backups"));
+ mLogIdMsg
+ + "Backup requested for "
+ + packages.length
+ + " packages, of them: "
+ + fullBackupList.size()
+ + " full backups, "
+ + kvBackupList.size()
+ + " k/v backups");
}
boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
- return new BackupParams(transportConnection, transportDirName, kvBackupList, fullBackupList,
- observer, monitor, listener, /* userInitiated */ true, nonIncrementalBackup,
+ return new BackupParams(
+ transportConnection,
+ transportDirName,
+ kvBackupList,
+ fullBackupList,
+ observer,
+ monitor,
+ listener, /* userInitiated */
+ true,
+ nonIncrementalBackup,
backupEligibilityRules);
}
@@ -1787,7 +1807,7 @@ public class UserBackupManagerService {
public void cancelBackups() {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
if (DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "cancelBackups() called."));
+ Slog.i(TAG, mLogIdMsg + "cancelBackups() called.");
}
final long oldToken = Binder.clearCallingIdentity();
try {
@@ -1795,14 +1815,24 @@ public class UserBackupManagerService {
mOperationStorage.operationTokensForOpType(OpType.BACKUP);
for (Integer token : operationsToCancel) {
- mOperationStorage.cancelOperation(token, /* cancelAll */ true,
- operationType -> { /* no callback needed here */ });
+ mOperationStorage.cancelOperation(
+ token, /* cancelAll */
+ true,
+ operationType -> {
+ /* no callback needed here */
+ });
}
// We don't want the backup jobs to kick in any time soon.
// Reschedules them to run in the distant future.
- KeyValueBackupJob.schedule(mUserId, mContext, BUSY_BACKOFF_MIN_MILLIS,
+ KeyValueBackupJob.schedule(
+ mUserId,
+ mContext,
+ BUSY_BACKOFF_MIN_MILLIS,
/* userBackupManagerService */ this);
- FullBackupJob.schedule(mUserId, mContext, 2 * BUSY_BACKOFF_MIN_MILLIS,
+ FullBackupJob.schedule(
+ mUserId,
+ mContext,
+ 2 * BUSY_BACKOFF_MIN_MILLIS,
/* userBackupManagerService */ this);
} finally {
Binder.restoreCallingIdentity(oldToken);
@@ -1810,35 +1840,34 @@ public class UserBackupManagerService {
}
/** Schedule a timeout message for the operation identified by {@code token}. */
- public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
- int operationType) {
+ public void prepareOperationTimeout(
+ int token, long interval, BackupRestoreTask callback, int operationType) {
if (operationType != OpType.BACKUP_WAIT && operationType != OpType.RESTORE_WAIT) {
Slog.wtf(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "prepareOperationTimeout() doesn't support operation "
- + Integer.toHexString(token)
- + " of type "
- + operationType));
+ mLogIdMsg
+ + "prepareOperationTimeout() doesn't support operation "
+ + Integer.toHexString(token)
+ + " of type "
+ + operationType);
return;
}
if (DEBUG) {
Slog.v(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "starting timeout: token="
- + Integer.toHexString(token)
- + " interval="
- + interval
- + " callback="
- + callback));
+ mLogIdMsg
+ + "starting timeout: token="
+ + Integer.toHexString(token)
+ + " interval="
+ + interval
+ + " callback="
+ + callback);
}
mOperationStorage.registerOperation(token, OpState.PENDING, callback, operationType);
- Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
- token, 0, callback);
+ Message msg =
+ mBackupHandler.obtainMessage(
+ getMessageIdForOperationType(operationType), token, 0, callback);
mBackupHandler.sendMessageDelayed(msg, interval);
}
@@ -1851,19 +1880,20 @@ public class UserBackupManagerService {
default:
Slog.wtf(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "getMessageIdForOperationType called on invalid operation type: "
- + operationType));
+ mLogIdMsg
+ + "getMessageIdForOperationType called on invalid operation type: "
+ + operationType);
return -1;
}
}
/** Block until we received an operation complete message (from the agent or cancellation). */
public boolean waitUntilOperationComplete(int token) {
- return mOperationStorage.waitUntilOperationComplete(token, operationType -> {
- mBackupHandler.removeMessages(getMessageIdForOperationType(operationType));
- });
+ return mOperationStorage.waitUntilOperationComplete(
+ token,
+ operationType -> {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(operationType));
+ });
}
/** Cancel the operation associated with {@code token}. */
@@ -1871,11 +1901,15 @@ public class UserBackupManagerService {
// Remove all pending timeout messages of types OpType.BACKUP_WAIT and
// OpType.RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
// doesn't require cancellation.
- mOperationStorage.cancelOperation(token, cancelAll, operationType -> {
- if (operationType == OpType.BACKUP_WAIT || operationType == OpType.RESTORE_WAIT) {
- mBackupHandler.removeMessages(getMessageIdForOperationType(operationType));
- }
- });
+ mOperationStorage.cancelOperation(
+ token,
+ cancelAll,
+ operationType -> {
+ if (operationType == OpType.BACKUP_WAIT
+ || operationType == OpType.RESTORE_WAIT) {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(operationType));
+ }
+ });
}
/** Returns {@code true} if a backup is currently running, else returns {@code false}. */
@@ -1885,9 +1919,7 @@ public class UserBackupManagerService {
// ----- Full-data backup scheduling -----
- /**
- * Schedule a job to tell us when it's a good time to run a full backup
- */
+ /** Schedule a job to tell us when it's a good time to run a full backup */
public void scheduleNextFullBackupJob(long transportMinLatency) {
synchronized (mQueueLock) {
if (mFullBackupQueue.size() > 0) {
@@ -1899,18 +1931,15 @@ public class UserBackupManagerService {
final long interval = mConstants.getFullBackupIntervalMilliseconds();
final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
final long latency = Math.max(transportMinLatency, appLatency);
- FullBackupJob.schedule(mUserId, mContext, latency,
- /* userBackupManagerService */ this);
+ FullBackupJob.schedule(
+ mUserId, mContext, latency, /* userBackupManagerService */ this);
} else {
- Slog.i(TAG,
- addUserIdToLogMessage(mUserId, "Full backup queue empty; not scheduling"));
+ Slog.i(TAG, mLogIdMsg + "Full backup queue empty; not scheduling");
}
}
}
- /**
- * Remove a package from the full-data queue.
- */
+ /** Remove a package from the full-data queue. */
@GuardedBy("mQueueLock")
private void dequeueFullBackupLocked(String packageName) {
final int numPackages = mFullBackupQueue.size();
@@ -1922,9 +1951,7 @@ public class UserBackupManagerService {
}
}
- /**
- * Enqueue full backup for the given app, with a note about when it last ran.
- */
+ /** Enqueue full backup for the given app, with a note about when it last ran. */
public void enqueueFullBackup(String packageName, long lastBackedUp) {
FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
synchronized (mQueueLock) {
@@ -1957,10 +1984,7 @@ public class UserBackupManagerService {
private boolean fullBackupAllowable(String transportName) {
if (!mTransportManager.isTransportRegistered(transportName)) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Transport not registered; full data backup not performed"));
+ Slog.w(TAG, mLogIdMsg + "Transport not registered; full data backup not performed");
return false;
}
@@ -1971,15 +1995,11 @@ public class UserBackupManagerService {
File stateDir = new File(mBaseStateDir, transportDirName);
File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
if (pmState.length() <= 0) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId,
- "Full backup requested but dataset not yet initialized"));
+ Slog.i(TAG, mLogIdMsg + "Full backup requested but dataset not yet initialized");
return false;
}
} catch (Exception e) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Unable to get transport name: " + e.getMessage()));
+ Slog.w(TAG, mLogIdMsg + "Unable to get transport name: " + e.getMessage());
return false;
}
@@ -1987,14 +2007,14 @@ public class UserBackupManagerService {
}
/**
- * Conditions are right for a full backup operation, so run one. The model we use is
- * to perform one app backup per scheduled job execution, and to reschedule the job
- * with zero latency as long as conditions remain right and we still have work to do.
+ * Conditions are right for a full backup operation, so run one. The model we use is to perform
+ * one app backup per scheduled job execution, and to reschedule the job with zero latency as
+ * long as conditions remain right and we still have work to do.
*
* <p>This is the "start a full backup operation" entry point called by the scheduled job.
*
- * @return Whether ongoing work will continue. The return value here will be passed
- * along as the return value to the scheduled job's onStartJob() callback.
+ * @return Whether ongoing work will continue. The return value here will be passed along as the
+ * return value to the scheduled job's onStartJob() callback.
*/
public boolean beginFullBackup(FullBackupJob scheduledJob) {
final long now = System.currentTimeMillis();
@@ -2012,33 +2032,34 @@ public class UserBackupManagerService {
// the job driving automatic backups; that job will be scheduled again when
// the user enables backup.
if (DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "beginFullBackup but enabled=" + mEnabled
- + " setupComplete=" + mSetupComplete + "; ignoring"));
+ Slog.i(
+ TAG,
+ mLogIdMsg
+ + "beginFullBackup but enabled="
+ + mEnabled
+ + " setupComplete="
+ + mSetupComplete
+ + "; ignoring");
}
return false;
}
// Don't run the backup if we're in battery saver mode, but reschedule
// to try again in the not-so-distant future.
- final PowerSaveState result =
- mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
+ final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
if (result.batterySaverEnabled) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId,
- "Deferring scheduled full backups in battery saver mode"));
- FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval,
- /* userBackupManagerService */ this);
+ Slog.i(TAG, mLogIdMsg + "Deferring scheduled full backups in battery saver mode");
+ FullBackupJob.schedule(
+ mUserId, mContext, keyValueBackupInterval, /* userBackupManagerService */ this);
return false;
}
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning scheduled full backup operation"));
+ Slog.i(TAG, mLogIdMsg + "Beginning scheduled full backup operation");
// Great; we're able to run full backup jobs now. See if we have any work to do.
synchronized (mQueueLock) {
if (mRunningFullBackupTask != null) {
- Slog.e(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Backup triggered but one already/still running!"));
+ Slog.e(TAG, mLogIdMsg + "Backup triggered but one already/still running!");
return false;
}
@@ -2053,8 +2074,7 @@ public class UserBackupManagerService {
// have emptied the queue.
if (mFullBackupQueue.size() == 0) {
// no work to do so just bow out
- Slog.i(TAG,
- addUserIdToLogMessage(mUserId, "Backup queue empty; doing nothing"));
+ Slog.i(TAG, mLogIdMsg + "Backup queue empty; doing nothing");
runBackup = false;
break;
}
@@ -2064,10 +2084,7 @@ public class UserBackupManagerService {
String transportName = mTransportManager.getCurrentTransportName();
if (!fullBackupAllowable(transportName)) {
if (DEBUG) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Preconditions not met; not running full backup"));
+ Slog.i(TAG, mLogIdMsg + "Preconditions not met; not running full backup");
}
runBackup = false;
// Typically this means we haven't run a key/value backup yet. Back off
@@ -2085,18 +2102,16 @@ public class UserBackupManagerService {
if (DEBUG) {
Slog.i(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Device ready but too early to back up next app"));
+ mLogIdMsg + "Device ready but too early to back up next app");
}
// Wait until the next app in the queue falls due for a full data backup
latency = fullBackupInterval - timeSinceRun;
- break; // we know we aren't doing work yet, so bail.
+ break; // we know we aren't doing work yet, so bail.
}
try {
- PackageInfo appInfo = mPackageManager.getPackageInfoAsUser(
- entry.packageName, 0, mUserId);
+ PackageInfo appInfo =
+ mPackageManager.getPackageInfoAsUser(entry.packageName, 0, mUserId);
if (!mScheduledBackupEligibility.appGetsFullBackup(appInfo)) {
// The head app isn't supposed to get full-data backups [any more];
// so we cull it and force a loop around to consider the new head
@@ -2104,12 +2119,11 @@ public class UserBackupManagerService {
if (DEBUG) {
Slog.i(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Culling package "
- + entry.packageName
- + " in full-backup queue but not"
- + " eligible"));
+ mLogIdMsg
+ + "Culling package "
+ + entry.packageName
+ + " in full-backup queue but not"
+ + " eligible");
}
mFullBackupQueue.remove(0);
headBusy = true; // force the while() condition
@@ -2117,19 +2131,24 @@ public class UserBackupManagerService {
}
final int privFlags = appInfo.applicationInfo.privateFlags;
- headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
- && mActivityManagerInternal.isAppForeground(
- appInfo.applicationInfo.uid);
+ headBusy =
+ (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
+ && mActivityManagerInternal.isAppForeground(
+ appInfo.applicationInfo.uid);
if (headBusy) {
- final long nextEligible = System.currentTimeMillis()
- + BUSY_BACKOFF_MIN_MILLIS
- + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
+ final long nextEligible =
+ System.currentTimeMillis()
+ + BUSY_BACKOFF_MIN_MILLIS
+ + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Slog.i(TAG, addUserIdToLogMessage(mUserId,
- "Full backup time but " + entry.packageName
- + " is busy; deferring to " + sdf.format(
- new Date(nextEligible))));
+ Slog.i(
+ TAG,
+ mLogIdMsg
+ + "Full backup time but "
+ + entry.packageName
+ + " is busy; deferring to "
+ + sdf.format(new Date(nextEligible)));
// This relocates the app's entry from the head of the queue to
// its order-appropriate position further down, so upon looping
// a new candidate will be considered at the head.
@@ -2147,21 +2166,22 @@ public class UserBackupManagerService {
if (runBackup) {
CountDownLatch latch = new CountDownLatch(1);
- String[] pkg = new String[]{entry.packageName};
+ String[] pkg = new String[] {entry.packageName};
try {
- mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
- this,
- mOperationStorage,
- /* observer */ null,
- pkg,
- /* updateSchedule */ true,
- scheduledJob,
- latch,
- /* backupObserver */ null,
- /* monitor */ null,
- /* userInitiated */ false,
- "BMS.beginFullBackup()",
- getEligibilityRulesForOperation(BackupDestination.CLOUD));
+ mRunningFullBackupTask =
+ PerformFullTransportBackupTask.newWithCurrentTransport(
+ this,
+ mOperationStorage,
+ /* observer */ null,
+ pkg,
+ /* updateSchedule */ true,
+ scheduledJob,
+ latch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ "BMS.beginFullBackup()",
+ getEligibilityRulesForOperation(BackupDestination.CLOUD));
} catch (IllegalStateException e) {
Slog.w(TAG, "Failed to start backup", e);
runBackup = false;
@@ -2169,12 +2189,15 @@ public class UserBackupManagerService {
}
if (!runBackup) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId,
- "Nothing pending full backup or failed to start the "
- + "operation; rescheduling +" + latency));
- final long deferTime = latency; // pin for the closure
- FullBackupJob.schedule(mUserId, mContext, deferTime,
- /* userBackupManagerService */ this);
+ Slog.i(
+ TAG,
+ mLogIdMsg
+ + "Nothing pending full backup or failed to start the "
+ + "operation; rescheduling +"
+ + latency);
+ final long deferTime = latency; // pin for the closure
+ FullBackupJob.schedule(
+ mUserId, mContext, deferTime, /* userBackupManagerService */ this);
return false;
}
@@ -2195,21 +2218,22 @@ public class UserBackupManagerService {
public void endFullBackup() {
// offload the mRunningFullBackupTask.handleCancel() call to another thread,
// as we might have to wait for mCancelLock
- Runnable endFullBackupRunnable = new Runnable() {
- @Override
- public void run() {
- PerformFullTransportBackupTask pftbt = null;
- synchronized (mQueueLock) {
- if (mRunningFullBackupTask != null) {
- pftbt = mRunningFullBackupTask;
+ Runnable endFullBackupRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ PerformFullTransportBackupTask pftbt = null;
+ synchronized (mQueueLock) {
+ if (mRunningFullBackupTask != null) {
+ pftbt = mRunningFullBackupTask;
+ }
+ }
+ if (pftbt != null) {
+ Slog.i(TAG, mLogIdMsg + "Telling running backup to stop");
+ pftbt.handleCancel(true);
+ }
}
- }
- if (pftbt != null) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Telling running backup to stop"));
- pftbt.handleCancel(true);
- }
- }
- };
+ };
new Thread(endFullBackupRunnable, "end-full-backup").start();
}
@@ -2217,7 +2241,7 @@ public class UserBackupManagerService {
public void restoreWidgetData(String packageName, byte[] widgetData) {
// Apply the restored widget state and generate the ID update for the app
if (DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Incorporating restored widget data"));
+ Slog.i(TAG, mLogIdMsg + "Incorporating restored widget data");
}
AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, mUserId);
}
@@ -2235,13 +2259,12 @@ public class UserBackupManagerService {
if (targets == null) {
Slog.w(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "dataChanged but no participant pkg='"
- + packageName
- + "'"
- + " uid="
- + Binder.getCallingUid()));
+ mLogIdMsg
+ + "dataChanged but no participant pkg='"
+ + packageName
+ + "'"
+ + " uid="
+ + Binder.getCallingUid());
return;
}
@@ -2253,10 +2276,7 @@ public class UserBackupManagerService {
BackupRequest req = new BackupRequest(packageName);
if (mPendingBackups.put(packageName, req) == null) {
if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Now staging backup of " + packageName));
+ Slog.d(TAG, mLogIdMsg + "Now staging backup of " + packageName);
}
// Journal this request in case of crash. The put()
@@ -2268,16 +2288,18 @@ public class UserBackupManagerService {
}
// ...and schedule a backup pass if necessary
- KeyValueBackupJob.schedule(mUserId, mContext,
- /* userBackupManagerService */ this);
+ KeyValueBackupJob.schedule(mUserId, mContext, /* userBackupManagerService */ this);
}
// Note: packageName is currently unused, but may be in the future
private HashSet<String> dataChangedTargets(String packageName) {
// If the caller does not hold the BACKUP permission, it can only request a
// backup of its own data.
- if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
- Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+ if ((mContext.checkPermission(
+ android.Manifest.permission.BACKUP,
+ Binder.getCallingPid(),
+ Binder.getCallingUid()))
+ == PackageManager.PERMISSION_DENIED) {
synchronized (mBackupParticipants) {
return mBackupParticipants.get(Binder.getCallingUid());
}
@@ -2298,10 +2320,7 @@ public class UserBackupManagerService {
if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
mJournal.addPackage(str);
} catch (IOException e) {
- Slog.e(
- TAG,
- addUserIdToLogMessage(mUserId, "Can't write " + str + " to backup journal"),
- e);
+ Slog.e(TAG, mLogIdMsg + "Can't write " + str + " to backup journal", e);
mJournal = null;
}
}
@@ -2314,31 +2333,28 @@ public class UserBackupManagerService {
if (targets == null) {
Slog.w(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "dataChanged but no participant pkg='"
- + packageName
- + "'"
- + " uid="
- + Binder.getCallingUid()));
+ mLogIdMsg
+ + "dataChanged but no participant pkg='"
+ + packageName
+ + "'"
+ + " uid="
+ + Binder.getCallingUid());
return;
}
- mBackupHandler.post(new Runnable() {
- public void run() {
- dataChangedImpl(packageName, targets);
- }
- });
+ mBackupHandler.post(
+ new Runnable() {
+ public void run() {
+ dataChangedImpl(packageName, targets);
+ }
+ });
}
/** Run an initialize operation for the given transport. */
public void initializeTransports(String[] transportNames, IBackupObserver observer) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "initializeTransport");
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "initializeTransport(): " + Arrays.asList(transportNames)));
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "initializeTransport");
+ Slog.d(TAG, mLogIdMsg + "initializeTransport(): " + Arrays.asList(transportNames));
final long oldId = Binder.clearCallingIdentity();
try {
@@ -2351,32 +2367,23 @@ public class UserBackupManagerService {
}
}
- /**
- * Sets the work profile serial number of the ancestral work profile.
- */
+ /** Sets the work profile serial number of the ancestral work profile. */
public void setAncestralSerialNumber(long ancestralSerialNumber) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "setAncestralSerialNumber");
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Setting ancestral work profile id to " + ancestralSerialNumber));
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.BACKUP, "setAncestralSerialNumber");
+ Slog.d(TAG, mLogIdMsg + "Setting ancestral work profile id to " + ancestralSerialNumber);
try (RandomAccessFile af =
new RandomAccessFile(getAncestralSerialNumberFile(), /* mode */ "rwd")) {
af.writeLong(ancestralSerialNumber);
} catch (IOException e) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Unable to write to work profile serial mapping file:"),
- e);
+ Slog.w(TAG, mLogIdMsg + "Unable to write to work profile serial mapping file:", e);
}
}
/**
- * Returns the work profile serial number of the ancestral device. This will be set by
- * {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set.
+ * Returns the work profile serial number of the ancestral device. This will be set by {@link
+ * #setAncestralSerialNumber(long)}. Will return {@code -1} if not set.
*/
public long getAncestralSerialNumber() {
try (RandomAccessFile af =
@@ -2385,20 +2392,15 @@ public class UserBackupManagerService {
} catch (FileNotFoundException e) {
// It's OK not to have the file present, so we just return -1 to indicate no value.
} catch (IOException e) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Unable to read work profile serial number file:"),
- e);
+ Slog.w(TAG, mLogIdMsg + "Unable to read work profile serial number file:", e);
}
return -1;
}
private File getAncestralSerialNumberFile() {
if (mAncestralSerialNumberFile == null) {
- mAncestralSerialNumberFile = new File(
- UserBackupManagerFiles.getBaseStateDir(getUserId()),
- SERIAL_ID_FILE);
+ mAncestralSerialNumberFile =
+ new File(UserBackupManagerFiles.getBaseStateDir(getUserId()), SERIAL_ID_FILE);
}
return mAncestralSerialNumberFile;
}
@@ -2408,39 +2410,36 @@ public class UserBackupManagerService {
mAncestralSerialNumberFile = ancestralSerialNumberFile;
}
-
/** Clear the given package's backup data from the current transport. */
public void clearBackupData(String transportName, String packageName) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "clearBackupData() of " + packageName + " on " + transportName));
+ Slog.d(TAG, mLogIdMsg + "clearBackupData() of " + packageName + " on " + transportName);
PackageInfo info;
try {
- info = mPackageManager.getPackageInfoAsUser(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
+ info =
+ mPackageManager.getPackageInfoAsUser(
+ packageName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
} catch (NameNotFoundException e) {
Slog.d(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "No such package '" + packageName + "' - not clearing backup data"));
+ mLogIdMsg + "No such package '" + packageName + "' - not clearing backup data");
return;
}
// If the caller does not hold the BACKUP permission, it can only request a
// wipe of its own backed-up data.
Set<String> apps;
- if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
- Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+ if ((mContext.checkPermission(
+ android.Manifest.permission.BACKUP,
+ Binder.getCallingPid(),
+ Binder.getCallingUid()))
+ == PackageManager.PERMISSION_DENIED) {
apps = mBackupParticipants.get(Binder.getCallingUid());
} else {
// a caller with full permission can ask to back up any participating app
// !!! TODO: allow data-clear of ANY app?
if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Privileged caller, allowing clear of other apps"));
+ Slog.v(TAG, mLogIdMsg + "Privileged caller, allowing clear of other apps");
}
apps = mProcessedPackagesJournal.getPackagesCopy();
}
@@ -2448,30 +2447,33 @@ public class UserBackupManagerService {
if (apps.contains(packageName)) {
// found it; fire off the clear request
if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(mUserId, "Found the app - running clear process"));
+ Slog.v(TAG, mLogIdMsg + "Found the app - running clear process");
}
mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
synchronized (mQueueLock) {
TransportConnection transportConnection =
- mTransportManager
- .getTransportClient(transportName, "BMS.clearBackupData()");
+ mTransportManager.getTransportClient(
+ transportName, "BMS.clearBackupData()");
if (transportConnection == null) {
// transport is currently unregistered -- make sure to retry
- Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
- new ClearRetryParams(transportName, packageName));
+ Message msg =
+ mBackupHandler.obtainMessage(
+ MSG_RETRY_CLEAR,
+ new ClearRetryParams(transportName, packageName));
mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
return;
}
final long oldId = Binder.clearCallingIdentity();
try {
- OnTaskFinishedListener listener = caller -> mTransportManager
- .disposeOfTransportClient(transportConnection, caller);
+ OnTaskFinishedListener listener =
+ caller ->
+ mTransportManager.disposeOfTransportClient(
+ transportConnection, caller);
mWakelock.acquire();
- Message msg = mBackupHandler.obtainMessage(
- MSG_RUN_CLEAR,
- new ClearParams(transportConnection, info, listener));
+ Message msg =
+ mBackupHandler.obtainMessage(
+ MSG_RUN_CLEAR,
+ new ClearParams(transportConnection, info, listener));
mBackupHandler.sendMessage(msg);
} finally {
Binder.restoreCallingIdentity(oldId);
@@ -2492,31 +2494,24 @@ public class UserBackupManagerService {
final PowerSaveState result =
mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
if (result.batterySaverEnabled) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "Not running backup while in battery save mode"));
+ Slog.d(TAG, mLogIdMsg + "Not running backup while in battery save mode");
// Try again in several hours.
- KeyValueBackupJob.schedule(mUserId, mContext,
- /* userBackupManagerService */ this);
+ KeyValueBackupJob.schedule(mUserId, mContext, /* userBackupManagerService */ this);
} else {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Scheduling immediate backup pass"));
+ Slog.d(TAG, mLogIdMsg + "Scheduling immediate backup pass");
synchronized (getQueueLock()) {
if (getPendingInits().size() > 0) {
// If there are pending init operations, we process those and then settle
// into the usual periodic backup schedule.
if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Init pending at scheduled backup"));
+ Slog.v(TAG, mLogIdMsg + "Init pending at scheduled backup");
}
try {
getAlarmManager().cancel(mRunInitIntent);
mRunInitIntent.send();
} catch (PendingIntent.CanceledException ce) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(mUserId, "Run init intent cancelled"));
+ Slog.w(TAG, mLogIdMsg + "Run init intent cancelled");
}
return;
}
@@ -2526,8 +2521,11 @@ public class UserBackupManagerService {
if (!isEnabled() || !isSetupComplete()) {
Slog.w(
TAG,
- addUserIdToLogMessage(mUserId, "Backup pass but enabled=" + isEnabled()
- + " setupComplete=" + isSetupComplete()));
+ mLogIdMsg
+ + "Backup pass but enabled="
+ + isEnabled()
+ + " setupComplete="
+ + isSetupComplete());
return;
}
@@ -2548,9 +2546,17 @@ public class UserBackupManagerService {
* return to the caller until the backup has been completed. It requires on-screen confirmation
* by the user.
*/
- public void adbBackup(ParcelFileDescriptor fd, boolean includeApks,
- boolean includeObbs, boolean includeShared, boolean doWidgets, boolean doAllApps,
- boolean includeSystem, boolean compress, boolean doKeyValue, String[] pkgList) {
+ public void adbBackup(
+ ParcelFileDescriptor fd,
+ boolean includeApks,
+ boolean includeObbs,
+ boolean includeShared,
+ boolean doWidgets,
+ boolean doAllApps,
+ boolean includeSystem,
+ boolean compress,
+ boolean doKeyValue,
+ String[] pkgList) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
final int callingUserHandle = UserHandle.getCallingUserId();
@@ -2574,47 +2580,66 @@ public class UserBackupManagerService {
final long oldId = Binder.clearCallingIdentity();
try {
if (!mSetupComplete) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Backup not supported before setup"));
+ Slog.i(TAG, mLogIdMsg + "Backup not supported before setup");
return;
}
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "Requesting backup: apks=" + includeApks + " obb=" + includeObbs + " shared="
- + includeShared + " all=" + doAllApps + " system=" + includeSystem
- + " includekeyvalue=" + doKeyValue + " pkgs=" + Arrays.toString(
- pkgList)));
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning adb backup..."));
-
- BackupEligibilityRules eligibilityRules = getEligibilityRulesForOperation(
- BackupDestination.ADB_BACKUP);
- AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
- includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
- pkgList, eligibilityRules);
+ Slog.d(
+ TAG,
+ mLogIdMsg
+ + "Requesting backup: apks="
+ + includeApks
+ + " obb="
+ + includeObbs
+ + " shared="
+ + includeShared
+ + " all="
+ + doAllApps
+ + " system="
+ + includeSystem
+ + " includekeyvalue="
+ + doKeyValue
+ + " pkgs="
+ + Arrays.toString(pkgList));
+ Slog.i(TAG, mLogIdMsg + "Beginning adb backup...");
+
+ BackupEligibilityRules eligibilityRules =
+ getEligibilityRulesForOperation(BackupDestination.ADB_BACKUP);
+ AdbBackupParams params =
+ new AdbBackupParams(
+ fd,
+ includeApks,
+ includeObbs,
+ includeShared,
+ doWidgets,
+ doAllApps,
+ includeSystem,
+ compress,
+ doKeyValue,
+ pkgList,
+ eligibilityRules);
final int token = generateRandomIntegerToken();
synchronized (mAdbBackupRestoreConfirmations) {
mAdbBackupRestoreConfirmations.put(token, params);
}
// start up the confirmation UI
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Starting backup confirmation UI"));
+ Slog.d(TAG, mLogIdMsg + "Starting backup confirmation UI");
if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
- Slog.e(
- TAG,
- addUserIdToLogMessage(mUserId, "Unable to launch backup confirmation UI"));
+ Slog.e(TAG, mLogIdMsg + "Unable to launch backup confirmation UI");
mAdbBackupRestoreConfirmations.delete(token);
return;
}
// make sure the screen is lit for the user interaction
- mPowerManager.userActivity(SystemClock.uptimeMillis(),
- PowerManager.USER_ACTIVITY_EVENT_OTHER,
- 0);
+ mPowerManager.userActivity(
+ SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER, 0);
// start the confirmation countdown
startConfirmationTimeout(token, params);
// wait for the backup to be performed
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Waiting for backup completion..."));
+ Slog.d(TAG, mLogIdMsg + "Waiting for backup completion...");
waitForCompletion(params);
} finally {
try {
@@ -2622,19 +2647,17 @@ public class UserBackupManagerService {
} catch (IOException e) {
Slog.e(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "IO error closing output for adb backup: " + e.getMessage()));
+ mLogIdMsg + "IO error closing output for adb backup: " + e.getMessage());
}
Binder.restoreCallingIdentity(oldId);
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Adb backup processing complete."));
+ Slog.d(TAG, mLogIdMsg + "Adb backup processing complete.");
}
}
/** Run a full backup pass for the given packages. Used by 'adb shell bmgr'. */
public void fullTransportBackup(String[] pkgNames) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "fullTransportBackup");
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.BACKUP, "fullTransportBackup");
final int callingUserHandle = UserHandle.getCallingUserId();
// TODO: http://b/22388012
if (callingUserHandle != UserHandle.USER_SYSTEM) {
@@ -2643,30 +2666,27 @@ public class UserBackupManagerService {
String transportName = mTransportManager.getCurrentTransportName();
if (!fullBackupAllowable(transportName)) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Full backup not currently possible -- key/value backup not yet run?"));
+ Slog.i(TAG, mLogIdMsg + "Full backup not possible. Key/value backup not yet run?");
} else {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "fullTransportBackup()"));
+ Slog.d(TAG, mLogIdMsg + "fullTransportBackup()");
final long oldId = Binder.clearCallingIdentity();
try {
CountDownLatch latch = new CountDownLatch(1);
- Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
- this,
- mOperationStorage,
- /* observer */ null,
- pkgNames,
- /* updateSchedule */ false,
- /* runningJob */ null,
- latch,
- /* backupObserver */ null,
- /* monitor */ null,
- /* userInitiated */ false,
- "BMS.fullTransportBackup()",
- getEligibilityRulesForOperation(BackupDestination.CLOUD));
+ Runnable task =
+ PerformFullTransportBackupTask.newWithCurrentTransport(
+ this,
+ mOperationStorage,
+ /* observer */ null,
+ pkgNames,
+ /* updateSchedule */ false,
+ /* runningJob */ null,
+ latch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ "BMS.fullTransportBackup()",
+ getEligibilityRulesForOperation(BackupDestination.CLOUD));
// Acquiring wakelock for PerformFullTransportBackupTask before its start.
mWakelock.acquire();
(new Thread(task, "full-transport-master")).start();
@@ -2692,7 +2712,7 @@ public class UserBackupManagerService {
}
}
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Done with full transport backup."));
+ Slog.d(TAG, mLogIdMsg + "Done with full transport backup.");
}
/**
@@ -2711,13 +2731,11 @@ public class UserBackupManagerService {
try {
if (!mSetupComplete) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(mUserId, "Full restore not permitted before setup"));
+ Slog.i(TAG, mLogIdMsg + "Full restore not permitted before setup");
return;
}
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning restore..."));
+ Slog.i(TAG, mLogIdMsg + "Beginning restore...");
AdbRestoreParams params = new AdbRestoreParams(fd);
final int token = generateRandomIntegerToken();
@@ -2726,38 +2744,31 @@ public class UserBackupManagerService {
}
// start up the confirmation UI
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "Starting restore confirmation UI, token=" + token));
+ Slog.d(TAG, mLogIdMsg + "Starting restore confirmation UI, token=" + token);
if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
- Slog.e(
- TAG,
- addUserIdToLogMessage(mUserId, "Unable to launch restore confirmation"));
+ Slog.e(TAG, mLogIdMsg + "Unable to launch restore confirmation");
mAdbBackupRestoreConfirmations.delete(token);
return;
}
// make sure the screen is lit for the user interaction
- mPowerManager.userActivity(SystemClock.uptimeMillis(),
- PowerManager.USER_ACTIVITY_EVENT_OTHER,
- 0);
+ mPowerManager.userActivity(
+ SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER, 0);
// start the confirmation countdown
startConfirmationTimeout(token, params);
// wait for the restore to be performed
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Waiting for restore completion..."));
+ Slog.d(TAG, mLogIdMsg + "Waiting for restore completion...");
waitForCompletion(params);
} finally {
try {
fd.close();
} catch (IOException e) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Error trying to close fd after adb restore: " + e));
+ Slog.w(TAG, mLogIdMsg + "Error trying to close fd after adb restore: " + e);
}
Binder.restoreCallingIdentity(oldId);
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "adb restore processing complete."));
+ Slog.i(TAG, mLogIdMsg + "adb restore processing complete.");
}
}
@@ -2766,13 +2777,14 @@ public class UserBackupManagerService {
* to the backup agent during restore.
*/
public void excludeKeysFromRestore(String packageName, List<String> keys) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "excludeKeysFromRestore");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "excludeKeysFromRestore");
mBackupPreferences.addExcludedKeys(packageName, keys);
}
- public void reportDelayedRestoreResult(String packageName,
- List<BackupRestoreEventLogger.DataTypeResult> results) {
+ /** See {@link BackupManager#reportDelayedRestoreResult(BackupRestoreEventLogger)}. */
+ public void reportDelayedRestoreResult(
+ String packageName, List<BackupRestoreEventLogger.DataTypeResult> results) {
String transport = mTransportManager.getCurrentTransportName();
if (transport == null) {
Slog.w(TAG, "Failed to send delayed restore logs as no transport selected");
@@ -2781,26 +2793,34 @@ public class UserBackupManagerService {
TransportConnection transportConnection = null;
try {
- PackageInfo packageInfo = getPackageManager().getPackageInfoAsUser(packageName,
- PackageManager.PackageInfoFlags.of(/* value */ 0), getUserId());
+ PackageInfo packageInfo =
+ getPackageManager()
+ .getPackageInfoAsUser(
+ packageName,
+ PackageManager.PackageInfoFlags.of(/* value */ 0),
+ getUserId());
- transportConnection = mTransportManager.getTransportClientOrThrow(
- transport, /* caller */"BMS.reportDelayedRestoreResult");
- BackupTransportClient transportClient = transportConnection.connectOrThrow(
- /* caller */ "BMS.reportDelayedRestoreResult");
+ transportConnection =
+ mTransportManager.getTransportClientOrThrow(
+ transport, /* caller */ "BMS.reportDelayedRestoreResult");
+ BackupTransportClient transportClient =
+ transportConnection.connectOrThrow(
+ /* caller */ "BMS.reportDelayedRestoreResult");
IBackupManagerMonitor monitor = transportClient.getBackupManagerMonitor();
- BackupManagerMonitorEventSender mBackupManagerMonitorEventSender =
+ BackupManagerMonitorEventSender mBackupManagerMonitorEventSender =
getBMMEventSender(monitor);
- mBackupManagerMonitorEventSender.sendAgentLoggingResults(packageInfo, results,
- BackupAnnotations.OperationType.RESTORE);
- } catch (NameNotFoundException | TransportNotAvailableException
- | TransportNotRegisteredException | RemoteException e) {
+ mBackupManagerMonitorEventSender.sendAgentLoggingResults(
+ packageInfo, results, BackupAnnotations.OperationType.RESTORE);
+ } catch (NameNotFoundException
+ | TransportNotAvailableException
+ | TransportNotRegisteredException
+ | RemoteException e) {
Slog.w(TAG, "Failed to send delayed restore logs: " + e);
} finally {
if (transportConnection != null) {
- mTransportManager.disposeOfTransportClient(transportConnection,
- /* caller */"BMS.reportDelayedRestoreResult");
+ mTransportManager.disposeOfTransportClient(
+ transportConnection, /* caller */ "BMS.reportDelayedRestoreResult");
}
}
}
@@ -2808,7 +2828,8 @@ public class UserBackupManagerService {
private boolean startConfirmationUi(int token, String action) {
try {
Intent confIntent = new Intent(action);
- confIntent.setClassName("com.android.backupconfirm",
+ confIntent.setClassName(
+ "com.android.backupconfirm",
"com.android.backupconfirm.BackupRestoreConfirmation");
confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
confIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
@@ -2821,11 +2842,14 @@ public class UserBackupManagerService {
private void startConfirmationTimeout(int token, AdbParams params) {
if (DEBUG) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Posting conf timeout msg after "
- + TIMEOUT_FULL_CONFIRMATION + " millis"));
+ Slog.d(
+ TAG,
+ mLogIdMsg
+ + "Posting conf timeout msg after "
+ + TIMEOUT_FULL_CONFIRMATION
+ + " millis");
}
- Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
- token, 0, params);
+ Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, token, 0, params);
mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
}
@@ -2834,7 +2858,9 @@ public class UserBackupManagerService {
while (!params.latch.get()) {
try {
params.latch.wait();
- } catch (InterruptedException e) { /* never interrupted */ }
+ } catch (InterruptedException e) {
+ /* never interrupted */
+ }
}
}
}
@@ -2851,15 +2877,20 @@ public class UserBackupManagerService {
* Confirm that the previously-requested full backup/restore operation can proceed. This is used
* to require a user-facing disclosure about the operation.
*/
- public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
- String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "acknowledgeAdbBackupOrRestore : token=" + token + " allow=" + allow));
+ public void acknowledgeAdbBackupOrRestore(
+ int token,
+ boolean allow,
+ String curPassword,
+ String encPpassword,
+ IFullBackupRestoreObserver observer) {
+ Slog.d(
+ TAG,
+ mLogIdMsg + "acknowledgeAdbBackupOrRestore : token=" + token + " allow=" + allow);
// TODO: possibly require not just this signature-only permission, but even
// require that the specific designated confirmation-UI app uid is the caller?
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "acknowledgeAdbBackupOrRestore");
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.BACKUP, "acknowledgeAdbBackupOrRestore");
final long oldId = Binder.clearCallingIdentity();
try {
@@ -2872,9 +2903,10 @@ public class UserBackupManagerService {
mAdbBackupRestoreConfirmations.delete(token);
if (allow) {
- final int verb = params instanceof AdbBackupParams
- ? MSG_RUN_ADB_BACKUP
- : MSG_RUN_ADB_RESTORE;
+ final int verb =
+ params instanceof AdbBackupParams
+ ? MSG_RUN_ADB_BACKUP
+ : MSG_RUN_ADB_RESTORE;
params.observer = observer;
params.curPassword = curPassword;
@@ -2882,28 +2914,20 @@ public class UserBackupManagerService {
params.encryptPassword = encPpassword;
if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Sending conf message with verb " + verb));
+ Slog.d(TAG, mLogIdMsg + "Sending conf message with verb " + verb);
}
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(verb, params);
mBackupHandler.sendMessage(msg);
} else {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "User rejected full backup/restore operation"));
+ Slog.w(TAG, mLogIdMsg + "User rejected full backup/restore operation");
// indicate completion without having actually transferred any data
signalAdbBackupRestoreCompletion(params);
}
} else {
Slog.w(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Attempted to ack full backup/restore with invalid token"));
+ mLogIdMsg + "Attempted to ack full backup/restore with invalid token");
}
}
} finally {
@@ -2922,10 +2946,10 @@ public class UserBackupManagerService {
}
private void setBackupEnabled(boolean enable, boolean persistToDisk) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupEnabled");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "setBackupEnabled");
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Backup enabled => " + enable));
+ Slog.i(TAG, mLogIdMsg + "Backup enabled => " + enable);
final long oldId = Binder.clearCallingIdentity();
try {
@@ -2944,23 +2968,25 @@ public class UserBackupManagerService {
}
synchronized void setFrameworkSchedulingEnabled(boolean isEnabled) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setFrameworkSchedulingEnabled");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "setFrameworkSchedulingEnabled");
boolean wasEnabled = isFrameworkSchedulingEnabled();
if (wasEnabled == isEnabled) {
return;
}
- Slog.i(TAG, addUserIdToLogMessage(mUserId,
- (isEnabled ? "Enabling" : "Disabling") + " backup scheduling"));
+ Slog.i(TAG, mLogIdMsg + (isEnabled ? "Enabling" : "Disabling") + " backup scheduling");
final long oldId = Binder.clearCallingIdentity();
try {
// TODO(b/264889098): Consider at a later point if we should us a sentinel file as
// setBackupEnabled.
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.BACKUP_SCHEDULING_ENABLED, isEnabled ? 1 : 0, mUserId);
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.BACKUP_SCHEDULING_ENABLED,
+ isEnabled ? 1 : 0,
+ mUserId);
if (!isEnabled) {
KeyValueBackupJob.cancel(mUserId, mContext);
@@ -2977,8 +3003,12 @@ public class UserBackupManagerService {
synchronized boolean isFrameworkSchedulingEnabled() {
// By default scheduling is enabled
final int defaultSetting = 1;
- int isEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.BACKUP_SCHEDULING_ENABLED, defaultSetting, mUserId);
+ int isEnabled =
+ Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.BACKUP_SCHEDULING_ENABLED,
+ defaultSetting,
+ mUserId);
return isEnabled == 1;
}
@@ -2992,7 +3022,7 @@ public class UserBackupManagerService {
} else if (!enable) {
// No longer enabled, so stop running backups
if (DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Opting out of backup"));
+ Slog.i(TAG, mLogIdMsg + "Opting out of backup");
}
KeyValueBackupJob.cancel(mUserId, mContext);
@@ -3012,11 +3042,7 @@ public class UserBackupManagerService {
dirName = mTransportManager.getTransportDirName(name);
} catch (TransportNotRegisteredException e) {
// Should never happen
- Slog.e(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Unexpected unregistered transport"),
- e);
+ Slog.e(TAG, mLogIdMsg + "Unexpected unregistered transport", e);
return;
}
transportNames.add(name);
@@ -3025,13 +3051,10 @@ public class UserBackupManagerService {
// build the set of transports for which we are posting an init
for (int i = 0; i < transportNames.size(); i++) {
- recordInitPending(
- true,
- transportNames.get(i),
- transportDirNames.get(i));
+ recordInitPending(true, transportNames.get(i), transportDirNames.get(i));
}
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
- mRunInitIntent);
+ mAlarmManager.set(
+ AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), mRunInitIntent);
}
}
}
@@ -3049,16 +3072,19 @@ public class UserBackupManagerService {
/** Enable/disable automatic restore of app data at install time. */
public void setAutoRestore(boolean doAutoRestore) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setAutoRestore");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "setAutoRestore");
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Auto restore => " + doAutoRestore));
+ Slog.i(TAG, mLogIdMsg + "Auto restore => " + doAutoRestore);
final long oldId = Binder.clearCallingIdentity();
try {
synchronized (this) {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0, mUserId);
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.BACKUP_AUTO_RESTORE,
+ doAutoRestore ? 1 : 0,
+ mUserId);
mAutoRestore = doAutoRestore;
}
} finally {
@@ -3068,21 +3094,18 @@ public class UserBackupManagerService {
/** Report whether the backup mechanism is currently enabled. */
public boolean isBackupEnabled() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "isBackupEnabled");
- return mEnabled; // no need to synchronize just to read it
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "isBackupEnabled");
+ return mEnabled; // no need to synchronize just to read it
}
/** Report the name of the currently active transport. */
public String getCurrentTransport() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getCurrentTransport");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "getCurrentTransport");
String currentTransport = mTransportManager.getCurrentTransportName();
if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "... getCurrentTransport() returning " + currentTransport));
+ Slog.v(TAG, mLogIdMsg + "... getCurrentTransport() returning " + currentTransport);
}
return currentTransport;
}
@@ -3107,16 +3130,16 @@ public class UserBackupManagerService {
/** Report all known, available backup transports by name. */
public String[] listAllTransports() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "listAllTransports");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "listAllTransports");
return mTransportManager.getRegisteredTransportNames();
}
/** Report all known, available backup transports by component. */
public ComponentName[] listAllTransportComponents() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "listAllTransportComponents");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "listAllTransportComponents");
return mTransportManager.getRegisteredTransportComponents();
}
@@ -3129,18 +3152,17 @@ public class UserBackupManagerService {
* @param transportComponent The identity of the transport being described.
* @param name A {@link String} with the new name for the transport. This is NOT for
* identification. MUST NOT be {@code null}.
- * @param configurationIntent An {@link Intent} that can be passed to
- * {@link Context#startActivity} in order to launch the transport's configuration UI. It may
- * be {@code null} if the transport does not offer any user-facing configuration UI.
+ * @param configurationIntent An {@link Intent} that can be passed to {@link
+ * Context#startActivity} in order to launch the transport's configuration UI. It may be
+ * {@code null} if the transport does not offer any user-facing configuration UI.
* @param currentDestinationString A {@link String} describing the destination to which the
* transport is currently sending data. MUST NOT be {@code null}.
- * @param dataManagementIntent An {@link Intent} that can be passed to
- * {@link Context#startActivity} in order to launch the transport's data-management UI. It
- * may be {@code null} if the transport does not offer any user-facing data
- * management UI.
+ * @param dataManagementIntent An {@link Intent} that can be passed to {@link
+ * Context#startActivity} in order to launch the transport's data-management UI. It may be
+ * {@code null} if the transport does not offer any user-facing data management UI.
* @param dataManagementLabel A {@link CharSequence} to be used as the label for the transport's
- * data management affordance. This MUST be {@code null} when dataManagementIntent is
- * {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+ * data management affordance. This MUST be {@code null} when dataManagementIntent is {@code
+ * null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
* @throws SecurityException If the UID of the calling process differs from the package UID of
* {@code transportComponent} or if the caller does NOT have BACKUP permission.
*/
@@ -3175,8 +3197,7 @@ public class UserBackupManagerService {
Objects.requireNonNull(transportComponent, "transportComponent can't be null");
Objects.requireNonNull(name, "name can't be null");
- Objects.requireNonNull(
- currentDestinationString, "currentDestinationString can't be null");
+ Objects.requireNonNull(currentDestinationString, "currentDestinationString can't be null");
Preconditions.checkArgument(
(dataManagementIntent == null) == (dataManagementLabel == null),
"dataManagementLabel should be null iff dataManagementIntent is null");
@@ -3211,7 +3232,7 @@ public class UserBackupManagerService {
* selected transport. Returns {@code null} if the transport is not registered.
*
* @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} instead.
+ * ISelectBackupTransportCallback)} instead.
*/
@Deprecated
@Nullable
@@ -3224,11 +3245,10 @@ public class UserBackupManagerService {
if (!mTransportManager.isTransportRegistered(transportName)) {
Slog.d(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Could not select transport "
- + transportName
- + ", as the transport is not registered."));
+ mLogIdMsg
+ + "Could not select transport "
+ + transportName
+ + ", as the transport is not registered.");
return null;
}
@@ -3236,12 +3256,11 @@ public class UserBackupManagerService {
updateStateForTransport(transportName);
Slog.d(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "selectBackupTransport(transport = "
- + transportName
- + "): previous transport = "
- + previousTransportName));
+ mLogIdMsg
+ + "selectBackupTransport(transport = "
+ + transportName
+ + "): previous transport = "
+ + previousTransportName);
return previousTransportName;
} finally {
Binder.restoreCallingIdentity(oldId);
@@ -3262,9 +3281,7 @@ public class UserBackupManagerService {
String transportString = transportComponent.flattenToShortString();
Slog.d(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "selectBackupTransportAsync(transport = " + transportString + ")"));
+ mLogIdMsg + "selectBackupTransportAsync(transport = " + transportString + ")");
mBackupHandler.post(
() -> {
String transportName = null;
@@ -3276,10 +3293,7 @@ public class UserBackupManagerService {
mTransportManager.getTransportName(transportComponent);
updateStateForTransport(transportName);
} catch (TransportNotRegisteredException e) {
- Slog.e(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Transport got unregistered"));
+ Slog.e(TAG, mLogIdMsg + "Transport got unregistered");
result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
}
@@ -3293,10 +3307,9 @@ public class UserBackupManagerService {
} catch (RemoteException e) {
Slog.e(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "ISelectBackupTransportCallback listener not"
- + " available"));
+ mLogIdMsg
+ + "ISelectBackupTransportCallback listener not"
+ + " available");
}
});
} finally {
@@ -3306,8 +3319,11 @@ public class UserBackupManagerService {
private void updateStateForTransport(String newTransportName) {
// Publish the name change
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- Settings.Secure.BACKUP_TRANSPORT, newTransportName, mUserId);
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.BACKUP_TRANSPORT,
+ newTransportName,
+ mUserId);
// And update our current-dataset bookkeeping
String callerLogString = "BMS.updateStateForTransport()";
@@ -3315,8 +3331,8 @@ public class UserBackupManagerService {
mTransportManager.getTransportClient(newTransportName, callerLogString);
if (transportConnection != null) {
try {
- BackupTransportClient transport = transportConnection.connectOrThrow(
- callerLogString);
+ BackupTransportClient transport =
+ transportConnection.connectOrThrow(callerLogString);
mCurrentToken = transport.getCurrentRestoreSet();
} catch (Exception e) {
// Oops. We can't know the current dataset token, so reset and figure it out
@@ -3324,21 +3340,19 @@ public class UserBackupManagerService {
mCurrentToken = 0;
Slog.w(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Transport "
- + newTransportName
- + " not available: current token = 0"));
+ mLogIdMsg
+ + "Transport "
+ + newTransportName
+ + " not available: current token = 0");
}
mTransportManager.disposeOfTransportClient(transportConnection, callerLogString);
} else {
Slog.w(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Transport "
- + newTransportName
- + " not registered: current token = 0"));
+ mLogIdMsg
+ + "Transport "
+ + newTransportName
+ + " not registered: current token = 0");
// The named transport isn't registered, so we can't know what its current dataset token
// is. Reset as above.
mCurrentToken = 0;
@@ -3351,24 +3365,20 @@ public class UserBackupManagerService {
* returns {@code null}.
*/
public Intent getConfigurationIntent(String transportName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getConfigurationIntent");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "getConfigurationIntent");
try {
Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "getConfigurationIntent() returning intent " + intent));
+ Slog.d(TAG, mLogIdMsg + "getConfigurationIntent() returning intent " + intent);
}
return intent;
} catch (TransportNotRegisteredException e) {
Slog.e(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Unable to get configuration intent from transport: "
- + e.getMessage()));
+ mLogIdMsg
+ + "Unable to get configuration intent from transport: "
+ + e.getMessage());
return null;
}
}
@@ -3389,42 +3399,36 @@ public class UserBackupManagerService {
try {
String string = mTransportManager.getTransportCurrentDestinationString(transportName);
if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "getDestinationString() returning " + string));
+ Slog.d(TAG, mLogIdMsg + "getDestinationString() returning " + string);
}
return string;
} catch (TransportNotRegisteredException e) {
Slog.e(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Unable to get destination string from transport: " + e.getMessage()));
+ mLogIdMsg
+ + "Unable to get destination string from transport: "
+ + e.getMessage());
return null;
}
}
/** Supply the manage-data intent for the given transport. */
public Intent getDataManagementIntent(String transportName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getDataManagementIntent");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "getDataManagementIntent");
try {
Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "getDataManagementIntent() returning intent " + intent));
+ Slog.d(TAG, mLogIdMsg + "getDataManagementIntent() returning intent " + intent);
}
return intent;
} catch (TransportNotRegisteredException e) {
Slog.e(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Unable to get management intent from transport: " + e.getMessage()));
+ mLogIdMsg
+ + "Unable to get management intent from transport: "
+ + e.getMessage());
return null;
}
}
@@ -3434,24 +3438,19 @@ public class UserBackupManagerService {
* transport.
*/
public CharSequence getDataManagementLabel(String transportName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getDataManagementLabel");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "getDataManagementLabel");
try {
CharSequence label = mTransportManager.getTransportDataManagementLabel(transportName);
if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "getDataManagementLabel() returning " + label));
+ Slog.d(TAG, mLogIdMsg + "getDataManagementLabel() returning " + label);
}
return label;
} catch (TransportNotRegisteredException e) {
Slog.e(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Unable to get management label from transport: " + e.getMessage()));
+ mLogIdMsg + "Unable to get management label from transport: " + e.getMessage());
return null;
}
}
@@ -3464,57 +3463,64 @@ public class UserBackupManagerService {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Slog.w(
TAG,
- addUserIdToLogMessage(
- mUserId,
- "Non-system process uid="
- + Binder.getCallingUid()
- + " attemping install-time restore"));
+ mLogIdMsg
+ + "Non-system process uid="
+ + Binder.getCallingUid()
+ + " attemping install-time restore");
return;
}
boolean skip = false;
long restoreSet = getAvailableRestoreToken(packageName);
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "restoreAtInstall pkg=" + packageName + " token=" + Integer.toHexString(token)
- + " restoreSet=" + Long.toHexString(restoreSet)));
+ Slog.d(
+ TAG,
+ mLogIdMsg
+ + "restoreAtInstall pkg="
+ + packageName
+ + " token="
+ + Integer.toHexString(token)
+ + " restoreSet="
+ + Long.toHexString(restoreSet));
if (restoreSet == 0) {
- if (DEBUG) Slog.i(TAG, addUserIdToLogMessage(mUserId, "No restore set"));
+ if (DEBUG) Slog.i(TAG, mLogIdMsg + "No restore set");
skip = true;
}
- BackupManagerMonitorEventSender mBMMEventSender =
- getBMMEventSender(/*monitor=*/ null);
+ BackupManagerMonitorEventSender mBMMEventSender = getBMMEventSender(/* monitor= */ null);
PackageInfo packageInfo = getPackageInfoForBMMLogging(packageName);
TransportConnection transportConnection =
mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
if (transportConnection == null) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId, "No transport client"));
+ Slog.w(TAG, mLogIdMsg + "No transport client");
skip = true;
} else if (Flags.enableIncreasedBmmLoggingForRestoreAtInstall()) {
try {
- BackupTransportClient transportClient = transportConnection.connectOrThrow(
- "BMS.restoreAtInstall");
+ BackupTransportClient transportClient =
+ transportConnection.connectOrThrow("BMS.restoreAtInstall");
mBMMEventSender.setMonitor(transportClient.getBackupManagerMonitor());
} catch (TransportNotAvailableException | RemoteException e) {
mBMMEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL, packageInfo,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
+ packageInfo,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ null);
}
}
if (Flags.enableIncreasedBmmLoggingForRestoreAtInstall()) {
mBMMEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_RESTORE_AT_INSTALL_INVOKED, packageInfo,
+ BackupManagerMonitor.LOG_EVENT_ID_RESTORE_AT_INSTALL_INVOKED,
+ packageInfo,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- mBMMEventSender.putMonitoringExtra(/*extras=*/null,
+ mBMMEventSender.putMonitoringExtra(
+ /* extras= */ null,
BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE,
BackupAnnotations.OperationType.RESTORE));
}
if (!mAutoRestore) {
- Slog.w(TAG,
- addUserIdToLogMessage(mUserId, "Non-restorable state: auto=" + mAutoRestore));
+ Slog.w(TAG, mLogIdMsg + "Non-restorable state: auto=" + mAutoRestore);
skip = true;
}
@@ -3526,15 +3532,14 @@ public class UserBackupManagerService {
mWakelock.acquire();
- OnTaskFinishedListener listener = caller -> {
- mTransportManager.disposeOfTransportClient(transportConnection, caller);
- mWakelock.release();
- };
+ OnTaskFinishedListener listener =
+ caller -> {
+ mTransportManager.disposeOfTransportClient(transportConnection, caller);
+ mWakelock.release();
+ };
if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(mUserId, "Restore at install of " + packageName));
+ Slog.d(TAG, mLogIdMsg + "Restore at install of " + packageName);
}
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj =
@@ -3550,10 +3555,7 @@ public class UserBackupManagerService {
mBackupHandler.sendMessage(msg);
} catch (Exception e) {
// Calling into the transport broke; back off and proceed with the installation.
- Slog.e(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Unable to contact transport: " + e.getMessage()));
+ Slog.e(TAG, mLogIdMsg + "Unable to contact transport: " + e.getMessage());
skip = true;
}
}
@@ -3563,9 +3565,11 @@ public class UserBackupManagerService {
if (Flags.enableIncreasedBmmLoggingForRestoreAtInstall()) {
mBMMEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_SKIP_RESTORE_AT_INSTALL, packageInfo,
+ BackupManagerMonitor.LOG_EVENT_ID_SKIP_RESTORE_AT_INSTALL,
+ packageInfo,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- mBMMEventSender.putMonitoringExtra(/*extras=*/null,
+ mBMMEventSender.putMonitoringExtra(
+ /* extras= */ null,
BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE,
BackupAnnotations.OperationType.RESTORE));
}
@@ -3576,10 +3580,12 @@ public class UserBackupManagerService {
}
// Tell the PackageManager to proceed with the post-install handling for this package.
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Finishing install immediately"));
+ Slog.d(TAG, mLogIdMsg + "Finishing install immediately");
try {
mPackageManagerBinder.finishPackageInstall(token, false);
- } catch (RemoteException e) { /* can't happen */ }
+ } catch (RemoteException e) {
+ /* can't happen */
+ }
}
}
@@ -3592,8 +3598,9 @@ public class UserBackupManagerService {
/** Hand off a restore session. */
public IRestoreSession beginRestoreSession(String packageName, String transport) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "beginRestoreSession: pkg=" + packageName + " transport=" + transport));
+ Slog.d(
+ TAG,
+ mLogIdMsg + "beginRestoreSession: pkg=" + packageName + " transport=" + transport);
boolean needPermission = true;
if (transport == null) {
@@ -3604,10 +3611,7 @@ public class UserBackupManagerService {
try {
app = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
} catch (NameNotFoundException nnf) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Asked to restore nonexistent pkg " + packageName));
+ Slog.w(TAG, mLogIdMsg + "Asked to restore nonexistent pkg " + packageName);
throw new IllegalArgumentException("Package " + packageName + " not found");
}
@@ -3624,46 +3628,45 @@ public class UserBackupManagerService {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "beginRestoreSession");
} else {
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "restoring self on current transport; no permission needed"));
+ Slog.d(TAG, mLogIdMsg + "restoring self on current transport; no permission needed");
}
int backupDestination;
TransportConnection transportConnection = null;
try {
- transportConnection = mTransportManager.getTransportClientOrThrow(
- transport, /* caller */"BMS.beginRestoreSession");
+ transportConnection =
+ mTransportManager.getTransportClientOrThrow(
+ transport, /* caller */ "BMS.beginRestoreSession");
backupDestination = getBackupDestinationFromTransport(transportConnection);
- } catch (TransportNotAvailableException | TransportNotRegisteredException
+ } catch (TransportNotAvailableException
+ | TransportNotRegisteredException
| RemoteException e) {
Slog.w(TAG, "Failed to get operation type from transport: " + e);
return null;
} finally {
if (transportConnection != null) {
- mTransportManager.disposeOfTransportClient(transportConnection,
- /* caller */"BMS.beginRestoreSession");
+ mTransportManager.disposeOfTransportClient(
+ transportConnection, /* caller */ "BMS.beginRestoreSession");
}
}
synchronized (this) {
if (mActiveRestoreSession != null) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Restore session requested but one already active"));
+ Slog.i(TAG, mLogIdMsg + "Restore session requested but one already active");
return null;
}
if (mBackupRunning) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Restore session requested but currently running backups"));
+ Slog.i(TAG, mLogIdMsg + "Restore session requested but currently running backups");
return null;
}
- mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport,
- getEligibilityRulesForOperation(backupDestination));
- mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+ mActiveRestoreSession =
+ new ActiveRestoreSession(
+ this,
+ packageName,
+ transport,
+ getEligibilityRulesForOperation(backupDestination));
+ mBackupHandler.sendEmptyMessageDelayed(
+ MSG_RESTORE_SESSION_TIMEOUT,
mAgentTimeoutParameters.getRestoreSessionTimeoutMillis());
}
return mActiveRestoreSession;
@@ -3673,10 +3676,9 @@ public class UserBackupManagerService {
public void clearRestoreSession(ActiveRestoreSession currentSession) {
synchronized (this) {
if (currentSession != mActiveRestoreSession) {
- Slog.e(TAG, addUserIdToLogMessage(mUserId, "ending non-current restore session"));
+ Slog.e(TAG, mLogIdMsg + "ending non-current restore session");
} else {
- Slog.d(TAG, addUserIdToLogMessage(mUserId,
- "Clearing restore session and halting timeout"));
+ Slog.d(TAG, mLogIdMsg + "Clearing restore session and halting timeout");
mActiveRestoreSession = null;
mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
}
@@ -3688,11 +3690,14 @@ public class UserBackupManagerService {
* outstanding asynchronous backup/restore operation.
*/
public void opComplete(int token, long result) {
- mOperationStorage.onOperationComplete(token, result, callback -> {
- Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(callback, result);
- Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
- mBackupHandler.sendMessage(msg);
- });
+ mOperationStorage.onOperationComplete(
+ token,
+ result,
+ callback -> {
+ Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(callback, result);
+ Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
+ mBackupHandler.sendMessage(msg);
+ });
}
/** Checks if the package is eligible for backup. */
@@ -3748,10 +3753,16 @@ public class UserBackupManagerService {
return getEligibilityRules(mPackageManager, mUserId, mContext, backupDestination);
}
- private static BackupEligibilityRules getEligibilityRules(PackageManager packageManager,
- int userId, Context context, @BackupDestination int backupDestination) {
- return new BackupEligibilityRules(packageManager,
- LocalServices.getService(PackageManagerInternal.class), userId, context,
+ private static BackupEligibilityRules getEligibilityRules(
+ PackageManager packageManager,
+ int userId,
+ Context context,
+ @BackupDestination int backupDestination) {
+ return new BackupEligibilityRules(
+ packageManager,
+ LocalServices.getService(PackageManagerInternal.class),
+ userId,
+ context,
backupDestination);
}
@@ -3793,20 +3804,20 @@ public class UserBackupManagerService {
}
private void dumpBMMEvents(PrintWriter pw) {
- BackupManagerMonitorDumpsysUtils bm =
- new BackupManagerMonitorDumpsysUtils();
+ BackupManagerMonitorDumpsysUtils bm = new BackupManagerMonitorDumpsysUtils();
if (bm.deleteExpiredBMMEvents()) {
pw.println("BACKUP MANAGER MONITOR EVENTS HAVE EXPIRED");
return;
}
File events = bm.getBMMEventsFile();
- if (events.length() == 0){
+ if (events.length() == 0) {
// We have not recorded BMMEvents yet.
pw.println("NO BACKUP MANAGER MONITOR EVENTS");
return;
- } else if (bm.isFileLargerThanSizeLimit(events)){
- pw.println("BACKUP MANAGER MONITOR EVENTS FILE OVER SIZE LIMIT - "
- + "future events will not be recorded");
+ } else if (bm.isFileLargerThanSizeLimit(events)) {
+ pw.println(
+ "BACKUP MANAGER MONITOR EVENTS FILE OVER SIZE LIMIT - "
+ + "future events will not be recorded");
}
pw.println("START OF BACKUP MANAGER MONITOR EVENTS");
try (BufferedReader reader = new BufferedReader(new FileReader(events))) {
@@ -3826,16 +3837,27 @@ public class UserBackupManagerService {
// Add prefix for only non-system users so that system user dumpsys is the same as before
String userPrefix = mUserId == UserHandle.USER_SYSTEM ? "" : "User " + mUserId + ":";
synchronized (mQueueLock) {
- pw.println(userPrefix + "Backup Manager is " + (mEnabled ? "enabled" : "disabled")
- + " / " + (!mSetupComplete ? "not " : "") + "setup complete / "
- + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
+ pw.println(
+ userPrefix
+ + "Backup Manager is "
+ + (mEnabled ? "enabled" : "disabled")
+ + " / "
+ + (!mSetupComplete ? "not " : "")
+ + "setup complete / "
+ + (this.mPendingInits.size() == 0 ? "not " : "")
+ + "pending init");
pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
if (mBackupRunning) pw.println("Backup currently running");
pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
- pw.println("Framework scheduling is "
- + (isFrameworkSchedulingEnabled() ? "enabled" : "disabled"));
- pw.println("Last backup pass started: " + mLastBackupPass
- + " (now = " + System.currentTimeMillis() + ')');
+ pw.println(
+ "Framework scheduling is "
+ + (isFrameworkSchedulingEnabled() ? "enabled" : "disabled"));
+ pw.println(
+ "Last backup pass started: "
+ + mLastBackupPass
+ + " (now = "
+ + System.currentTimeMillis()
+ + ')');
pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId));
pw.println(userPrefix + "Transport whitelist:");
@@ -3848,21 +3870,27 @@ public class UserBackupManagerService {
final String[] transports = listAllTransports();
if (transports != null) {
for (String t : transports) {
- pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? " * "
- : " ") + t);
+ pw.println(
+ (t.equals(mTransportManager.getCurrentTransportName())
+ ? " * "
+ : " ")
+ + t);
try {
- File dir = new File(mBaseStateDir,
- mTransportManager.getTransportDirName(t));
- pw.println(" destination: "
- + mTransportManager.getTransportCurrentDestinationString(t));
- pw.println(" intent: "
- + mTransportManager.getTransportConfigurationIntent(t));
+ File dir =
+ new File(mBaseStateDir, mTransportManager.getTransportDirName(t));
+ pw.println(
+ " destination: "
+ + mTransportManager.getTransportCurrentDestinationString(
+ t));
+ pw.println(
+ " intent: "
+ + mTransportManager.getTransportConfigurationIntent(t));
for (File f : dir.listFiles()) {
pw.println(
" " + f.getName() + " - " + f.length() + " state bytes");
}
} catch (Exception e) {
- Slog.e(TAG, addUserIdToLogMessage(mUserId, "Error in transport"), e);
+ Slog.e(TAG, mLogIdMsg + "Error in transport", e);
pw.println(" Error: " + e);
}
}
@@ -3892,8 +3920,10 @@ public class UserBackupManagerService {
}
}
- pw.println(userPrefix + "Ancestral packages: "
- + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
+ pw.println(
+ userPrefix
+ + "Ancestral packages: "
+ + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
if (mAncestralPackages != null) {
for (String pkg : mAncestralPackages) {
pw.println(" " + pkg);
@@ -3919,29 +3949,35 @@ public class UserBackupManagerService {
pw.println(entry.packageName);
}
pw.println(userPrefix + "Agent timeouts:");
- pw.println(" KvBackupAgentTimeoutMillis: "
- + mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis());
- pw.println(" FullBackupAgentTimeoutMillis: "
- + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis());
- pw.println(" SharedBackupAgentTimeoutMillis: "
- + mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis());
- pw.println(" RestoreAgentTimeoutMillis (system): "
- + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
- Process.FIRST_APPLICATION_UID - 1));
- pw.println(" RestoreAgentTimeoutMillis: "
- + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
- Process.FIRST_APPLICATION_UID));
- pw.println(" RestoreAgentFinishedTimeoutMillis: "
- + mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis());
- pw.println(" QuotaExceededTimeoutMillis: "
- + mAgentTimeoutParameters.getQuotaExceededTimeoutMillis());
-
+ pw.println(
+ " KvBackupAgentTimeoutMillis: "
+ + mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis());
+ pw.println(
+ " FullBackupAgentTimeoutMillis: "
+ + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis());
+ pw.println(
+ " SharedBackupAgentTimeoutMillis: "
+ + mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis());
+ pw.println(
+ " RestoreAgentTimeoutMillis (system): "
+ + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
+ Process.FIRST_APPLICATION_UID - 1));
+ pw.println(
+ " RestoreAgentTimeoutMillis: "
+ + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
+ Process.FIRST_APPLICATION_UID));
+ pw.println(
+ " RestoreAgentFinishedTimeoutMillis: "
+ + mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis());
+ pw.println(
+ " QuotaExceededTimeoutMillis: "
+ + mAgentTimeoutParameters.getQuotaExceededTimeoutMillis());
}
}
@VisibleForTesting
- @BackupDestination int getBackupDestinationFromTransport(
- TransportConnection transportConnection)
+ @BackupDestination
+ int getBackupDestinationFromTransport(TransportConnection transportConnection)
throws TransportNotAvailableException, RemoteException {
if (!shouldUseNewBackupEligibilityRules()) {
// Return the default to stick to the legacy behaviour.
@@ -3950,8 +3986,9 @@ public class UserBackupManagerService {
final long oldCallingId = Binder.clearCallingIdentity();
try {
- BackupTransportClient transport = transportConnection.connectOrThrow(
- /* caller */ "BMS.getBackupDestinationFromTransport");
+ BackupTransportClient transport =
+ transportConnection.connectOrThrow(
+ /* caller */ "BMS.getBackupDestinationFromTransport");
if ((transport.getTransportFlags() & BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER) != 0) {
return BackupDestination.DEVICE_TRANSFER;
} else {
@@ -3964,15 +4001,10 @@ public class UserBackupManagerService {
@VisibleForTesting
boolean shouldUseNewBackupEligibilityRules() {
- return FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES);
+ return FeatureFlagUtils.isEnabled(
+ mContext, FeatureFlagUtils.SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES);
}
- private static String addUserIdToLogMessage(int userId, String message) {
- return "[UserID:" + userId + "] " + message;
- }
-
-
public IBackupManager getBackupManagerBinder() {
return mBackupManagerBinder;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index f401e6b66093..c385fbad02a5 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -490,7 +490,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
? mParams.getBlockedActivities()
: mParams.getAllowedActivities());
- if (Flags.vdmCustomIme() && mParams.getInputMethodComponent() != null) {
+ if (mParams.getInputMethodComponent() != null) {
final String imeId = mParams.getInputMethodComponent().flattenToShortString();
Slog.d(TAG, "Setting custom input method " + imeId + " as default for virtual device "
+ deviceId);
@@ -807,7 +807,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
// Clear any previously set custom IME components.
- if (Flags.vdmCustomIme() && mParams.getInputMethodComponent() != null) {
+ if (mParams.getInputMethodComponent() != null) {
InputMethodManagerInternal.get().setVirtualDeviceInputMethodForAllUsers(
mDeviceId, null);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d76c04ac7f31..27e9e44f1090 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -4062,8 +4062,7 @@ class UserController implements Handler.Callback {
synchronized (mUserSwitchingDialogLock) {
dismissUserSwitchingDialog(null);
mUserSwitchingDialog = new UserSwitchingDialog(mService.mContext, fromUser, toUser,
- switchingFromSystemUserMessage, switchingToSystemUserMessage,
- getWindowManager());
+ switchingFromSystemUserMessage, switchingToSystemUserMessage);
mUserSwitchingDialog.show(onShown);
}
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 2d7456471be4..d1fcb9d1ca37 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -39,7 +39,6 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.util.Slog;
import android.util.TypedValue;
import android.view.View;
@@ -53,7 +52,6 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.util.ObjectUtils;
import com.android.internal.util.UserIcons;
-import com.android.server.wm.WindowManagerService;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -80,14 +78,11 @@ class UserSwitchingDialog extends Dialog {
protected final UserInfo mNewUser;
private final String mSwitchingFromSystemUserMessage;
private final String mSwitchingToSystemUserMessage;
- private final WindowManagerService mWindowManager;
protected final Context mContext;
private final int mTraceCookie;
- private final boolean mNeedToFreezeScreen;
UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser,
- String switchingFromSystemUserMessage, String switchingToSystemUserMessage,
- WindowManagerService windowManager) {
+ String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
super(context, R.style.Theme_Material_NoActionBar_Fullscreen);
mContext = context;
@@ -97,8 +92,6 @@ class UserSwitchingDialog extends Dialog {
mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
mDisableAnimations = SystemProperties.getBoolean(
"debug.usercontroller.disable_user_switching_dialog_animations", false);
- mWindowManager = windowManager;
- mNeedToFreezeScreen = !mDisableAnimations && !isUserSetupComplete(newUser);
mTraceCookie = UserHandle.MAX_SECONDARY_USER_ID * oldUser.id + newUser.id;
inflateContent();
@@ -183,11 +176,6 @@ class UserSwitchingDialog extends Dialog {
: res.getString(R.string.user_switching_message, mNewUser.name);
}
- private boolean isUserSetupComplete(UserInfo user) {
- return Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, /* default= */ 0, user.id) == 1;
- }
-
@Override
public void show() {
asyncTraceBegin("dialog", 0);
@@ -197,7 +185,6 @@ class UserSwitchingDialog extends Dialog {
@Override
public void dismiss() {
super.dismiss();
- stopFreezingScreen();
asyncTraceEnd("dialog", 0);
}
@@ -205,7 +192,6 @@ class UserSwitchingDialog extends Dialog {
if (DEBUG) Slog.d(TAG, "show called");
show();
startShowAnimation(() -> {
- startFreezingScreen();
onShown.run();
});
}
@@ -223,24 +209,6 @@ class UserSwitchingDialog extends Dialog {
}
}
- private void startFreezingScreen() {
- if (!mNeedToFreezeScreen) {
- return;
- }
- traceBegin("startFreezingScreen");
- mWindowManager.startFreezingScreen(0, 0);
- traceEnd("startFreezingScreen");
- }
-
- private void stopFreezingScreen() {
- if (!mNeedToFreezeScreen) {
- return;
- }
- traceBegin("stopFreezingScreen");
- mWindowManager.stopFreezingScreen();
- traceEnd("stopFreezingScreen");
- }
-
private void startShowAnimation(Runnable onAnimationEnd) {
if (mDisableAnimations) {
onAnimationEnd.run();
@@ -260,7 +228,7 @@ class UserSwitchingDialog extends Dialog {
}
private void startDismissAnimation(Runnable onAnimationEnd) {
- if (mDisableAnimations || mNeedToFreezeScreen) {
+ if (mDisableAnimations) {
// animations are disabled or screen is frozen, no need to play an animation
onAnimationEnd.run();
return;
@@ -352,14 +320,4 @@ class UserSwitchingDialog extends Dialog {
Trace.asyncTraceEnd(TRACE_TAG, TAG + subTag, mTraceCookie + subCookie);
if (DEBUG) Slog.d(TAG, "asyncTraceEnd-" + subTag);
}
-
- private void traceBegin(String msg) {
- if (DEBUG) Slog.d(TAG, "traceBegin-" + msg);
- Trace.traceBegin(TRACE_TAG, msg);
- }
-
- private void traceEnd(String msg) {
- Trace.traceEnd(TRACE_TAG);
- if (DEBUG) Slog.d(TAG, "traceEnd-" + msg);
- }
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b9b06701a11b..c125d2d2e8cd 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -256,6 +256,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
+import com.android.modules.expresslog.Counter;
import com.android.server.EventLogTags;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
@@ -838,9 +839,49 @@ public class AudioService extends IAudioService.Stub
new AudioServiceUserRestrictionsListener();
private final IAudioManagerNative mNativeShim = new IAudioManagerNative.Stub() {
+ static final String METRIC_COUNTERS_PLAYBACK_PARTIAL =
+ "media_audio.value_audio_playback_hardening_partial_restriction";
+ static final String METRIC_COUNTERS_PLAYBACK_STRICT =
+ "media_audio.value_audio_playback_hardening_strict_would_restrict";
+
+ String getPackNameForUid(int uid) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final String[] names = AudioService.this.mContext.
+ getPackageManager().getPackagesForUid(uid);
+ if (names == null
+ || names.length == 0
+ || TextUtils.isEmpty(names[0])) {
+ return "[" + uid + "]";
+ }
+ return names[0];
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
// oneway
@Override
public void playbackHardeningEvent(int uid, byte type, boolean bypassed) {
+ if (Binder.getCallingUid() != Process.AUDIOSERVER_UID) {
+ return;
+ }
+ if (type == HardeningType.PARTIAL) {
+ Counter.logIncrementWithUid(METRIC_COUNTERS_PLAYBACK_PARTIAL, uid);
+ } else if (type == HardeningType.FULL) {
+ Counter.logIncrementWithUid(METRIC_COUNTERS_PLAYBACK_STRICT, uid);
+ } else {
+ Slog.wtf(TAG, "Unexpected hardening type" + type);
+ return;
+ }
+ String msg = "AudioHardening background playback "
+ + (bypassed ? "would be " : "")
+ + "muted for "
+ + getPackNameForUid(uid) + " (" + uid + "), "
+ + "level: " + (type == HardeningType.PARTIAL ? "partial" : "full");
+
+ AudioService.this.mHardeningLogger.enqueueAndSlog(msg,
+ bypassed ? EventLogger.Event.ALOGI : EventLogger.Event.ALOGW, TAG);
}
@Override
@@ -1544,7 +1585,8 @@ public class AudioService extends IAudioService.Stub
mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive(), mAppOps,
- context.getPackageManager());
+ context.getPackageManager(),
+ mHardeningLogger);
}
private void initVolumeStreamStates() {
@@ -12691,6 +12733,7 @@ public class AudioService extends IAudioService.Stub
static final int LOG_NB_EVENTS_DYN_POLICY = 10;
static final int LOG_NB_EVENTS_SPATIAL = 30;
static final int LOG_NB_EVENTS_SOUND_DOSE = 50;
+ static final int LOG_NB_EVENTS_HARDENING = 50;
static final int LOG_NB_EVENTS_LOUDNESS_CODEC = 30;
@@ -12729,6 +12772,9 @@ public class AudioService extends IAudioService.Stub
mDynPolicyLogger = new EventLogger(LOG_NB_EVENTS_DYN_POLICY,
"dynamic policy events (logged when command received by AudioService)");
+ private final EventLogger mHardeningLogger = new EventLogger(
+ LOG_NB_EVENTS_HARDENING, "Hardening enforcement");
+
private static final String[] RINGER_MODE_NAMES = new String[] {
"SILENT",
"VIBRATE",
@@ -12803,7 +12849,7 @@ public class AudioService extends IAudioService.Stub
pw.println("\nMessage handler is null");
}
dumpFlags(pw);
- mHardeningEnforcer.dump(pw);
+ mHardeningLogger.dump(pw);
mMediaFocusControl.dump(pw);
dumpStreamStates(pw);
dumpVolumeGroups(pw);
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
index 661111070aae..f69a810b314f 100644
--- a/services/core/java/com/android/server/audio/HardeningEnforcer.java
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -54,8 +54,7 @@ public class HardeningEnforcer {
final ActivityManager mActivityManager;
final PackageManager mPackageManager;
- final EventLogger mEventLogger = new EventLogger(LOG_NB_EVENTS,
- "Hardening enforcement");
+ final EventLogger mEventLogger;
// capacity = 4 for each of the focus request types
static final SparseArray<String> METRIC_COUNTERS_FOCUS_DENIAL = new SparseArray<>(4);
@@ -108,17 +107,13 @@ public class HardeningEnforcer {
public static final int METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS = 300;
public HardeningEnforcer(Context ctxt, boolean isAutomotive, AppOpsManager appOps,
- PackageManager pm) {
+ PackageManager pm, EventLogger logger) {
mContext = ctxt;
mIsAutomotive = isAutomotive;
mAppOps = appOps;
mActivityManager = ctxt.getSystemService(ActivityManager.class);
mPackageManager = pm;
- }
-
- protected void dump(PrintWriter pw) {
- // log
- mEventLogger.dump(pw);
+ mEventLogger = logger;
}
/**
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 1c01fb9f19e0..e2e06b63c7d6 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -1745,7 +1745,6 @@ public final class PlaybackActivityMonitor
eventValues[0] = eventValue;
sEventLogger.enqueue(
new PlayerEvent(piid, PLAYER_UPDATE_MUTED, eventValues));
-
final AudioPlaybackConfiguration apc = mPlayers.get(piid);
if (apc == null || !apc.handleMutedEvent(eventValue)) {
break; // do not dispatch
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 93d9b8d30a2e..25a2f60b85b2 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -643,8 +643,9 @@ final class ColorFade {
.setSecure(isSecure)
.setBLASTLayer();
mBLASTSurfaceControl = b.build();
- mBLASTBufferQueue = new BLASTBufferQueue("ColorFade", mBLASTSurfaceControl,
- mDisplayWidth, mDisplayHeight, PixelFormat.TRANSLUCENT);
+ mBLASTBufferQueue = new BLASTBufferQueue("ColorFade", /*updateDestinationFrame*/ true);
+ mBLASTBufferQueue.update(mBLASTSurfaceControl, mDisplayWidth, mDisplayHeight,
+ PixelFormat.TRANSLUCENT);
mSurface = mBLASTBufferQueue.createSurface();
}
return true;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index d37dd3018fde..b49c01b3e2a8 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -83,6 +83,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.boot.emulator.circular";
+ private static final double DEFAULT_DISPLAY_SIZE = 24.0;
+
private final LongSparseArray<LocalDisplayDevice> mDevices = new LongSparseArray<>();
private final Injector mInjector;
@@ -526,6 +528,21 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private int getLogicalDensity() {
DensityMapping densityMapping = getDisplayDeviceConfig().getDensityMapping();
if (densityMapping == null) {
+ if (getFeatureFlags().isBaseDensityForExternalDisplaysEnabled()
+ && !mStaticDisplayInfo.isInternal) {
+ double dpi;
+
+ if (mActiveSfDisplayMode.xDpi > 0 && mActiveSfDisplayMode.yDpi > 0) {
+ dpi = Math.sqrt((Math.pow(mActiveSfDisplayMode.xDpi, 2)
+ + Math.pow(mActiveSfDisplayMode.yDpi, 2)) / 2);
+ } else {
+ // xDPI and yDPI is missing, calculate DPI from display resolution and
+ // default display size
+ dpi = Math.sqrt(Math.pow(mInfo.width, 2) + Math.pow(mInfo.height, 2))
+ / DEFAULT_DISPLAY_SIZE;
+ }
+ return (int) (dpi + 0.5);
+ }
return (int) (mStaticDisplayInfo.density * 160 + 0.5);
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 0c04be10d06d..67b1ec305d7f 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.BIND_DREAM_SERVICE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.service.dreams.Flags.allowDreamWhenPostured;
import static android.service.dreams.Flags.cleanupDreamSettingsOnUninstall;
import static android.service.dreams.Flags.dreamHandlesBeingObscured;
@@ -110,12 +111,13 @@ public final class DreamManagerService extends SystemService {
/** Constants for the when to activate dreams. */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({DREAM_ON_DOCK, DREAM_ON_CHARGE, DREAM_ON_DOCK_OR_CHARGE})
+ @IntDef({DREAM_DISABLED, DREAM_ON_DOCK, DREAM_ON_CHARGE, DREAM_ON_POSTURED})
public @interface WhenToDream {}
- private static final int DREAM_DISABLED = 0x0;
- private static final int DREAM_ON_DOCK = 0x1;
- private static final int DREAM_ON_CHARGE = 0x2;
- private static final int DREAM_ON_DOCK_OR_CHARGE = 0x3;
+
+ private static final int DREAM_DISABLED = 0;
+ private static final int DREAM_ON_DOCK = 1 << 0;
+ private static final int DREAM_ON_CHARGE = 1 << 1;
+ private static final int DREAM_ON_POSTURED = 1 << 2;
private final Object mLock = new Object();
@@ -137,6 +139,7 @@ public final class DreamManagerService extends SystemService {
private final boolean mDreamsEnabledByDefaultConfig;
private final boolean mDreamsActivatedOnChargeByDefault;
private final boolean mDreamsActivatedOnDockByDefault;
+ private final boolean mDreamsActivatedOnPosturedByDefault;
private final boolean mKeepDreamingWhenUnpluggingDefault;
private final boolean mDreamsDisabledByAmbientModeSuppressionConfig;
@@ -152,6 +155,7 @@ public final class DreamManagerService extends SystemService {
@WhenToDream private int mWhenToDream;
private boolean mIsDocked;
private boolean mIsCharging;
+ private boolean mIsPostured;
// A temporary dream component that, when present, takes precedence over user configured dream
// component.
@@ -270,6 +274,8 @@ public final class DreamManagerService extends SystemService {
com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
mDreamsActivatedOnDockByDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
+ mDreamsActivatedOnPosturedByDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault);
mSettingsObserver = new SettingsObserver(mHandler);
mKeepDreamingWhenUnpluggingDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_keepDreamingWhenUnplugging);
@@ -328,6 +334,9 @@ public final class DreamManagerService extends SystemService {
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK),
false, mSettingsObserver, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SCREENSAVER_ENABLED),
false, mSettingsObserver, UserHandle.USER_ALL);
@@ -392,6 +401,8 @@ public final class DreamManagerService extends SystemService {
pw.println("mDreamsEnabledSetting=" + mDreamsEnabledSetting);
pw.println("mDreamsActivatedOnDockByDefault=" + mDreamsActivatedOnDockByDefault);
pw.println("mDreamsActivatedOnChargeByDefault=" + mDreamsActivatedOnChargeByDefault);
+ pw.println("mDreamsActivatedOnPosturedByDefault="
+ + mDreamsActivatedOnPosturedByDefault);
pw.println("mIsDocked=" + mIsDocked);
pw.println("mIsCharging=" + mIsCharging);
pw.println("mWhenToDream=" + mWhenToDream);
@@ -409,15 +420,28 @@ public final class DreamManagerService extends SystemService {
synchronized (mLock) {
final ContentResolver resolver = mContext.getContentResolver();
- final int activateWhenCharging = (Settings.Secure.getIntForUser(resolver,
+ mWhenToDream = DREAM_DISABLED;
+
+ if ((Settings.Secure.getIntForUser(resolver,
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
mDreamsActivatedOnChargeByDefault ? 1 : 0,
- UserHandle.USER_CURRENT) != 0) ? DREAM_ON_CHARGE : DREAM_DISABLED;
- final int activateWhenDocked = (Settings.Secure.getIntForUser(resolver,
+ UserHandle.USER_CURRENT) != 0)) {
+ mWhenToDream |= DREAM_ON_CHARGE;
+ }
+
+ if (Settings.Secure.getIntForUser(resolver,
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
mDreamsActivatedOnDockByDefault ? 1 : 0,
- UserHandle.USER_CURRENT) != 0) ? DREAM_ON_DOCK : DREAM_DISABLED;
- mWhenToDream = activateWhenCharging + activateWhenDocked;
+ UserHandle.USER_CURRENT) != 0) {
+ mWhenToDream |= DREAM_ON_DOCK;
+ }
+
+ if (Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ mDreamsActivatedOnPosturedByDefault ? 1 : 0,
+ UserHandle.USER_CURRENT) != 0) {
+ mWhenToDream |= DREAM_ON_POSTURED;
+ }
mDreamsEnabledSetting = (Settings.Secure.getIntForUser(resolver,
Settings.Secure.SCREENSAVER_ENABLED,
@@ -508,6 +532,10 @@ public final class DreamManagerService extends SystemService {
return mIsDocked;
}
+ if ((mWhenToDream & DREAM_ON_POSTURED) == DREAM_ON_POSTURED) {
+ return mIsPostured;
+ }
+
return false;
}
}
@@ -646,6 +674,14 @@ public final class DreamManagerService extends SystemService {
}
}
+ private void setDevicePosturedInternal(boolean isPostured) {
+ Slog.d(TAG, "Device postured: " + isPostured);
+ synchronized (mLock) {
+ mIsPostured = isPostured;
+ mHandler.post(() -> mPowerManagerInternal.setDevicePostured(isPostured));
+ }
+ }
+
/**
* If doze is true, returns the doze component for the user.
* Otherwise, returns the system dream component, if present.
@@ -1294,6 +1330,22 @@ public final class DreamManagerService extends SystemService {
}
}
+ @Override
+ public void setDevicePostured(boolean isPostured) {
+ if (!allowDreamWhenPostured()) {
+ return;
+ }
+
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setDevicePosturedInternal(isPostured);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
boolean canLaunchDreamActivity(String dreamPackageName, String packageName,
int callingUid) {
if (dreamPackageName == null || packageName == null) {
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
index ff1a74af02e2..9118c46e3b22 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
@@ -61,6 +61,11 @@ final class DeviceSelectActionFromTv extends HdmiCecFeatureAction {
@VisibleForTesting
static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3;
+ // State in which we wait for device to complete a possible power state change triggered by
+ // <Set Stream Path>.
+ @VisibleForTesting
+ static final int STATE_WAIT_FOR_POWER_STATE_CHANGE = 4;
+
private final HdmiDeviceInfo mTarget;
private final HdmiCecMessage mGivePowerStatus;
private final boolean mIsCec20;
@@ -100,7 +105,12 @@ final class DeviceSelectActionFromTv extends HdmiCecFeatureAction {
// Wake-up on <Set Stream Path> was not mandatory before CEC 2.0.
// The message is re-sent at the end of the action for devices that don't support 2.0.
sendSetStreamPath();
+ mState = STATE_WAIT_FOR_POWER_STATE_CHANGE;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ return true;
+ }
+ private void checkForPowerStateChange() {
if (!mIsCec20) {
queryDevicePowerStatus();
} else {
@@ -114,12 +124,11 @@ final class DeviceSelectActionFromTv extends HdmiCecFeatureAction {
queryDevicePowerStatus();
} else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- return true;
+ return;
}
}
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
- return true;
}
private void queryDevicePowerStatus() {
@@ -210,6 +219,9 @@ final class DeviceSelectActionFromTv extends HdmiCecFeatureAction {
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
break;
+ case STATE_WAIT_FOR_POWER_STATE_CHANGE:
+ checkForPowerStateChange();
+ break;
}
}
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index 93fdbc787ed0..fd755e3cefe2 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -87,7 +87,20 @@ final class InputGestureManager {
createKeyTrigger(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON),
createKeyTrigger(KeyEvent.KEYCODE_X, KeyEvent.META_CTRL_ON),
createKeyTrigger(KeyEvent.KEYCODE_Z, KeyEvent.META_CTRL_ON),
- createKeyTrigger(KeyEvent.KEYCODE_Y, KeyEvent.META_CTRL_ON)
+ createKeyTrigger(KeyEvent.KEYCODE_Y, KeyEvent.META_CTRL_ON),
+ // Used for magnification viewport control.
+ createKeyTrigger(KeyEvent.KEYCODE_MINUS,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_EQUALS,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_DPAD_LEFT,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_DPAD_RIGHT,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_DPAD_UP,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_DPAD_DOWN,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON)
));
public InputGestureManager(Context context) {
@@ -216,24 +229,6 @@ final class InputGestureManager {
systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_T,
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK));
- systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_MINUS,
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT));
- systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_EQUALS,
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN));
- systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT));
- systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT));
- systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP));
- systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_DOWN,
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN));
systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_M,
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION));
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 45c7cffd462b..7b81fc92e83d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2189,7 +2189,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
if (mVdmInternal == null) {
mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
}
- if (mVdmInternal == null || !android.companion.virtual.flags.Flags.vdmCustomIme()) {
+ if (mVdmInternal == null) {
return currentMethodId;
}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index efc1b9959c0f..e47cbdc3546f 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -16,18 +16,33 @@
package com.android.server.media.quality;
+import static android.media.quality.AmbientBacklightEvent.AMBIENT_BACKLIGHT_EVENT_ENABLED;
+import static android.media.quality.AmbientBacklightEvent.AMBIENT_BACKLIGHT_EVENT_DISABLED;
+import static android.media.quality.AmbientBacklightEvent.AMBIENT_BACKLIGHT_EVENT_METADATA_AVAILABLE;
+import static android.media.quality.AmbientBacklightEvent.AMBIENT_BACKLIGHT_EVENT_INTERRUPTED;
+
+import android.annotation.NonNull;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.hardware.tv.mediaquality.AmbientBacklightColorFormat;
import android.hardware.tv.mediaquality.IMediaQuality;
+import android.hardware.tv.mediaquality.PictureParameter;
+import android.hardware.tv.mediaquality.PictureParameters;
+import android.hardware.tv.mediaquality.SoundParameter;
+import android.hardware.tv.mediaquality.SoundParameters;
+import android.media.quality.AmbientBacklightEvent;
+import android.media.quality.AmbientBacklightMetadata;
import android.media.quality.AmbientBacklightSettings;
import android.media.quality.IAmbientBacklightCallback;
import android.media.quality.IMediaQualityManager;
import android.media.quality.IPictureProfileCallback;
import android.media.quality.ISoundProfileCallback;
import android.media.quality.MediaQualityContract.BaseParameters;
+import android.media.quality.MediaQualityContract.PictureQuality;
+import android.media.quality.MediaQualityContract.SoundQuality;
import android.media.quality.MediaQualityManager;
import android.media.quality.ParameterCapability;
import android.media.quality.PictureProfile;
@@ -42,6 +57,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -60,6 +76,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -76,13 +93,16 @@ public class MediaQualityService extends SystemService {
private final MediaQualityDbHelper mMediaQualityDbHelper;
private final BiMap<Long, String> mPictureProfileTempIdMap;
private final BiMap<Long, String> mSoundProfileTempIdMap;
+ private IMediaQuality mMediaQuality;
+ private final HalAmbientBacklightCallback mHalAmbientBacklightCallback;
+ private final Map<String, AmbientBacklightCallbackRecord> mCallbackRecords = new HashMap<>();
private final PackageManager mPackageManager;
private final SparseArray<UserState> mUserStates = new SparseArray<>();
- private IMediaQuality mMediaQuality;
public MediaQualityService(Context context) {
super(context);
mContext = context;
+ mHalAmbientBacklightCallback = new HalAmbientBacklightCallback();
mPackageManager = mContext.getPackageManager();
mPictureProfileTempIdMap = new BiMap<>();
mSoundProfileTempIdMap = new BiMap<>();
@@ -97,6 +117,13 @@ public class MediaQualityService extends SystemService {
if (binder != null) {
Slogf.d(TAG, "binder is not null");
mMediaQuality = IMediaQuality.Stub.asInterface(binder);
+ if (mMediaQuality != null) {
+ try {
+ mMediaQuality.setAmbientBacklightCallback(mHalAmbientBacklightCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set ambient backlight detector callback", e);
+ }
+ }
}
publishBinderService(Context.MEDIA_QUALITY_SERVICE, new BinderService());
@@ -282,10 +309,208 @@ public class MediaQualityService extends SystemService {
notifyOnPictureProfileError(profileId, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
- // TODO: pass the profile ID to MediaQuality HAL when ready.
+
+ PictureProfile pictureProfile = getPictureProfile(
+ mPictureProfileTempIdMap.getKey(profileId));
+ PersistableBundle params = pictureProfile.getParameters();
+
+ try {
+ if (mMediaQuality != null) {
+ PictureParameter[] pictureParameters =
+ convertPersistableBundleToPictureParameterList(params);
+
+ PictureParameters pp = new PictureParameters();
+ pp.pictureParameters = pictureParameters;
+
+ mMediaQuality.sendDefaultPictureParameters(pp);
+ return true;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set default picture profile", e);
+ }
return false;
}
+ private PictureParameter[] convertPersistableBundleToPictureParameterList(
+ PersistableBundle params) {
+ List<PictureParameter> pictureParams = new ArrayList<>();
+ if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) {
+ pictureParams.add(PictureParameter.brightness(params.getLong(
+ PictureQuality.PARAMETER_BRIGHTNESS)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_CONTRAST)) {
+ pictureParams.add(PictureParameter.contrast(params.getInt(
+ PictureQuality.PARAMETER_CONTRAST)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_SHARPNESS)) {
+ pictureParams.add(PictureParameter.sharpness(params.getInt(
+ PictureQuality.PARAMETER_SHARPNESS)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_SATURATION)) {
+ pictureParams.add(PictureParameter.saturation(params.getInt(
+ PictureQuality.PARAMETER_SATURATION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_HUE)) {
+ pictureParams.add(PictureParameter.hue(params.getInt(
+ PictureQuality.PARAMETER_HUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)) {
+ pictureParams.add(PictureParameter.colorTunerBrightness(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)) {
+ pictureParams.add(PictureParameter.colorTunerSaturation(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE)) {
+ pictureParams.add(PictureParameter.colorTunerHue(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTunerRedOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTunerGreenOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTunerBlueOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
+ pictureParams.add(PictureParameter.colorTunerRedGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
+ pictureParams.add(PictureParameter.colorTunerGreenGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
+ pictureParams.add(PictureParameter.colorTunerBlueGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
+ pictureParams.add(PictureParameter.noiseReduction(
+ (byte) params.getInt(PictureQuality.PARAMETER_NOISE_REDUCTION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
+ pictureParams.add(PictureParameter.mpegNoiseReduction(
+ (byte) params.getInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_FLESH_TONE)) {
+ pictureParams.add(PictureParameter.fleshTone(
+ (byte) params.getInt(PictureQuality.PARAMETER_FLESH_TONE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_DECONTOUR)) {
+ pictureParams.add(PictureParameter.deContour(
+ (byte) params.getInt(PictureQuality.PARAMETER_DECONTOUR)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
+ pictureParams.add(PictureParameter.dynamicLumaControl(
+ (byte) params.getInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_FILM_MODE)) {
+ pictureParams.add(PictureParameter.filmMode(params.getBoolean(
+ PictureQuality.PARAMETER_FILM_MODE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_BLUE_STRETCH)) {
+ pictureParams.add(PictureParameter.blueStretch(params.getBoolean(
+ PictureQuality.PARAMETER_BLUE_STRETCH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNE)) {
+ pictureParams.add(PictureParameter.colorTune(params.getBoolean(
+ PictureQuality.PARAMETER_COLOR_TUNE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
+ pictureParams.add(PictureParameter.colorTemperature(
+ (byte) params.getInt(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
+ pictureParams.add(PictureParameter.globeDimming(params.getBoolean(
+ PictureQuality.PARAMETER_GLOBAL_DIMMING)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)) {
+ pictureParams.add(PictureParameter.autoPictureQualityEnabled(params.getBoolean(
+ PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)) {
+ pictureParams.add(PictureParameter.autoSuperResolutionEnabled(params.getBoolean(
+ PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
+ pictureParams.add(PictureParameter.colorTemperatureRedGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
+ pictureParams.add(PictureParameter.colorTemperatureGreenGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
+ pictureParams.add(PictureParameter.colorTemperatureBlueGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
+ }
+
+ /**
+ * TODO: add conversion for following after adding to MediaQualityContract
+ *
+ * PictureParameter.levelRange
+ * PictureParameter.gamutMapping
+ * PictureParameter.pcMode
+ * PictureParameter.lowLatency
+ * PictureParameter.vrr
+ * PictureParameter.cvrr
+ * PictureParameter.hdmiRgbRange
+ * PictureParameter.colorSpace
+ * PictureParameter.panelInitMaxLuminceNits
+ * PictureParameter.panelInitMaxLuminceValid
+ * PictureParameter.gamma
+ * PictureParameter.colorTemperatureRedOffset
+ * PictureParameter.colorTemperatureGreenOffset
+ * PictureParameter.colorTemperatureBlueOffset
+ * PictureParameter.elevenPointRed
+ * PictureParameter.elevenPointGreen
+ * PictureParameter.elevenPointBlue
+ * PictureParameter.lowBlueLight
+ * PictureParameter.LdMode
+ * PictureParameter.osdRedGain
+ * PictureParameter.osdGreenGain
+ * PictureParameter.osdBlueGain
+ * PictureParameter.osdRedOffset
+ * PictureParameter.osdGreenOffset
+ * PictureParameter.osdBlueOffset
+ * PictureParameter.osdHue
+ * PictureParameter.osdSaturation
+ * PictureParameter.osdContrast
+ * PictureParameter.colorTunerSwitch
+ * PictureParameter.colorTunerHueRed
+ * PictureParameter.colorTunerHueGreen
+ * PictureParameter.colorTunerHueBlue
+ * PictureParameter.colorTunerHueCyan
+ * PictureParameter.colorTunerHueMagenta
+ * PictureParameter.colorTunerHueYellow
+ * PictureParameter.colorTunerHueFlesh
+ * PictureParameter.colorTunerSaturationRed
+ * PictureParameter.colorTunerSaturationGreen
+ * PictureParameter.colorTunerSaturationBlue
+ * PictureParameter.colorTunerSaturationCyan
+ * PictureParameter.colorTunerSaturationMagenta
+ * PictureParameter.colorTunerSaturationYellow
+ * PictureParameter.colorTunerSaturationFlesh
+ * PictureParameter.colorTunerLuminanceRed
+ * PictureParameter.colorTunerLuminanceGreen
+ * PictureParameter.colorTunerLuminanceBlue
+ * PictureParameter.colorTunerLuminanceCyan
+ * PictureParameter.colorTunerLuminanceMagenta
+ * PictureParameter.colorTunerLuminanceYellow
+ * PictureParameter.colorTunerLuminanceFlesh
+ * PictureParameter.activeProfile
+ * PictureParameter.pictureQualityEventType
+ */
+ return (PictureParameter[]) pictureParams.toArray();
+ }
+
@Override
public List<String> getPictureProfilePackageNames(UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
@@ -503,10 +728,77 @@ public class MediaQualityService extends SystemService {
notifyOnSoundProfileError(profileId, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
- // TODO: pass the profile ID to MediaQuality HAL when ready.
+
+ SoundProfile soundProfile = getSoundProfile(mSoundProfileTempIdMap.getKey(profileId));
+ PersistableBundle params = soundProfile.getParameters();
+
+ try {
+ if (mMediaQuality != null) {
+ SoundParameter[] soundParameters =
+ convertPersistableBundleToSoundParameterList(params);
+
+ SoundParameters sp = new SoundParameters();
+ sp.soundParameters = soundParameters;
+
+ mMediaQuality.sendDefaultSoundParameters(sp);
+ return true;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set default sound profile", e);
+ }
return false;
}
+ private SoundParameter[] convertPersistableBundleToSoundParameterList(
+ PersistableBundle params) {
+ List<SoundParameter> soundParams = new ArrayList<>();
+ if (params.containsKey(SoundQuality.PARAMETER_BALANCE)) {
+ soundParams.add(SoundParameter.balance(params.getInt(
+ SoundQuality.PARAMETER_BALANCE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_BASS)) {
+ soundParams.add(SoundParameter.bass(params.getInt(SoundQuality.PARAMETER_BASS)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_TREBLE)) {
+ soundParams.add(SoundParameter.treble(params.getInt(
+ SoundQuality.PARAMETER_TREBLE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_SURROUND_SOUND)) {
+ soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
+ SoundQuality.PARAMETER_SURROUND_SOUND)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS)) {
+ soundParams.add(SoundParameter.speakersEnabled(params.getBoolean(
+ SoundQuality.PARAMETER_SPEAKERS)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)) {
+ soundParams.add(SoundParameter.speakersDelayMs(params.getInt(
+ SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)) {
+ soundParams.add(SoundParameter.autoVolumeControl(params.getBoolean(
+ SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DTS_DRC)) {
+ soundParams.add(SoundParameter.dtsDrc(params.getBoolean(
+ SoundQuality.PARAMETER_DTS_DRC)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)) {
+ soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
+ SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)));
+ }
+ //TODO: equalizerDetail
+ //TODO: downmixMode
+ //TODO: enhancedAudioReturnChannelEnabled
+ //TODO: dolbyAudioProcessing
+ //TODO: dolbyDialogueEnhancer
+ //TODO: dtsVirtualX
+ //TODO: digitalOutput
+ //TODO: activeProfile
+ //TODO: soundStyle
+ return (SoundParameter[]) soundParams.toArray();
+ }
+
@Override
public List<String> getSoundProfilePackageNames(UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
@@ -905,24 +1197,86 @@ public class MediaQualityService extends SystemService {
@Override
public void registerAmbientBacklightCallback(IAmbientBacklightCallback callback) {
+ if (DEBUG) {
+ Slogf.d(TAG, "registerAmbientBacklightCallback");
+ }
+
if (!hasReadColorZonesPermission()) {
//TODO: error handling
}
+
+ String callingPackageName = getPackageOfCallingUid();
+
+ synchronized (mCallbackRecords) {
+ AmbientBacklightCallbackRecord record = mCallbackRecords.get(callingPackageName);
+ if (record != null) {
+ if (record.mCallback.asBinder().equals(callback.asBinder())) {
+ Slog.w(TAG, "AmbientBacklight Callback already registered");
+ return;
+ }
+ record.release();
+ mCallbackRecords.remove(callingPackageName);
+ }
+ mCallbackRecords.put(callingPackageName,
+ new AmbientBacklightCallbackRecord(callingPackageName, callback));
+ }
}
@Override
public void setAmbientBacklightSettings(
AmbientBacklightSettings settings, UserHandle user) {
+ if (DEBUG) {
+ Slogf.d(TAG, "setAmbientBacklightSettings " + settings);
+ }
+
if (!hasReadColorZonesPermission()) {
//TODO: error handling
}
+
+ try {
+ if (mMediaQuality != null) {
+ android.hardware.tv.mediaquality.AmbientBacklightSettings halSettings =
+ new android.hardware.tv.mediaquality.AmbientBacklightSettings();
+ halSettings.uid = Binder.getCallingUid();
+ halSettings.source = (byte) settings.getSource();
+ halSettings.maxFramerate = settings.getMaxFps();
+ halSettings.colorFormat = (byte) settings.getColorFormat();
+ halSettings.hZonesNumber = settings.getHorizontalZonesCount();
+ halSettings.vZonesNumber = settings.getVerticalZonesCount();
+ halSettings.hasLetterbox = settings.isLetterboxOmitted();
+ halSettings.colorThreshold = settings.getThreshold();
+
+ mMediaQuality.setAmbientBacklightDetector(halSettings);
+
+ mHalAmbientBacklightCallback.setAmbientBacklightClientPackageName(
+ getPackageOfCallingUid());
+
+ if (DEBUG) {
+ Slogf.d(TAG, "set ambient settings package: " + halSettings.uid);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set ambient backlight settings", e);
+ }
}
@Override
public void setAmbientBacklightEnabled(boolean enabled, UserHandle user) {
+ if (DEBUG) {
+ Slogf.d(TAG, "setAmbientBacklightEnabled " + enabled);
+ }
if (!hasReadColorZonesPermission()) {
//TODO: error handling
}
+ try {
+ if (mMediaQuality != null) {
+ mMediaQuality.setAmbientBacklightDetectionEnabled(enabled);
+ }
+ } catch (UnsupportedOperationException e) {
+ Slog.e(TAG, "The current device is not supported");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set ambient backlight enabled", e);
+ }
}
@Override
@@ -979,10 +1333,10 @@ public class MediaQualityService extends SystemService {
try {
if (mMediaQuality != null) {
- mMediaQuality.setAutoPqEnabled(enabled);
+ if (mMediaQuality.isAutoPqSupported()) {
+ mMediaQuality.setAutoPqEnabled(enabled);
+ }
}
- } catch (UnsupportedOperationException e) {
- Slog.e(TAG, "The current device is not supported");
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set auto picture quality", e);
}
@@ -992,10 +1346,10 @@ public class MediaQualityService extends SystemService {
public boolean isAutoPictureQualityEnabled(UserHandle user) {
try {
if (mMediaQuality != null) {
- return mMediaQuality.getAutoPqEnabled();
+ if (mMediaQuality.isAutoPqSupported()) {
+ mMediaQuality.getAutoPqEnabled();
+ }
}
- } catch (UnsupportedOperationException e) {
- Slog.e(TAG, "The current device is not supported");
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get auto picture quality", e);
}
@@ -1011,12 +1365,12 @@ public class MediaQualityService extends SystemService {
try {
if (mMediaQuality != null) {
- mMediaQuality.setAutoSrEnabled(enabled);
+ if (mMediaQuality.isAutoSrSupported()) {
+ mMediaQuality.setAutoSrEnabled(enabled);
+ }
}
- } catch (UnsupportedOperationException e) {
- Slog.e(TAG, "The current device is not supported");
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to set auto super resolution", e);
+ Slog.e(TAG, "Failed to set super resolution", e);
}
}
@@ -1024,12 +1378,12 @@ public class MediaQualityService extends SystemService {
public boolean isSuperResolutionEnabled(UserHandle user) {
try {
if (mMediaQuality != null) {
- return mMediaQuality.getAutoSrEnabled();
+ if (mMediaQuality.isAutoSrSupported()) {
+ mMediaQuality.getAutoSrEnabled();
+ }
}
- } catch (UnsupportedOperationException e) {
- Slog.e(TAG, "The current device is not supported");
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to get auto super resolution", e);
+ Slog.e(TAG, "Failed to get super resolution", e);
}
return false;
}
@@ -1043,12 +1397,12 @@ public class MediaQualityService extends SystemService {
try {
if (mMediaQuality != null) {
- mMediaQuality.setAutoAqEnabled(enabled);
+ if (mMediaQuality.isAutoAqSupported()) {
+ mMediaQuality.setAutoAqEnabled(enabled);
+ }
}
- } catch (UnsupportedOperationException e) {
- Slog.e(TAG, "The current device is not supported");
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to set auto audio quality", e);
+ Slog.e(TAG, "Failed to set auto sound quality", e);
}
}
@@ -1056,12 +1410,12 @@ public class MediaQualityService extends SystemService {
public boolean isAutoSoundQualityEnabled(UserHandle user) {
try {
if (mMediaQuality != null) {
- return mMediaQuality.getAutoAqEnabled();
+ if (mMediaQuality.isAutoAqSupported()) {
+ mMediaQuality.getAutoAqEnabled();
+ }
}
- } catch (UnsupportedOperationException e) {
- Slog.e(TAG, "The current device is not supported");
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to get auto audio quality", e);
+ Slog.e(TAG, "Failed to get auto sound quality", e);
}
return false;
}
@@ -1119,4 +1473,167 @@ public class MediaQualityService extends SystemService {
private UserState getUserStateLocked(int userId) {
return mUserStates.get(userId);
}
+
+ private final class AmbientBacklightCallbackRecord implements IBinder.DeathRecipient {
+ final String mPackageName;
+ final IAmbientBacklightCallback mCallback;
+
+ AmbientBacklightCallbackRecord(@NonNull String pkgName,
+ @NonNull IAmbientBacklightCallback cb) {
+ mPackageName = pkgName;
+ mCallback = cb;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death", e);
+ }
+ }
+
+ void release() {
+ try {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Slog.e(TAG, "Failed to unlink to death", e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mCallbackRecords) {
+ mCallbackRecords.remove(mPackageName);
+ }
+ }
+ }
+
+ private final class HalAmbientBacklightCallback
+ extends android.hardware.tv.mediaquality.IMediaQualityCallback.Stub {
+ private final Object mLock = new Object();
+ private String mAmbientBacklightClientPackageName;
+
+ void setAmbientBacklightClientPackageName(@NonNull String packageName) {
+ synchronized (mLock) {
+ if (TextUtils.equals(mAmbientBacklightClientPackageName, packageName)) {
+ return;
+ }
+ handleAmbientBacklightInterrupted();
+ mAmbientBacklightClientPackageName = packageName;
+ }
+ }
+
+ void handleAmbientBacklightInterrupted() {
+ synchronized (mCallbackRecords) {
+ if (mAmbientBacklightClientPackageName == null) {
+ Slog.e(TAG, "Invalid package name in interrupted event");
+ return;
+ }
+ AmbientBacklightCallbackRecord record = mCallbackRecords.get(
+ mAmbientBacklightClientPackageName);
+ if (record == null) {
+ Slog.e(TAG, "Callback record not found for ambient backlight");
+ return;
+ }
+ AmbientBacklightEvent event =
+ new AmbientBacklightEvent(
+ AMBIENT_BACKLIGHT_EVENT_INTERRUPTED, null);
+ try {
+ record.mCallback.onAmbientBacklightEvent(event);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Deliver ambient backlight interrupted event failed", e);
+ }
+ }
+ }
+
+ void handleAmbientBacklightEnabled(boolean enabled) {
+ AmbientBacklightEvent event =
+ new AmbientBacklightEvent(
+ enabled ? AMBIENT_BACKLIGHT_EVENT_ENABLED :
+ AMBIENT_BACKLIGHT_EVENT_DISABLED, null);
+ synchronized (mCallbackRecords) {
+ for (AmbientBacklightCallbackRecord record : mCallbackRecords.values()) {
+ try {
+ record.mCallback.onAmbientBacklightEvent(event);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Deliver ambient backlight enabled event failed", e);
+ }
+ }
+ }
+ }
+
+ void handleAmbientBacklightMetadataEvent(
+ @NonNull android.hardware.tv.mediaquality.AmbientBacklightMetadata
+ halMetadata) {
+ String halPackageName = mContext.getPackageManager()
+ .getNameForUid(halMetadata.settings.uid);
+ if (!TextUtils.equals(mAmbientBacklightClientPackageName, halPackageName)) {
+ Slog.e(TAG, "Invalid package name in metadata event");
+ return;
+ }
+
+ AmbientBacklightColorFormat[] zonesColorsUnion = halMetadata.zonesColors;
+ int[] zonesColorsInt = new int[zonesColorsUnion.length];
+
+ for (int i = 0; i < zonesColorsUnion.length; i++) {
+ zonesColorsInt[i] = zonesColorsUnion[i].RGB888;
+ }
+
+ AmbientBacklightMetadata metadata =
+ new AmbientBacklightMetadata(
+ halPackageName,
+ halMetadata.compressAlgorithm,
+ halMetadata.settings.source,
+ halMetadata.settings.colorFormat,
+ halMetadata.settings.hZonesNumber,
+ halMetadata.settings.vZonesNumber,
+ zonesColorsInt);
+ AmbientBacklightEvent event =
+ new AmbientBacklightEvent(
+ AMBIENT_BACKLIGHT_EVENT_METADATA_AVAILABLE, metadata);
+
+ synchronized (mCallbackRecords) {
+ AmbientBacklightCallbackRecord record = mCallbackRecords
+ .get(halPackageName);
+ if (record == null) {
+ Slog.e(TAG, "Callback record not found for ambient backlight metadata");
+ return;
+ }
+
+ try {
+ record.mCallback.onAmbientBacklightEvent(event);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Deliver ambient backlight metadata event failed", e);
+ }
+ }
+ }
+
+ @Override
+ public void notifyAmbientBacklightEvent(
+ android.hardware.tv.mediaquality.AmbientBacklightEvent halEvent) {
+ synchronized (mLock) {
+ if (halEvent.getTag() == android.hardware.tv.mediaquality
+ .AmbientBacklightEvent.Tag.enabled) {
+ boolean enabled = halEvent.getEnabled();
+ if (enabled) {
+ handleAmbientBacklightEnabled(true);
+ } else {
+ handleAmbientBacklightEnabled(false);
+ }
+ } else if (halEvent.getTag() == android.hardware.tv.mediaquality
+ .AmbientBacklightEvent.Tag.metadata) {
+ handleAmbientBacklightMetadataEvent(halEvent.getMetadata());
+ } else {
+ Slog.e(TAG, "Invalid event type in ambient backlight event");
+ }
+ }
+ }
+
+ @Override
+ public synchronized String getInterfaceHash() throws android.os.RemoteException {
+ return android.hardware.tv.mediaquality.IMediaQualityCallback.Stub.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() throws android.os.RemoteException {
+ return android.hardware.tv.mediaquality.IMediaQualityCallback.Stub.VERSION;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 837003f87598..ef39f1811876 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -762,6 +762,7 @@ public class NotificationManagerService extends SystemService {
private int mWarnRemoteViewsSizeBytes;
private int mStripRemoteViewsSizeBytes;
+ private String[] mDefaultUnsupportedAdjustments;
@VisibleForTesting
protected boolean mShowReviewPermissionsNotification;
@@ -2938,6 +2939,9 @@ public class NotificationManagerService extends SystemService {
mShowReviewPermissionsNotification = getContext().getResources().getBoolean(
R.bool.config_notificationReviewPermissions);
+ mDefaultUnsupportedAdjustments = getContext().getResources().getStringArray(
+ R.array.config_notificationDefaultUnsupportedAdjustments);
+
init(handler, new RankingHandlerWorker(mRankingThread.getLooper()),
AppGlobals.getPackageManager(), getContext().getPackageManager(),
getLocalService(LightsManager.class),
@@ -3033,10 +3037,9 @@ public class NotificationManagerService extends SystemService {
switch(atomTag) {
case PACKAGE_NOTIFICATION_PREFERENCES:
if (notificationClassificationUi()) {
- Set<String> pkgs = mAssistants.getPackagesWithKeyTypeAdjustmentSettings();
mPreferencesHelper.pullPackagePreferencesStats(data,
getAllUsersNotificationPermissions(),
- getPackageSpecificAdjustmentKeyTypes(pkgs));
+ new ArrayMap<>());
} else {
mPreferencesHelper.pullPackagePreferencesStats(data,
getAllUsersNotificationPermissions());
@@ -4397,16 +4400,16 @@ public class NotificationManagerService extends SystemService {
@Override
@FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- public @NonNull int[] getAllowedAdjustmentKeyTypesForPackage(String pkg) {
+ public @NonNull String[] getTypeAdjustmentDeniedPackages() {
checkCallerIsSystemOrSystemUiOrShell();
- return mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg);
+ return mAssistants.getTypeAdjustmentDeniedPackages();
}
+ @Override
@FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- public void setAssistantAdjustmentKeyTypeStateForPackage(String pkg, int type,
- boolean enabled) {
+ public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) {
checkCallerIsSystemOrSystemUiOrShell();
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, type, enabled);
+ mAssistants.setTypeAdjustmentForPackageState(pkg, enabled);
handleSavePolicyFile();
}
@@ -7215,7 +7218,7 @@ public class NotificationManagerService extends SystemService {
toRemove.add(potentialKey);
} else if (notificationClassificationUi()
&& !mAssistants.isTypeAdjustmentAllowedForPackage(
- r.getSbn().getPackageName(), adjustments.getInt(KEY_TYPE))) {
+ r.getSbn().getPackageName())) {
toRemove.add(potentialKey);
}
}
@@ -7552,24 +7555,6 @@ public class NotificationManagerService extends SystemService {
return allPermissions;
}
- @VisibleForTesting
- @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- protected @NonNull Map<String, Set<Integer>> getPackageSpecificAdjustmentKeyTypes(
- Set<String> pkgs) {
- ArrayMap<String, Set<Integer>> pkgToAllowedTypes = new ArrayMap<>();
- for (String pkg : pkgs) {
- int[] allowedTypesArray = mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg);
- if (allowedTypesArray != null) {
- Set<Integer> allowedTypes = new ArraySet<Integer>();
- for (int i : allowedTypesArray) {
- allowedTypes.add(i);
- }
- pkgToAllowedTypes.append(pkg, allowedTypes);
- }
- }
- return pkgToAllowedTypes;
- }
-
private void dumpJson(PrintWriter pw, @NonNull DumpFilter filter,
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
JSONObject dump = new JSONObject();
@@ -11885,11 +11870,7 @@ public class NotificationManagerService extends SystemService {
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";
- // Encapsulates a list of packages and the bundle types enabled for each package.
- private static final String TAG_TYPES_ENABLED_FOR_APPS = "types_enabled_for_apps";
- // Encapsulates the bundle types enabled for a package.
- private static final String ATT_APP_ENABLED_TYPES = "app_enabled_types";
- private static final String ATT_PACKAGE = "package";
+ private static final String ATT_TYPES_DENIED_APPS = "types_denied_apps";
private final Object mLock = new Object();
@@ -11905,14 +11886,8 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mLock")
private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>();
- // Types of classifications (aka bundles) enabled/allowed for this package.
- // If the set is NULL (or package is not in the list), default classification allow list
- // (the global one) should be used.
- // If the set is empty, that indicates the package explicitly has all classifications
- // disallowed.
@GuardedBy("mLock")
- private Map<String, Set<Integer>> mClassificationTypePackagesEnabledTypes =
- new ArrayMap<>();
+ private Set<String> mClassificationTypeDeniedPackages = new ArraySet<>();
protected ComponentName mDefaultFromConfig = null;
@@ -11990,6 +11965,9 @@ public class NotificationManagerService extends SystemService {
}
} else {
mAllowedAdjustmentKeyTypes.addAll(List.of(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES));
+ if (mDefaultUnsupportedAdjustments != null) {
+ mAllowedAdjustments.removeAll(List.of(mDefaultUnsupportedAdjustments));
+ }
}
}
@@ -12113,104 +12091,41 @@ public class NotificationManagerService extends SystemService {
}
}
- /**
- * Returns whether the type adjustment is allowed for this particular package.
- * If no package-specific restrictions have been set, defaults to the same value as
- * isAdjustmentKeyTypeAllowed(type).
- */
@FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- protected boolean isTypeAdjustmentAllowedForPackage(String pkg,
- @Adjustment.Types int type) {
+ protected @NonNull boolean isTypeAdjustmentAllowedForPackage(String pkg) {
synchronized (mLock) {
if (notificationClassificationUi()) {
- if (mClassificationTypePackagesEnabledTypes.containsKey(pkg)) {
- Set<Integer> enabled = mClassificationTypePackagesEnabledTypes.get(pkg);
- if (enabled != null) {
- return enabled.contains(type);
- }
- }
- // If mClassificationTypePackagesEnabledTypes does not contain the pkg, or
- // the stored set is null, return the default.
- return isAdjustmentKeyTypeAllowed(type);
- }
- }
- return false;
- }
-
- @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- protected @NonNull Set<String> getPackagesWithKeyTypeAdjustmentSettings() {
- if (notificationClassificationUi()) {
- Set<String> packagesWithModifications = new ArraySet<String>();
- synchronized (mLock) {
- for (String pkg : mClassificationTypePackagesEnabledTypes.keySet()) {
- if (mClassificationTypePackagesEnabledTypes.get(pkg) != null) {
- packagesWithModifications.add(pkg);
- }
- }
+ return !mClassificationTypeDeniedPackages.contains(pkg);
}
- return packagesWithModifications;
}
- return new ArraySet<String>();
+ return true;
}
@FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- protected @NonNull int[] getAllowedAdjustmentKeyTypesForPackage(String pkg) {
+ protected @NonNull String[] getTypeAdjustmentDeniedPackages() {
synchronized (mLock) {
if (notificationClassificationUi()) {
- if (mClassificationTypePackagesEnabledTypes.containsKey(pkg)) {
- Set<Integer> enabled = mClassificationTypePackagesEnabledTypes.get(pkg);
- if (enabled != null) {
- // Convert Set to int[] for return.
- int[] returnEnabled = new int[enabled.size()];
- int i = 0;
- for (int val: enabled) {
- returnEnabled[i] = val;
- i++;
- }
- return returnEnabled;
- }
- }
- // If package is not in the map, or the value is null, return the default.
- return getAllowedAdjustmentKeyTypes();
+ return mClassificationTypeDeniedPackages.toArray(new String[0]);
}
}
- return new int[]{};
+ return new String[]{};
}
/**
* Set whether a particular package can have its notification channels adjusted to have a
* different type by NotificationAssistants.
- * Note: once this method is called to enable or disable a specific type for a package,
- * the global default is set as the starting point, and the type is enabled/disabled from
- * there. Future changes to the global default will not apply automatically to this package.
*/
@FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- public void setAssistantAdjustmentKeyTypeStateForPackage(String pkg,
- @Adjustment.Types int type,
- boolean enabled) {
+ public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) {
if (!notificationClassificationUi()) {
return;
}
synchronized (mLock) {
- Set<Integer> enabledTypes = null;
- if (mClassificationTypePackagesEnabledTypes.containsKey(pkg)) {
- enabledTypes = mClassificationTypePackagesEnabledTypes.get(pkg);
- }
- if (enabledTypes == null) {
- // Use global default to start.
- enabledTypes = new ArraySet<Integer>();
- // Convert from int[] to Set<Integer>
- for (int value : getAllowedAdjustmentKeyTypes()) {
- enabledTypes.add(value);
- }
- }
-
if (enabled) {
- enabledTypes.add(type);
+ mClassificationTypeDeniedPackages.remove(pkg);
} else {
- enabledTypes.remove(type);
+ mClassificationTypeDeniedPackages.add(pkg);
}
- mClassificationTypePackagesEnabledTypes.put(pkg, enabledTypes);
}
}
@@ -12677,25 +12592,16 @@ public class NotificationManagerService extends SystemService {
TextUtils.join(",", mAllowedAdjustmentKeyTypes));
out.endTag(null, ATT_ENABLED_TYPES);
if (notificationClassificationUi()) {
- out.startTag(null, TAG_TYPES_ENABLED_FOR_APPS);
- for (String pkg: mClassificationTypePackagesEnabledTypes.keySet()) {
- Set<Integer> allowedTypes =
- mClassificationTypePackagesEnabledTypes.get(pkg);
- if (allowedTypes != null) {
- out.startTag(null, ATT_APP_ENABLED_TYPES);
- out.attribute(null, ATT_PACKAGE, pkg);
- out.attribute(null, ATT_TYPES, TextUtils.join(",", allowedTypes));
- out.endTag(null, ATT_APP_ENABLED_TYPES);
- }
- }
- out.endTag(null, TAG_TYPES_ENABLED_FOR_APPS);
+ out.startTag(null, ATT_TYPES_DENIED_APPS);
+ out.attribute(null, ATT_TYPES,
+ TextUtils.join(",", mClassificationTypeDeniedPackages));
+ out.endTag(null, ATT_TYPES_DENIED_APPS);
}
}
}
@Override
- protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException,
- XmlPullParserException {
+ protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
if (!notificationClassification()) {
return;
}
@@ -12722,25 +12628,12 @@ public class NotificationManagerService extends SystemService {
}
}
}
- } else if (TAG_TYPES_ENABLED_FOR_APPS.equals(tag)) {
- final int appsOuterDepth = parser.getDepth();
+ } else if (notificationClassificationUi() && ATT_TYPES_DENIED_APPS.equals(tag)) {
+ final String apps = XmlUtils.readStringAttribute(parser, ATT_TYPES);
synchronized (mLock) {
- mClassificationTypePackagesEnabledTypes.clear();
- while (XmlUtils.nextElementWithin(parser, appsOuterDepth)) {
- if (!ATT_APP_ENABLED_TYPES.equals(parser.getName())) {
- continue;
- }
- final String app = XmlUtils.readStringAttribute(parser, ATT_PACKAGE);
- Set<Integer> allowedTypes = new ArraySet<>();
- final String typesString = XmlUtils.readStringAttribute(parser, ATT_TYPES);
- if (!TextUtils.isEmpty(typesString)) {
- allowedTypes = Arrays.stream(typesString.split(","))
- .map(Integer::valueOf)
- .collect(Collectors.toSet());
- }
- // Empty type list is allowed, because empty type list signifies the user
- // has manually cleared the package of allowed types.
- mClassificationTypePackagesEnabledTypes.put(app, allowedTypes);
+ mClassificationTypeDeniedPackages.clear();
+ if (!TextUtils.isEmpty(apps)) {
+ mClassificationTypeDeniedPackages.addAll(Arrays.asList(apps.split(",")));
}
}
}
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 1aa5ac046ae9..7e853d9d2d0b 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import android.annotation.Nullable;
import android.app.NotificationManager;
import android.content.ComponentName;
import android.media.AudioManager;
@@ -26,6 +27,7 @@ import android.provider.Settings.Global;
import android.service.notification.IConditionProvider;
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ConfigOrigin;
import android.service.notification.ZenModeDiff;
import android.util.LocalLog;
@@ -119,16 +121,17 @@ public class ZenLog {
append(TYPE_UNSUBSCRIBE, uri + "," + subscribeResult(provider, e));
}
- public static void traceConfig(String reason, ComponentName triggeringComponent,
- ZenModeConfig oldConfig, ZenModeConfig newConfig, int callingUid) {
+ public static void traceConfig(@ConfigOrigin int origin, String reason,
+ @Nullable ComponentName triggeringComponent, ZenModeConfig oldConfig,
+ ZenModeConfig newConfig, int callingUid) {
ZenModeDiff.ConfigDiff diff = new ZenModeDiff.ConfigDiff(oldConfig, newConfig);
- if (diff == null || !diff.hasDiff()) {
- append(TYPE_CONFIG, reason + " no changes");
+ if (!diff.hasDiff()) {
+ append(TYPE_CONFIG, reason + " (" + originToString(origin) + ") no changes");
} else {
- append(TYPE_CONFIG, reason
- + " - " + triggeringComponent + " : " + callingUid
- + ",\n" + (newConfig != null ? newConfig.toString() : null)
- + ",\n" + diff);
+ append(TYPE_CONFIG, reason + " (" + originToString(origin) + ") from uid " + callingUid
+ + (triggeringComponent != null ? " - " + triggeringComponent : "") + ",\n"
+ + (newConfig != null ? newConfig.toString() : null) + ",\n"
+ + diff);
}
}
@@ -241,7 +244,22 @@ public class ZenLog {
}
}
- private static String componentToString(ComponentName component) {
+ private static String originToString(@ConfigOrigin int origin) {
+ return switch (origin) {
+ case ZenModeConfig.ORIGIN_UNKNOWN -> "ORIGIN_UNKNOWN";
+ case ZenModeConfig.ORIGIN_INIT -> "ORIGIN_INIT";
+ case ZenModeConfig.ORIGIN_INIT_USER -> "ORIGIN_INIT_USER";
+ case ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI -> "ORIGIN_USER_IN_SYSTEMUI";
+ case ZenModeConfig.ORIGIN_APP -> "ORIGIN_APP";
+ case ZenModeConfig.ORIGIN_SYSTEM -> "ORIGIN_SYSTEM";
+ case ZenModeConfig.ORIGIN_RESTORE_BACKUP -> "ORIGIN_RESTORE_BACKUP";
+ case ZenModeConfig.ORIGIN_USER_IN_APP -> "ORIGIN_USER_IN_APP";
+ default -> origin + "??";
+ };
+ }
+
+ @Nullable
+ private static String componentToString(@Nullable ComponentName component) {
return component != null ? component.toShortString() : null;
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 0a63f3fb36d0..f089c3acdd70 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -659,7 +659,8 @@ public class ZenModeHelper {
mContext.getString(R.string.zen_mode_implicit_deactivated),
STATE_FALSE);
setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
- deactivated, ORIGIN_APP, callingUid);
+ deactivated, ORIGIN_APP,
+ "applyGlobalZenModeAsImplicitZenRule: " + callingPkg, callingUid);
}
} else {
// Either create a new rule with a default ZenPolicy, or update an existing rule's
@@ -971,26 +972,27 @@ public class ZenModeHelper {
if (Flags.modesApi()) {
if (rule != null && canManageAutomaticZenRule(rule, callingUid)) {
setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
- condition, origin, callingUid);
+ condition, origin, "setAzrState: " + rule.id, callingUid);
}
} else {
ArrayList<ZenRule> rules = new ArrayList<>();
rules.add(rule); // rule may be null and throw NPE in the next method.
- setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin, callingUid);
+ setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin,
+ "setAzrState: " + (rule != null ? rule.id : "null!"), callingUid);
}
}
}
- void setAutomaticZenRuleStateFromConditionProvider(UserHandle user, Uri ruleDefinition,
+ void setAutomaticZenRuleStateFromConditionProvider(UserHandle user, Uri ruleConditionId,
Condition condition, @ConfigOrigin int origin, int callingUid) {
- checkSetRuleStateOrigin("setAutomaticZenRuleState(Uri ruleDefinition)", origin);
+ checkSetRuleStateOrigin("setAutomaticZenRuleStateFromConditionProvider", origin);
ZenModeConfig newConfig;
synchronized (mConfigLock) {
ZenModeConfig config = getConfigLocked(user);
if (config == null) return;
newConfig = config.copy();
- List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleDefinition, condition);
+ List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleConditionId, condition);
if (Flags.modesApi()) {
for (int i = matchingRules.size() - 1; i >= 0; i--) {
if (!canManageAutomaticZenRule(matchingRules.get(i), callingUid)) {
@@ -998,13 +1000,14 @@ public class ZenModeHelper {
}
}
}
- setAutomaticZenRuleStateLocked(newConfig, matchingRules, condition, origin, callingUid);
+ setAutomaticZenRuleStateLocked(newConfig, matchingRules, condition, origin,
+ "setAzrStateFromCps: " + ruleConditionId, callingUid);
}
}
@GuardedBy("mConfigLock")
private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
- Condition condition, @ConfigOrigin int origin, int callingUid) {
+ Condition condition, @ConfigOrigin int origin, String reason, int callingUid) {
if (rules == null || rules.isEmpty()) return;
if (!Flags.modesUi()) {
@@ -1015,7 +1018,7 @@ public class ZenModeHelper {
for (ZenRule rule : rules) {
applyConditionAndReconsiderOverride(rule, condition, origin);
- setConfigLocked(config, rule.component, origin, "conditionChanged", callingUid);
+ setConfigLocked(config, rule.component, origin, reason, callingUid);
}
}
@@ -2111,13 +2114,14 @@ public class ZenModeHelper {
}
@GuardedBy("mConfigLock")
- private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
- @ConfigOrigin int origin, String reason, int callingUid) {
+ private boolean setConfigLocked(ZenModeConfig config,
+ @Nullable ComponentName triggeringComponent, @ConfigOrigin int origin, String reason,
+ int callingUid) {
return setConfigLocked(config, origin, reason, triggeringComponent, true /*setRingerMode*/,
callingUid);
}
- void setConfig(ZenModeConfig config, ComponentName triggeringComponent,
+ void setConfig(ZenModeConfig config, @Nullable ComponentName triggeringComponent,
@ConfigOrigin int origin, String reason, int callingUid) {
synchronized (mConfigLock) {
setConfigLocked(config, triggeringComponent, origin, reason, callingUid);
@@ -2126,7 +2130,7 @@ public class ZenModeHelper {
@GuardedBy("mConfigLock")
private boolean setConfigLocked(ZenModeConfig config, @ConfigOrigin int origin,
- String reason, ComponentName triggeringComponent, boolean setRingerMode,
+ String reason, @Nullable ComponentName triggeringComponent, boolean setRingerMode,
int callingUid) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -2149,7 +2153,7 @@ public class ZenModeHelper {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
- ZenLog.traceConfig(reason, triggeringComponent, mConfig, config, callingUid);
+ ZenLog.traceConfig(origin, reason, triggeringComponent, mConfig, config, callingUid);
// send some broadcasts
Policy newPolicy = getNotificationPolicy(config);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f60e086e7c5d..61429a41370c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4255,8 +4255,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
CarrierAppUtils.disableCarrierAppsUntilPrivileged(
mContext.getOpPackageName(), UserHandle.USER_SYSTEM, mContext);
- disableSkuSpecificApps();
-
// Read the compatibilty setting when the system is ready.
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
@@ -4390,29 +4388,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
}
- //TODO: b/111402650
- private void disableSkuSpecificApps() {
- String[] apkList = mContext.getResources().getStringArray(
- R.array.config_disableApksUnlessMatchedSku_apk_list);
- String[] skuArray = mContext.getResources().getStringArray(
- R.array.config_disableApkUnlessMatchedSku_skus_list);
- if (ArrayUtils.isEmpty(apkList)) {
- return;
- }
- String sku = SystemProperties.get("ro.boot.hardware.sku");
- if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) {
- return;
- }
- final Computer snapshot = snapshotComputer();
- for (String packageName : apkList) {
- setSystemAppHiddenUntilInstalled(snapshot, packageName, true);
- final List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(false);
- for (int i = 0; i < users.size(); i++) {
- setSystemAppInstallState(snapshot, packageName, false, users.get(i).id);
- }
- }
- }
-
public PackageFreezer freezePackage(String packageName, int userId, String killReason,
int exitInfoReason, InstallRequest request) {
return freezePackage(packageName, userId, killReason, exitInfoReason, request,
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index deaa8d8feae1..44d787f790cf 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -356,7 +356,7 @@ public final class PermissionPolicyService extends SystemService {
try {
manager = new PermissionControllerManager(
getUserContext(getContext(), user), PermissionThread.getHandler());
- } catch (IllegalArgumentException exception) {
+ } catch (IllegalStateException exception) {
// There's a possible race condition when a user is being removed
Log.e(LOG_TAG, "Could not create PermissionControllerManager for user"
+ user, exception);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index f9e4022f04a0..090707db50a5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -30,6 +30,7 @@ import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
import static android.os.PowerManagerInternal.isInteractive;
import static android.os.PowerManagerInternal.wakefulnessToString;
+import static android.service.dreams.Flags.allowDreamWhenPostured;
import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN;
import static com.android.server.deviceidle.Flags.disableWakelocksInLightIdle;
@@ -216,6 +217,8 @@ public final class PowerManagerService extends SystemService
private static final int DIRTY_ATTENTIVE = 1 << 14;
// Dirty bit: display group wakefulness has changed
private static final int DIRTY_DISPLAY_GROUP_WAKEFULNESS = 1 << 16;
+ // Dirty bit: device postured state has changed
+ private static final int DIRTY_POSTURED_STATE = 1 << 17;
// Summarizes the state of all active wakelocks.
static final int WAKE_LOCK_CPU = 1 << 0;
@@ -500,6 +503,11 @@ public final class PowerManagerService extends SystemService
// The current dock state.
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ /**
+ * Whether the device is upright and stationary.
+ */
+ private boolean mDevicePostured;
+
// True to decouple auto-suspend mode from the display state.
private boolean mDecoupleHalAutoSuspendModeFromDisplayConfig;
@@ -530,6 +538,9 @@ public final class PowerManagerService extends SystemService
// Default value for dreams activate-on-dock
private boolean mDreamsActivatedOnDockByDefaultConfig;
+ /** Default value for whether dreams are activated when postured (stationary + upright) */
+ private boolean mDreamsActivatedWhilePosturedByDefaultConfig;
+
// True if dreams can run while not plugged in.
private boolean mDreamsEnabledOnBatteryConfig;
@@ -558,6 +569,9 @@ public final class PowerManagerService extends SystemService
// True if dreams should be activated on dock.
private boolean mDreamsActivateOnDockSetting;
+ /** Whether dreams should be activated when device is postured (stationary and upright) */
+ private boolean mDreamsActivateWhilePosturedSetting;
+
// True if doze should not be started until after the screen off transition.
private boolean mDozeAfterScreenOff;
@@ -1471,6 +1485,9 @@ public final class PowerManagerService extends SystemService
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK),
false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED),
+ false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_OFF_TIMEOUT),
false, mSettingsObserver, UserHandle.USER_ALL);
@@ -1549,6 +1566,8 @@ public final class PowerManagerService extends SystemService
com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
mDreamsActivatedOnDockByDefaultConfig = resources.getBoolean(
com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
+ mDreamsActivatedWhilePosturedByDefaultConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault);
mDreamsEnabledOnBatteryConfig = resources.getBoolean(
com.android.internal.R.bool.config_dreamsEnabledOnBattery);
mDreamsBatteryLevelMinimumWhenPoweredConfig = resources.getInteger(
@@ -1589,6 +1608,10 @@ public final class PowerManagerService extends SystemService
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
mDreamsActivatedOnDockByDefaultConfig ? 1 : 0,
UserHandle.USER_CURRENT) != 0);
+ mDreamsActivateWhilePosturedSetting = (Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ mDreamsActivatedWhilePosturedByDefaultConfig ? 1 : 0,
+ UserHandle.USER_CURRENT) != 0);
mScreenOffTimeoutSetting = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT,
UserHandle.USER_CURRENT);
@@ -3336,7 +3359,7 @@ public final class PowerManagerService extends SystemService
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
| DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
| DIRTY_DOCK_STATE | DIRTY_ATTENTIVE | DIRTY_SETTINGS
- | DIRTY_SCREEN_BRIGHTNESS_BOOST)) == 0) {
+ | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_POSTURED_STATE)) == 0) {
return changed;
}
final long time = mClock.uptimeMillis();
@@ -3375,7 +3398,8 @@ public final class PowerManagerService extends SystemService
private boolean shouldNapAtBedTimeLocked() {
return mDreamsActivateOnSleepSetting
|| (mDreamsActivateOnDockSetting
- && mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ && mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED)
+ || (mDreamsActivateWhilePosturedSetting && mDevicePostured);
}
/**
@@ -4489,6 +4513,17 @@ public final class PowerManagerService extends SystemService
}
}
+ private void setDevicePosturedInternal(boolean isPostured) {
+ if (!allowDreamWhenPostured()) {
+ return;
+ }
+ synchronized (mLock) {
+ mDevicePostured = isPostured;
+ mDirty |= DIRTY_POSTURED_STATE;
+ updatePowerStateLocked();
+ }
+ }
+
private void setUserActivityTimeoutOverrideFromWindowManagerInternal(long timeoutMillis) {
synchronized (mLock) {
if (mUserActivityTimeoutOverrideFromWindowManager != timeoutMillis) {
@@ -4794,6 +4829,8 @@ public final class PowerManagerService extends SystemService
+ mDreamsActivatedOnSleepByDefaultConfig);
pw.println(" mDreamsActivatedOnDockByDefaultConfig="
+ mDreamsActivatedOnDockByDefaultConfig);
+ pw.println(" mDreamsActivatedWhilePosturedByDefaultConfig="
+ + mDreamsActivatedWhilePosturedByDefaultConfig);
pw.println(" mDreamsEnabledOnBatteryConfig="
+ mDreamsEnabledOnBatteryConfig);
pw.println(" mDreamsBatteryLevelMinimumWhenPoweredConfig="
@@ -4805,6 +4842,8 @@ public final class PowerManagerService extends SystemService
pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting);
pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
+ pw.println(" mDreamsActivateWhilePosturedSetting="
+ + mDreamsActivateWhilePosturedSetting);
pw.println(" mDozeAfterScreenOff=" + mDozeAfterScreenOff);
pw.println(" mBrightWhenDozingConfig=" + mBrightWhenDozingConfig);
pw.println(" mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig);
@@ -7388,6 +7427,11 @@ public final class PowerManagerService extends SystemService
public boolean isAmbientDisplaySuppressed() {
return mAmbientDisplaySuppressionController.isSuppressed();
}
+
+ @Override
+ public void setDevicePostured(boolean isPostured) {
+ setDevicePosturedInternal(isPostured);
+ }
}
/**
diff --git a/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java b/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java
index 6798a6146ae0..2452dc59bea5 100644
--- a/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java
+++ b/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java
@@ -17,6 +17,7 @@
package com.android.server.security.authenticationpolicy;
import static android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE;
+import static android.security.Flags.disableAdaptiveAuthCounterLock;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
@@ -39,6 +40,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.provider.Settings;
import android.security.authenticationpolicy.AuthenticationPolicyManager;
import android.security.authenticationpolicy.DisableSecureLockDeviceParams;
import android.security.authenticationpolicy.EnableSecureLockDeviceParams;
@@ -251,6 +253,17 @@ public class AuthenticationPolicyService extends SystemService {
return;
}
+ if (disableAdaptiveAuthCounterLock() && Build.IS_DEBUGGABLE) {
+ final boolean disabled = Settings.Secure.getIntForUser(
+ getContext().getContentResolver(),
+ Settings.Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK,
+ 0 /* default */, userId) != 0;
+ if (disabled) {
+ Slog.d(TAG, "not locking (disabled by user)");
+ return;
+ }
+ }
+
//TODO: additionally consider the trust signal before locking device
lockDevice(userId);
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 40ea9319c6be..7f2c68ff60b1 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -118,6 +118,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.health.connect.HealthConnectManager;
import android.media.AudioManager;
import android.media.MediaDrm;
import android.media.UnsupportedSchemeException;
@@ -4115,7 +4116,7 @@ public class StatsPullAtomService extends SystemService {
int nOps = opsList.size();
for (int i = 0; i < nOps; i++) {
AppOpEntry entry = opsList.get(i);
- if (entry.mHash >= samplingRate) {
+ if (entry.mHash >= samplingRate || isHealthAppOp(entry.mOp.getOpCode())) {
continue;
}
StatsEvent e;
@@ -4301,6 +4302,11 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
+ if (isHealthAppOp(AppOpsManager.strOpToOp(message.getOp()))) {
+ // Not log sensitive health app ops.
+ return StatsManager.PULL_SKIP;
+ }
+
pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, message.getUid(),
message.getPackageName(), "",
message.getAttributionTag() == null ? "" : message.getAttributionTag(),
@@ -4893,7 +4899,7 @@ public class StatsPullAtomService extends SystemService {
Slog.e(TAG, "Disconnected from keystore service. Cannot pull.", e);
return StatsManager.PULL_SKIP;
} catch (ServiceSpecificException e) {
- Slog.e(TAG, "pulling keystore metrics failed", e);
+ Slog.e(TAG, "Pulling keystore atom with tag " + atomTag + " failed", e);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(callingToken);
@@ -5370,6 +5376,11 @@ public class StatsPullAtomService extends SystemService {
}
}
+ private boolean isHealthAppOp(int opCode) {
+ String permission = AppOpsManager.opToPermission(opCode);
+ return permission != null && HealthConnectManager.isHealthPermission(mContext, permission);
+ }
+
// Thermal event received from vendor thermal management subsystem
private static final class ThermalEventListener extends IThermalEventListener.Stub {
@Override
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index a94f6252cd68..cc9cd90fae06 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -39,7 +39,7 @@ class AppCompatController {
@NonNull
private final AppCompatOverrides mAppCompatOverrides;
@NonNull
- private final AppCompatDeviceStateQuery mAppCompatDeviceStateQuery;
+ private final AppCompatDeviceStateQuery mDeviceStateQuery;
@NonNull
private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
@NonNull
@@ -50,11 +50,11 @@ class AppCompatController {
final PackageManager packageManager = wmService.mContext.getPackageManager();
final OptPropFactory optPropBuilder = new OptPropFactory(packageManager,
activityRecord.packageName);
- mAppCompatDeviceStateQuery = new AppCompatDeviceStateQuery(activityRecord);
+ mDeviceStateQuery = new AppCompatDeviceStateQuery(activityRecord);
mTransparentPolicy = new TransparentPolicy(activityRecord,
wmService.mAppCompatConfiguration);
mAppCompatOverrides = new AppCompatOverrides(activityRecord, packageManager,
- wmService.mAppCompatConfiguration, optPropBuilder, mAppCompatDeviceStateQuery);
+ wmService.mAppCompatConfiguration, optPropBuilder, mDeviceStateQuery);
mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, mAppCompatOverrides);
mAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
mTransparentPolicy, mAppCompatOverrides);
@@ -129,8 +129,8 @@ class AppCompatController {
}
@NonNull
- AppCompatDeviceStateQuery getAppCompatDeviceStateQuery() {
- return mAppCompatDeviceStateQuery;
+ AppCompatDeviceStateQuery getDeviceStateQuery() {
+ return mDeviceStateQuery;
}
@NonNull
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
index 087edc184b6f..a7c52bd1fc38 100644
--- a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
@@ -107,7 +107,7 @@ class AppCompatReachabilityPolicy {
return;
}
final AppCompatDeviceStateQuery deviceStateQuery = mActivityRecord.mAppCompatController
- .getAppCompatDeviceStateQuery();
+ .getDeviceStateQuery();
final boolean isInFullScreenBookMode = deviceStateQuery
.isDisplayFullScreenAndSeparatingHinge()
&& mAppCompatConfiguration.getIsAutomaticReachabilityInBookModeEnabled();
@@ -153,7 +153,7 @@ class AppCompatReachabilityPolicy {
return;
}
final AppCompatDeviceStateQuery deviceStateQuery = mActivityRecord.mAppCompatController
- .getAppCompatDeviceStateQuery();
+ .getDeviceStateQuery();
final boolean isInFullScreenTabletopMode = deviceStateQuery
.isDisplayFullScreenAndSeparatingHinge();
final int letterboxPositionForVerticalReachability = mAppCompatConfiguration
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index 601b17c46c03..576e5d5d0cd2 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -51,6 +51,7 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
+import android.view.ContextThemeWrapper;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
@@ -498,10 +499,21 @@ class AppWarnings {
}
}
if (!hasPackageFlag(userId, ar.packageName, FLAG_HIDE_PAGE_SIZE_MISMATCH)) {
+ Context context = getUiContextForActivity(ar);
+ // PageSizeMismatchDialog has link in message which should open in browser.
+ // Starting activity from non-activity context is not allowed and flag
+ // FLAG_ACTIVITY_NEW_TASK is needed to start activity.
+ context = new ContextThemeWrapper(context, context.getThemeResId()) {
+ @Override
+ public void startActivity(Intent intent) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ super.startActivity(intent);
+ }
+ };
pageSizeMismatchDialog =
new PageSizeMismatchDialog(
AppWarnings.this,
- getUiContextForActivity(ar),
+ context,
ar.info.applicationInfo,
userId,
warning);
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 5bec4424269a..4b30a43db5d9 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -53,6 +53,12 @@ import java.util.List;
* Policy that manages {@link DisplayArea}.
*/
public abstract class DisplayAreaPolicy {
+ /**
+ * No corresponding use case yet (see b/154719717). The current implementation still uses
+ * {@link WindowState#shouldMagnify}.
+ */
+ static final boolean USE_DISPLAY_AREA_FOR_FULLSCREEN_MAGNIFICATION = false;
+
protected final WindowManagerService mWmService;
/**
@@ -161,14 +167,17 @@ public abstract class DisplayAreaPolicy {
TYPE_STATUS_BAR, TYPE_NOTIFICATION_SHADE, TYPE_WALLPAPER)
.build());
}
+ if (USE_DISPLAY_AREA_FOR_FULLSCREEN_MAGNIFICATION) {
+ rootHierarchy.addFeature(
+ new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",
+ FEATURE_FULLSCREEN_MAGNIFICATION)
+ .all()
+ .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD,
+ TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY,
+ TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
+ .build());
+ }
rootHierarchy
- .addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",
- FEATURE_FULLSCREEN_MAGNIFICATION)
- .all()
- .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD,
- TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY,
- TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
- .build())
.addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder",
FEATURE_IME_PLACEHOLDER)
.and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index fa748d3a22a5..c418349e6158 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -28,9 +28,11 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.content.ClipData;
import android.content.Context;
+import android.hardware.display.DisplayTopology;
import android.hardware.input.InputManagerGlobal;
import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -50,12 +52,14 @@ import android.window.IUnhandledDragCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
+import com.android.window.flags.Flags;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
/**
* Managing drag and drop operations initiated by View#startDragAndDrop.
@@ -83,6 +87,8 @@ class DragDropController {
private WindowManagerService mService;
private final Handler mHandler;
+ private final Consumer<DisplayTopology> mDisplayTopologyListener =
+ this::handleDisplayTopologyChange;
// The global drag listener for handling cross-window drags
private IGlobalDragListener mGlobalDragListener;
@@ -108,6 +114,10 @@ class DragDropController {
DragDropController(WindowManagerService service, Looper looper) {
mService = service;
mHandler = new DragHandler(service, looper);
+ if (Flags.enableConnectedDisplaysDnd()) {
+ mService.mDisplayManager.registerTopologyListener(
+ new HandlerExecutor(mService.mH), mDisplayTopologyListener);
+ }
}
@VisibleForTesting
@@ -481,6 +491,19 @@ class DragDropController {
}
}
+ @VisibleForTesting
+ void handleDisplayTopologyChange(DisplayTopology unused) {
+ synchronized (mService.mGlobalLock) {
+ if (mDragState == null) {
+ return;
+ }
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "DisplayTopology changed, cancelling DragAndDrop");
+ }
+ cancelDragAndDrop(mDragState.mToken, true /* skipAnimation */);
+ }
+ }
+
/**
* Handles motion events.
* @param keepHandling Whether if the drag operation is continuing or this is the last motion
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 02a7db19f405..0fbf56d120a8 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -77,8 +77,9 @@ class EmulatorDisplayOverlay {
mOverlay = context.getDrawable(
com.android.internal.R.drawable.emulator_circular_window_overlay);
- mBlastBufferQueue = new BLASTBufferQueue(TITLE, mSurfaceControl, mScreenSize.x,
- mScreenSize.y, PixelFormat.RGBA_8888);
+ mBlastBufferQueue = new BLASTBufferQueue(TITLE, /* updateDestinationFrame */ true);
+ mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y,
+ PixelFormat.RGBA_8888);
mSurface = mBlastBufferQueue.createSurface();
}
diff --git a/services/core/java/com/android/server/wm/PageSizeMismatchDialog.java b/services/core/java/com/android/server/wm/PageSizeMismatchDialog.java
index 29922f0f85c5..24235ef2d585 100644
--- a/services/core/java/com/android/server/wm/PageSizeMismatchDialog.java
+++ b/services/core/java/com/android/server/wm/PageSizeMismatchDialog.java
@@ -24,8 +24,10 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.text.Html;
+import android.text.method.LinkMovementMethod;
import android.view.Window;
import android.view.WindowManager;
+import android.widget.TextView;
import com.android.internal.R;
@@ -69,6 +71,14 @@ class PageSizeMismatchDialog extends AppWarnings.BaseDialog {
mDialog.create();
final Window window = mDialog.getWindow();
- window.setType(WindowManager.LayoutParams.TYPE_PHONE);
+ window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ // Make the links in dialog clickable
+ final TextView msgTxt = (TextView) mDialog.findViewById(android.R.id.message);
+ msgTxt.setMovementMethod(LinkMovementMethod.getInstance());
}
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 44f000da3d73..b9550feeab8a 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -40,7 +40,6 @@ import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TASKS;
-import static com.android.launcher3.Flags.enableUseTopVisibleActivityForExcludeFromRecentTask;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
@@ -1528,12 +1527,7 @@ class RecentTasks {
}
// The Recents is only supported on default display now, we should only keep the
// most recent task of home display.
- boolean isMostRecentTask;
- if (enableUseTopVisibleActivityForExcludeFromRecentTask()) {
- isMostRecentTask = task.getTopVisibleActivity() != null;
- } else {
- isMostRecentTask = taskIndex == 0;
- }
+ boolean isMostRecentTask = task.getTopVisibleActivity() != null;
return (task.isOnHomeDisplay() && isMostRecentTask);
}
}
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index cdf6b08b1c57..b6365ad47535 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -63,8 +63,9 @@ class StrictModeFlash {
mSurfaceControl = ctrl;
mDrawNeeded = true;
- mBlastBufferQueue = new BLASTBufferQueue(TITLE, mSurfaceControl, 1 /* width */,
- 1 /* height */, PixelFormat.RGBA_8888);
+ mBlastBufferQueue = new BLASTBufferQueue(TITLE, /* updateDestinationFrame */ true);
+ mBlastBufferQueue.update(mSurfaceControl, 1 /* width */, 1 /* height */,
+ PixelFormat.RGBA_8888);
mSurface = mBlastBufferQueue.createSurface();
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 295759c2fc7e..7a88338d8ac5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5297,40 +5297,29 @@ class Task extends TaskFragment {
return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea());
}
- void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTask,
- boolean isTaskSwitch, ActivityOptions options, @Nullable ActivityRecord sourceRecord) {
- Task rTask = r.getTask();
+ void startActivityLocked(@NonNull ActivityRecord r, @Nullable Task topTask, boolean newTask,
+ boolean isTaskSwitch, @Nullable ActivityOptions options,
+ @Nullable ActivityRecord sourceRecord) {
final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
- final boolean isOrhasTask = rTask == this || hasChild(rTask);
+ final Task activityTask = r.getTask();
+ final boolean isThisOrHasChildTask = activityTask == this || hasChild(activityTask);
+
// mLaunchTaskBehind tasks get placed at the back of the task stack.
- if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) {
+ if (!r.mLaunchTaskBehind && allowMoveToFront && (!isThisOrHasChildTask || newTask)) {
// Last activity in task had been removed or ActivityManagerService is reusing task.
// Insert or replace.
// Might not even be in.
- positionChildAtTop(rTask);
+ positionChildAtTop(activityTask);
}
- Task task = null;
- if (!newTask && isOrhasTask && !r.shouldBeVisible()) {
+
+ if (!newTask && isThisOrHasChildTask && !r.shouldBeVisible()) {
ActivityOptions.abort(options);
return;
}
- // Place a new activity at top of root task, so it is next to interact with the user.
-
- // If we are not placing the new activity frontmost, we do not want to deliver the
- // onUserLeaving callback to the actual frontmost activity
- final Task activityTask = r.getTask();
- if (task == activityTask && mChildren.indexOf(task) != (getChildCount() - 1)) {
- mTaskSupervisor.mUserLeaving = false;
- if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
- "startActivity() behind front, mUserLeaving=false");
- }
-
- task = activityTask;
-
// Slot the activity into the history root task and proceed
- ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "
- + "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());
+ ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s callers: %s", r,
+ activityTask, new RuntimeException("here").fillInStackTrace());
if (isActivityTypeHomeOrRecents() && getActivityBelow(r) == null) {
// If this is the first activity, don't do any fancy animations,
@@ -5346,15 +5335,15 @@ class Task extends TaskFragment {
return;
}
- final DisplayContent dc = mDisplayContent;
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare open transition: starting " + r);
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: starting " + r);
+
+ // Place a new activity at top of root task, so it is next to interact with the user.
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- dc.prepareAppTransition(TRANSIT_NONE);
+ mDisplayContent.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(r);
mTransitionController.setNoAnimation(r);
} else {
- dc.prepareAppTransition(TRANSIT_OPEN);
+ mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
mTaskSupervisor.mNoAnimActivities.remove(r);
}
if (newTask && !r.mLaunchTaskBehind) {
@@ -5405,8 +5394,7 @@ class Task extends TaskFragment {
// "has the same starting icon" as the next one. This allows the
// window manager to keep the previous window it had previously
// created, if it still had one.
- Task baseTask = r.getTask();
- final ActivityRecord prev = baseTask.getActivity(
+ final ActivityRecord prev = activityTask.getActivity(
a -> a.mStartingData != null && a.showToCurrentUser());
mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask,
isTaskSwitch, sourceRecord);
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 9780d3317e11..eb6eeb31e8fb 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -126,8 +126,9 @@ class Watermark {
} catch (OutOfResourcesException e) {
}
mSurfaceControl = ctrl;
- mBlastBufferQueue = new BLASTBufferQueue(TITLE, mSurfaceControl, 1 /* width */,
- 1 /* height */, PixelFormat.RGBA_8888);
+ mBlastBufferQueue = new BLASTBufferQueue(TITLE, /* updateDestinationFrame */ true);
+ mBlastBufferQueue.update(mSurfaceControl, 1 /* width */, 1 /* height */,
+ PixelFormat.RGBA_8888);
mSurface = mBlastBufferQueue.createSurface();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 36d52ddb40e6..1754d7346220 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -753,8 +753,6 @@ public class WindowManagerService extends IWindowManager.Stub
final static int WINDOWS_FREEZING_SCREENS_TIMEOUT = 2;
int mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
- /** Indicates that the system server is actively demanding the screen be frozen. */
- boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
@VisibleForTesting
@@ -3354,60 +3352,6 @@ public class WindowManagerService extends IWindowManager.Stub
return getDefaultDisplayContentLocked().mAppTransition.isIdle();
}
-
- // -------------------------------------------------------------
- // Misc IWindowSession methods
- // -------------------------------------------------------------
-
- /** Freeze the screen during a user-switch event. Called by UserController. */
- @Override
- public void startFreezingScreen(int exitAnim, int enterAnim) {
- if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN,
- "startFreezingScreen()")) {
- throw new SecurityException("Requires FREEZE_SCREEN permission");
- }
-
- synchronized (mGlobalLock) {
- if (!mClientFreezingScreen) {
- mClientFreezingScreen = true;
- final long origId = Binder.clearCallingIdentity();
- try {
- startFreezingDisplay(exitAnim, enterAnim);
- mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
- mH.sendEmptyMessageDelayed(H.CLIENT_FREEZE_TIMEOUT, 5000);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
- }
-
- /**
- * No longer actively demand that the screen remain frozen.
- * Called by UserController after a user-switch.
- * This doesn't necessarily immediately unlock the screen; it just allows it if we're ready.
- */
- @Override
- public void stopFreezingScreen() {
- if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN,
- "stopFreezingScreen()")) {
- throw new SecurityException("Requires FREEZE_SCREEN permission");
- }
-
- synchronized (mGlobalLock) {
- if (mClientFreezingScreen) {
- mClientFreezingScreen = false;
- mLastFinishedFreezeSource = "client";
- final long origId = Binder.clearCallingIdentity();
- try {
- stopFreezingDisplayLocked();
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
- }
-
@Override
public void disableKeyguard(IBinder token, String tag, int userId) {
userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
@@ -5669,7 +5613,6 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
public static final int SHOW_STRICT_MODE_VIOLATION = 25;
- public static final int CLIENT_FREEZE_TIMEOUT = 30;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
public static final int NEW_ANIMATOR_SCALE = 34;
@@ -5759,17 +5702,6 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- case CLIENT_FREEZE_TIMEOUT: {
- synchronized (mGlobalLock) {
- if (mClientFreezingScreen) {
- mClientFreezingScreen = false;
- mLastFinishedFreezeSource = "client-timeout";
- stopFreezingDisplayLocked();
- }
- }
- break;
- }
-
case REPORT_WINDOWS_CHANGE: {
if (mWindowsChanged) {
synchronized (mGlobalLock) {
@@ -6552,14 +6484,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (waitingForConfig || waitingForRemoteDisplayChange || mAppsFreezingScreen > 0
|| mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
- || mClientFreezingScreen || numOpeningApps > 0) {
+ || numOpeningApps > 0) {
ProtoLog.d(WM_DEBUG_ORIENTATION, "stopFreezingDisplayLocked: Returning "
+ "waitingForConfig=%b, waitingForRemoteDisplayChange=%b, "
+ "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, "
- + "mClientFreezingScreen=%b, mOpeningApps.size()=%d",
+ + "mOpeningApps.size()=%d",
waitingForConfig, waitingForRemoteDisplayChange,
mAppsFreezingScreen, mWindowsFreezingScreen,
- mClientFreezingScreen, numOpeningApps);
+ numOpeningApps);
return;
}
@@ -6589,7 +6521,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
ProtoLog.i(WM_ERROR, "%s", sb.toString());
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
- mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
if (PROFILE_ORIENTATION) {
Debug.stopMethodTracing();
}
@@ -7096,7 +7027,6 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mTransactionSequence="); pw.println(mTransactionSequence);
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
pw.print(" windows="); pw.print(mWindowsFreezingScreen);
- pw.print(" client="); pw.print(mClientFreezingScreen);
pw.print(" apps="); pw.println(mAppsFreezingScreen);
final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
pw.print(" mRotation="); pw.println(defaultDisplayContent.getRotation());
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index c0bc8e094a39..2aa0c6b6dd0b 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -258,12 +258,33 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
if (propagateCancellation) {
mProviders.values().forEach(ProviderSession::cancelProviderRemoteSession);
}
- mRequestSessionMetric.logApiCalledAtFinish(apiStatus);
mRequestSessionStatus = RequestSessionStatus.COMPLETE;
+ if (Flags.fixMetricDuplicationEmits()) {
+ logTrackOneCandidatesAndPrepareFinalPhaseLogs(apiStatus);
+ }
+ mRequestSessionMetric.logApiCalledAtFinish(apiStatus);
mProviders.clear();
clearRequestSessionLocked();
}
+ /**
+ * Ensures all logging done in final phase methods only occur within the 'finishSession'.
+ */
+ private void logTrackOneCandidatesAndPrepareFinalPhaseLogs(int apiStatus) {
+ mRequestSessionMetric.logCandidateAggregateMetrics(mProviders);
+ if (isRespondingWithError(apiStatus)) {
+ mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(
+ /*hasException=*/ true, ProviderStatusForMetrics.FINAL_FAILURE);
+ } else if (isRespondingWithUserCanceledError(apiStatus)) {
+ mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(
+ /*hasException=*/false, ProviderStatusForMetrics.FINAL_FAILURE
+ );
+ } else if (isRespondingWithSuccess(apiStatus)) {
+ mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(/*hasException=*/ false,
+ ProviderStatusForMetrics.FINAL_SUCCESS);
+ }
+ }
+
void cancelExistingPendingIntent() {
if (mPendingIntent != null) {
try {
@@ -343,9 +364,11 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
* @param response the response associated with the API call that just completed
*/
protected void respondToClientWithResponseAndFinish(V response) {
- mRequestSessionMetric.logCandidateAggregateMetrics(mProviders);
- mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(/*has_exception=*/ false,
- ProviderStatusForMetrics.FINAL_SUCCESS);
+ if (!Flags.fixMetricDuplicationEmits()) {
+ mRequestSessionMetric.logCandidateAggregateMetrics(mProviders);
+ mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(/*hasException=*/ false,
+ ProviderStatusForMetrics.FINAL_SUCCESS);
+ }
if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
Slog.w(TAG, "Request has already been completed. This is strange.");
return;
@@ -360,8 +383,10 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
finishSession(/*propagateCancellation=*/false,
ApiStatus.SUCCESS.getMetricCode());
} catch (RemoteException e) {
- mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(
- /*has_exception=*/ true, ProviderStatusForMetrics.FINAL_FAILURE);
+ if (!Flags.fixMetricDuplicationEmits()) {
+ mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(
+ /*hasException=*/ true, ProviderStatusForMetrics.FINAL_FAILURE);
+ }
Slog.e(TAG, "Issue while responding to client with a response : " + e);
finishSession(/*propagateCancellation=*/false, ApiStatus.FAILURE.getMetricCode());
}
@@ -374,9 +399,11 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
* @param errorMsg the error message given back in the flow
*/
protected void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
- mRequestSessionMetric.logCandidateAggregateMetrics(mProviders);
- mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(
- /*has_exception=*/ true, ProviderStatusForMetrics.FINAL_FAILURE);
+ if (!Flags.fixMetricDuplicationEmits()) {
+ mRequestSessionMetric.logCandidateAggregateMetrics(mProviders);
+ mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(
+ /*hasException=*/ true, ProviderStatusForMetrics.FINAL_FAILURE);
+ }
if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
Slog.w(TAG, "Request has already been completed. This is strange.");
return;
@@ -385,7 +412,6 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
finishSession(/*propagateCancellation=*/true, ApiStatus.CLIENT_CANCELED.getMetricCode());
return;
}
-
try {
invokeClientCallbackError(errorType, errorMsg);
} catch (RemoteException e) {
@@ -393,7 +419,9 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
}
boolean isUserCanceled = errorType.contains(MetricUtilities.USER_CANCELED_SUBSTRING);
if (isUserCanceled) {
- mRequestSessionMetric.setHasExceptionFinalPhase(/* has_exception */ false);
+ if (!Flags.fixMetricDuplicationEmits()) {
+ mRequestSessionMetric.setHasExceptionFinalPhase(/* hasException */ false);
+ }
finishSession(/*propagateCancellation=*/false,
ApiStatus.USER_CANCELED.getMetricCode());
} else {
@@ -421,4 +449,26 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
finishSession(isUiWaitingForData(), ApiStatus.CLIENT_CANCELED.getMetricCode());
}
}
+
+ /**
+ * This captures the final state of the apiStatus as presented in 'finishSession'.
+ */
+ private boolean isRespondingWithError(int apiStatus) {
+ return apiStatus == ApiStatus.FAILURE.getMetricCode()
+ || apiStatus == ApiStatus.CLIENT_CANCELED.getMetricCode();
+ }
+
+ /**
+ * A unique failure case, where we do not set the exception bit to be true.
+ */
+ private boolean isRespondingWithUserCanceledError(int apiStatus) {
+ return apiStatus == ApiStatus.USER_CANCELED.getMetricCode();
+ }
+
+ /**
+ * This captures the final state of the apiStatus as presented in 'finishSession'.
+ */
+ private boolean isRespondingWithSuccess(int apiStatus) {
+ return apiStatus == ApiStatus.SUCCESS.getMetricCode();
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fadab1f8832e..25e9f8a38f89 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1527,6 +1527,8 @@ public final class SystemServer implements Dumpable {
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
+ boolean isDesktop = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC);
+
boolean isWatch = RoSystemFeatures.hasFeatureWatch(context);
boolean isArc = context.getPackageManager().hasSystemFeature(
@@ -1656,7 +1658,7 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
}
- if (!isTv) {
+ if (!isTv && !isDesktop) {
t.traceBegin("StartVibratorManagerService");
mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
t.traceEnd();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index f5bed999d5a0..5393e20889c0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -455,8 +455,9 @@ public class LocalDisplayAdapterTest {
* Confirm that external display uses physical density.
*/
@Test
- public void testDpiValues() throws Exception {
+ public void testDpiValues_baseDensityForExternalDisplaysDisabled() throws Exception {
// needs default one always
+ doReturn(false).when(mFlags).isBaseDensityForExternalDisplaysEnabled();
setUpDisplay(new FakeDisplay(PORT_A));
setUpDisplay(new FakeDisplay(PORT_B));
updateAvailableDisplays();
@@ -472,6 +473,25 @@ public class LocalDisplayAdapterTest {
16000);
}
+ @Test
+ public void testDpiValues_baseDensityForExternalDisplaysEnabled() throws Exception {
+ // needs default one always
+ doReturn(true).when(mFlags).isBaseDensityForExternalDisplaysEnabled();
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertDisplayDpi(
+ mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, 100, 100,
+ 100);
+ assertDisplayDpi(
+ mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_B, 100, 100,
+ 100);
+ }
+
private static class DisplayModeWrapper {
public SurfaceControl.DisplayMode mode;
public float[] expectedAlternativeRefreshRates;
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index d9256247b835..6b138b986fe7 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -27,6 +27,7 @@ import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
+import static android.service.dreams.Flags.FLAG_ALLOW_DREAM_WHEN_POSTURED;
import static com.android.server.deviceidle.Flags.FLAG_DISABLE_WAKELOCKS_IN_LIGHT_IDLE;
@@ -81,8 +82,8 @@ import android.os.BatterySaverPolicyConfig;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IWakeLockCallback;
import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
@@ -120,8 +121,8 @@ import com.android.server.power.PowerManagerService.WakeLock;
import com.android.server.power.batterysaver.BatterySaverController;
import com.android.server.power.batterysaver.BatterySaverPolicy;
import com.android.server.power.batterysaver.BatterySaverStateMachine;
-import com.android.server.power.feature.flags.Flags;
import com.android.server.power.feature.PowerManagerFlags;
+import com.android.server.power.feature.flags.Flags;
import com.android.server.testutils.OffsettableClock;
import com.google.testing.junit.testparameterinjector.TestParameter;
@@ -279,6 +280,8 @@ public class PowerManagerServiceTest {
Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
Settings.Secure.putInt(mContextSpy.getContentResolver(),
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 0);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED, 0);
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
@@ -1215,6 +1218,52 @@ public class PowerManagerServiceTest {
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
}
+ @EnableFlags(FLAG_ALLOW_DREAM_WHEN_POSTURED)
+ @Test
+ public void testDreamActivateWhilePosturedEnabled_postured_afterTimeout_goesToDreaming() {
+ when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(true);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED, 1);
+
+ doAnswer(inv -> {
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ return null;
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
+
+ setMinimumScreenOffTimeoutConfig(5);
+ createService();
+ startSystem();
+ mService.getLocalServiceInstance().setDevicePostured(true);
+
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ advanceTime(15000);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
+ }
+
+ @EnableFlags(FLAG_ALLOW_DREAM_WHEN_POSTURED)
+ @Test
+ public void testDreamActivateWhilePosturedEnabled_notPostured_afterTimeout_goesToDozing() {
+ when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(true);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED, 1);
+
+ doAnswer(inv -> {
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ return null;
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
+
+ setMinimumScreenOffTimeoutConfig(5);
+ createService();
+ startSystem();
+ mService.getLocalServiceInstance().setDevicePostured(false);
+
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ advanceTime(15000);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
+ }
+
@EnableFlags(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
@SuppressWarnings("GuardedBy")
@Test
diff --git a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
index cc5be7ebba62..1522954c123f 100644
--- a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
@@ -46,17 +46,14 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
-import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.os.IBinaryTransparencyService;
-import com.android.server.pm.BackgroundInstallControlService;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.pm.BackgroundInstallControlCallbackHelper;
-import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.BackgroundInstallControlService;
import com.android.server.pm.pkg.PackageStateInternal;
import org.junit.After;
@@ -82,7 +79,7 @@ public class BinaryTransparencyServiceTest {
private Context mContext;
private BinaryTransparencyService mBinaryTransparencyService;
private BinaryTransparencyService.BinaryTransparencyServiceImpl mTestInterface;
- private DeviceConfig.Properties mOriginalBiometricsFlags;
+ private String mOriginalBiometricsFlag;
@Mock
private BinaryTransparencyService.BiometricLogger mBiometricLogger;
@@ -117,17 +114,15 @@ public class BinaryTransparencyServiceTest {
mBinaryTransparencyService = new BinaryTransparencyService(mContext, mBiometricLogger);
mTestInterface = mBinaryTransparencyService.new BinaryTransparencyServiceImpl();
- mOriginalBiometricsFlags = DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BIOMETRICS);
+ mOriginalBiometricsFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
+ BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION);
}
@After
- public void tearDown() throws Exception {
- try {
- DeviceConfig.setProperties(mOriginalBiometricsFlags);
- } catch (DeviceConfig.BadConfigException e) {
- Log.e(TAG, "Failed to reset biometrics flags to the original values before test. "
- + e);
- }
+ public void tearDown() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
+ BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
+ mOriginalBiometricsFlag, false /* makeDefault */);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
index 464fee2bfc11..fb31cfe762f2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER;
+import static com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES;
import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER;
import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
@@ -39,6 +40,10 @@ import android.content.Context;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Looper;
import android.os.SystemClock;
+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.provider.Settings;
import android.util.SparseArray;
import android.view.Display;
@@ -55,12 +60,14 @@ import com.android.server.LocalServices;
import com.android.server.accessibility.gestures.TouchExplorer;
import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
+import com.android.server.accessibility.magnification.MagnificationKeyHandler;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -88,8 +95,16 @@ public class AccessibilityInputFilterTest {
| FLAG_FEATURE_INJECT_MOTION_EVENTS
| FLAG_FEATURE_FILTER_KEY_EVENTS;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
// The expected order of EventStreamTransformations.
private final Class[] mExpectedEventHandlerTypes =
+ {MagnificationKeyHandler.class, KeyboardInterceptor.class, MotionEventInjector.class,
+ FullScreenMagnificationGestureHandler.class, TouchExplorer.class,
+ AutoclickController.class, AccessibilityInputFilter.class};
+
+ private final Class[] mExpectedEventHandlerTypesWithoutMagKeyboard =
{KeyboardInterceptor.class, MotionEventInjector.class,
FullScreenMagnificationGestureHandler.class, TouchExplorer.class,
AutoclickController.class, AccessibilityInputFilter.class};
@@ -176,6 +191,7 @@ public class AccessibilityInputFilterTest {
}
@Test
+ @RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
public void testEventHandler_shouldIncreaseAndHaveCorrectOrderAfterOnDisplayAdded() {
prepareLooper();
@@ -191,9 +207,9 @@ public class AccessibilityInputFilterTest {
EventStreamTransformation next = mEventHandler.get(SECOND_DISPLAY);
assertNotNull(next);
- // Start from index 1 because KeyboardInterceptor only exists in EventHandler for
- // DEFAULT_DISPLAY.
- for (int i = 1; next != null; i++) {
+ // Start from index 2 because KeyboardInterceptor and MagnificationKeyHandler only exist in
+ // EventHandler for DEFAULT_DISPLAY.
+ for (int i = 2; next != null; i++) {
assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
next = next.getNext();
}
@@ -232,6 +248,7 @@ public class AccessibilityInputFilterTest {
}
@Test
+ @RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
public void testEventHandler_shouldHaveCorrectOrderForEventStreamTransformation() {
prepareLooper();
@@ -248,10 +265,36 @@ public class AccessibilityInputFilterTest {
}
next = mEventHandler.get(SECOND_DISPLAY);
+ // Start from index 2 because KeyboardInterceptor and MagnificationKeyHandler only exist
+ // in EventHandler for DEFAULT_DISPLAY.
+ for (int i = 2; next != null; i++) {
+ assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
+ next = next.getNext();
+ }
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+ public void testEventHandler_shouldHaveCorrectOrderForEventStreamTransformation_noMagKeys() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ assertEquals(2, mEventHandler.size());
+
+ // Check if mEventHandler for each display has correct order of the
+ // EventStreamTransformations.
+ EventStreamTransformation next = mEventHandler.get(DEFAULT_DISPLAY);
+ for (int i = 0; next != null; i++) {
+ assertEquals(next.getClass(), mExpectedEventHandlerTypesWithoutMagKeyboard[i]);
+ next = next.getNext();
+ }
+
+ next = mEventHandler.get(SECOND_DISPLAY);
// Start from index 1 because KeyboardInterceptor only exists in EventHandler for
// DEFAULT_DISPLAY.
for (int i = 1; next != null; i++) {
- assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
+ assertEquals(next.getClass(), mExpectedEventHandlerTypesWithoutMagKeyboard[i]);
next = next.getNext();
}
}
@@ -387,7 +430,6 @@ public class AccessibilityInputFilterTest {
assertNotNull(handler);
assertEquals(WindowMagnificationGestureHandler.class, handler.getClass());
assertEquals(nextEventStream.getClass(), handler.getNext().getClass());
-
}
@Test public void
@@ -412,6 +454,32 @@ public class AccessibilityInputFilterTest {
assertEquals(nextEventStream.getClass(), handler.getNext().getClass());
}
+ @Test
+ @RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+ public void testEnabledFeatures_windowMagnificationMode_expectedMagnificationKeyHandler() {
+ prepareLooper();
+ doReturn(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW).when(
+ mAms).getMagnificationMode(DEFAULT_DISPLAY);
+
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+
+ MagnificationKeyHandler handler = getMagnificationKeyHandlerFromEventHandler();
+ assertNotNull(handler);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+ public void testEnabledFeatures_fullscreenMagnificationMode_expectedMagnificationKeyHandler() {
+ prepareLooper();
+ doReturn(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN).when(
+ mAms).getMagnificationMode(DEFAULT_DISPLAY);
+
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+
+ MagnificationKeyHandler handler = getMagnificationKeyHandlerFromEventHandler();
+ assertNotNull(handler);
+ }
+
private static void prepareLooper() {
if (Looper.myLooper() == null) {
Looper.prepare();
@@ -458,4 +526,16 @@ public class AccessibilityInputFilterTest {
}
return null;
}
+
+ @Nullable
+ private MagnificationKeyHandler getMagnificationKeyHandlerFromEventHandler() {
+ EventStreamTransformation next = mEventHandler.get(DEFAULT_DISPLAY);
+ while (next != null) {
+ if (next instanceof MagnificationKeyHandler) {
+ return (MagnificationKeyHandler) next;
+ }
+ next = next.getNext();
+ }
+ return null;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java
new file mode 100644
index 000000000000..d1ef33d8fb70
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import static com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES;
+import static com.android.server.accessibility.magnification.MagnificationController.PAN_DIRECTION_DOWN;
+import static com.android.server.accessibility.magnification.MagnificationController.PAN_DIRECTION_LEFT;
+import static com.android.server.accessibility.magnification.MagnificationController.PAN_DIRECTION_RIGHT;
+import static com.android.server.accessibility.magnification.MagnificationController.PAN_DIRECTION_UP;
+import static com.android.server.accessibility.magnification.MagnificationController.ZOOM_DIRECTION_IN;
+import static com.android.server.accessibility.magnification.MagnificationController.ZOOM_DIRECTION_OUT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.Display;
+import android.view.KeyEvent;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.accessibility.EventStreamTransformation;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link MagnificationKeyHandler}.
+ */
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+public class MagnificationKeyHandlerTest {
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private MagnificationKeyHandler mMkh;
+
+ @Mock
+ MagnificationKeyHandler.Callback mCallback;
+
+ @Mock
+ EventStreamTransformation mNextHandler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mMkh = new MagnificationKeyHandler(mCallback);
+ mMkh.setNext(mNextHandler);
+ }
+
+ @Test
+ public void onKeyEvent_unusedKeyPress_sendToNext() {
+ final KeyEvent event = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_L, 0, 0);
+ mMkh.onKeyEvent(event, 0);
+
+ // No callbacks were called.
+ verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
+ verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt());
+ verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt());
+ verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt());
+
+ // The event was passed on.
+ verify(mNextHandler, times(1)).onKeyEvent(event, 0);
+ }
+
+ @Test
+ public void onKeyEvent_arrowKeyPressWithIncorrectModifiers_sendToNext() {
+ final KeyEvent event =
+ new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
+ 0, KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(event, 0);
+
+ // No callbacks were called.
+ verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
+ verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt());
+ verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt());
+ verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt());
+
+ // The event was passed on.
+ verify(mNextHandler, times(1)).onKeyEvent(event, 0);
+ }
+
+ @Test
+ public void onKeyEvent_unusedKeyPressWithCorrectModifiers_sendToNext() {
+ final KeyEvent event =
+ new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_J, 0,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(event, 0);
+
+ // No callbacks were called.
+ verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
+ verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt());
+ verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt());
+ verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt());
+
+ // The event was passed on.
+ verify(mNextHandler, times(1)).onKeyEvent(event, 0);
+ }
+
+ @Test
+ public void onKeyEvent_panStartAndEnd_left() {
+ testPanMagnification(KeyEvent.KEYCODE_DPAD_LEFT, PAN_DIRECTION_LEFT);
+ }
+
+ @Test
+ public void onKeyEvent_panStartAndEnd_right() {
+ testPanMagnification(KeyEvent.KEYCODE_DPAD_RIGHT, PAN_DIRECTION_RIGHT);
+ }
+
+ @Test
+ public void onKeyEvent_panStartAndEnd_up() {
+ testPanMagnification(KeyEvent.KEYCODE_DPAD_UP, PAN_DIRECTION_UP);
+ }
+
+ @Test
+ public void onKeyEvent_panStartAndEnd_down() {
+ testPanMagnification(KeyEvent.KEYCODE_DPAD_DOWN, PAN_DIRECTION_DOWN);
+ }
+
+ @Test
+ public void onKeyEvent_scaleStartAndEnd_zoomIn() {
+ testScaleMagnification(KeyEvent.KEYCODE_EQUALS, ZOOM_DIRECTION_IN);
+ }
+
+ @Test
+ public void onKeyEvent_scaleStartAndEnd_zoomOut() {
+ testScaleMagnification(KeyEvent.KEYCODE_MINUS, ZOOM_DIRECTION_OUT);
+ }
+
+ @Test
+ public void onKeyEvent_panStartAndStop_diagonal() {
+ final KeyEvent downLeftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_DPAD_LEFT, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(downLeftEvent, 0);
+ verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_LEFT);
+ verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt());
+
+ // Also press the down arrow key.
+ final KeyEvent downDownEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_DPAD_DOWN, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(downDownEvent, 0);
+ verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_LEFT);
+ verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_DOWN);
+ verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt());
+
+ // Lift the left arrow key.
+ final KeyEvent upLeftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_DPAD_LEFT, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(upLeftEvent, 0);
+ verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_LEFT);
+ verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_DOWN);
+ verify(mCallback, times(1)).onPanMagnificationStop(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_LEFT);
+ verify(mCallback, times(0)).onPanMagnificationStop(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_DOWN);
+
+ // Lift the down arrow key.
+ final KeyEvent upDownEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_DPAD_DOWN, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(upDownEvent, 0);
+ verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_LEFT);
+ verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_DOWN);
+ verify(mCallback, times(1)).onPanMagnificationStop(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_LEFT);
+ verify(mCallback, times(1)).onPanMagnificationStop(Display.DEFAULT_DISPLAY,
+ PAN_DIRECTION_DOWN);
+
+ // The event was not passed on.
+ verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt());
+ }
+
+ private void testPanMagnification(int keyCode, int panDirection) {
+ final KeyEvent downEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(downEvent, 0);
+
+ // Pan started.
+ verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, panDirection);
+ verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt());
+
+ final KeyEvent upEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(upEvent, 0);
+
+ // Pan ended.
+ verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, panDirection);
+ verify(mCallback, times(1)).onPanMagnificationStop(Display.DEFAULT_DISPLAY, panDirection);
+
+ // Scale callbacks were not called.
+ verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt());
+ verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt());
+
+ // The events were not passed on.
+ verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt());
+ }
+
+ private void testScaleMagnification(int keyCode, int zoomDirection) {
+ final KeyEvent downEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(downEvent, 0);
+
+ // Scale started.
+ verify(mCallback, times(1)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY,
+ zoomDirection);
+ verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt());
+
+ final KeyEvent upEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
+ mMkh.onKeyEvent(upEvent, 0);
+
+ // Scale ended.
+ verify(mCallback, times(1)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY,
+ zoomDirection);
+ verify(mCallback, times(1)).onScaleMagnificationStop(Display.DEFAULT_DISPLAY,
+ zoomDirection);
+
+ // Pan callbacks were not called.
+ verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
+ verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt());
+
+ // The events were not passed on.
+ verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt());
+
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index eb4a628e14e5..792faab5b196 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -25,6 +25,7 @@ import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.DeviceSelectActionFromTv.STATE_WAIT_FOR_DEVICE_POWER_ON;
+import static com.android.server.hdmi.DeviceSelectActionFromTv.STATE_WAIT_FOR_POWER_STATE_CHANGE;
import static com.android.server.hdmi.DeviceSelectActionFromTv.STATE_WAIT_FOR_REPORT_POWER_STATUS;
import static com.google.common.truth.Truth.assertThat;
@@ -230,11 +231,15 @@ public class DeviceSelectActionFromTvTest {
"testDeviceSelect");
action.start();
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_ON);
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@@ -249,10 +254,14 @@ public class DeviceSelectActionFromTvTest {
/*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_STANDBY);
mTestLooper.dispatchAll();
+
HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed(
ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER);
assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed);
@@ -261,6 +270,7 @@ public class DeviceSelectActionFromTvTest {
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.processCommand(REPORT_POWER_STATUS_ON);
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@@ -275,8 +285,11 @@ public class DeviceSelectActionFromTvTest {
/*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_STANDBY);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
@@ -288,6 +301,7 @@ public class DeviceSelectActionFromTvTest {
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_ON);
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@@ -302,8 +316,11 @@ public class DeviceSelectActionFromTvTest {
/*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_STANDBY);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
@@ -316,6 +333,7 @@ public class DeviceSelectActionFromTvTest {
action.handleTimerEvent(STATE_WAIT_FOR_REPORT_POWER_STATUS);
// Give up getting power status, and just send <Set Stream Path>
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@@ -332,7 +350,10 @@ public class DeviceSelectActionFromTvTest {
"testDeviceSelect");
action.start();
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@@ -348,11 +369,15 @@ public class DeviceSelectActionFromTvTest {
"testDeviceSelect");
action.start();
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_ON);
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@@ -369,10 +394,14 @@ public class DeviceSelectActionFromTvTest {
/*isCec20=*/true);
action.start();
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_STANDBY);
mTestLooper.dispatchAll();
+
HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed(
ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(userControlPressed);
@@ -381,6 +410,7 @@ public class DeviceSelectActionFromTvTest {
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.processCommand(REPORT_POWER_STATUS_ON);
mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
diff --git a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
index ee8eb9b35088..b76e0bc8cd14 100644
--- a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
@@ -42,8 +42,10 @@ import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.events.AuthenticationFailedInfo;
import android.hardware.biometrics.events.AuthenticationSucceededInfo;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
@@ -151,6 +153,8 @@ public class AuthenticationPolicyServiceTest {
when(mSecureLockDeviceService.disableSecureLockDevice(any()))
.thenReturn(ERROR_UNSUPPORTED);
}
+
+ toggleAdaptiveAuthSettingsOverride(PRIMARY_USER_ID, false /* disable */);
}
@After
@@ -252,8 +256,24 @@ public class AuthenticationPolicyServiceTest {
}
@Test
- public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked()
+ @EnableFlags({android.security.Flags.FLAG_DISABLE_ADAPTIVE_AUTH_COUNTER_LOCK})
+ public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked_deviceLockEnabled()
+ throws RemoteException {
+ testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked(
+ true /* enabled */);
+ }
+
+ @Test
+ @EnableFlags({android.security.Flags.FLAG_DISABLE_ADAPTIVE_AUTH_COUNTER_LOCK})
+ public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked_deviceLockDisabled()
throws RemoteException {
+ toggleAdaptiveAuthSettingsOverride(PRIMARY_USER_ID, true /* disabled */);
+ testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked(
+ false /* enabled */);
+ }
+
+ private void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked(
+ boolean enabled) throws RemoteException {
// Device is currently not locked and Keyguard is not showing
when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(false);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
@@ -264,7 +284,11 @@ public class AuthenticationPolicyServiceTest {
}
waitForAuthCompletion();
- verifyLockDevice(PRIMARY_USER_ID);
+ if (enabled) {
+ verifyLockDevice(PRIMARY_USER_ID);
+ } else {
+ verifyNotLockDevice(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS, PRIMARY_USER_ID);
+ }
}
@Test
@@ -300,8 +324,24 @@ public class AuthenticationPolicyServiceTest {
}
@Test
- public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser()
+ @EnableFlags({android.security.Flags.FLAG_DISABLE_ADAPTIVE_AUTH_COUNTER_LOCK})
+ public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser_deviceLockEnabled()
throws RemoteException {
+ testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser(
+ true /* enabled */);
+ }
+
+ @Test
+ @EnableFlags({android.security.Flags.FLAG_DISABLE_ADAPTIVE_AUTH_COUNTER_LOCK})
+ public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser_deviceLockDisabled()
+ throws RemoteException {
+ toggleAdaptiveAuthSettingsOverride(PRIMARY_USER_ID, true /* disabled */);
+ testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser(
+ false /* enabled */);
+ }
+
+ private void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser(
+ boolean enabled) throws RemoteException {
// Three failed primary auth attempts
for (int i = 0; i < 3; i++) {
mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID);
@@ -313,7 +353,11 @@ public class AuthenticationPolicyServiceTest {
}
waitForAuthCompletion();
- verifyLockDevice(PRIMARY_USER_ID);
+ if (enabled) {
+ verifyLockDevice(PRIMARY_USER_ID);
+ } else {
+ verifyNotLockDevice(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS, PRIMARY_USER_ID);
+ }
}
@Test
@@ -366,10 +410,13 @@ public class AuthenticationPolicyServiceTest {
REASON_UNKNOWN, true, userId).build();
}
-
private AuthenticationFailedInfo authFailedInfo(int userId) {
return new AuthenticationFailedInfo.Builder(BiometricSourceType.FINGERPRINT, REASON_UNKNOWN,
userId).build();
}
+ private void toggleAdaptiveAuthSettingsOverride(int userId, boolean disable) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK, disable ? 1 : 0, userId);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 1df8e3deb84b..d2f8d14c5007 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -19,13 +19,9 @@ 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_OTHER;
import static android.service.notification.Adjustment.TYPE_PROMOTION;
-import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
-import static android.service.notification.Flags.notificationClassification;
import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENTS;
-import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES;
import static com.google.common.truth.Truth.assertThat;
@@ -160,17 +156,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
mAssistants.readXml(parser, mNm::canUseManagedServices, false, USER_ALL);
}
- private void setDefaultAllowedAdjustmentKeyTypes(NotificationAssistants assistants) {
- assistants.setAssistantAdjustmentKeyTypeState(TYPE_OTHER, false);
- assistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
- assistants.setAssistantAdjustmentKeyTypeState(TYPE_SOCIAL_MEDIA, false);
- assistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false);
- assistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, false);
-
- for (int type : DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES) {
- assistants.setAssistantAdjustmentKeyTypeState(type, true);
- }
- }
@Before
public void setUp() throws Exception {
@@ -181,9 +166,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
com.android.internal.R.string.config_defaultAssistantAccessComponent,
mCn.flattenToString());
mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm));
- if (notificationClassification()) {
- setDefaultAllowedAdjustmentKeyTypes(mAssistants);
- }
when(mNm.getBinderService()).thenReturn(mINm);
mContext.ensureTestableResources();
@@ -727,7 +709,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
- .containsExactly(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION);
+ .containsExactlyElementsIn(List.of(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION));
}
@Test
@@ -748,7 +730,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
writeXmlAndReload(USER_ALL);
assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
- .containsExactly(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION);
+ .containsExactlyElementsIn(List.of(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION));
}
@Test
@@ -765,168 +747,98 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
@Test
@EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
- public void testGetPackagesWithKeyTypeAdjustmentSettings() throws Exception {
+ public void testGetTypeAdjustmentDeniedPackages() throws Exception {
String pkg = "my.package";
String pkg2 = "my.package.2";
- setDefaultAllowedAdjustmentKeyTypes(mAssistants);
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue();
- assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings()).isEmpty();
+ assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg)).isTrue();
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, true);
- assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings())
+ mAssistants.setTypeAdjustmentForPackageState(pkg, true);
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
+ mAssistants.setTypeAdjustmentForPackageState(pkg, false);
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
.containsExactly(pkg);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false);
- assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings())
+ mAssistants.setTypeAdjustmentForPackageState(pkg2, true);
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
.containsExactly(pkg);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg2, TYPE_NEWS, true);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg2, TYPE_PROMOTION, false);
- assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings())
+ mAssistants.setTypeAdjustmentForPackageState(pkg2, false);
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
.containsExactly(pkg, pkg2);
}
@Test
@EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- public void testSetAssistantAdjustmentKeyTypeStateForPackage_usesGlobalDefault() {
- String pkg = "my.package";
- setDefaultAllowedAdjustmentKeyTypes(mAssistants);
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue();
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isFalse();
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
- .containsExactlyElementsIn(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES);
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- public void testSetAssistantAdjustmentKeyTypeStateForPackage_allowsAndDenies() {
- setDefaultAllowedAdjustmentKeyTypes(mAssistants);
- // Given that a package is set to have a type adjustment allowed,
- String pkg = "my.package";
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, true);
+ public void testSetTypeAdjustmentForPackageState_allowsAndDenies() {
+ // Given that a package is allowed to have its type adjusted,
+ String allowedPackage = "allowed.package";
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
+ mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true);
- // The newly set state is the combination of the global default and the newly set type.
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
+ assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
// Set type adjustment disallowed for this package
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, false);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false);
+ mAssistants.setTypeAdjustmentForPackageState(allowedPackage, false);
// Then the package is marked as denied
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).isEmpty();
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isFalse();
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
+ .containsExactly(allowedPackage);
+ assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
// Set type adjustment allowed again
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, true);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, true);
+ mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true);
// Then the package is marked as allowed again
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue();
-
- // Set type adjustment promotions false,
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false);
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
- .containsExactly(TYPE_NEWS);
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isFalse();
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
+ assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
}
@Test
@EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- public void testSetAssistantAdjustmentKeyTypeStateForPackage_allowsMultiplePkgs() {
- setDefaultAllowedAdjustmentKeyTypes(mAssistants);
- // Given packages allowed to have their type adjusted to TYPE_NEWS,
- String allowedPkg1 = "allowed.Pkg1";
- String allowedPkg2 = "allowed.Pkg2";
- String allowedPkg3 = "allowed.Pkg3";
- // Set type adjustment allowed for these packages
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg1, TYPE_NEWS, true);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_NEWS, true);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_NEWS, true);
-
- // The newly set state is the combination of the global default and the newly set type.
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg1)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg2)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg3)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg1, TYPE_NEWS)).isTrue();
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg2, TYPE_NEWS)).isTrue();
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg3, TYPE_NEWS)).isTrue();
-
- // And when we deny some of them,
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_NEWS, false);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_PROMOTION,
- false);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_PROMOTION,
- false);
-
- // Then the rest of the original packages are still marked as allowed.
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg1)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg2)).isEmpty();
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg3)).asList()
- .containsExactly(TYPE_NEWS);
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg1, TYPE_NEWS)).isTrue();
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg2, TYPE_NEWS)).isFalse();
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg3, TYPE_NEWS)).isTrue();
+ public void testSetAssistantAdjustmentKeyTypeStateForPackage_deniesMultiple() {
+ // Given packages not allowed to have their type adjusted,
+ String deniedPkg1 = "denied.Pkg1";
+ String deniedPkg2 = "denied.Pkg2";
+ String deniedPkg3 = "denied.Pkg3";
+ // Set type adjustment disallowed for these packages
+ mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false);
+ mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, false);
+ mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false);
+
+ // Then the packages are marked as denied
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
+ .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg2, deniedPkg3));
+ assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1));
+ assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2));
+ assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3));
+
+ // And when we re-allow one of them,
+ mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, true);
+
+ // Then the rest of the original packages are still marked as denied.
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
+ .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3));
+ assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1));
+ assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2));
+ assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3));
}
@Test
@EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
public void testSetAssistantAdjustmentKeyTypeStateForPackage_readWriteXml() throws Exception {
- setDefaultAllowedAdjustmentKeyTypes(mAssistants);
mAssistants.loadDefaultsFromConfig(true);
String deniedPkg1 = "denied.Pkg1";
String allowedPkg2 = "allowed.Pkg2";
- String allowedPkg3 = "allowed.Pkg3";
+ String deniedPkg3 = "denied.Pkg3";
// Set type adjustment disallowed or allowed for these packages
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(deniedPkg1, TYPE_PROMOTION, false);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_NEWS, true);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_NEWS, true);
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_SOCIAL_MEDIA,
- true);
+ mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false);
+ mAssistants.setTypeAdjustmentForPackageState(allowedPkg2, true);
+ mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false);
writeXmlAndReload(USER_ALL);
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(deniedPkg1)).isEmpty();
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg2)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg3)).asList()
- .containsExactly(TYPE_NEWS, TYPE_SOCIAL_MEDIA, TYPE_PROMOTION);
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
- public void testSetAssistantAdjustmentKeyTypeStateForPackage_noGlobalImpact() throws Exception {
- setDefaultAllowedAdjustmentKeyTypes(mAssistants);
- // When the global state is changed,
- mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true);
-
- // The package state reflects the global state.
- String pkg = "my.package";
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue();
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
-
- // Once the package specific state is modified,
- mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_SOCIAL_MEDIA, true);
-
- // The package specific state combines the global state with those modifications
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_SOCIAL_MEDIA)).isTrue();
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION, TYPE_SOCIAL_MEDIA);
-
- // And further changes to the global state are ignored.
- mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false);
- assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
- assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
- .containsExactly(TYPE_NEWS, TYPE_PROMOTION, TYPE_SOCIAL_MEDIA);
+ assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
+ .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3));
}
@Test
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 fdb6a6802b7e..85c592084acf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -369,9 +369,6 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -387,6 +384,9 @@ import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@RunWithLooper
@@ -7662,7 +7662,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
- when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
// Set up notifications that will be adjusted
final NotificationRecord r1 = spy(generateNotificationRecord(
@@ -17512,7 +17512,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationManagerService.WorkerHandler.class);
mService.setHandler(handler);
when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
- when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
Bundle signals = new Bundle();
signals.putInt(KEY_TYPE, TYPE_NEWS);
@@ -17556,11 +17556,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationManagerService.WorkerHandler.class);
mService.setHandler(handler);
when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
- when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), eq(TYPE_NEWS)))
- .thenReturn(true);
- // Blocking adjustments for a different type does nothing
- when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), eq(TYPE_PROMOTION)))
- .thenReturn(false);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
Bundle signals = new Bundle();
signals.putInt(KEY_TYPE, TYPE_NEWS);
@@ -17575,9 +17571,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
- // When we block adjustments for this package/type
- when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), eq(TYPE_PROMOTION)))
- .thenReturn(false);
+ // When we block adjustments for this package
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(false);
signals.putInt(KEY_TYPE, TYPE_PROMOTION);
mBinderService.applyAdjustmentFromAssistant(null, adjustment);
@@ -17907,7 +17902,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
- when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
// Post a single notification
final boolean hasOriginalSummary = false;
@@ -17947,7 +17942,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
- when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
// Post grouped notifications
final String originalGroupName = "originalGroup";
@@ -17996,7 +17991,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
- when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
// Post grouped notifications
final String originalGroupName = "originalGroup";
@@ -18047,7 +18042,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
- when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
// Post a single notification
final boolean hasOriginalSummary = false;
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 6ef078b6da8a..96fddf13cdd0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -211,7 +211,9 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.io.Reader;
+import java.io.StringWriter;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
@@ -7406,6 +7408,43 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.isEqualTo(mZenModeHelper.getDefaultZenPolicy());
}
+ @Test
+ public void setAutomaticZenRuleState_logsOriginToZenLog() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, azr,
+ ORIGIN_APP, "adding", CUSTOM_PKG_UID);
+ ZenLog.clear();
+
+ // User enables manually from QS:
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
+ new Condition(azr.getConditionId(), "", STATE_TRUE), ORIGIN_USER_IN_SYSTEMUI,
+ 123456);
+
+ assertThat(getZenLog()).contains(
+ "config: setAzrState: " + ruleId + " (ORIGIN_USER_IN_SYSTEMUI) from uid " + 1234);
+ }
+
+ @Test
+ public void setAutomaticZenRuleStateFromConditionProvider_logsOriginToZenLog() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.parse("cond/cond"))
+ .setOwner(new ComponentName(CUSTOM_PKG_NAME, "SomeConditionProvider"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, azr,
+ ORIGIN_APP, "adding", CUSTOM_PKG_UID);
+ ZenLog.clear();
+
+ // App enables rule through CPS
+ mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT,
+ Uri.parse("cond/cond"), new Condition(azr.getConditionId(), "", STATE_TRUE),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(getZenLog()).contains(
+ "config: setAzrStateFromCps: cond/cond (ORIGIN_APP) from uid " + CUSTOM_PKG_UID);
+ }
+
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
@@ -7530,6 +7569,12 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(dndProto.getNotificationList().getNumber()).isEqualTo(STATE_ALLOW);
}
+ private static String getZenLog() {
+ StringWriter zenLogWriter = new StringWriter();
+ ZenLog.dump(new PrintWriter(zenLogWriter), "");
+ return zenLogWriter.toString();
+ }
+
private static void withoutWtfCrash(Runnable test) {
Log.TerribleFailureHandler oldHandler = Log.setWtfHandler((tag, what, system) -> {
});
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 22e411ea1500..3f3b24a21d22 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -202,7 +202,7 @@ public class DisplayAreaPolicyBuilderTest {
}
@Test
- public void testBuilder_defaultPolicy_hasWindowedMagnificationFeature() {
+ public void testBuilder_defaultPolicy_hasMagnificationFeature() {
final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
resourcesWithProvider(""));
final DisplayAreaPolicyBuilder.Result defaultPolicy =
@@ -210,28 +210,16 @@ public class DisplayAreaPolicyBuilderTest {
mRoot, mImeContainer);
final List<Feature> features = defaultPolicy.getFeatures();
boolean hasWindowedMagnificationFeature = false;
- for (Feature feature : features) {
- hasWindowedMagnificationFeature |= feature.getId() == FEATURE_WINDOWED_MAGNIFICATION;
- }
-
- assertThat(hasWindowedMagnificationFeature).isTrue();
- }
-
- @Test
- public void testBuilder_defaultPolicy_hasFullscreenMagnificationFeature() {
- final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
- resourcesWithProvider(""));
- final DisplayAreaPolicyBuilder.Result defaultPolicy =
- (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
- mRoot, mImeContainer);
- final List<Feature> features = defaultPolicy.getFeatures();
boolean hasFullscreenMagnificationFeature = false;
for (Feature feature : features) {
+ hasWindowedMagnificationFeature |= feature.getId() == FEATURE_WINDOWED_MAGNIFICATION;
hasFullscreenMagnificationFeature |=
feature.getId() == FEATURE_FULLSCREEN_MAGNIFICATION;
}
- assertThat(hasFullscreenMagnificationFeature).isTrue();
+ assertThat(hasWindowedMagnificationFeature).isTrue();
+ assertThat(hasFullscreenMagnificationFeature).isEqualTo(
+ DisplayAreaPolicy.USE_DISPLAY_AREA_FOR_FULLSCREEN_MAGNIFICATION);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index f509706d1692..523b7235140a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -36,6 +36,7 @@ import static com.android.server.wm.DragDropController.MSG_UNHANDLED_DROP_LISTEN
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.assertNull;
import static org.junit.Assert.assertTrue;
@@ -337,12 +338,13 @@ public class DragDropControllerTests extends WindowTestsBase {
// Verify the drop event is only sent for the global intercept window
assertTrue(nonLocalWindowDragEvents.isEmpty());
- assertTrue(last(localWindowDragEvents).getAction() != ACTION_DROP);
- assertTrue(last(globalInterceptWindowDragEvents).getAction() == ACTION_DROP);
+ assertNotEquals(ACTION_DROP, localWindowDragEvents.getLast().getAction());
+ assertEquals(ACTION_DROP,
+ globalInterceptWindowDragEvents.getLast().getAction());
// Verify that item extras were not sent with the drop event
- assertNull(last(localWindowDragEvents).getClipData());
- assertFalse(last(globalInterceptWindowDragEvents).getClipData()
+ assertNull(localWindowDragEvents.getLast().getClipData());
+ assertFalse(globalInterceptWindowDragEvents.getLast().getClipData()
.willParcelWithActivityInfo());
});
}
@@ -384,7 +386,7 @@ public class DragDropControllerTests extends WindowTestsBase {
}
@Test
- public void testDragEventCoordinates() {
+ public void testDragEventCoordinatesOverlappingWindows() {
int dragStartX = mWindow.getBounds().centerX();
int dragStartY = mWindow.getBounds().centerY();
int startOffsetPx = 10;
@@ -429,7 +431,7 @@ public class DragDropControllerTests extends WindowTestsBase {
// Verify only window2 received the DROP event and coords are sent as-is.
assertEquals(1, dragEvents.size());
assertEquals(2, dragEvents2.size());
- final DragEvent dropEvent = last(dragEvents2);
+ final DragEvent dropEvent = dragEvents2.getLast();
assertEquals(ACTION_DROP, dropEvent.getAction());
assertEquals(dropCoordsPx, dropEvent.getX(), 0.0 /* delta */);
assertEquals(dropCoordsPx, dropEvent.getY(), 0.0 /* delta */);
@@ -437,10 +439,10 @@ public class DragDropControllerTests extends WindowTestsBase {
mTarget.reportDropResult(iwindow2, true);
// Verify both windows received ACTION_DRAG_ENDED event.
- assertEquals(ACTION_DRAG_ENDED, last(dragEvents).getAction());
- assertEquals(window2.getDisplayId(), last(dragEvents).getDisplayId());
- assertEquals(ACTION_DRAG_ENDED, last(dragEvents2).getAction());
- assertEquals(window2.getDisplayId(), last(dragEvents2).getDisplayId());
+ assertEquals(ACTION_DRAG_ENDED, dragEvents.getLast().getAction());
+ assertEquals(window2.getDisplayId(), dragEvents.getLast().getDisplayId());
+ assertEquals(ACTION_DRAG_ENDED, dragEvents2.getLast().getAction());
+ assertEquals(window2.getDisplayId(), dragEvents2.getLast().getDisplayId());
} finally {
mTarget.continueDragStateClose();
}
@@ -493,7 +495,7 @@ public class DragDropControllerTests extends WindowTestsBase {
// Verify only window2 received the DROP event and coords are sent as-is
assertEquals(1, dragEvents.size());
assertEquals(2, dragEvents2.size());
- final DragEvent dropEvent = last(dragEvents2);
+ final DragEvent dropEvent = dragEvents2.getLast();
assertEquals(ACTION_DROP, dropEvent.getAction());
assertEquals(dropCoordsPx, dropEvent.getX(), 0.0 /* delta */);
assertEquals(dropCoordsPx, dropEvent.getY(), 0.0 /* delta */);
@@ -501,10 +503,12 @@ public class DragDropControllerTests extends WindowTestsBase {
mTarget.reportDropResult(iwindow2, true);
// Verify both windows received ACTION_DRAG_ENDED event.
- assertEquals(ACTION_DRAG_ENDED, last(dragEvents).getAction());
- assertEquals(testDisplay.getDisplayId(), last(dragEvents).getDisplayId());
- assertEquals(ACTION_DRAG_ENDED, last(dragEvents2).getAction());
- assertEquals(testDisplay.getDisplayId(), last(dragEvents2).getDisplayId());
+ assertEquals(ACTION_DRAG_ENDED, dragEvents.getLast().getAction());
+ assertEquals(testDisplay.getDisplayId(),
+ dragEvents.getLast().getDisplayId());
+ assertEquals(ACTION_DRAG_ENDED, dragEvents2.getLast().getAction());
+ assertEquals(testDisplay.getDisplayId(),
+ dragEvents2.getLast().getDisplayId());
} finally {
mTarget.continueDragStateClose();
}
@@ -561,8 +565,23 @@ public class DragDropControllerTests extends WindowTestsBase {
});
}
- private DragEvent last(ArrayList<DragEvent> list) {
- return list.get(list.size() - 1);
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_DND)
+ public void testDragCancelledOnTopologyChange() {
+ // Necessary for now since DragState.sendDragStartedLocked() will recycle drag events
+ // immediately after dispatching, which is a problem when using mockito arguments captor
+ // because it returns and modifies the same drag event.
+ TestIWindow iwindow = (TestIWindow) mWindow.mClient;
+ final ArrayList<DragEvent> dragEvents = new ArrayList<>();
+ iwindow.setDragEventJournal(dragEvents);
+
+ startDrag(0, 0, View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ,
+ ClipData.newPlainText("label", "text"), (surface) -> {
+ // Simulate display topology change to trigger drag-and-drop cancellation.
+ mTarget.handleDisplayTopologyChange(null /* displayTopology */);
+ assertEquals(2, dragEvents.size());
+ assertEquals(ACTION_DRAG_ENDED, dragEvents.getLast().getAction());
+ });
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 25b9f4b8035b..8a7e7434e604 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -70,8 +70,6 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserManager;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
@@ -82,11 +80,9 @@ import android.window.TaskSnapshot;
import androidx.test.filters.MediumTest;
-import com.android.launcher3.Flags;
import com.android.server.wm.RecentTasks.Callbacks;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -717,18 +713,7 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
- public void testVisibleTasks_excludedFromRecents() {
- testVisibleTasks_excludedFromRecents_internal();
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_withRefactorFlag() {
- testVisibleTasks_excludedFromRecents_internal();
- }
-
- private void testVisibleTasks_excludedFromRecents_internal() {
mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
Task invisibleExcludedTask = createTaskBuilder(".ExcludedTask1")
@@ -766,19 +751,7 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
- @Ignore("b/342627272")
- @DisableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
- public void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask() {
- testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal();
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_withRefactorFlag() {
- testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal();
- }
-
- private void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal() {
mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
Task invisibleExcludedTask = createTaskBuilder(".ExcludedTask1")
@@ -816,18 +789,7 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
- public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible() {
- testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal();
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible_withRefactorFlag() {
- testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal();
- }
-
- private void testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal() {
// Create some set of tasks, some of which are visible and some are not
Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask")
.setParentTask(mTaskContainer.getRootHomeTask())
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index a95093d7e113..59335d3bb135 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -264,6 +264,7 @@ public class SystemServicesTestRule implements TestRule {
spyOn(dmg);
doNothing().when(dmg).registerDisplayListener(
any(), any(Executor.class), anyLong(), anyString());
+ doNothing().when(dmg).registerTopologyListener(any(Executor.class), any(), anyString());
}
private void setUpLocalServices() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index c876663dd749..8a068cc7837a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -16,9 +16,6 @@
package com.android.server.wm;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.os.Build.HW_TIMEOUT_MULTIPLIER;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -41,10 +38,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
-import android.media.ImageReader;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -57,13 +51,14 @@ import android.widget.LinearLayout;
import androidx.test.filters.MediumTest;
import com.android.server.wm.utils.CommonUtils;
+import com.android.server.wm.utils.VirtualDisplayTestRule;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -76,9 +71,9 @@ import java.util.function.Predicate;
@MediumTest
public class TaskStackChangedListenerTest {
+ @Rule
+ public VirtualDisplayTestRule mVirtualDisplayTestRule = new VirtualDisplayTestRule();
private ITaskStackListener mTaskStackListener;
- private VirtualDisplay mVirtualDisplay;
- private ImageReader mImageReader;
private final ArrayList<Activity> mStartedActivities = new ArrayList<>();
private static final int WAIT_TIMEOUT_MS = 5000 * HW_TIMEOUT_MULTIPLIER;
@@ -94,10 +89,6 @@ public class TaskStackChangedListenerTest {
if (mTaskStackListener != null) {
ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
}
- if (mVirtualDisplay != null) {
- mVirtualDisplay.release();
- mImageReader.close();
- }
// Finish from bottom to top.
final int size = mStartedActivities.size();
for (int i = 0; i < size; i++) {
@@ -116,21 +107,9 @@ public class TaskStackChangedListenerTest {
private VirtualDisplay createVirtualDisplay() {
final int width = 800;
final int height = 600;
- final int density = 160;
- final DisplayManager displayManager = getInstrumentation().getContext().getSystemService(
- DisplayManager.class);
- mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
- 2 /* maxImages */);
- final int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
- | VIRTUAL_DISPLAY_FLAG_PUBLIC;
final String name = getClass().getSimpleName() + "_VirtualDisplay";
- mVirtualDisplay = displayManager.createVirtualDisplay(name, width, height, density,
- mImageReader.getSurface(), flags);
- mVirtualDisplay.setSurface(mImageReader.getSurface());
- assertNotNull("display must be registered",
- Arrays.stream(displayManager.getDisplays()).filter(
- d -> d.getName().equals(name)).findAny());
- return mVirtualDisplay;
+ return mVirtualDisplayTestRule.createDisplayManagerAttachedVirtualDisplay(name, width,
+ height);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/VirtualDisplayTestRule.java b/services/tests/wmtests/src/com/android/server/wm/utils/VirtualDisplayTestRule.java
new file mode 100644
index 000000000000..e92e6846e161
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/VirtualDisplayTestRule.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** Provides wrapper test rule for creating and managing the cleanup for VirtualDisplay */
+public class VirtualDisplayTestRule implements TestRule {
+ private static final int DISPLAY_DENSITY = 160;
+
+ private final List<VirtualDisplay> mVirtualDisplays = new ArrayList<>();
+ private final List<ImageReader> mImageReaders = new ArrayList<>();
+ private final DisplayManager mDisplayManager;
+
+ public VirtualDisplayTestRule() {
+ mDisplayManager = getInstrumentation().getTargetContext().getSystemService(
+ DisplayManager.class);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ base.evaluate();
+ } finally {
+ tearDown();
+ }
+ }
+ };
+ }
+
+ private void tearDown() {
+ mVirtualDisplays.forEach(VirtualDisplay::release);
+ mImageReaders.forEach(ImageReader::close);
+ }
+
+ /**
+ * The virtual display in WindowTestsBase#createMockSimulatedDisplay is only attached to WM
+ * DisplayWindowSettingsProvider. DisplayManager is not aware of mock simulated display and
+ * therefore couldn't be used for actual Display-related testing (e.g. display listeners).
+ * This method creates real VirtualDisplay through DisplayManager.
+ */
+ public VirtualDisplay createDisplayManagerAttachedVirtualDisplay(String name, int width,
+ int height) {
+ final ImageReader imageReader = ImageReader.newInstance(width, height,
+ PixelFormat.RGBA_8888, 2 /* maxImages */);
+ mImageReaders.add(imageReader);
+ final int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ | VIRTUAL_DISPLAY_FLAG_PUBLIC;
+ final VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(name, width,
+ height, DISPLAY_DENSITY, imageReader.getSurface(), flags);
+ mVirtualDisplays.add(virtualDisplay);
+ assertNotNull("display must be registered", Arrays.stream(
+ mDisplayManager.getDisplays()).filter(d -> d.getName().equals(name)).findAny());
+ return virtualDisplay;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
index 819e73df955b..6dda7ea3eb59 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
@@ -48,6 +48,13 @@ public abstract class UsbACTerminal extends UsbACInterface {
return mAssocTerminal;
}
+ public boolean isInputTerminal() {
+ return mTerminalType == UsbTerminalTypes.TERMINAL_IN_MIC
+ || mTerminalType == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET
+ || mTerminalType == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
+ || mTerminalType == UsbTerminalTypes.TERMINAL_EXTERN_LINE;
+ }
+
@Override
public int parseRawDescriptors(ByteStream stream) {
mTerminalID = stream.getByte();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index ba178845a536..bfa4ecd71f5a 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -524,27 +524,21 @@ public final class UsbDescriptorParser {
* @hide
*/
public boolean hasMic() {
- boolean hasMic = false;
-
ArrayList<UsbDescriptor> acDescriptors =
getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal inDescr = (UsbACTerminal) descriptor;
- if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC
- || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET
- || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
- || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_LINE) {
- hasMic = true;
- break;
+ if (inDescr.isInputTerminal()) {
+ return true;
}
} else {
Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
- return hasMic;
+ return false;
}
/**
@@ -913,18 +907,20 @@ public final class UsbDescriptorParser {
float probability = 0.0f;
- // Look for a microphone
- boolean hasMic = hasMic();
-
// Look for a "speaker"
boolean hasSpeaker = hasSpeaker();
- if (hasMic && hasSpeaker) {
- probability += 0.75f;
- }
-
- if (hasMic && hasHIDInterface()) {
- probability += 0.25f;
+ if (hasMic()) {
+ if (hasSpeaker) {
+ probability += 0.75f;
+ }
+ if (hasHIDInterface()) {
+ probability += 0.25f;
+ }
+ if (getMaximumInputChannelCount() > 1) {
+ // A headset is more likely to only support mono capture.
+ probability -= 0.25f;
+ }
}
return probability;
@@ -935,9 +931,11 @@ public final class UsbDescriptorParser {
* headset. The probability range is between 0.0f (definitely NOT a headset) and
* 1.0f (definitely IS a headset). A probability of 0.75f seems sufficient
* to count on the peripheral being a headset.
+ * To align with the output device type, only treat the device as input headset if it is
+ * an output headset.
*/
public boolean isInputHeadset() {
- return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER;
+ return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER && isOutputHeadset();
}
// TODO: Up/Downmix process descriptor is not yet parsed, which may affect the result here.
@@ -952,6 +950,32 @@ public final class UsbDescriptorParser {
return maxChannelCount;
}
+ private int getMaximumInputChannelCount() {
+ int maxChannelCount = 0;
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (!(descriptor instanceof UsbACTerminal)) {
+ continue;
+ }
+ UsbACTerminal inDescr = (UsbACTerminal) descriptor;
+ if (!inDescr.isInputTerminal()) {
+ continue;
+ }
+ // For an input terminal, it should at lease has 1 channel.
+ // Comparing the max channel count with 1 here in case the USB device doesn't report
+ // audio channel cluster.
+ maxChannelCount = Math.max(maxChannelCount, 1);
+ if (!(descriptor instanceof UsbAudioChannelCluster)) {
+ continue;
+ }
+ maxChannelCount = Math.max(maxChannelCount,
+ ((UsbAudioChannelCluster) descriptor).getChannelCount());
+ }
+ return maxChannelCount;
+ }
+
/**
* @hide
*/
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 8c04f647fb2f..e0532633d40b 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -736,30 +736,6 @@ class KeyGestureControllerTests {
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "META + ALT + '-' -> Magnification Zoom Out",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_MINUS
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT,
- intArrayOf(KeyEvent.KEYCODE_MINUS),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
- "META + ALT + '=' -> Magnification Zoom In",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_EQUALS
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN,
- intArrayOf(KeyEvent.KEYCODE_EQUALS),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
"META + ALT + M -> Toggle Magnification",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
@@ -784,54 +760,6 @@ class KeyGestureControllerTests {
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "META + ALT + 'Down' -> Magnification Pan Down",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_DPAD_DOWN
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN,
- intArrayOf(KeyEvent.KEYCODE_DPAD_DOWN),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
- "META + ALT + 'Up' -> Magnification Pan Up",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_DPAD_UP
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP,
- intArrayOf(KeyEvent.KEYCODE_DPAD_UP),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
- "META + ALT + 'Left' -> Magnification Pan Left",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_DPAD_LEFT
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT,
- intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
- "META + ALT + 'Right' -> Magnification Pan Right",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_DPAD_RIGHT
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT,
- intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
"META + ALT + 'V' -> Toggle Voice Access",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
diff --git a/tests/NetworkSecurityConfigTest/Android.bp b/tests/NetworkSecurityConfigTest/Android.bp
index 4c48eaa4622e..6a68f2bb7ff9 100644
--- a/tests/NetworkSecurityConfigTest/Android.bp
+++ b/tests/NetworkSecurityConfigTest/Android.bp
@@ -11,11 +11,12 @@ android_test {
name: "NetworkSecurityConfigTests",
certificate: "platform",
libs: [
- "android.test.runner.stubs.system",
"android.test.base.stubs.system",
+ "android.test.runner.stubs.system",
],
static_libs: ["junit"],
// Include all test java files.
srcs: ["src/**/*.java"],
platform_apis: true,
+ test_suites: ["general-tests"],
}
diff --git a/tests/NetworkSecurityConfigTest/TEST_MAPPING b/tests/NetworkSecurityConfigTest/TEST_MAPPING
new file mode 100644
index 000000000000..d1b9aa1e3b53
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "NetworkSecurityConfigTests"
+ }
+ ]
+}
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index a5962292b5b0..82ad9fa05145 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -2778,7 +2778,7 @@ bool ManifestExtractor::Extract(android::IDiagnostics* diag) {
auto file_path = it->Next()->GetSource().path.c_str();
const char* last_slash =
- android::util::ValidLibraryPathLastSlash(file_path, has_renderscript_bitcode, false);
+ android::util::ValidLibraryPathLastSlash(file_path, has_renderscript_bitcode);
if (last_slash) {
architectures_from_apk.insert(std::string(file_path + APK_LIB_LEN, last_slash));
}