summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp11
-rw-r--r--OWNERS1
-rw-r--r--api/StubLibraries.bp1
-rwxr-xr-xcmds/am/am.sh3
-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/AssociationInfo.java20
-rw-r--r--core/java/android/companion/AssociationRequest.java9
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java51
-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/hardware/location/ContextHubManager.java17
-rw-r--r--core/java/android/hardware/location/IContextHubService.aidl4
-rw-r--r--core/java/android/os/AppZygote.java87
-rw-r--r--core/java/android/os/ChildZygoteProcess.java38
-rw-r--r--core/java/android/os/Parcel.java16
-rw-r--r--core/java/android/os/PerfettoTrace.java170
-rw-r--r--core/java/android/os/PerfettoTrackEventExtra.java481
-rw-r--r--core/java/android/os/PowerManagerInternal.java6
-rw-r--r--core/java/android/os/ZygoteProcess.java2
-rw-r--r--core/java/android/os/flags.aconfig7
-rw-r--r--core/java/android/provider/Settings.java23
-rw-r--r--core/java/android/security/flags.aconfig11
-rw-r--r--core/java/android/service/dreams/IDreamManager.aidl2
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java10
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java18
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java55
-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/util/proto/ProtoFieldFilter.java334
-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/accessibility/AccessibilityManager.java3
-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/CompoundButton.java13
-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/java/com/android/internal/widget/remotecompose/core/CoreDocument.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java144
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java96
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java67
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java17
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java20
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java175
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java175
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java95
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java32
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java100
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java124
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java124
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java126
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java38
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java39
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java42
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java65
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java15
-rw-r--r--core/jni/android_os_PerfettoTrace.cpp49
-rw-r--r--core/jni/android_os_PerfettoTrackEventExtra.cpp54
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp38
-rw-r--r--core/jni/fd_utils.cpp3
-rw-r--r--core/proto/android/providers/settings/secure.proto1
-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/os/PerfettoTraceTest.java226
-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--core/tests/utiltests/src/android/util/proto/ProtoFieldFilterTest.java230
-rw-r--r--data/etc/privapp-permissions-platform.xml4
-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.bp15
-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/common/SyncTransactionQueue.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt728
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt97
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt36
-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/pip/phone/PipController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java22
-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/recents/RecentsTransitionHandler.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java30
-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/CarWindowDecorViewModel.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java22
-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/DesktopModeKeyGestureHandlerTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt40
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt86
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt126
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java109
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java2
-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/DesktopModeWindowDecorViewModelTests.kt3
-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/ActionButtonsPreference/res/color/settingslib_expressive_actionbutton_background.xml24
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/color/settingslib_expressive_actionbutton_content_color.xml25
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml11
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java2
-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/SettingsLib/src/com/android/settingslib/notification/modes/DndDurationDialogFactory.java (renamed from packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenDurationDialog.java)9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableDndDialogFactory.java (renamed from packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java)23
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableDndDialogMetricsLogger.java (renamed from packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeDialogMetricsLogger.java)6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/DndDurationDialogFactoryTest.java (renamed from packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenDurationDialogTest.java)92
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/EnableDndDialogFactoryTest.java (renamed from packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/EnableZenModeDialogTest.java)69
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/Android.bp7
-rw-r--r--packages/SystemUI/OWNERS1
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig17
-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.kt30
-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/spatialaudio/ui/composable/SpatialAudioPopup.kt9
-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/log/src/com/android/systemui/log/LogBuffer.kt10
-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/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt10
-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/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt43
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt20
-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/base/logging/QSTileLoggerTest.kt2
-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/collection/NotifCollectionTest.java1
-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/notification/promoted/PromotedNotificationContentExtractorImplTest.kt6
-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/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt3
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java5
-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/layout/contextual_edu_dialog.xml2
-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.aidl19
-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/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt21
-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/AodToPrimaryBouncerTransitionViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt7
-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/navigationbar/TaskbarDelegate.java18
-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/DndTile.java12
-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/QSEnableDndDialogMetricsLogger.java (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/QSZenModeDialogMetricsLogger.java)6
-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/NotifCollection.java5
-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/logging/dagger/NotificationsLogModule.kt48
-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/notification/stack/NotificationStackScrollLayout.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt19
-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/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/SystemInfoCombinedVisibilityModel.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/VisibilityModel.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt226
-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/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java11
-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/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java7
-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/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt2
-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/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt4
-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/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt2
-rw-r--r--ravenwood/texts/ravenwood-standard-options.txt2
-rw-r--r--ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt2
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt2
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt5
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt35
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt3
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt172
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt172
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java112
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickController.java18
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java41
-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/CompanionDeviceManagerService.java10
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java4
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java35
-rw-r--r--services/core/java/com/android/server/BootReceiver.java129
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java11
-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/appop/HistoricalRegistry.java23
-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/BrightnessTracker.java11
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java5
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java37
-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/location/contexthub/ContextHubService.java9
-rw-r--r--services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java119
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java605
-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/os/NativeTombstoneManager.java22
-rw-r--r--services/core/java/com/android/server/os/core_os_flags.aconfig2
-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/ActivityRecord.java48
-rw-r--r--services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java46
-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.java50
-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/AutoclickControllerTest.java242
-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/TaskTests.java38
-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--telephony/java/android/telephony/TelephonyManager.java37
-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
490 files changed, 12834 insertions, 4536 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index f249884cb1a0..2ce3221e56cf 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -102,6 +102,7 @@ aconfig_declarations_group {
"com.android.media.flags.projection-aconfig-java",
"com.android.net.http.flags-aconfig-exported-java",
"com.android.net.thread.platform.flags-aconfig-java",
+ "com.android.permission.flags-aconfig-java-export",
"com.android.ranging.flags.ranging-aconfig-java-export",
"com.android.server.contextualsearch.flags-java",
"com.android.server.flags.services-aconfig-java",
@@ -115,6 +116,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 +165,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 +1419,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/OWNERS b/OWNERS
index 058ea3619a58..aa93a275100a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -16,6 +16,7 @@ omakoto@google.com #{LAST_RESORT_SUGGESTION}
roosa@google.com #{LAST_RESORT_SUGGESTION}
smoreland@google.com #{LAST_RESORT_SUGGESTION}
yamasani@google.com #{LAST_RESORT_SUGGESTION}
+timmurray@google.com #{LAST_RESORT_SUGGESTION}
# API changes are already covered by API-Review+1 (http://mdb/android-api-council)
# via https://android.git.corp.google.com/All-Projects/+/refs/meta/config/rules.pl.
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/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/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 2f161150a89b..ceafce2bdbb7 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -287,8 +287,8 @@ public final class AssociationInfo implements Parcelable {
/**
* Get the device icon of the associated device. The device icon represents the device type.
*
- * @return the device icon, or {@code null} if no device icon has been set for the
- * associated device.
+ * @return the device icon with size 24dp x 24dp.
+ * If the associated device has no icon set, it returns {@code null}.
*
* @see AssociationRequest.Builder#setDeviceIcon(Icon)
*/
@@ -377,6 +377,7 @@ public final class AssociationInfo implements Parcelable {
if (this == o) return true;
if (!(o instanceof AssociationInfo)) return false;
final AssociationInfo that = (AssociationInfo) o;
+
return mId == that.mId
&& mUserId == that.mUserId
&& mSelfManaged == that.mSelfManaged
@@ -391,11 +392,17 @@ public final class AssociationInfo implements Parcelable {
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
&& Objects.equals(mAssociatedDevice, that.mAssociatedDevice)
&& mSystemDataSyncFlags == that.mSystemDataSyncFlags
- && (mDeviceIcon == null ? that.mDeviceIcon == null
- : mDeviceIcon.sameAs(that.mDeviceIcon))
+ && isSameIcon(mDeviceIcon, that.mDeviceIcon)
&& Objects.equals(mDeviceId, that.mDeviceId);
}
+ private boolean isSameIcon(Icon iconA, Icon iconB) {
+ // Because we've already rescaled and converted both icons to bitmaps,
+ // we can now directly compare them by bitmap.
+ return (iconA == null && iconB == null)
+ || (iconA != null && iconB != null && iconA.getBitmap().sameAs(iconB.getBitmap()));
+ }
+
@Override
public int hashCode() {
return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
@@ -425,7 +432,7 @@ public final class AssociationInfo implements Parcelable {
dest.writeLong(mTimeApprovedMs);
dest.writeLong(mLastTimeConnectedMs);
dest.writeInt(mSystemDataSyncFlags);
- if (mDeviceIcon != null) {
+ if (Flags.associationDeviceIcon() && mDeviceIcon != null) {
dest.writeInt(1);
mDeviceIcon.writeToParcel(dest, flags);
} else {
@@ -455,7 +462,8 @@ public final class AssociationInfo implements Parcelable {
mTimeApprovedMs = in.readLong();
mLastTimeConnectedMs = in.readLong();
mSystemDataSyncFlags = in.readInt();
- if (in.readInt() == 1) {
+ int deviceIcon = in.readInt();
+ if (Flags.associationDeviceIcon() && deviceIcon == 1) {
mDeviceIcon = Icon.CREATOR.createFromParcel(in);
} else {
mDeviceIcon = null;
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 32cbf326c923..a098a6067491 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -385,6 +385,10 @@ public final class AssociationRequest implements Parcelable {
public void setAssociatedDevice(AssociatedDevice associatedDevice) {
mAssociatedDevice = associatedDevice;
}
+ /** @hide */
+ public void setDeviceIcon(Icon deviceIcon) {
+ mDeviceIcon = deviceIcon;
+ }
/** @hide */
@NonNull
@@ -492,9 +496,10 @@ public final class AssociationRequest implements Parcelable {
/**
* Set the device icon for the self-managed device and to display the icon in the
* self-managed association dialog.
+ * <p>The given device icon will be resized to 24dp x 24dp.
*
- * @throws IllegalArgumentException if the icon is not exactly 24dp by 24dp
- * or if it is {@link Icon#TYPE_URI} or {@link Icon#TYPE_URI_ADAPTIVE_BITMAP}.
+ * @throws IllegalArgumentException if the icon is
+ * {@link Icon#TYPE_URI} or {@link Icon#TYPE_URI_ADAPTIVE_BITMAP}.
* @see #setSelfManaged(boolean)
*/
@NonNull
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index a96ba11eb482..566e78a8de35 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -23,7 +23,6 @@ import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH;
import static android.graphics.drawable.Icon.TYPE_URI;
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
-
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -52,10 +51,10 @@ import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import android.graphics.drawable.VectorDrawable;
import android.net.MacAddress;
import android.os.Binder;
import android.os.Handler;
@@ -110,6 +109,7 @@ import java.util.function.Consumer;
@RequiresFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)
public final class CompanionDeviceManager {
private static final String TAG = "CDM_CompanionDeviceManager";
+ private static final int ICON_TARGET_SIZE = 24;
/** @hide */
@IntDef(prefix = {"RESULT_"}, value = {
@@ -474,10 +474,8 @@ public final class CompanionDeviceManager {
if (Flags.associationDeviceIcon()) {
final Icon deviceIcon = request.getDeviceIcon();
-
- if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) {
- throw new IllegalArgumentException("The size of the device icon must be "
- + "24dp x 24dp to ensure proper display");
+ if (deviceIcon != null) {
+ request.setDeviceIcon(scaleIcon(deviceIcon, mContext));
}
}
@@ -547,10 +545,8 @@ public final class CompanionDeviceManager {
if (Flags.associationDeviceIcon()) {
final Icon deviceIcon = request.getDeviceIcon();
-
- if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) {
- throw new IllegalArgumentException("The size of the device icon must be "
- + "24dp x 24dp to ensure proper display");
+ if (deviceIcon != null) {
+ request.setDeviceIcon(scaleIcon(deviceIcon, mContext));
}
}
@@ -2024,33 +2020,26 @@ public final class CompanionDeviceManager {
}
}
- private boolean isValidIcon(Icon icon, Context context) {
+ private Icon scaleIcon(Icon icon, Context context) {
+ if (icon == null) return null;
if (icon.getType() == TYPE_URI_ADAPTIVE_BITMAP || icon.getType() == TYPE_URI) {
throw new IllegalArgumentException("The URI based Icon is not supported.");
}
- Drawable drawable = icon.loadDrawable(context);
- float density = context.getResources().getDisplayMetrics().density;
+ Bitmap bitmap;
+ Drawable drawable = icon.loadDrawable(context);
if (drawable instanceof BitmapDrawable) {
- Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
-
- float widthDp = bitmap.getWidth() / density;
- float heightDp = bitmap.getHeight() / density;
-
- if (widthDp != 24 || heightDp != 24) {
- return false;
- }
- } else if (drawable instanceof VectorDrawable) {
- VectorDrawable vectorDrawable = (VectorDrawable) drawable;
- float widthDp = vectorDrawable.getIntrinsicWidth() / density;
- float heightDp = vectorDrawable.getIntrinsicHeight() / density;
-
- if (widthDp != 24 || heightDp != 24) {
- return false;
- }
+ bitmap = Bitmap.createScaledBitmap(
+ ((BitmapDrawable) drawable).getBitmap(), ICON_TARGET_SIZE, ICON_TARGET_SIZE,
+ false);
} else {
- throw new IllegalArgumentException("The format of the device icon is unsupported.");
+ bitmap = Bitmap.createBitmap(context.getResources().getDisplayMetrics(),
+ ICON_TARGET_SIZE, ICON_TARGET_SIZE, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
}
- return true;
+
+ return Icon.createWithBitmap(bitmap);
}
}
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/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 9181bd0cb2ed..953ee08800cf 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -771,6 +771,7 @@ public final class ContextHubManager {
*/
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
private IContextHubEndpointDiscoveryCallback createDiscoveryCallback(
+ IContextHubService service,
Executor executor,
HubEndpointDiscoveryCallback callback,
@Nullable String serviceDescriptor) {
@@ -779,6 +780,7 @@ public final class ContextHubManager {
public void onEndpointsStarted(HubEndpointInfo[] hubEndpointInfoList) {
if (hubEndpointInfoList.length == 0) {
Log.w(TAG, "onEndpointsStarted: received empty discovery list");
+ invokeCallbackFinished(service);
return;
}
executor.execute(
@@ -791,6 +793,7 @@ public final class ContextHubManager {
} else {
callback.onEndpointsStarted(discoveryList);
}
+ invokeCallbackFinished(service);
});
}
@@ -798,6 +801,7 @@ public final class ContextHubManager {
public void onEndpointsStopped(HubEndpointInfo[] hubEndpointInfoList, int reason) {
if (hubEndpointInfoList.length == 0) {
Log.w(TAG, "onEndpointsStopped: received empty discovery list");
+ invokeCallbackFinished(service);
return;
}
executor.execute(
@@ -810,8 +814,17 @@ public final class ContextHubManager {
} else {
callback.onEndpointsStopped(discoveryList, reason);
}
+ invokeCallbackFinished(service);
});
}
+
+ private void invokeCallbackFinished(IContextHubService service) {
+ try {
+ service.onDiscoveryCallbackFinished();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
};
}
@@ -873,7 +886,7 @@ public final class ContextHubManager {
Objects.requireNonNull(executor, "executor cannot be null");
Objects.requireNonNull(callback, "callback cannot be null");
IContextHubEndpointDiscoveryCallback iCallback =
- createDiscoveryCallback(executor, callback, null);
+ createDiscoveryCallback(mService, executor, callback, null);
try {
mService.registerEndpointDiscoveryCallbackId(endpointId, iCallback);
} catch (RemoteException e) {
@@ -919,7 +932,7 @@ public final class ContextHubManager {
}
IContextHubEndpointDiscoveryCallback iCallback =
- createDiscoveryCallback(executor, callback, serviceDescriptor);
+ createDiscoveryCallback(mService, executor, callback, serviceDescriptor);
try {
mService.registerEndpointDiscoveryCallbackDescriptor(serviceDescriptor, iCallback);
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index d5b3fa251e82..bb5491d98cf9 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -150,4 +150,8 @@ interface IContextHubService {
// Unregister an endpoint with the context hub
@EnforcePermission("ACCESS_CONTEXT_HUB")
void unregisterEndpointDiscoveryCallback(in IContextHubEndpointDiscoveryCallback callback);
+
+ // Called when a discovery callback is finished executing
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
+ void onDiscoveryCallbackFinished();
}
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
index 0541a96e990e..69b6597d717a 100644
--- a/core/java/android/os/AppZygote.java
+++ b/core/java/android/os/AppZygote.java
@@ -16,15 +16,22 @@
package android.os;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProcessInfo;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.Zygote;
import dalvik.system.VMRuntime;
+import java.util.Map;
+
/**
* AppZygote is responsible for interfacing with an application-specific zygote.
*
@@ -94,12 +101,90 @@ public class AppZygote {
return mAppInfo;
}
+ /**
+ * Start a new process.
+ *
+ * <p>Wrap ZygoteProcess.start with retry logic.
+ *
+ * @param processClass The class to use as the process's main entry
+ * point.
+ * @param niceName A more readable name to use for the process.
+ * @param uid The user-id under which the process will run.
+ * @param gids Additional group-ids associated with the process.
+ * @param runtimeFlags Additional flags.
+ * @param targetSdkVersion The target SDK version for the app.
+ * @param seInfo null-ok SELinux information for the new process.
+ * @param abi non-null the ABI this app should be started with.
+ * @param instructionSet null-ok the instruction set to use.
+ * @param appDataDir null-ok the data directory of the app.
+ * @param packageName null-ok the name of the package this process belongs to.
+ * @param isTopApp Whether the process starts for high priority application.
+ * @param disabledCompatChanges null-ok list of disabled compat changes for the process being
+ * started.
+ * @param pkgDataInfoMap Map from related package names to private data directory
+ * volume UUID and inode number.
+ * @param allowlistedDataInfoList Map from allowlisted package names to private data directory
+ * volume UUID and inode number.
+ * @param zygoteArgs Additional arguments to supply to the Zygote process.
+ * @return An object that describes the result of the attempt to start the process.
+ * @throws RuntimeException on fatal start failure
+ */
+ public final Process.ProcessStartResult startProcess(@NonNull final String processClass,
+ final String niceName,
+ int uid, @Nullable int[] gids,
+ int runtimeFlags, int mountExternal,
+ int targetSdkVersion,
+ @Nullable String seInfo,
+ @NonNull String abi,
+ @Nullable String instructionSet,
+ @Nullable String appDataDir,
+ @Nullable String packageName,
+ boolean isTopApp,
+ @Nullable long[] disabledCompatChanges,
+ @Nullable Map<String, Pair<String, Long>>
+ pkgDataInfoMap,
+ @Nullable Map<String, Pair<String, Long>>
+ allowlistedDataInfoList,
+ @Nullable String[] zygoteArgs) {
+ try {
+ return getProcess().start(processClass,
+ niceName, uid, uid, gids, runtimeFlags, mountExternal,
+ targetSdkVersion, seInfo, abi, instructionSet,
+ appDataDir, null, packageName,
+ /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
+ disabledCompatChanges, pkgDataInfoMap, allowlistedDataInfoList,
+ false, false, false,
+ zygoteArgs);
+ } catch (RuntimeException e) {
+ if (!Flags.appZygoteRetryStart()) {
+ throw e;
+ }
+ final boolean zygote_dead = getProcess().isDead();
+ if (!zygote_dead) {
+ throw e; // Zygote process is alive. Do nothing.
+ }
+ }
+ // Retry here if the previous start fails.
+ Log.w(LOG_TAG, "retry starting process " + niceName);
+ stopZygote();
+ return getProcess().start(processClass,
+ niceName, uid, uid, gids, runtimeFlags, mountExternal,
+ targetSdkVersion, seInfo, abi, instructionSet,
+ appDataDir, null, packageName,
+ /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
+ disabledCompatChanges, pkgDataInfoMap, allowlistedDataInfoList,
+ false, false, false,
+ zygoteArgs);
+ }
+
@GuardedBy("mLock")
private void stopZygoteLocked() {
if (mZygote != null) {
mZygote.close();
// use killProcessGroup() here, so we kill all untracked children as well.
- Process.killProcessGroup(mZygoteUid, mZygote.getPid());
+ if (!mZygote.isDead()) {
+ Process.killProcessGroup(mZygoteUid, mZygote.getPid());
+ }
mZygote = null;
}
}
diff --git a/core/java/android/os/ChildZygoteProcess.java b/core/java/android/os/ChildZygoteProcess.java
index 337a3e279a1a..d8f825a2ee60 100644
--- a/core/java/android/os/ChildZygoteProcess.java
+++ b/core/java/android/os/ChildZygoteProcess.java
@@ -17,6 +17,10 @@
package android.os;
import android.net.LocalSocketAddress;
+import android.system.ErrnoException;
+import android.system.Os;
+
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Represents a connection to a child-zygote process. A child-zygote is spawend from another
@@ -30,9 +34,23 @@ public class ChildZygoteProcess extends ZygoteProcess {
*/
private final int mPid;
- ChildZygoteProcess(LocalSocketAddress socketAddress, int pid) {
+ /**
+ * The UID of the child zygote process.
+ */
+ private final int mUid;
+
+
+ /**
+ * If this zygote process was dead;
+ */
+ private AtomicBoolean mDead;
+
+
+ ChildZygoteProcess(LocalSocketAddress socketAddress, int pid, int uid) {
super(socketAddress, null);
mPid = pid;
+ mUid = uid;
+ mDead = new AtomicBoolean(false);
}
/**
@@ -41,4 +59,22 @@ public class ChildZygoteProcess extends ZygoteProcess {
public int getPid() {
return mPid;
}
+
+ /**
+ * Check if child-zygote process is dead
+ */
+ public boolean isDead() {
+ if (mDead.get()) {
+ return true;
+ }
+ try {
+ if (Os.stat("/proc/" + mPid).st_uid == mUid) {
+ return false;
+ }
+ } catch (ErrnoException e) {
+ // Do nothing, it's dead.
+ }
+ mDead.set(true);
+ return true;
+ }
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 4aa74621bd62..875b9098843f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2831,9 +2831,11 @@ public final class Parcel {
}
}
- private void resetSqaushingState() {
+ private void resetSquashingState() {
if (mAllowSquashing) {
- Slog.wtf(TAG, "allowSquashing wasn't restored.");
+ String error = "allowSquashing wasn't restored.";
+ Slog.wtf(TAG, error);
+ throw new BadParcelableException(error);
}
mWrittenSquashableParcelables = null;
mReadSquashableParcelables = null;
@@ -2950,9 +2952,11 @@ public final class Parcel {
for (int i = 0; i < mReadSquashableParcelables.size(); i++) {
sb.append(mReadSquashableParcelables.keyAt(i)).append(' ');
}
- Slog.wtfStack(TAG, "Map doesn't contain offset "
+ String error = "Map doesn't contain offset "
+ firstAbsolutePos
- + " : contains=" + sb.toString());
+ + " : contains=" + sb.toString();
+ Slog.wtfStack(TAG, error);
+ throw new BadParcelableException(error);
}
return (T) p;
}
@@ -5505,7 +5509,7 @@ public final class Parcel {
private void freeBuffer() {
mFlags = 0;
- resetSqaushingState();
+ resetSquashingState();
if (mOwnsNativeParcelObject) {
nativeFreeBuffer(mNativePtr);
}
@@ -5513,7 +5517,7 @@ public final class Parcel {
}
private void destroy() {
- resetSqaushingState();
+ resetSquashingState();
if (mNativePtr != 0) {
if (mOwnsNativeParcelObject) {
nativeDestroy(mNativePtr);
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index 164561acac32..e3f251e34b45 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -22,7 +22,6 @@ import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
/**
* Writes trace events to the perfetto trace buffer. These trace events can be
@@ -72,7 +71,7 @@ public final class PerfettoTrace {
* @param name The category name.
*/
public Category(String name) {
- this(name, null, null);
+ this(name, "", "");
}
/**
@@ -82,7 +81,7 @@ public final class PerfettoTrace {
* @param tag An atrace tag name that this category maps to.
*/
public Category(String name, String tag) {
- this(name, tag, null);
+ this(name, tag, "");
}
/**
@@ -155,9 +154,6 @@ public final class PerfettoTrace {
}
}
- @FastNative
- private static native void native_event(int type, long tag, String name, long ptr);
-
@CriticalNative
private static native long native_get_process_track_uuid();
@@ -170,176 +166,98 @@ public final class PerfettoTrace {
/**
* Writes a trace message to indicate a given section of code was invoked.
*
- * @param category The perfetto category pointer.
+ * @param category The perfetto category.
* @param eventName The event name to appear in the trace.
- * @param extra The extra arguments.
*/
- public static void instant(Category category, String eventName, PerfettoTrackEventExtra extra) {
+ public static PerfettoTrackEventExtra.Builder instant(Category category, String eventName) {
if (!category.isEnabled()) {
- return;
+ return PerfettoTrackEventExtra.noOpBuilder();
}
- native_event(PERFETTO_TE_TYPE_INSTANT, category.getPtr(), eventName, extra.getPtr());
- extra.reset();
- }
-
- /**
- * Writes a trace message to indicate a given section of code was invoked.
- *
- * @param category The perfetto category.
- * @param eventName The event name to appear in the trace.
- * @param extraConfig Consumer for the extra arguments.
- */
- public static void instant(Category category, String eventName,
- Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
- PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
- extraConfig.accept(extra);
- instant(category, eventName, extra.build());
- }
-
- /**
- * Writes a trace message to indicate a given section of code was invoked.
- *
- * @param category The perfetto category.
- * @param eventName The event name to appear in the trace.
- */
- public static void instant(Category category, String eventName) {
- instant(category, eventName, PerfettoTrackEventExtra.builder().build());
+ return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_INSTANT, category)
+ .setEventName(eventName);
}
/**
* Writes a trace message to indicate the start of a given section of code.
*
- * @param category The perfetto category pointer.
+ * @param category The perfetto category.
* @param eventName The event name to appear in the trace.
- * @param extra The extra arguments.
*/
- public static void begin(Category category, String eventName, PerfettoTrackEventExtra extra) {
+ public static PerfettoTrackEventExtra.Builder begin(Category category, String eventName) {
if (!category.isEnabled()) {
- return;
+ return PerfettoTrackEventExtra.noOpBuilder();
}
- native_event(PERFETTO_TE_TYPE_SLICE_BEGIN, category.getPtr(), eventName, extra.getPtr());
- extra.reset();
- }
-
- /**
- * Writes a trace message to indicate the start of a given section of code.
- *
- * @param category The perfetto category pointer.
- * @param eventName The event name to appear in the trace.
- * @param extraConfig Consumer for the extra arguments.
- */
- public static void begin(Category category, String eventName,
- Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
- PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
- extraConfig.accept(extra);
- begin(category, eventName, extra.build());
- }
-
- /**
- * Writes a trace message to indicate the start of a given section of code.
- *
- * @param category The perfetto category pointer.
- * @param eventName The event name to appear in the trace.
- */
- public static void begin(Category category, String eventName) {
- begin(category, eventName, PerfettoTrackEventExtra.builder().build());
+ return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_BEGIN, category)
+ .setEventName(eventName);
}
/**
* Writes a trace message to indicate the end of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param extra The extra arguments.
+ * @param category The perfetto category.
*/
- public static void end(Category category, PerfettoTrackEventExtra extra) {
+ public static PerfettoTrackEventExtra.Builder end(Category category) {
if (!category.isEnabled()) {
- return;
+ return PerfettoTrackEventExtra.noOpBuilder();
}
- native_event(PERFETTO_TE_TYPE_SLICE_END, category.getPtr(), "", extra.getPtr());
- extra.reset();
- }
-
- /**
- * Writes a trace message to indicate the end of a given section of code.
- *
- * @param category The perfetto category pointer.
- * @param extraConfig Consumer for the extra arguments.
- */
- public static void end(Category category,
- Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
- PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
- extraConfig.accept(extra);
- end(category, extra.build());
- }
-
- /**
- * Writes a trace message to indicate the end of a given section of code.
- *
- * @param category The perfetto category pointer.
- */
- public static void end(Category category) {
- end(category, PerfettoTrackEventExtra.builder().build());
+ return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_END, category);
}
/**
* Writes a trace message to indicate the value of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param extra The extra arguments.
+ * @param category The perfetto category.
+ * @param value The value of the counter.
*/
- public static void counter(Category category, PerfettoTrackEventExtra extra) {
+ public static PerfettoTrackEventExtra.Builder counter(Category category, long value) {
if (!category.isEnabled()) {
- return;
+ return PerfettoTrackEventExtra.noOpBuilder();
}
- native_event(PERFETTO_TE_TYPE_COUNTER, category.getPtr(), "", extra.getPtr());
- extra.reset();
+ return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
+ .setCounter(value);
}
/**
* Writes a trace message to indicate the value of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param extraConfig Consumer for the extra arguments.
+ * @param category The perfetto category.
+ * @param value The value of the counter.
+ * @param trackName The trackName for the event.
*/
- public static void counter(Category category,
- Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
- PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
- extraConfig.accept(extra);
- counter(category, extra.build());
+ public static PerfettoTrackEventExtra.Builder counter(
+ Category category, long value, String trackName) {
+ return counter(category, value).usingProcessCounterTrack(trackName);
}
/**
* Writes a trace message to indicate the value of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param trackName The trackName for the event.
+ * @param category The perfetto category.
* @param value The value of the counter.
*/
- public static void counter(Category category, String trackName, long value) {
- PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
- .usingCounterTrack(trackName, PerfettoTrace.getProcessTrackUuid())
- .setCounter(value)
- .build();
- counter(category, extra);
+ public static PerfettoTrackEventExtra.Builder counter(Category category, double value) {
+ if (!category.isEnabled()) {
+ return PerfettoTrackEventExtra.noOpBuilder();
+ }
+
+ return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
+ .setCounter(value);
}
/**
* Writes a trace message to indicate the value of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param trackName The trackName for the event.
+ * @param category The perfetto category.
* @param value The value of the counter.
+ * @param trackName The trackName for the event.
*/
- public static void counter(Category category, String trackName, double value) {
- PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
- .usingCounterTrack(trackName, PerfettoTrace.getProcessTrackUuid())
- .setCounter(value)
- .build();
- counter(category, extra);
+ public static PerfettoTrackEventExtra.Builder counter(
+ Category category, double value, String trackName) {
+ return counter(category, value).usingProcessCounterTrack(trackName);
}
/**
@@ -360,7 +278,7 @@ public final class PerfettoTrace {
* Returns the process track uuid that can be used as a parent track uuid.
*/
public static long getProcessTrackUuid() {
- if (IS_FLAG_ENABLED) {
+ if (!IS_FLAG_ENABLED) {
return 0;
}
return native_get_process_track_uuid();
@@ -370,7 +288,7 @@ public final class PerfettoTrace {
* Given a thread tid, returns the thread track uuid that can be used as a parent track uuid.
*/
public static long getThreadTrackUuid(long tid) {
- if (IS_FLAG_ENABLED) {
+ if (!IS_FLAG_ENABLED) {
return 0;
}
return native_get_thread_track_uuid(tid);
@@ -380,7 +298,7 @@ public final class PerfettoTrace {
* Activates a trigger by name {@code triggerName} with expiry in {@code ttlMs}.
*/
public static void activateTrigger(String triggerName, int ttlMs) {
- if (IS_FLAG_ENABLED) {
+ if (!IS_FLAG_ENABLED) {
return;
}
native_activate_trigger(triggerName, ttlMs);
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index a219b3b5678b..e034fb3726e3 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -31,6 +31,7 @@ import java.util.function.Supplier;
*/
public final class PerfettoTrackEventExtra {
private static final int DEFAULT_EXTRA_CACHE_SIZE = 5;
+ private static final Builder NO_OP_BUILDER = new NoOpBuilder();
private static final ThreadLocal<PerfettoTrackEventExtra> sTrackEventExtra =
new ThreadLocal<PerfettoTrackEventExtra>() {
@Override
@@ -40,7 +41,6 @@ public final class PerfettoTrackEventExtra {
};
private static final AtomicLong sNamedTrackId = new AtomicLong();
- private boolean mIsInUse;
private CounterInt64 mCounterInt64;
private CounterDouble mCounterDouble;
private Proto mProto;
@@ -123,15 +123,299 @@ public final class PerfettoTrackEventExtra {
}
}
+ public interface Builder {
+ /**
+ * Emits the track event.
+ */
+ void emit();
+
+ /**
+ * Initialize the builder for a new trace event.
+ */
+ Builder init(int traceType, PerfettoTrace.Category category);
+
+ /**
+ * Sets the event name for the track event.
+ *
+ * @param eventName can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code eventName} should be the string itself.
+ * @param args format arguments if {@code eventName} is specified.
+ */
+ Builder setEventName(String eventName, Object... args);
+
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
+ Builder addArg(String name, long val);
+
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
+ Builder addArg(String name, boolean val);
+
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
+ Builder addArg(String name, double val);
+
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ *
+ * @param val can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code val} should be the string itself.
+ * @param args format arguments if {@code val} is specified.
+ */
+ Builder addArg(String name, String val, Object... args);
+
+ /**
+ * Adds a flow with {@code id}.
+ */
+ Builder addFlow(int id);
+
+ /**
+ * Adds a terminating flow with {@code id}.
+ */
+ Builder addTerminatingFlow(int id);
+
+ /**
+ * Adds the events to a named track instead of the thread track where the
+ * event occurred.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingNamedTrack(long parentUuid, String name, Object... args);
+
+ /**
+ * Adds the events to a process scoped named track instead of the thread track where the
+ * event occurred.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingProcessNamedTrack(String name, Object... args);
+
+ /**
+ * Adds the events to a thread scoped named track instead of the thread track where the
+ * event occurred.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingThreadNamedTrack(long tid, String name, Object... args);
+
+ /**
+ * Adds the events to a counter track instead. This is required for
+ * setting counter values.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingCounterTrack(long parentUuid, String name, Object... args);
+
+ /**
+ * Adds the events to a process scoped counter track instead. This is required for
+ * setting counter values.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code eventName} is specified.
+ */
+ Builder usingProcessCounterTrack(String name, Object... args);
+
+ /**
+ * Adds the events to a thread scoped counter track instead. This is required for
+ * setting counter values.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingThreadCounterTrack(long tid, String name, Object... args);
+
+ /**
+ * Sets a long counter value on the event.
+ *
+ */
+ Builder setCounter(long val);
+
+ /**
+ * Sets a double counter value on the event.
+ *
+ */
+ Builder setCounter(double val);
+
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
+ Builder addField(long id, long val);
+
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
+ Builder addField(long id, double val);
+
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ *
+ * @param val can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code val} should be the string itself.
+ * @param args format arguments if {@code val} is specified.
+ */
+ Builder addField(long id, String val, Object... args);
+
+ /**
+ * Begins a proto field with field
+ * Fields can be added from this point and there must be a corresponding
+ * {@link endProto}.
+ *
+ * The proto field is a singleton and all proto fields get added inside the
+ * one {@link beginProto} and {@link endProto} within the {@link Builder}.
+ */
+ Builder beginProto();
+
+ /**
+ * Ends a proto field.
+ */
+ Builder endProto();
+
+ /**
+ * Begins a nested proto field with field id {@code id}.
+ * Fields can be added from this point and there must be a corresponding
+ * {@link endNested}.
+ */
+ Builder beginNested(long id);
+
+ /**
+ * Ends a nested proto field.
+ */
+ Builder endNested();
+ }
+
+ public static final class NoOpBuilder implements Builder {
+ @Override
+ public void emit() {}
+ @Override
+ public Builder init(int traceType, PerfettoTrace.Category category) {
+ return this;
+ }
+ @Override
+ public Builder setEventName(String eventName, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder addArg(String name, long val) {
+ return this;
+ }
+ @Override
+ public Builder addArg(String name, boolean val) {
+ return this;
+ }
+ @Override
+ public Builder addArg(String name, double val) {
+ return this;
+ }
+ @Override
+ public Builder addArg(String name, String val, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder addFlow(int id) {
+ return this;
+ }
+ @Override
+ public Builder addTerminatingFlow(int id) {
+ return this;
+ }
+ @Override
+ public Builder usingNamedTrack(long parentUuid, String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingProcessNamedTrack(String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingThreadNamedTrack(long tid, String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingCounterTrack(long parentUuid, String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingProcessCounterTrack(String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingThreadCounterTrack(long tid, String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder setCounter(long val) {
+ return this;
+ }
+ @Override
+ public Builder setCounter(double val) {
+ return this;
+ }
+ @Override
+ public Builder addField(long id, long val) {
+ return this;
+ }
+ @Override
+ public Builder addField(long id, double val) {
+ return this;
+ }
+ @Override
+ public Builder addField(long id, String val, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder beginProto() {
+ return this;
+ }
+ @Override
+ public Builder endProto() {
+ return this;
+ }
+ @Override
+ public Builder beginNested(long id) {
+ return this;
+ }
+ @Override
+ public Builder endNested() {
+ return this;
+ }
+ }
+
/**
* Builder for Perfetto track event extras.
*/
- public static final class Builder {
+ public static final class BuilderImpl implements Builder {
// For performance reasons, we hold a reference to mExtra as a holder for
// perfetto pointers being added. This way, we avoid an additional list to hold
// the pointers in Java and we can pass them down directly to native code.
private final PerfettoTrackEventExtra mExtra;
+
+ private int mTraceType;
+ private PerfettoTrace.Category mCategory;
+ private String mEventName;
private boolean mIsBuilt;
+
private Builder mParent;
private FieldContainer mCurrentContainer;
@@ -151,16 +435,10 @@ public final class PerfettoTrackEventExtra {
private final Pool<FieldString> mFieldStringCache;
private final Pool<FieldNested> mFieldNestedCache;
private final Pool<Flow> mFlowCache;
- private final Pool<Builder> mBuilderCache;
+ private final Pool<BuilderImpl> mBuilderCache;
- private Builder() {
- this(sTrackEventExtra.get(), null, null);
- }
-
- private Builder(PerfettoTrackEventExtra extra, Builder parent, FieldContainer container) {
- mExtra = extra;
- mParent = parent;
- mCurrentContainer = container;
+ private BuilderImpl() {
+ mExtra = sTrackEventExtra.get();
mNamedTrackCache = mExtra.mNamedTrackCache;
mCounterTrackCache = mExtra.mCounterTrackCache;
@@ -180,25 +458,39 @@ public final class PerfettoTrackEventExtra {
mProto = mExtra.getProto();
}
- /**
- * Builds the track event extra.
- */
- public PerfettoTrackEventExtra build() {
+ @Override
+ public void emit() {
checkParent();
mIsBuilt = true;
+ native_emit(mTraceType, mCategory.getPtr(), mEventName, mExtra.getPtr());
+ // Reset after emitting to free any the extras used to trace the event.
+ mExtra.reset();
+ }
+
+ @Override
+ public Builder init(int traceType, PerfettoTrace.Category category) {
+ mTraceType = traceType;
+ mCategory = category;
+ mEventName = "";
mFieldInt64Cache.reset();
mFieldDoubleCache.reset();
mFieldStringCache.reset();
mFieldNestedCache.reset();
mBuilderCache.reset();
- return mExtra;
+ mExtra.reset();
+ // Reset after on init in case the thread created builders without calling emit
+ return initInternal(this, null);
}
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
+ @Override
+ public Builder setEventName(String eventName, Object... args) {
+ mEventName = toString(eventName, args);
+ return this;
+ }
+
+ @Override
public Builder addArg(String name, long val) {
checkParent();
ArgInt64 arg = mArgInt64Cache.get(name.hashCode());
@@ -211,9 +503,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
+ @Override
public Builder addArg(String name, boolean val) {
checkParent();
ArgBool arg = mArgBoolCache.get(name.hashCode());
@@ -226,9 +516,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
+ @Override
public Builder addArg(String name, double val) {
checkParent();
ArgDouble arg = mArgDoubleCache.get(name.hashCode());
@@ -241,24 +529,20 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- public Builder addArg(String name, String val) {
+ @Override
+ public Builder addArg(String name, String val, Object... args) {
checkParent();
ArgString arg = mArgStringCache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgString(name);
mArgStringCache.put(name.hashCode(), arg);
}
- arg.setValue(val);
+ arg.setValue(toString(val, args));
mExtra.addPerfettoPointer(arg);
return this;
}
- /**
- * Adds a flow with {@code id}.
- */
+ @Override
public Builder addFlow(int id) {
checkParent();
Flow flow = mFlowCache.get(Flow::new);
@@ -267,9 +551,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a terminating flow with {@code id}.
- */
+ @Override
public Builder addTerminatingFlow(int id) {
checkParent();
Flow flow = mFlowCache.get(Flow::new);
@@ -278,12 +560,11 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds the events to a named track instead of the thread track where the
- * event occurred.
- */
- public Builder usingNamedTrack(String name, long parentUuid) {
+ @Override
+ public Builder usingNamedTrack(long parentUuid, String name, Object... args) {
checkParent();
+ name = toString(name, args);
+
NamedTrack track = mNamedTrackCache.get(name.hashCode());
if (track == null || !track.getName().equals(name)) {
track = new NamedTrack(name, parentUuid);
@@ -293,13 +574,21 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds the events to a counter track instead. This is required for
- * setting counter values.
- *
- */
- public Builder usingCounterTrack(String name, long parentUuid) {
+ @Override
+ public Builder usingProcessNamedTrack(String name, Object... args) {
+ return usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), name, args);
+ }
+
+ @Override
+ public Builder usingThreadNamedTrack(long tid, String name, Object... args) {
+ return usingNamedTrack(PerfettoTrace.getThreadTrackUuid(tid), name, args);
+ }
+
+ @Override
+ public Builder usingCounterTrack(long parentUuid, String name, Object... args) {
checkParent();
+ name = toString(name, args);
+
CounterTrack track = mCounterTrackCache.get(name.hashCode());
if (track == null || !track.getName().equals(name)) {
track = new CounterTrack(name, parentUuid);
@@ -309,10 +598,17 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Sets a long counter value on the event.
- *
- */
+ @Override
+ public Builder usingProcessCounterTrack(String name, Object... args) {
+ return usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), name, args);
+ }
+
+ @Override
+ public Builder usingThreadCounterTrack(long tid, String name, Object... args) {
+ return usingCounterTrack(PerfettoTrace.getThreadTrackUuid(tid), name, args);
+ }
+
+ @Override
public Builder setCounter(long val) {
checkParent();
mCounterInt64.setValue(val);
@@ -320,10 +616,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Sets a double counter value on the event.
- *
- */
+ @Override
public Builder setCounter(double val) {
checkParent();
mCounterDouble.setValue(val);
@@ -331,9 +624,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
+ @Override
public Builder addField(long id, long val) {
checkContainer();
FieldInt64 field = mFieldInt64Cache.get(FieldInt64::new);
@@ -342,9 +633,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
+ @Override
public Builder addField(long id, double val) {
checkContainer();
FieldDouble field = mFieldDoubleCache.get(FieldDouble::new);
@@ -353,35 +642,24 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
- public Builder addField(long id, String val) {
+ @Override
+ public Builder addField(long id, String val, Object... args) {
checkContainer();
FieldString field = mFieldStringCache.get(FieldString::new);
- field.setValue(id, val);
+ field.setValue(id, toString(val, args));
mCurrentContainer.addField(field);
return this;
}
- /**
- * Begins a proto field with field
- * Fields can be added from this point and there must be a corresponding
- * {@link endProto}.
- *
- * The proto field is a singleton and all proto fields get added inside the
- * one {@link beginProto} and {@link endProto} within the {@link Builder}.
- */
+ @Override
public Builder beginProto() {
checkParent();
mProto.clearFields();
mExtra.addPerfettoPointer(mProto);
- return mBuilderCache.get(Builder::new).init(this, mProto);
+ return mBuilderCache.get(BuilderImpl::new).initInternal(this, mProto);
}
- /**
- * Ends a proto field.
- */
+ @Override
public Builder endProto() {
if (mParent == null || mCurrentContainer == null) {
throw new IllegalStateException("No proto to end");
@@ -389,22 +667,16 @@ public final class PerfettoTrackEventExtra {
return mParent;
}
- /**
- * Begins a nested proto field with field id {@code id}.
- * Fields can be added from this point and there must be a corresponding
- * {@link endNested}.
- */
+ @Override
public Builder beginNested(long id) {
checkContainer();
FieldNested field = mFieldNestedCache.get(FieldNested::new);
field.setId(id);
mCurrentContainer.addField(field);
- return mBuilderCache.get(Builder::new).init(this, field);
+ return mBuilderCache.get(BuilderImpl::new).initInternal(this, field);
}
- /**
- * Ends a nested proto field.
- */
+ @Override
public Builder endNested() {
if (mParent == null || mCurrentContainer == null) {
throw new IllegalStateException("No nested field to end");
@@ -412,21 +684,15 @@ public final class PerfettoTrackEventExtra {
return mParent;
}
- /**
- * Initializes a {@link Builder}.
- */
- public Builder init(Builder parent, FieldContainer container) {
+ private static String toString(String val, Object... args) {
+ return args == null || args.length == 0 ? val : String.format(val, args);
+ }
+
+ private Builder initInternal(Builder parent, FieldContainer container) {
mParent = parent;
mCurrentContainer = container;
mIsBuilt = false;
- if (mParent == null) {
- if (mExtra.mIsInUse) {
- throw new IllegalStateException("Cannot create a new builder when another"
- + " extra is in use");
- }
- mExtra.mIsInUse = true;
- }
return this;
}
@@ -439,9 +705,8 @@ public final class PerfettoTrackEventExtra {
private void checkParent() {
checkState();
- if (mParent != null) {
- throw new IllegalStateException(
- "This builder has already been used. Create a new builder for another event.");
+ if (!this.equals(mParent)) {
+ throw new IllegalStateException("Operation not supported for proto");
}
}
@@ -458,7 +723,14 @@ public final class PerfettoTrackEventExtra {
* Start a {@link Builder} to build a {@link PerfettoTrackEventExtra}.
*/
public static Builder builder() {
- return sTrackEventExtra.get().mBuilderCache.get(Builder::new).init(null, null);
+ return sTrackEventExtra.get().mBuilderCache.get(BuilderImpl::new).initInternal(null, null);
+ }
+
+ /**
+ * Returns a no-op {@link Builder}. Useful if a category is disabled.
+ */
+ public static Builder noOpBuilder() {
+ return NO_OP_BUILDER;
}
private final RingBuffer<NamedTrack> mNamedTrackCache =
@@ -476,7 +748,7 @@ public final class PerfettoTrackEventExtra {
private final Pool<FieldString> mFieldStringCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<FieldNested> mFieldNestedCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<Flow> mFlowCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
- private final Pool<Builder> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+ private final Pool<BuilderImpl> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
@@ -509,7 +781,6 @@ public final class PerfettoTrackEventExtra {
*/
public void reset() {
native_clear_args(mPtr);
- mIsInUse = false;
}
private CounterInt64 getCounterInt64() {
@@ -1078,4 +1349,6 @@ public final class PerfettoTrackEventExtra {
private static native void native_add_arg(long ptr, long extraPtr);
@CriticalNative
private static native void native_clear_args(long ptr);
+ @FastNative
+ private static native void native_emit(int type, long tag, String name, long ptr);
}
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/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 73a74b2f8abc..2d487b1e77d5 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -1319,6 +1319,6 @@ public class ZygoteProcess {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
}
- return new ChildZygoteProcess(serverAddress, result.pid);
+ return new ChildZygoteProcess(serverAddress, result.pid, uid);
}
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 8b8369890d1b..b12433a73a31 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -150,6 +150,13 @@ flag {
}
flag {
+ name: "app_zygote_retry_start"
+ namespace: "arc_next"
+ description: "Guard the new added retry logic in app zygote."
+ bug: "361799815"
+}
+
+flag {
name: "battery_part_status_api"
is_exported: true
namespace: "phoenix"
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0cfec2cc7314..a22333bd1812 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9314,6 +9314,16 @@ public final class Settings {
"accessibility_autoclick_cursor_area_size";
/**
+ * Setting that specifies whether minor cursor movement will be ignored when
+ * {@link #ACCESSIBILITY_AUTOCLICK_ENABLED} is set.
+ *
+ * @see #ACCESSIBILITY_AUTOCLICK_ENABLED
+ * @hide
+ */
+ public static final String ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT =
+ "accessibility_autoclick_ignore_minor_cursor_movement";
+
+ /**
* Whether or not larger size icons are used for the pointer of mouse/trackpad for
* accessibility.
* (0 = false, 1 = true)
@@ -12869,6 +12879,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..792e6ff52d01 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -43,7 +43,7 @@ flag {
flag {
name: "secure_array_zeroization"
- namespace: "platform_security"
+ namespace: "security"
description: "Enable secure array zeroization"
bug: "320392352"
metadata {
@@ -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/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index b5251db4e539..051885e10132 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -25,6 +25,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
import java.io.Closeable;
import java.util.concurrent.Executor;
@@ -232,6 +233,15 @@ public interface QuickAccessWalletClient extends Closeable {
Drawable getTileIcon();
/**
+ * Returns the user that should receive the wallet intents
+ *
+ * @return UserHandle
+ * @hide
+ */
+ @Nullable
+ UserHandle getUser();
+
+ /**
* Returns the service label specified by {@code android:label} in the service manifest entry.
*
* @hide
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 97a4beff633f..177164296eea 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -243,8 +243,9 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
return null;
}
String packageName = mServiceInfo.getComponentName().getPackageName();
+ int userId = mServiceInfo.getUserId();
String walletActivity = mServiceInfo.getWalletActivity();
- return createIntent(walletActivity, packageName, ACTION_VIEW_WALLET);
+ return createIntent(walletActivity, packageName, userId, ACTION_VIEW_WALLET);
}
@Override
@@ -302,12 +303,15 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
}
String packageName = mServiceInfo.getComponentName().getPackageName();
String settingsActivity = mServiceInfo.getSettingsActivity();
- return createIntent(settingsActivity, packageName, ACTION_VIEW_WALLET_SETTINGS);
+ return createIntent(settingsActivity, packageName, UserHandle.myUserId(),
+ ACTION_VIEW_WALLET_SETTINGS);
}
@Nullable
- private Intent createIntent(@Nullable String activityName, String packageName, String action) {
- PackageManager pm = mContext.getPackageManager();
+ private Intent createIntent(@Nullable String activityName, String packageName,
+ int userId, String action) {
+ Context userContext = mContext.createContextAsUser(UserHandle.of(userId), 0);
+ PackageManager pm = userContext.getPackageManager();
if (TextUtils.isEmpty(activityName)) {
activityName = queryActivityForAction(pm, packageName, action);
}
@@ -361,6 +365,12 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
return mServiceInfo == null ? null : mServiceInfo.getTileIcon();
}
+ @Nullable
+ @Override
+ public UserHandle getUser() {
+ return mServiceInfo == null ? null : UserHandle.of(mServiceInfo.getUserId());
+ }
+
@Override
@Nullable
public CharSequence getServiceLabel() {
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index 8a3f6ceb852b..01de54354a04 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -16,6 +16,10 @@
package android.service.quickaccesswallet;
+import static android.permission.flags.Flags.walletRoleCrossUserEnabled;
+
+import static com.android.permission.flags.Flags.crossUserRoleEnabled;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,10 +36,12 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.os.Binder;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.util.Xml;
import com.android.internal.R;
@@ -59,22 +65,29 @@ class QuickAccessWalletServiceInfo {
private final ServiceInfo mServiceInfo;
private final ServiceMetadata mServiceMetadata;
private final TileServiceMetadata mTileServiceMetadata;
+ private final int mUserId;
private QuickAccessWalletServiceInfo(
@NonNull ServiceInfo serviceInfo,
@NonNull ServiceMetadata metadata,
- @NonNull TileServiceMetadata tileServiceMetadata) {
+ @NonNull TileServiceMetadata tileServiceMetadata,
+ int userId) {
mServiceInfo = serviceInfo;
mServiceMetadata = metadata;
mTileServiceMetadata = tileServiceMetadata;
+ mUserId = userId;
}
@Nullable
static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) {
String defaultAppPackageName = null;
+ int defaultAppUser = UserHandle.myUserId();
+
if (isWalletRoleAvailable(context)) {
- defaultAppPackageName = getDefaultWalletApp(context);
+ Pair<String, Integer> roleAndUser = getDefaultWalletApp(context);
+ defaultAppPackageName = roleAndUser.first;
+ defaultAppUser = roleAndUser.second;
} else {
ComponentName defaultPaymentApp = getDefaultPaymentApp(context);
if (defaultPaymentApp == null) {
@@ -83,7 +96,8 @@ class QuickAccessWalletServiceInfo {
defaultAppPackageName = defaultPaymentApp.getPackageName();
}
- ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultAppPackageName);
+ ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultAppPackageName,
+ defaultAppUser);
if (serviceInfo == null) {
return null;
}
@@ -98,15 +112,32 @@ class QuickAccessWalletServiceInfo {
ServiceMetadata metadata = parseServiceMetadata(context, serviceInfo);
TileServiceMetadata tileServiceMetadata =
new TileServiceMetadata(parseTileServiceMetadata(context, serviceInfo));
- return new QuickAccessWalletServiceInfo(serviceInfo, metadata, tileServiceMetadata);
+ return new QuickAccessWalletServiceInfo(serviceInfo, metadata, tileServiceMetadata,
+ defaultAppUser);
}
- private static String getDefaultWalletApp(Context context) {
+ @NonNull
+ private static Pair<String, Integer> getDefaultWalletApp(Context context) {
+ UserHandle user = UserHandle.of(UserHandle.myUserId());
+
final long token = Binder.clearCallingIdentity();
try {
RoleManager roleManager = context.getSystemService(RoleManager.class);
- List<String> roleHolders = roleManager.getRoleHolders(RoleManager.ROLE_WALLET);
- return roleHolders.isEmpty() ? null : roleHolders.get(0);
+
+ if (walletRoleCrossUserEnabled()
+ && crossUserRoleEnabled()
+ && context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED) {
+ user = roleManager.getActiveUserForRole(RoleManager.ROLE_WALLET);
+ if (user == null) {
+ return new Pair<>(null, user.getIdentifier());
+ }
+ }
+ List<String> roleHolders = roleManager.getRoleHoldersAsUser(RoleManager.ROLE_WALLET,
+ user);
+ return new Pair<>(roleHolders.isEmpty() ? null : roleHolders.get(0),
+ user.getIdentifier());
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -128,15 +159,16 @@ class QuickAccessWalletServiceInfo {
return comp == null ? null : ComponentName.unflattenFromString(comp);
}
- private static ServiceInfo getWalletServiceInfo(Context context, String packageName) {
+ private static ServiceInfo getWalletServiceInfo(Context context, String packageName,
+ int userId) {
Intent intent = new Intent(QuickAccessWalletService.SERVICE_INTERFACE);
intent.setPackage(packageName);
List<ResolveInfo> resolveInfos =
- context.getPackageManager().queryIntentServices(intent,
+ context.getPackageManager().queryIntentServicesAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DEFAULT_ONLY
- | PackageManager.GET_META_DATA);
+ | PackageManager.GET_META_DATA, userId);
return resolveInfos.isEmpty() ? null : resolveInfos.get(0).serviceInfo;
}
@@ -247,6 +279,9 @@ class QuickAccessWalletServiceInfo {
return mServiceInfo.getComponentName();
}
+ int getUserId() {
+ return mUserId;
+ }
/**
* @return the fully qualified name of the activity that hosts the full wallet. If available,
* this intent should be started with the action
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/util/proto/ProtoFieldFilter.java b/core/java/android/util/proto/ProtoFieldFilter.java
new file mode 100644
index 000000000000..9c4144403237
--- /dev/null
+++ b/core/java/android/util/proto/ProtoFieldFilter.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.proto;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.function.Predicate;
+
+/**
+ * A utility class that reads raw protobuf data from an InputStream
+ * and copies only those fields for which a given predicate returns true.
+ *
+ * <p>
+ * This is a low-level approach that does not fully decode fields
+ * (unless necessary to determine lengths). It simply:
+ * <ul>
+ * <li>Parses each field's tag (varint for field number & wire type)</li>
+ * <li>If {@code includeFn(fieldNumber) == true}, copies
+ * the tag bytes and the field bytes directly to the output</li>
+ * <li>Otherwise, skips that field in the input</li>
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * Because we do not re-encode, unknown or unrecognized fields are copied
+ * <i>verbatim</i> and remain exactly as in the input (useful for partial
+ * parsing or partial transformations).
+ * </p>
+ *
+ * <p>
+ * Note: This class only filters based on top-level field numbers. For length-delimited
+ * fields (including nested messages), the entire contents are either copied or skipped
+ * as a single unit. The class is not capable of nested filtering.
+ * </p>
+ *
+ * @hide
+ */
+public class ProtoFieldFilter {
+
+ private static final int BUFFER_SIZE_BYTES = 4096;
+
+ private final Predicate<Integer> mFieldPredicate;
+ // General-purpose buffer for reading proto fields and their data
+ private final byte[] mBuffer;
+ // Buffer specifically designated to hold varint values (max 10 bytes in protobuf encoding)
+ private final byte[] mVarIntBuffer = new byte[10];
+
+ /**
+ * Constructs a ProtoFieldFilter with a predicate that considers depth.
+ *
+ * @param fieldPredicate A predicate returning true if the given fieldNumber should be
+ * included in the output.
+ * @param bufferSize The size of the internal buffer used for processing proto fields.
+ * Larger buffers may improve performance when processing large
+ * length-delimited fields.
+ */
+ public ProtoFieldFilter(Predicate<Integer> fieldPredicate, int bufferSize) {
+ this.mFieldPredicate = fieldPredicate;
+ this.mBuffer = new byte[bufferSize];
+ }
+
+ /**
+ * Constructs a ProtoFieldFilter with a predicate that considers depth and
+ * uses a default buffer size.
+ *
+ * @param fieldPredicate A predicate returning true if the given fieldNumber should be
+ * included in the output.
+ */
+ public ProtoFieldFilter(Predicate<Integer> fieldPredicate) {
+ this(fieldPredicate, BUFFER_SIZE_BYTES);
+ }
+
+ /**
+ * Reads raw protobuf data from {@code in} and writes only those fields
+ * passing {@code includeFn} to {@code out}. The predicate is given
+ * (fieldNumber, wireType) for each encountered field.
+ *
+ * @param in The input stream of protobuf data
+ * @param out The output stream to which we write the filtered protobuf
+ * @throws IOException If reading or writing fails, or if the protobuf data is corrupted
+ */
+ public void filter(InputStream in, OutputStream out) throws IOException {
+ int tagBytesLength;
+ while ((tagBytesLength = readRawVarint(in)) > 0) {
+ // Parse the varint loaded in mVarIntBuffer, through readRawVarint
+ long tagVal = parseVarint(mVarIntBuffer, tagBytesLength);
+ int fieldNumber = (int) (tagVal >>> ProtoStream.FIELD_ID_SHIFT);
+ int wireType = (int) (tagVal & ProtoStream.WIRE_TYPE_MASK);
+
+ if (fieldNumber == 0) {
+ break;
+ }
+ if (mFieldPredicate.test(fieldNumber)) {
+ out.write(mVarIntBuffer, 0, tagBytesLength);
+ copyFieldData(in, out, wireType);
+ } else {
+ skipFieldData(in, wireType);
+ }
+ }
+ }
+
+ /**
+ * Reads a varint (up to 10 bytes) from the stream as raw bytes
+ * and returns it in a byte array. If the stream is at EOF, returns null.
+ *
+ * @param in The input stream
+ * @return the size of the varint bytes moved to mVarIntBuffer
+ * @throws IOException If an error occurs, or if we detect a malformed varint
+ */
+ private int readRawVarint(InputStream in) throws IOException {
+ // We attempt to read 1 byte. If none available => null
+ int b = in.read();
+ if (b < 0) {
+ return 0;
+ }
+ int count = 0;
+ mVarIntBuffer[count++] = (byte) b;
+ // If the continuation bit is set, we continue
+ while ((b & 0x80) != 0) {
+ // read next byte
+ b = in.read();
+ // EOF
+ if (b < 0) {
+ throw new IOException("Malformed varint: reached EOF mid-varint");
+ }
+ // max 10 bytes for varint 64
+ if (count >= 10) {
+ throw new IOException("Malformed varint: too many bytes (max 10)");
+ }
+ mVarIntBuffer[count++] = (byte) b;
+ }
+ return count;
+ }
+
+ /**
+ * Parses a varint from the given raw bytes and returns it as a long.
+ *
+ * @param rawVarint The bytes representing the varint
+ * @param byteLength The number of bytes to read from rawVarint
+ * @return The decoded long value
+ */
+ private static long parseVarint(byte[] rawVarint, int byteLength) throws IOException {
+ long result = 0;
+ int shift = 0;
+ for (int i = 0; i < byteLength; i++) {
+ result |= ((rawVarint[i] & 0x7F) << shift);
+ shift += 7;
+ if (shift > 63) {
+ throw new IOException("Malformed varint: exceeds 64 bits");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Copies the wire data for a single field from {@code in} to {@code out},
+ * assuming we have already read the field's tag.
+ *
+ * @param in The input stream (protobuf data)
+ * @param out The output stream
+ * @param wireType The wire type (0=varint, 1=fixed64, 2=length-delim, 5=fixed32)
+ * @throws IOException if reading/writing fails or data is malformed
+ */
+ private void copyFieldData(InputStream in, OutputStream out, int wireType)
+ throws IOException {
+ switch (wireType) {
+ case ProtoStream.WIRE_TYPE_VARINT:
+ copyVarint(in, out);
+ break;
+ case ProtoStream.WIRE_TYPE_FIXED64:
+ copyFixed(in, out, 8);
+ break;
+ case ProtoStream.WIRE_TYPE_LENGTH_DELIMITED:
+ copyLengthDelimited(in, out);
+ break;
+ case ProtoStream.WIRE_TYPE_FIXED32:
+ copyFixed(in, out, 4);
+ break;
+ // case WIRE_TYPE_START_GROUP:
+ // Not Supported
+ // case WIRE_TYPE_END_GROUP:
+ // Not Supported
+ default:
+ // Error or unrecognized wire type
+ throw new IOException("Unknown or unsupported wire type: " + wireType);
+ }
+ }
+
+ /**
+ * Skips the wire data for a single field from {@code in},
+ * assuming the field's tag was already read.
+ */
+ private void skipFieldData(InputStream in, int wireType) throws IOException {
+ switch (wireType) {
+ case ProtoStream.WIRE_TYPE_VARINT:
+ skipVarint(in);
+ break;
+ case ProtoStream.WIRE_TYPE_FIXED64:
+ skipBytes(in, 8);
+ break;
+ case ProtoStream.WIRE_TYPE_LENGTH_DELIMITED:
+ skipLengthDelimited(in);
+ break;
+ case ProtoStream.WIRE_TYPE_FIXED32:
+ skipBytes(in, 4);
+ break;
+ // case WIRE_TYPE_START_GROUP:
+ // Not Supported
+ // case WIRE_TYPE_END_GROUP:
+ // Not Supported
+ default:
+ throw new IOException("Unknown or unsupported wire type: " + wireType);
+ }
+ }
+
+ /** Copies a varint (the field's value) from in to out. */
+ private static void copyVarint(InputStream in, OutputStream out) throws IOException {
+ while (true) {
+ int b = in.read();
+ if (b < 0) {
+ throw new IOException("EOF while copying varint");
+ }
+ out.write(b);
+ if ((b & 0x80) == 0) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Copies exactly {@code length} bytes from {@code in} to {@code out}.
+ */
+ private void copyFixed(InputStream in, OutputStream out,
+ int length) throws IOException {
+ int toRead = length;
+ while (toRead > 0) {
+ int chunk = Math.min(toRead, mBuffer.length);
+ int readCount = in.read(mBuffer, 0, chunk);
+ if (readCount < 0) {
+ throw new IOException("EOF while copying fixed" + (length * 8) + " field");
+ }
+ out.write(mBuffer, 0, readCount);
+ toRead -= readCount;
+ }
+ }
+
+ /** Copies a length-delimited field */
+ private void copyLengthDelimited(InputStream in,
+ OutputStream out) throws IOException {
+ // 1) read length varint (and copy)
+ int lengthVarintLength = readRawVarint(in);
+ if (lengthVarintLength <= 0) {
+ throw new IOException("EOF reading length for length-delimited field");
+ }
+ out.write(mVarIntBuffer, 0, lengthVarintLength);
+
+ long lengthVal = parseVarint(mVarIntBuffer, lengthVarintLength);
+ if (lengthVal < 0 || lengthVal > Integer.MAX_VALUE) {
+ throw new IOException("Invalid length for length-delimited field: " + lengthVal);
+ }
+
+ // 2) copy that many bytes
+ copyFixed(in, out, (int) lengthVal);
+ }
+
+ /** Skips a varint in the input (does not write anything). */
+ private static void skipVarint(InputStream in) throws IOException {
+ int bytesSkipped = 0;
+ while (true) {
+ int b = in.read();
+ if (b < 0) {
+ throw new IOException("EOF while skipping varint");
+ }
+ if ((b & 0x80) == 0) {
+ break;
+ }
+ bytesSkipped++;
+ if (bytesSkipped > 10) {
+ throw new IOException("Malformed varint: exceeds maximum length of 10 bytes");
+ }
+ }
+ }
+
+ /** Skips exactly n bytes. */
+ private void skipBytes(InputStream in, long n) throws IOException {
+ long skipped = in.skip(n);
+ // If skip fails, fallback to reading the remaining bytes
+ if (skipped < n) {
+ long bytesRemaining = n - skipped;
+
+ while (bytesRemaining > 0) {
+ int bytesToRead = (int) Math.min(bytesRemaining, mBuffer.length);
+ int bytesRead = in.read(mBuffer, 0, bytesToRead);
+ if (bytesRemaining < 0) {
+ throw new IOException("EOF while skipping bytes");
+ }
+ bytesRemaining -= bytesRead;
+ }
+ }
+ }
+
+ /**
+ * Skips a length-delimited field.
+ * 1) read the length as varint,
+ * 2) skip that many bytes
+ */
+ private void skipLengthDelimited(InputStream in) throws IOException {
+ int lengthVarintLength = readRawVarint(in);
+ if (lengthVarintLength <= 0) {
+ throw new IOException("EOF reading length for length-delimited field");
+ }
+ long lengthVal = parseVarint(mVarIntBuffer, lengthVarintLength);
+ if (lengthVal < 0 || lengthVal > Integer.MAX_VALUE) {
+ throw new IOException("Invalid length to skip: " + lengthVal);
+ }
+ skipBytes(in, lengthVal);
+ }
+
+}
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/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 64277b14098d..d267c9451d1a 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -160,6 +160,9 @@ public final class AccessibilityManager {
/** @hide */
public static final int AUTOCLICK_CURSOR_AREA_INCREMENT_SIZE = 20;
+ /** @hide */
+ public static final boolean AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT = false;
+
/**
* Activity action: Launch UI to manage which accessibility service or feature is assigned
* to the navigation bar Accessibility button.
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/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index ed6ec32fca25..3cc0042f2bcc 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.view.accessibility.Flags.triStateChecked;
+
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -209,6 +211,10 @@ public abstract class CompoundButton extends Button implements Checkable {
mCheckedFromResource = false;
mChecked = checked;
refreshDrawableState();
+ if (triStateChecked()) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_CHECKED);
+ }
// Avoid infinite recursions if setChecked() is called from a listener
if (mBroadcasting) {
@@ -490,7 +496,12 @@ public abstract class CompoundButton extends Button implements Checkable {
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
info.setCheckable(true);
- info.setChecked(mChecked);
+ if (triStateChecked()) {
+ info.setChecked(mChecked ? AccessibilityNodeInfo.CHECKED_STATE_TRUE :
+ AccessibilityNodeInfo.CHECKED_STATE_FALSE);
+ } else {
+ info.setChecked(mChecked);
+ }
}
@Override
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/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 26b0d11955d2..f5f4e4332d28 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -62,7 +62,7 @@ public class CoreDocument {
// We also keep a more fine-grained BUILD number, exposed as
// ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
- static final float BUILD = 0.2f;
+ static final float BUILD = 0.3f;
@NonNull ArrayList<Operation> mOperations = new ArrayList<>();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 9a37a22390a2..0b6a3c415e4a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -88,6 +88,8 @@ import com.android.internal.widget.remotecompose.core.operations.layout.TouchUpM
import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleColumnLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
@@ -97,6 +99,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostNamedActionOperation;
@@ -111,6 +114,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerExpressionChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueStringChangeActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
@@ -208,7 +212,9 @@ public class Operations {
public static final int LAYOUT_CONTENT = 201;
public static final int LAYOUT_BOX = 202;
public static final int LAYOUT_ROW = 203;
+ public static final int LAYOUT_COLLAPSIBLE_ROW = 230;
public static final int LAYOUT_COLUMN = 204;
+ public static final int LAYOUT_COLLAPSIBLE_COLUMN = 233;
public static final int LAYOUT_CANVAS = 205;
public static final int LAYOUT_CANVAS_CONTENT = 207;
public static final int LAYOUT_TEXT = 208;
@@ -218,6 +224,8 @@ public class Operations {
public static final int MODIFIER_WIDTH = 16;
public static final int MODIFIER_HEIGHT = 67;
+ public static final int MODIFIER_WIDTH_IN = 231;
+ public static final int MODIFIER_HEIGHT_IN = 232;
public static final int MODIFIER_BACKGROUND = 55;
public static final int MODIFIER_BORDER = 107;
public static final int MODIFIER_PADDING = 58;
@@ -324,6 +332,8 @@ public class Operations {
map.put(MODIFIER_WIDTH, WidthModifierOperation::read);
map.put(MODIFIER_HEIGHT, HeightModifierOperation::read);
+ map.put(MODIFIER_WIDTH_IN, WidthInModifierOperation::read);
+ map.put(MODIFIER_HEIGHT_IN, HeightInModifierOperation::read);
map.put(MODIFIER_PADDING, PaddingModifierOperation::read);
map.put(MODIFIER_BACKGROUND, BackgroundModifierOperation::read);
map.put(MODIFIER_BORDER, BorderModifierOperation::read);
@@ -359,7 +369,9 @@ public class Operations {
map.put(LAYOUT_CONTENT, LayoutComponentContent::read);
map.put(LAYOUT_BOX, BoxLayout::read);
map.put(LAYOUT_COLUMN, ColumnLayout::read);
+ map.put(LAYOUT_COLLAPSIBLE_COLUMN, CollapsibleColumnLayout::read);
map.put(LAYOUT_ROW, RowLayout::read);
+ map.put(LAYOUT_COLLAPSIBLE_ROW, CollapsibleRowLayout::read);
map.put(LAYOUT_CANVAS, CanvasLayout::read);
map.put(LAYOUT_CANVAS_CONTENT, CanvasContent::read);
map.put(LAYOUT_TEXT, TextLayout::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 39cc997fadc2..1cb8fefde80c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -86,6 +86,8 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LoopOper
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleColumnLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
@@ -691,6 +693,12 @@ public class RemoteComposeBuffer {
return out;
}
+ /**
+ * Append a path to an existing path
+ *
+ * @param id id of the path to append to
+ * @param path the path to append
+ */
public void pathAppend(int id, float... path) {
PathAppend.apply(mBuffer, id, path);
}
@@ -772,8 +780,8 @@ public class RemoteComposeBuffer {
* @param text The text to be drawn
* @param start The index of the first character in text to draw
* @param end (end - 1) is the index of the last character in text to draw
- * @param contextStart
- * @param contextEnd
+ * @param contextStart the context start
+ * @param contextEnd the context end
* @param x The x-coordinate of the origin of the text being drawn
* @param y The y-coordinate of the baseline of the text being drawn
* @param rtl Draw RTTL
@@ -798,8 +806,8 @@ public class RemoteComposeBuffer {
* @param textId The text to be drawn
* @param start The index of the first character in text to draw
* @param end (end - 1) is the index of the last character in text to draw
- * @param contextStart
- * @param contextEnd
+ * @param contextStart the context start
+ * @param contextEnd the context end
* @param x The x-coordinate of the origin of the text being drawn
* @param y The y-coordinate of the baseline of the text being drawn
* @param rtl Draw RTTL
@@ -986,6 +994,11 @@ public class RemoteComposeBuffer {
///////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * inflate the buffer into a list of operations
+ *
+ * @param operations the operations list to add to
+ */
public void inflateFromBuffer(@NonNull ArrayList<Operation> operations) {
mBuffer.setIndex(0);
while (mBuffer.available()) {
@@ -1001,6 +1014,12 @@ public class RemoteComposeBuffer {
}
}
+ /**
+ * Read the next operation from the buffer
+ *
+ * @param buffer The buff to read
+ * @param operations the operations list to add to
+ */
public static void readNextOperation(
@NonNull WireBuffer buffer, @NonNull ArrayList<Operation> operations) {
int opId = buffer.readByte();
@@ -1014,6 +1033,11 @@ public class RemoteComposeBuffer {
operation.read(buffer, operations);
}
+ /**
+ * copy the current buffer to a new one
+ *
+ * @return A new RemoteComposeBuffer
+ */
@NonNull
RemoteComposeBuffer copy() {
ArrayList<Operation> operations = new ArrayList<>();
@@ -1022,6 +1046,11 @@ public class RemoteComposeBuffer {
return copyFromOperations(operations, buffer);
}
+ /**
+ * add a set theme
+ *
+ * @param theme The theme to set
+ */
public void setTheme(int theme) {
Theme.apply(mBuffer, theme);
}
@@ -1040,6 +1069,14 @@ public class RemoteComposeBuffer {
return buffer;
}
+ /**
+ * Create a RemoteComposeBuffer from a file
+ *
+ * @param file A file
+ * @param remoteComposeState The RemoteComposeState
+ * @return A RemoteComposeBuffer
+ * @throws IOException if the file cannot be read
+ */
@NonNull
public RemoteComposeBuffer fromFile(
@NonNull File file, @NonNull RemoteComposeState remoteComposeState) throws IOException {
@@ -1048,6 +1085,13 @@ public class RemoteComposeBuffer {
return buffer;
}
+ /**
+ * Create a RemoteComposeBuffer from an InputStream
+ *
+ * @param inputStream An InputStream
+ * @param remoteComposeState The RemoteComposeState
+ * @return A RemoteComposeBuffer
+ */
@NonNull
public static RemoteComposeBuffer fromInputStream(
@NonNull InputStream inputStream, @NonNull RemoteComposeState remoteComposeState) {
@@ -1056,6 +1100,13 @@ public class RemoteComposeBuffer {
return buffer;
}
+ /**
+ * Create a RemoteComposeBuffer from an array of operations
+ *
+ * @param operations An array of operations
+ * @param buffer A RemoteComposeBuffer
+ * @return A RemoteComposeBuffer
+ */
@NonNull
RemoteComposeBuffer copyFromOperations(
@NonNull ArrayList<Operation> operations, @NonNull RemoteComposeBuffer buffer) {
@@ -1834,12 +1885,12 @@ public class RemoteComposeBuffer {
/**
* Add a marquee modifier
*
- * @param iterations
- * @param animationMode
- * @param repeatDelayMillis
- * @param initialDelayMillis
- * @param spacing
- * @param velocity
+ * @param iterations number of iterations
+ * @param animationMode animation mode
+ * @param repeatDelayMillis repeat delay
+ * @param initialDelayMillis initial delay
+ * @param spacing spacing between items
+ * @param velocity velocity of the marquee
*/
public void addModifierMarquee(
int iterations,
@@ -1861,14 +1912,21 @@ public class RemoteComposeBuffer {
/**
* Add a graphics layer
*
- * @param scaleX
- * @param scaleY
- * @param rotationX
- * @param rotationY
- * @param rotationZ
- * @param shadowElevation
- * @param transformOriginX
- * @param transformOriginY
+ * @param scaleX scale x
+ * @param scaleY scale y
+ * @param rotationX rotation in X
+ * @param rotationY rotation in Y
+ * @param rotationZ rotation in Z
+ * @param shadowElevation shadow elevation
+ * @param transformOriginX transform origin x
+ * @param transformOriginY transform origin y
+ * @param alpha alpha value
+ * @param cameraDistance camera distance
+ * @param blendMode blend mode
+ * @param spotShadowColorId spot shadow color
+ * @param ambientShadowColorId ambient shadow color
+ * @param colorFilterId id of color filter
+ * @param renderEffectId id of render effect
*/
public void addModifierGraphicsLayer(
float scaleX,
@@ -1923,14 +1981,32 @@ public class RemoteComposeBuffer {
ClipRectModifierOperation.apply(mBuffer);
}
+ /**
+ * add start of loop
+ *
+ * @param indexId id of the variable
+ * @param from start value
+ * @param step step value
+ * @param until stop value
+ */
public void addLoopStart(int indexId, float from, float step, float until) {
LoopOperation.apply(mBuffer, indexId, from, step, until);
}
+ /** Add a loop end */
public void addLoopEnd() {
ContainerEnd.apply(mBuffer);
}
+ /**
+ * add a state layout
+ *
+ * @param componentId id of the state
+ * @param animationId animation id
+ * @param horizontal horizontal alignment
+ * @param vertical vertical alignment
+ * @param indexId index of the state
+ */
public void addStateLayout(
int componentId, int animationId, int horizontal, int vertical, int indexId) {
mLastComponentId = getComponentId(componentId);
@@ -1966,6 +2042,22 @@ public class RemoteComposeBuffer {
}
/**
+ * Add a row start tag
+ *
+ * @param componentId component id
+ * @param animationId animation id
+ * @param horizontal horizontal alignment
+ * @param vertical vertical alignment
+ * @param spacedBy spacing between items
+ */
+ public void addCollapsibleRowStart(
+ int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
+ mLastComponentId = getComponentId(componentId);
+ CollapsibleRowLayout.apply(
+ mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
+ }
+
+ /**
* Add a column start tag
*
* @param componentId component id
@@ -1981,6 +2073,22 @@ public class RemoteComposeBuffer {
}
/**
+ * Add a column start tag
+ *
+ * @param componentId component id
+ * @param animationId animation id
+ * @param horizontal horizontal alignment
+ * @param vertical vertical alignment
+ * @param spacedBy spacing between items
+ */
+ public void addCollapsibleColumnStart(
+ int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
+ mLastComponentId = getComponentId(componentId);
+ CollapsibleColumnLayout.apply(
+ mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
+ }
+
+ /**
* Add a canvas start tag
*
* @param componentId component id
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 43f8ea7dc78f..363b82bdf70c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -67,8 +67,8 @@ public class RemoteComposeState implements CollectionsAccess {
* Get Object based on id. The system will cache things like bitmaps Paths etc. They can be
* accessed with this command
*
- * @param id
- * @return
+ * @param id the id of the object
+ * @return the object
*/
@Nullable
public Object getFromId(int id) {
@@ -78,8 +78,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* true if the cache contain this id
*
- * @param id
- * @return
+ * @param id the id of the object
+ * @return true if the cache contain this id
*/
public boolean containsId(int id) {
return mIntDataMap.get(id) != null;
@@ -138,8 +138,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Get the path asociated with the Data
*
- * @param id
- * @return
+ * @param id of path
+ * @return path object
*/
public Object getPath(int id) {
return mPathMap.get(id);
@@ -180,7 +180,7 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Adds a data Override.
*
- * @param id
+ * @param id the id of the data
* @param item the new value
*/
public void overrideData(int id, @NonNull Object item) {
@@ -222,8 +222,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Adds a float Override.
*
- * @param id
- * @param value the new value
+ * @param id The id of the float
+ * @param value the override value
*/
public void overrideFloat(int id, float value) {
float previous = mFloatMap.get(id);
@@ -235,7 +235,12 @@ public class RemoteComposeState implements CollectionsAccess {
}
}
- /** Insert an item in the cache */
+ /**
+ * Insert an item in the cache
+ *
+ * @param item integer item to cache
+ * @return the id of the integer
+ */
public int cacheInteger(int item) {
int id = nextId();
mIntegerMap.put(id, item);
@@ -243,7 +248,12 @@ public class RemoteComposeState implements CollectionsAccess {
return id;
}
- /** Insert an integer item in the cache */
+ /**
+ * Insert an integer item in the cache
+ *
+ * @param id the id of the integer
+ * @param value the value of the integer
+ */
public void updateInteger(int id, int value) {
if (!mIntegerOverride[id]) {
int previous = mIntegerMap.get(id);
@@ -292,10 +302,10 @@ public class RemoteComposeState implements CollectionsAccess {
}
/**
- * Get the float value
+ * Get the color from the cache
*
- * @param id
- * @return
+ * @param id The id of the color
+ * @return The color
*/
public int getColor(int id) {
return mColorMap.get(id);
@@ -377,6 +387,9 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Method to determine if a cached value has been written to the documents WireBuffer based on
* its id.
+ *
+ * @param id id to check
+ * @return true if the value has not been written to the WireBuffer
*/
public boolean wasNotWritten(int id) {
return !mIntWrittenMap.get(id);
@@ -406,7 +419,7 @@ public class RemoteComposeState implements CollectionsAccess {
* Get the next available id 0 is normal (float,int,String,color) 1 is VARIABLES 2 is
* collections
*
- * @return
+ * @return return a unique id in the set
*/
public int nextId(int type) {
if (0 == type) {
@@ -418,7 +431,7 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Set the next id
*
- * @param id
+ * @param id set the id to increment off of
*/
public void setNextId(int id) {
mNextId = id;
@@ -440,8 +453,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Commands that listen to variables add themselves.
*
- * @param id
- * @param variableSupport
+ * @param id id of variable to listen to
+ * @param variableSupport command that listens to variable
*/
public void listenToVar(int id, @NonNull VariableSupport variableSupport) {
add(id, variableSupport);
@@ -450,8 +463,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Is any command listening to this variable
*
- * @param id
- * @return
+ * @param id The Variable id
+ * @return true if any command is listening to this variable
*/
public boolean hasListener(int id) {
return mVarListeners.get(id) != null;
@@ -460,8 +473,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* List of Commands that need to be updated
*
- * @param context
- * @return
+ * @param context The context
+ * @return The number of ops to update
*/
public int getOpsToUpdate(@NonNull RemoteContext context) {
if (mVarListeners.get(RemoteContext.ID_CONTINUOUS_SEC) != null) {
@@ -479,7 +492,7 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Set the width of the overall document on screen.
*
- * @param width
+ * @param width the width of the document in pixels
*/
public void setWindowWidth(float width) {
updateFloat(RemoteContext.ID_WINDOW_WIDTH, width);
@@ -488,12 +501,18 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Set the width of the overall document on screen.
*
- * @param height
+ * @param height the height of the document in pixels
*/
public void setWindowHeight(float height) {
updateFloat(RemoteContext.ID_WINDOW_HEIGHT, height);
}
+ /**
+ * Add an array access
+ *
+ * @param id The id of the array Access
+ * @param collection The array access
+ */
public void addCollection(int id, @NonNull ArrayAccess collection) {
mCollectionMap.put(id & 0xFFFFF, collection);
}
@@ -513,10 +532,22 @@ public class RemoteComposeState implements CollectionsAccess {
return mCollectionMap.get(id & 0xFFFFF).getId(index);
}
+ /**
+ * adds a DataMap to the cache
+ *
+ * @param id The id of the data map
+ * @param map The data map
+ */
public void putDataMap(int id, @NonNull DataMap map) {
mDataMapMap.put(id, map);
}
+ /**
+ * Get the DataMap asociated with the id
+ *
+ * @param id the id of the DataMap
+ * @return the DataMap
+ */
public @Nullable DataMap getDataMap(int id) {
return mDataMapMap.get(id);
}
@@ -526,15 +557,32 @@ public class RemoteComposeState implements CollectionsAccess {
return mCollectionMap.get(id & 0xFFFFF).getLength();
}
+ /**
+ * sets the RemoteContext
+ *
+ * @param context the context
+ */
public void setContext(@NonNull RemoteContext context) {
mRemoteContext = context;
mRemoteContext.clearLastOpCount();
}
+ /**
+ * Add an object to the cache. Uses the id for the item and adds it to the cache based
+ *
+ * @param id the id of the object
+ * @param value the object
+ */
public void updateObject(int id, @NonNull Object value) {
mObjectMap.put(id, value);
}
+ /**
+ * Get an object from the cache
+ *
+ * @param id The id of the object
+ * @return The object
+ */
public @Nullable Object getObject(int id) {
return mObjectMap.get(id);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 23c362830713..36e4ec1ff303 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -47,7 +47,7 @@ public abstract class RemoteContext {
new RemoteComposeState(); // todo, is this a valid use of RemoteComposeState -- bbade@
@Nullable protected PaintContext mPaintContext = null;
- protected float mDensity = 2.75f;
+ protected float mDensity = Float.NaN;
@NonNull ContextMode mMode = ContextMode.UNSET;
@@ -77,7 +77,7 @@ public abstract class RemoteContext {
* @param density
*/
public void setDensity(float density) {
- if (density > 0) {
+ if (!Float.isNaN(density) && density > 0) {
mDensity = density;
}
}
@@ -234,23 +234,60 @@ public abstract class RemoteContext {
*/
public abstract void addCollection(int id, @NonNull ArrayAccess collection);
+ /**
+ * put DataMap under an id
+ *
+ * @param id the id of the DataMap
+ * @param map the DataMap
+ */
public abstract void putDataMap(int id, @NonNull DataMap map);
+ /**
+ * Get a DataMap given an id
+ *
+ * @param id the id of the DataMap
+ * @return the DataMap
+ */
public abstract @Nullable DataMap getDataMap(int id);
+ /**
+ * Run an action
+ *
+ * @param id the id of the action
+ * @param metadata the metadata of the action
+ */
public abstract void runAction(int id, @NonNull String metadata);
// TODO: we might add an interface to group all valid parameter types
+
+ /**
+ * Run an action with a named parameter
+ *
+ * @param textId the text id of the action
+ * @param value the value of the parameter
+ */
public abstract void runNamedAction(int textId, Object value);
+ /**
+ * Put an object under an id
+ *
+ * @param mId the id of the object
+ * @param command the object
+ */
public abstract void putObject(int mId, @NonNull Object command);
+ /**
+ * Get an object given an id
+ *
+ * @param mId the id of the object
+ * @return the object
+ */
public abstract @Nullable Object getObject(int mId);
/**
* Add a touch listener to the context
*
- * @param touchExpression
+ * @param touchExpression the touch expression
*/
public void addTouchListener(TouchListener touchExpression) {}
@@ -668,11 +705,24 @@ public abstract class RemoteContext {
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Is this a time id float
+ *
+ * @param fl the floatId to test
+ * @return true if it is a time id
+ */
public static boolean isTime(float fl) {
int value = Utils.idFromNan(fl);
return value >= ID_CONTINUOUS_SEC && value <= ID_DAY_OF_MONTH;
}
+ /**
+ * get the time from a float id that indicates a type of time
+ *
+ * @param fl id of the type of time information requested
+ * @return various time information such as seconds or min
+ */
public static float getTime(float fl) {
LocalDateTime dateTime =
LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly?
@@ -716,6 +766,17 @@ public abstract class RemoteContext {
return fl;
}
+ /**
+ * Add a click area to the doc
+ *
+ * @param id the id of the click area
+ * @param contentDescription the content description of the click area
+ * @param left the left bounds of the click area
+ * @param top the top bounds of the click area
+ * @param right the right bounds of the click area
+ * @param bottom the
+ * @param metadataId the id of the metadata string
+ */
public abstract void addClickArea(
int id,
int contentDescription,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
index b55f25c911fe..06ef9979a267 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
@@ -102,6 +102,12 @@ public class ClipPath extends PaintOperation {
return OP_CODE;
}
+ /**
+ * Apply this operation to the buffer
+ *
+ * @param buffer the buffer to apply the operation to
+ * @param id the id of the path
+ */
public static void apply(@NonNull WireBuffer buffer, int id) {
buffer.start(OP_CODE);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
index ac6271c6328e..7a72b109b2a8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
@@ -70,6 +70,13 @@ public class DataListFloat extends Operation implements VariableSupport, ArrayAc
return "DataListFloat[" + Utils.idString(mId) + "] " + Arrays.toString(mValues);
}
+ /**
+ * Write this operation to the buffer
+ *
+ * @param buffer the buffer to apply the operation to
+ * @param id the id of the array
+ * @param values the values of the array
+ */
public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] values) {
buffer.start(OP_CODE);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
index 47cbff36d492..7e29620ec104 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
@@ -62,6 +62,13 @@ public class DataListIds extends Operation implements VariableSupport, ArrayAcce
return "map[" + Utils.idString(mId) + "] \"" + Arrays.toString(mIds) + "\"";
}
+ /**
+ * Write this operation to the buffer
+ *
+ * @param buffer the buffer to apply the operation to
+ * @param id the id of the array
+ * @param ids the values of the array
+ */
public static void apply(@NonNull WireBuffer buffer, int id, @NonNull int[] ids) {
buffer.start(OP_CODE);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
index ff85721027f7..33752e0b2134 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
@@ -89,6 +89,15 @@ public class DataMapIds extends Operation {
return builder.toString();
}
+ /**
+ * Write this operation to the buffer
+ *
+ * @param buffer the buffer to apply the operation to
+ * @param id the id
+ * @param names the names of the variables
+ * @param type the types of the variables
+ * @param ids the ids of the variables
+ */
public static void apply(
@NonNull WireBuffer buffer,
int id,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
index c1e2e662ca80..7f1ba6f94065 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -31,8 +31,8 @@ import java.util.List;
/** Base class for commands that take 3 float */
public abstract class DrawBase2 extends PaintOperation implements VariableSupport {
@NonNull protected String mName = "DrawRectBase";
- float mV1;
- float mV2;
+ protected float mV1;
+ protected float mV2;
float mValue1;
float mValue2;
@@ -76,6 +76,13 @@ public abstract class DrawBase2 extends PaintOperation implements VariableSuppor
return mName + " " + floatToString(mV1) + " " + floatToString(mV2);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param maker the maker of the operation
+ * @param buffer the buffer to read
+ * @param operations the list of operations to add to
+ */
public static void read(
@NonNull Maker maker, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float v1 = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
index 6fedea3245a2..a6bfda8beccd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -92,6 +92,13 @@ public abstract class DrawBase3 extends PaintOperation implements VariableSuppor
+ floatToString(mV3);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param maker the maker of the operation
+ * @param buffer the buffer to read
+ * @param operations the list of operations to add to
+ */
public static void read(
@NonNull Maker maker, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float v1 = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
index aa9cc68e6552..1e96bcd9cebf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
@@ -102,6 +102,13 @@ public abstract class DrawBase4 extends PaintOperation implements VariableSuppor
+ floatToString(mY2Value, mY2);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param maker the maker of the operation
+ * @param buffer the buffer to read
+ * @param operations the list of operations to add to
+ */
public static void read(
@NonNull Maker maker, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float v1 = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
index 64c2730e5f9a..bc5904584527 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
@@ -116,6 +116,13 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor
DrawBase6 create(float v1, float v2, float v3, float v4, float v5, float v6);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param build interface to construct the component
+ * @param buffer the buffer to read from
+ * @param operations the list of operations to add to
+ */
public static void read(
@NonNull Maker build, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float sv1 = buffer.readFloat();
@@ -132,13 +139,13 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor
/**
* writes out a the operation to the buffer.
*
- * @param v1
- * @param v2
- * @param v3
- * @param v4
- * @param v5
- * @param v6
- * @return
+ * @param v1 the first parameter
+ * @param v2 the second parameter
+ * @param v3 the third parameter
+ * @param v4 the fourth parameter
+ * @param v5 the fifth parameter
+ * @param v6 the sixth parameter
+ * @return the operation
*/
@Nullable
public Operation construct(float v1, float v2, float v3, float v4, float v5, float v6) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
index cdb527dee460..40d3bede0912 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
@@ -137,6 +137,17 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer the buffer to write to
+ * @param id the id of the Bitmap
+ * @param left left most x coordinate
+ * @param top top most y coordinate
+ * @param right right most x coordinate
+ * @param bottom bottom most y coordinate
+ * @param descriptionId string id of the description
+ */
public static void apply(
@NonNull WireBuffer buffer,
int id,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
index 638fe148d746..013dd1ae9db8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
@@ -131,6 +131,21 @@ public class DrawBitmapInt extends PaintOperation implements AccessibleComponent
return OP_CODE;
}
+ /**
+ * Draw a bitmap using integer coordinates
+ *
+ * @param buffer the buffer to write to
+ * @param imageId the id of the bitmap
+ * @param srcLeft the left most pixel in the bitmap
+ * @param srcTop the top most pixel in the bitmap
+ * @param srcRight the right most pixel in the bitmap
+ * @param srcBottom the bottom most pixel in the bitmap
+ * @param dstLeft the left most pixel in the destination
+ * @param dstTop the top most pixel in the destination
+ * @param dstRight the right most pixel in the destination
+ * @param dstBottom the bottom most pixel in the destination
+ * @param cdId the content discription id
+ */
public static void apply(
@NonNull WireBuffer buffer,
int imageId,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
index d6467c926747..e1070f97d5aa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
@@ -217,6 +217,23 @@ public class DrawBitmapScaled extends PaintOperation
return OP_CODE;
}
+ /**
+ * Draw a bitmap using integer coordinates
+ *
+ * @param buffer the buffer to write to
+ * @param imageId the id of the image
+ * @param srcLeft the left most pixel in the image to draw
+ * @param srcTop the right most pixel in the image to draw
+ * @param srcRight the right most pixel in the image to draw
+ * @param srcBottom the bottom most pixel in the image to draw
+ * @param dstLeft the left most pixel in the destination
+ * @param dstTop the top most pixel in the destination
+ * @param dstRight the right most pixel in the destination
+ * @param dstBottom the bottom most pixel in the destination
+ * @param scaleType the type of scale operation
+ * @param scaleFactor the scalefactor to use with fixed scale
+ * @param cdId the content discription id
+ */
public static void apply(
@NonNull WireBuffer buffer,
int imageId,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
index 398cf4892e12..db9c4d3efafa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
@@ -81,6 +81,12 @@ public class DrawPath extends PaintOperation {
return Operations.DRAW_PATH;
}
+ /**
+ * Draw a path
+ *
+ * @param buffer the buffer to write to
+ * @param id the id of the path
+ */
public static void apply(@NonNull WireBuffer buffer, int id) {
buffer.start(Operations.DRAW_PATH);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
index 86f3c992f2fb..3ab4a87c614c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
@@ -117,6 +117,15 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
return Operations.DRAW_TEXT_ON_PATH;
}
+ /**
+ * add a draw text on path operation to the buffer
+ *
+ * @param buffer the buffer to add to
+ * @param textId the id of the text string
+ * @param pathId the id of the path
+ * @param hOffset the horizontal offset to position the string
+ * @param vOffset the vertical offset to position the string
+ */
public static void apply(
@NonNull WireBuffer buffer, int textId, int pathId, float hOffset, float vOffset) {
buffer.start(OP_CODE);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
index d4d4a5ecf6b9..e2883949022c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
@@ -127,6 +127,16 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
return Operations.DRAW_TWEEN_PATH;
}
+ /**
+ * add a draw tween path operation to the buffer
+ *
+ * @param buffer the buffer to add to
+ * @param path1Id the first path
+ * @param path2Id the second path
+ * @param tween the amount of the tween
+ * @param start the start sub range to draw
+ * @param stop the end of the sub range to draw
+ */
public static void apply(
@NonNull WireBuffer buffer,
int path1Id,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
index 044430d1e3c1..66daa13dd21c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
@@ -74,6 +74,11 @@ public class MatrixRestore extends PaintOperation {
return OP_CODE;
}
+ /**
+ * add a matrix restore operation to the buffer
+ *
+ * @param buffer the buffer to add to
+ */
public static void apply(@NonNull WireBuffer buffer) {
buffer.start(OP_CODE);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
index aec316aea361..ec918e8260b9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
@@ -72,6 +72,11 @@ public class MatrixSave extends PaintOperation {
return OP_CODE;
}
+ /**
+ * add a matrix save operation to the buffer
+ *
+ * @param buffer the buffer to add to
+ */
public static void apply(@NonNull WireBuffer buffer) {
buffer.start(Operations.MATRIX_SAVE);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
index daf2c5502c5d..f756b76b86c3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
@@ -32,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.paint.PaintBund
import java.util.List;
+/** Paint data operation */
public class PaintData extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.PAINT_VALUES;
private static final String CLASS_NAME = "PaintData";
@@ -80,6 +81,12 @@ public class PaintData extends PaintOperation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * add a paint data to the buffer
+ *
+ * @param buffer the buffer to add to
+ * @param paintBundle the paint bundle
+ */
public static void apply(@NonNull WireBuffer buffer, @NonNull PaintBundle paintBundle) {
buffer.start(Operations.PAINT_VALUES);
paintBundle.writeBundle(buffer);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
index 7ff879e41cac..e7cce03f0c4b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
@@ -101,6 +101,7 @@ public class PathAppend extends PaintOperation implements VariableSupport {
public static final int CUBIC = 14;
public static final int CLOSE = 15;
public static final int DONE = 16;
+ public static final int RESET = 17;
public static final float MOVE_NAN = Utils.asNan(MOVE);
public static final float LINE_NAN = Utils.asNan(LINE);
public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC);
@@ -108,6 +109,7 @@ public class PathAppend extends PaintOperation implements VariableSupport {
public static final float CUBIC_NAN = Utils.asNan(CUBIC);
public static final float CLOSE_NAN = Utils.asNan(CLOSE);
public static final float DONE_NAN = Utils.asNan(DONE);
+ public static final float RESET_NAN = Utils.asNan(RESET);
/**
* The name of the class
@@ -128,6 +130,14 @@ public class PathAppend extends PaintOperation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * add a path append operation to the buffer. With PathCreate allows you create a path
+ * dynamically
+ *
+ * @param buffer add the data to this buffer
+ * @param id id of the path
+ * @param data the path data to append
+ */
public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] data) {
buffer.start(OP_CODE);
buffer.writeInt(id);
@@ -175,6 +185,10 @@ public class PathAppend extends PaintOperation implements VariableSupport {
public void apply(@NonNull RemoteContext context) {
float[] data = context.getPathData(mInstanceId);
float[] out = mOutputPath;
+ if (Float.floatToRawIntBits(out[0]) == Float.floatToRawIntBits(RESET_NAN)) {
+ context.loadPathData(mInstanceId, new float[0]);
+ return;
+ }
if (data != null) {
out = new float[data.length + mOutputPath.length];
@@ -190,6 +204,12 @@ public class PathAppend extends PaintOperation implements VariableSupport {
context.loadPathData(mInstanceId, out);
}
+ /**
+ * Convert a path to a string
+ *
+ * @param path the path to convert
+ * @return text representation of path
+ */
@NonNull
public static String pathString(@Nullable float[] path) {
if (path == null) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
index 75562cd8fb4c..1f76639b1b1f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
@@ -131,6 +131,14 @@ public class PathCreate extends PaintOperation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * add a create path operation
+ *
+ * @param buffer buffer to add to
+ * @param id the id of the path
+ * @param startX the start x of the path (moveTo x,y)
+ * @param startY the start y of the path (moveTo x,y)
+ */
public static void apply(@NonNull WireBuffer buffer, int id, float startX, float startY) {
buffer.start(OP_CODE);
buffer.writeInt(id);
@@ -165,6 +173,12 @@ public class PathCreate extends PaintOperation implements VariableSupport {
.field(FLOAT, "startX", "initial start y");
}
+ /**
+ * convert a path to a string
+ *
+ * @param path path to convert (expressed as an array of floats)
+ * @return the text representing the path
+ */
@NonNull
public static String pathString(@Nullable float[] path) {
if (path == null) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index 85a01fc7cbc7..45d99a716443 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -131,6 +131,13 @@ public class PathData extends Operation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * add a create path operation
+ *
+ * @param buffer buffer to add to
+ * @param id the id of the path
+ * @param data the path
+ */
public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] data) {
buffer.start(Operations.DATA_PATH);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index d48de37996ee..5788d8f4da64 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -73,6 +73,13 @@ public class TextData extends Operation implements SerializableToString {
return OP_CODE;
}
+ /**
+ * add a text data operation
+ *
+ * @param buffer buffer to add to
+ * @param textId the id for the text
+ * @param text the data to encode
+ */
public static void apply(@NonNull WireBuffer buffer, int textId, @NonNull String text) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
index 37ea567f5913..a6570a371f15 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
@@ -50,6 +50,11 @@ public class TextLength extends Operation {
return CLASS_NAME + "[" + mLengthId + "] = " + mTextId;
}
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
public static @NonNull String name() {
return CLASS_NAME;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
index d51b38924126..58cd68e2a5db 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
@@ -66,6 +66,11 @@ public class TextMeasure extends PaintOperation {
return "FloatConstant[" + mId + "] = " + mTextId + " " + mType;
}
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
public static @NonNull String name() {
return CLASS_NAME;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index e71cb9a51830..dcd334822010 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -36,10 +36,12 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DimensionModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
@@ -204,6 +206,9 @@ public class LayoutComponent extends Component {
mPaddingRight = 0f;
mPaddingBottom = 0f;
+ WidthInModifierOperation widthInConstraints = null;
+ HeightInModifierOperation heightInConstraints = null;
+
for (OperationInterface op : mComponentModifiers.getList()) {
if (op instanceof PaddingModifierOperation) {
// We are accumulating padding modifiers to compute the margin
@@ -221,6 +226,10 @@ public class LayoutComponent extends Component {
mWidthModifier = (WidthModifierOperation) op;
} else if (op instanceof HeightModifierOperation && mHeightModifier == null) {
mHeightModifier = (HeightModifierOperation) op;
+ } else if (op instanceof WidthInModifierOperation) {
+ widthInConstraints = (WidthInModifierOperation) op;
+ } else if (op instanceof HeightInModifierOperation) {
+ heightInConstraints = (HeightInModifierOperation) op;
} else if (op instanceof ZIndexModifierOperation) {
mZIndexModifier = (ZIndexModifierOperation) op;
} else if (op instanceof GraphicsLayerModifierOperation) {
@@ -241,6 +250,12 @@ public class LayoutComponent extends Component {
if (mHeightModifier == null) {
mHeightModifier = new HeightModifierOperation(DimensionModifierOperation.Type.WRAP);
}
+ if (widthInConstraints != null) {
+ mWidthModifier.setWidthIn(widthInConstraints);
+ }
+ if (heightInConstraints != null) {
+ mHeightModifier.setHeightIn(heightInConstraints);
+ }
setWidth(computeModifierDefinedWidth(null));
setHeight(computeModifierDefinedHeight(null));
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
new file mode 100644
index 000000000000..afc41b1873ef
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+
+import java.util.List;
+
+public class CollapsibleColumnLayout extends ColumnLayout {
+
+ public CollapsibleColumnLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ super(
+ parent,
+ componentId,
+ animationId,
+ x,
+ y,
+ width,
+ height,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
+ }
+
+ public CollapsibleColumnLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ super(
+ parent,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
+ }
+
+ @NonNull
+ @Override
+ protected String getSerializedName() {
+ return "COLLAPSIBLE_COLUMN";
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return Operations.LAYOUT_COLLAPSIBLE_COLUMN;
+ }
+
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer wire buffer
+ * @param componentId component id
+ * @param animationId animation id (-1 if not set)
+ * @param horizontalPositioning horizontal positioning rules
+ * @param verticalPositioning vertical positioning rules
+ * @param spacedBy spaced by value
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ buffer.start(id());
+ buffer.writeInt(componentId);
+ buffer.writeInt(animationId);
+ buffer.writeInt(horizontalPositioning);
+ buffer.writeInt(verticalPositioning);
+ buffer.writeFloat(spacedBy);
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int componentId = buffer.readInt();
+ int animationId = buffer.readInt();
+ int horizontalPositioning = buffer.readInt();
+ int verticalPositioning = buffer.readInt();
+ float spacedBy = buffer.readFloat();
+ operations.add(
+ new CollapsibleColumnLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy));
+ }
+
+ @Override
+ protected boolean hasVerticalIntrinsicDimension() {
+ return true;
+ }
+
+ @Override
+ public void computeWrapSize(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @NonNull Size size) {
+ super.computeWrapSize(
+ context, maxWidth, Float.MAX_VALUE, horizontalWrap, verticalWrap, measure, size);
+ }
+
+ @Override
+ public boolean applyVisibility(
+ float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
+ float childrenWidth = 0f;
+ float childrenHeight = 0f;
+ boolean changedVisibility = false;
+ for (Component child : mChildrenComponents) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
+ if (childrenHeight + childMeasure.getH() > selfHeight) {
+ childMeasure.setVisibility(Visibility.GONE);
+ changedVisibility = true;
+ } else {
+ childrenHeight += childMeasure.getH();
+ childrenWidth = Math.max(childrenWidth, childMeasure.getW());
+ }
+ }
+ return changedVisibility;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
new file mode 100644
index 000000000000..0e7eb8676f46
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+
+import java.util.List;
+
+public class CollapsibleRowLayout extends RowLayout {
+
+ public CollapsibleRowLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ super(
+ parent,
+ componentId,
+ animationId,
+ x,
+ y,
+ width,
+ height,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
+ }
+
+ public CollapsibleRowLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ super(
+ parent,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
+ }
+
+ @NonNull
+ @Override
+ protected String getSerializedName() {
+ return "COLLAPSIBLE_ROW";
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return Operations.LAYOUT_COLLAPSIBLE_ROW;
+ }
+
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer wire buffer
+ * @param componentId component id
+ * @param animationId animation id (-1 if not set)
+ * @param horizontalPositioning horizontal positioning rules
+ * @param verticalPositioning vertical positioning rules
+ * @param spacedBy spaced by value
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ buffer.start(id());
+ buffer.writeInt(componentId);
+ buffer.writeInt(animationId);
+ buffer.writeInt(horizontalPositioning);
+ buffer.writeInt(verticalPositioning);
+ buffer.writeFloat(spacedBy);
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int componentId = buffer.readInt();
+ int animationId = buffer.readInt();
+ int horizontalPositioning = buffer.readInt();
+ int verticalPositioning = buffer.readInt();
+ float spacedBy = buffer.readFloat();
+ operations.add(
+ new CollapsibleRowLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy));
+ }
+
+ @Override
+ protected boolean hasHorizontalIntrinsicDimension() {
+ return true;
+ }
+
+ @Override
+ public void computeWrapSize(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @NonNull Size size) {
+ super.computeWrapSize(
+ context, Float.MAX_VALUE, maxHeight, horizontalWrap, verticalWrap, measure, size);
+ }
+
+ @Override
+ public boolean applyVisibility(
+ float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
+ float childrenWidth = 0f;
+ float childrenHeight = 0f;
+ boolean changedVisibility = false;
+ for (Component child : mChildrenComponents) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
+ if (childrenWidth + childMeasure.getW() > selfWidth) {
+ childMeasure.setVisibility(Visibility.GONE);
+ changedVisibility = true;
+ } else {
+ childrenWidth += childMeasure.getW();
+ childrenHeight = Math.max(childrenHeight, childMeasure.getH());
+ }
+ }
+ return changedVisibility;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index f68d7b439578..4d0cbefb0c92 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -32,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LayoutCo
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import java.util.List;
@@ -93,7 +94,8 @@ public class ColumnLayout extends LayoutManager {
@NonNull
@Override
public String toString() {
- return "COLUMN ["
+ return getSerializedName()
+ + " ["
+ mComponentId
+ ":"
+ mAnimationId
@@ -213,41 +215,62 @@ public class ColumnLayout extends LayoutManager {
selfHeight =
mComponentModifiers.getVerticalScrollDimension() - mPaddingTop - mPaddingBottom;
}
- boolean hasWeights = false;
- float totalWeights = 0f;
- for (Component child : mChildrenComponents) {
- ComponentMeasure childMeasure = measure.get(child);
- if (childMeasure.getVisibility() == Visibility.GONE) {
- continue;
- }
- if (child instanceof LayoutComponent
- && ((LayoutComponent) child).getHeightModifier().hasWeight()) {
- hasWeights = true;
- totalWeights += ((LayoutComponent) child).getHeightModifier().getValue();
- } else {
- childrenHeight += childMeasure.getH();
- }
- }
- if (hasWeights) {
- float availableSpace = selfHeight - childrenHeight;
+ boolean checkWeights = true;
+ while (checkWeights) {
+ checkWeights = false;
+ boolean hasWeights = false;
+ float totalWeights = 0f;
for (Component child : mChildrenComponents) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getHeightModifier().hasWeight()) {
- ComponentMeasure childMeasure = measure.get(child);
- if (childMeasure.getVisibility() == Visibility.GONE) {
- continue;
+ hasWeights = true;
+ totalWeights += ((LayoutComponent) child).getHeightModifier().getValue();
+ } else {
+ childrenHeight += childMeasure.getH();
+ }
+ }
+ if (hasWeights) {
+ float availableSpace = selfHeight - childrenHeight;
+ for (Component child : mChildrenComponents) {
+ if (child instanceof LayoutComponent
+ && ((LayoutComponent) child).getHeightModifier().hasWeight()) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
+ float weight = ((LayoutComponent) child).getHeightModifier().getValue();
+ float childHeight = (weight * availableSpace) / totalWeights;
+ HeightInModifierOperation heightInConstraints =
+ ((LayoutComponent) child).getHeightModifier().getHeightIn();
+ if (heightInConstraints != null) {
+ float min = heightInConstraints.getMin();
+ float max = heightInConstraints.getMax();
+ if (min != -1) {
+ childHeight = Math.max(min, childHeight);
+ }
+ if (max != -1) {
+ childHeight = Math.min(max, childHeight);
+ }
+ }
+ childMeasure.setH(childHeight);
+ child.measure(
+ context,
+ childMeasure.getW(),
+ childMeasure.getW(),
+ childMeasure.getH(),
+ childMeasure.getH(),
+ measure);
}
- float weight = ((LayoutComponent) child).getHeightModifier().getValue();
- childMeasure.setH((weight * availableSpace) / totalWeights);
- child.measure(
- context,
- childMeasure.getW(),
- childMeasure.getW(),
- childMeasure.getH(),
- childMeasure.getH(),
- measure);
}
}
+
+ if (applyVisibility(selfWidth, selfHeight, measure) && hasWeights) {
+ checkWeights = true;
+ }
}
childrenHeight = 0f;
@@ -360,6 +383,16 @@ public class ColumnLayout extends LayoutManager {
return Operations.LAYOUT_COLUMN;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer wire buffer
+ * @param componentId component id
+ * @param animationId animation id (-1 if not set)
+ * @param horizontalPositioning horizontal positioning rules
+ * @param verticalPositioning vertical positioning rules
+ * @param spacedBy spaced by value
+ */
public static void apply(
@NonNull WireBuffer buffer,
int componentId,
@@ -367,7 +400,7 @@ public class ColumnLayout extends LayoutManager {
int horizontalPositioning,
int verticalPositioning,
float spacedBy) {
- buffer.start(Operations.LAYOUT_COLUMN);
+ buffer.start(id());
buffer.writeInt(componentId);
buffer.writeInt(animationId);
buffer.writeInt(horizontalPositioning);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index edfd69cbfa96..8b52bbe5cdf8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -43,6 +43,18 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
super(parent, componentId, animationId, x, y, width, height);
}
+ /**
+ * Allows layout managers to override elements visibility
+ *
+ * @param selfWidth intrinsic width of the layout manager content
+ * @param selfHeight intrinsic height of the layout manager content
+ * @param measure measure pass
+ */
+ public boolean applyVisibility(
+ float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
+ return false;
+ }
+
/** Implemented by subclasses to provide a layout/measure pass */
public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
// nothing here
@@ -197,7 +209,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
}
if (!hasWrap) {
- if (hasHorizontalScroll()) {
+ if (hasHorizontalIntrinsicDimension()) {
mCachedWrapSize.setWidth(0f);
mCachedWrapSize.setHeight(0f);
computeWrapSize(
@@ -210,15 +222,19 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
mCachedWrapSize);
float w = mCachedWrapSize.getWidth();
computeSize(context, 0f, w, 0, measuredHeight, measure);
- mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
- } else if (hasVerticalScroll()) {
+ if (hasHorizontalScroll()) {
+ mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
+ }
+ } else if (hasVerticalIntrinsicDimension()) {
mCachedWrapSize.setWidth(0f);
mCachedWrapSize.setHeight(0f);
computeWrapSize(
context, maxWidth, Float.MAX_VALUE, false, false, measure, mCachedWrapSize);
float h = mCachedWrapSize.getHeight();
computeSize(context, 0f, measuredWidth, 0, h, measure);
- mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
+ if (hasVerticalScroll()) {
+ mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
+ }
} else {
float maxChildWidth = measuredWidth - mPaddingLeft - mPaddingRight;
float maxChildHeight = measuredHeight - mPaddingTop - mPaddingBottom;
@@ -246,6 +262,14 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
return mComponentModifiers.hasHorizontalScroll();
}
+ protected boolean hasHorizontalIntrinsicDimension() {
+ return hasHorizontalScroll();
+ }
+
+ protected boolean hasVerticalIntrinsicDimension() {
+ return hasVerticalScroll();
+ }
+
private boolean hasVerticalScroll() {
return mComponentModifiers.hasVerticalScroll();
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index b688f6e4175a..5b35c4c70702 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -32,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LayoutCo
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import java.util.List;
@@ -91,7 +92,8 @@ public class RowLayout extends LayoutManager {
@NonNull
@Override
public String toString() {
- return "ROW ["
+ return getSerializedName()
+ + " ["
+ mComponentId
+ ":"
+ mAnimationId
@@ -212,44 +214,66 @@ public class RowLayout extends LayoutManager {
mComponentModifiers.getVerticalScrollDimension() - mPaddingTop - mPaddingBottom;
}
- boolean hasWeights = false;
- float totalWeights = 0f;
- for (Component child : mChildrenComponents) {
- ComponentMeasure childMeasure = measure.get(child);
- if (childMeasure.getVisibility() == Visibility.GONE) {
- continue;
- }
- if (child instanceof LayoutComponent
- && ((LayoutComponent) child).getWidthModifier().hasWeight()) {
- hasWeights = true;
- totalWeights += ((LayoutComponent) child).getWidthModifier().getValue();
- } else {
- childrenWidth += childMeasure.getW();
- }
- }
+ boolean checkWeights = true;
- // TODO: need to move the weight measuring in the measure function,
- // currently we'll measure unnecessarily
- if (hasWeights) {
- float availableSpace = selfWidth - childrenWidth;
+ while (checkWeights) {
+ checkWeights = false;
+ boolean hasWeights = false;
+ float totalWeights = 0f;
for (Component child : mChildrenComponents) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getWidthModifier().hasWeight()) {
- ComponentMeasure childMeasure = measure.get(child);
- if (childMeasure.getVisibility() == Visibility.GONE) {
- continue;
+ hasWeights = true;
+ totalWeights += ((LayoutComponent) child).getWidthModifier().getValue();
+ } else {
+ childrenWidth += childMeasure.getW();
+ }
+ }
+
+ // TODO: need to move the weight measuring in the measure function,
+ // currently we'll measure unnecessarily
+ if (hasWeights) {
+ float availableSpace = selfWidth - childrenWidth;
+ for (Component child : mChildrenComponents) {
+ if (child instanceof LayoutComponent
+ && ((LayoutComponent) child).getWidthModifier().hasWeight()) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
+ float weight = ((LayoutComponent) child).getWidthModifier().getValue();
+ float childWidth = (weight * availableSpace) / totalWeights;
+ WidthInModifierOperation widthInConstraints =
+ ((LayoutComponent) child).getWidthModifier().getWidthIn();
+ if (widthInConstraints != null) {
+ float min = widthInConstraints.getMin();
+ float max = widthInConstraints.getMax();
+ if (min != -1) {
+ childWidth = Math.max(min, childWidth);
+ }
+ if (max != -1) {
+ childWidth = Math.min(max, childWidth);
+ }
+ }
+ childMeasure.setW(childWidth);
+ child.measure(
+ context,
+ childMeasure.getW(),
+ childMeasure.getW(),
+ childMeasure.getH(),
+ childMeasure.getH(),
+ measure);
}
- float weight = ((LayoutComponent) child).getWidthModifier().getValue();
- childMeasure.setW((weight * availableSpace) / totalWeights);
- child.measure(
- context,
- childMeasure.getW(),
- childMeasure.getW(),
- childMeasure.getH(),
- childMeasure.getH(),
- measure);
}
}
+
+ if (applyVisibility(selfWidth, selfHeight, measure) && hasWeights) {
+ checkWeights = true;
+ }
}
childrenWidth = 0f;
@@ -363,6 +387,16 @@ public class RowLayout extends LayoutManager {
return Operations.LAYOUT_ROW;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer wire buffer
+ * @param componentId component id
+ * @param animationId animation id (-1 if not set)
+ * @param horizontalPositioning horizontal positioning rules
+ * @param verticalPositioning vertical positioning rules
+ * @param spacedBy spaced by value
+ */
public static void apply(
@NonNull WireBuffer buffer,
int componentId,
@@ -370,7 +404,7 @@ public class RowLayout extends LayoutManager {
int horizontalPositioning,
int verticalPositioning,
float spacedBy) {
- buffer.start(Operations.LAYOUT_ROW);
+ buffer.start(id());
buffer.writeInt(componentId);
buffer.writeInt(animationId);
buffer.writeInt(horizontalPositioning);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
new file mode 100644
index 000000000000..c19bd2f6b7c0
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
@@ -0,0 +1,124 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.DrawBase2;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Set the min / max height dimension on a component */
+public class HeightInModifierOperation extends DrawBase2 implements ModifierOperation {
+ private static final int OP_CODE = Operations.MODIFIER_HEIGHT_IN;
+ public static final String CLASS_NAME = "HeightInModifierOperation";
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ Maker m = HeightInModifierOperation::new;
+ read(m, buffer, operations);
+ }
+
+ /**
+ * Returns the min value
+ *
+ * @return minimum value
+ */
+ public float getMin() {
+ return mV1;
+ }
+
+ /**
+ * Returns the max value
+ *
+ * @return maximum value
+ */
+ public float getMax() {
+ return mV2;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ @Override
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
+ apply(buffer, v1, v2);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "HeightInModifierOperation")
+ .description("Add additional constraints to the height")
+ .field(DocumentedOperation.FLOAT, "min", "The minimum height, -1 if not applied")
+ .field(DocumentedOperation.FLOAT, "max", "The maximum height, -1 if not applied");
+ }
+
+ public HeightInModifierOperation(float min, float max) {
+ super(min, max);
+ mName = CLASS_NAME;
+ }
+
+ @Override
+ public void paint(@NonNull PaintContext context) {}
+
+ /**
+ * Writes out the HeightInModifier to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
+ */
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
+ write(buffer, OP_CODE, x1, y1);
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(indent, "HEIGHT_IN = [" + getMin() + ", " + getMax() + "]");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
index ec078a9e73ea..4b50a916b9cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
@@ -31,6 +31,7 @@ import java.util.List;
public class HeightModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_HEIGHT;
public static final String CLASS_NAME = "HeightModifierOperation";
+ private HeightInModifierOperation mHeightIn = null;
/**
* The name of the class
@@ -110,4 +111,22 @@ public class HeightModifierOperation extends DimensionModifierOperation {
.field(INT, "type", "")
.field(FLOAT, "value", "");
}
+
+ /**
+ * Set height in constraints
+ *
+ * @param heightInConstraints height constraints
+ */
+ public void setHeightIn(HeightInModifierOperation heightInConstraints) {
+ mHeightIn = heightInConstraints;
+ }
+
+ /**
+ * Returns height in constraints
+ *
+ * @return height in constraints
+ */
+ public HeightInModifierOperation getHeightIn() {
+ return mHeightIn;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
new file mode 100644
index 000000000000..c3624e5b3d88
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
@@ -0,0 +1,124 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.DrawBase2;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Set the min / max width dimension on a component */
+public class WidthInModifierOperation extends DrawBase2 implements ModifierOperation {
+ private static final int OP_CODE = Operations.MODIFIER_WIDTH_IN;
+ public static final String CLASS_NAME = "WidthInModifierOperation";
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ Maker m = WidthInModifierOperation::new;
+ read(m, buffer, operations);
+ }
+
+ /**
+ * Returns the min value
+ *
+ * @return minimum value
+ */
+ public float getMin() {
+ return mV1;
+ }
+
+ /**
+ * Returns the max value
+ *
+ * @return maximum value
+ */
+ public float getMax() {
+ return mV2;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ @Override
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
+ apply(buffer, v1, v2);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "WidthInModifierOperation")
+ .description("Add additional constraints to the width")
+ .field(DocumentedOperation.FLOAT, "min", "The minimum width, -1 if not applied")
+ .field(DocumentedOperation.FLOAT, "max", "The maximum width, -1 if not applied");
+ }
+
+ public WidthInModifierOperation(float min, float max) {
+ super(min, max);
+ mName = CLASS_NAME;
+ }
+
+ @Override
+ public void paint(@NonNull PaintContext context) {}
+
+ /**
+ * Writes out the WidthInModifier to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
+ */
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
+ write(buffer, OP_CODE, x1, y1);
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(indent, "WIDTH_IN = [" + getMin() + ", " + getMax() + "]");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
index 05305988a49f..532027ab2087 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
@@ -31,6 +31,7 @@ import java.util.List;
public class WidthModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_WIDTH;
public static final String CLASS_NAME = "WidthModifierOperation";
+ private WidthInModifierOperation mWidthIn = null;
/**
* The name of the class
@@ -110,4 +111,22 @@ public class WidthModifierOperation extends DimensionModifierOperation {
.field(INT, "type", "")
.field(FLOAT, "value", "");
}
+
+ /**
+ * Set width in constraints
+ *
+ * @param widthInConstraints width constraints
+ */
+ public void setWidthIn(WidthInModifierOperation widthInConstraints) {
+ mWidthIn = widthInConstraints;
+ }
+
+ /**
+ * Returns width in constraints
+ *
+ * @return width in constraints
+ */
+ public WidthInModifierOperation getWidthIn() {
+ return mWidthIn;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
index b2ea0afd8fab..eb834a97c723 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -150,17 +150,47 @@ public class AnimatedFloatExpression {
/** RAND_SEED operator */
public static final float RAND_SEED = asNan(OFFSET + 40);
+ /** NOISE_FROM operator calculate a random 0..1 number based on a seed */
+ public static final float NOISE_FROM = asNan(OFFSET + 41);
+
+ /** RANDOM_IN_RANGE random number in range */
+ public static final float RAND_IN_RANGE = asNan(OFFSET + 42);
+
+ /** SQUARE_SUM the sum of the square of two numbers */
+ public static final float SQUARE_SUM = asNan(OFFSET + 43);
+
+ /** STEP x > edge ? 1 : 0; */
+ public static final float STEP = asNan(OFFSET + 44);
+
+ /** SQUARE x*x; */
+ public static final float SQUARE = asNan(OFFSET + 45);
+
+ /** DUP x,x; */
+ public static final float DUP = asNan(OFFSET + 46);
+
+ /** HYPOT sqrt(x*x+y*y); */
+ public static final float HYPOT = asNan(OFFSET + 47);
+
+ /** SWAP y,x; */
+ public static final float SWAP = asNan(OFFSET + 48);
+
+ /** LERP (1-t)*x+t*y; */
+ public static final float LERP = asNan(OFFSET + 49);
+
+ /** SMOOTH_STEP (1-smoothstep(edge0,edge1,x)); */
+ public static final float SMOOTH_STEP = asNan(OFFSET + 50);
+
/** LAST valid operator */
- public static final int LAST_OP = OFFSET + 40;
+ public static final int LAST_OP = OFFSET + 50;
/** VAR1 operator */
- public static final float VAR1 = asNan(OFFSET + 41);
+ public static final float VAR1 = asNan(OFFSET + 51);
/** VAR2 operator */
- public static final float VAR2 = asNan(OFFSET + 42);
+ public static final float VAR2 = asNan(OFFSET + 52);
/** VAR2 operator */
- public static final float VAR3 = asNan(OFFSET + 43);
+ public static final float VAR3 = asNan(OFFSET + 53);
// TODO SQUARE, DUP, HYPOT, SWAP
// private static final float FP_PI = (float) Math.PI;
@@ -399,6 +429,17 @@ public class AnimatedFloatExpression {
sNames.put(k++, "RAND");
sNames.put(k++, "RAND_SEED");
+ sNames.put(k++, "noise_from");
+ sNames.put(k++, "rand_in_range");
+ sNames.put(k++, "square_sum");
+ sNames.put(k++, "step");
+ sNames.put(k++, "square");
+ sNames.put(k++, "dup");
+ sNames.put(k++, "hypot");
+ sNames.put(k++, "swap");
+ sNames.put(k++, "lerp");
+ sNames.put(k++, "smooth_step");
+
sNames.put(k++, "a[0]");
sNames.put(k++, "a[1]");
sNames.put(k++, "a[2]");
@@ -615,9 +656,20 @@ public class AnimatedFloatExpression {
private static final int OP_RAND = OFFSET + 39;
private static final int OP_RAND_SEED = OFFSET + 40;
- private static final int OP_FIRST_VAR = OFFSET + 41;
- private static final int OP_SECOND_VAR = OFFSET + 42;
- private static final int OP_THIRD_VAR = OFFSET + 43;
+ private static final int OP_NOISE_FROM = OFFSET + 41;
+ private static final int OP_RAND_IN_RANGE = OFFSET + 42;
+ private static final int OP_SQUARE_SUM = OFFSET + 43;
+ private static final int OP_STEP = OFFSET + 44;
+ private static final int OP_SQUARE = OFFSET + 45;
+ private static final int OP_DUP = OFFSET + 46;
+ private static final int OP_HYPOT = OFFSET + 47;
+ private static final int OP_SWAP = OFFSET + 48;
+ private static final int OP_LERP = OFFSET + 49;
+ private static final int OP_SMOOTH_STEP = OFFSET + 50;
+
+ private static final int OP_FIRST_VAR = OFFSET + 51;
+ private static final int OP_SECOND_VAR = OFFSET + 52;
+ private static final int OP_THIRD_VAR = OFFSET + 53;
int opEval(int sp, int id) {
float[] array;
@@ -824,6 +876,66 @@ public class AnimatedFloatExpression {
}
}
return sp - 1;
+ case OP_NOISE_FROM:
+ int x = Float.floatToRawIntBits(mStack[sp]);
+ x = (x << 13) ^ x; // / Bitwise scrambling return
+ mStack[sp] =
+ (1.0f
+ - ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff)
+ / 1073741824.0f);
+ return sp;
+
+ case OP_RAND_IN_RANGE:
+ if (sRandom == null) {
+ sRandom = new Random();
+ }
+ mStack[sp] = sRandom.nextFloat() * (mStack[sp] - mStack[sp - 1]) + mStack[sp - 1];
+ return sp;
+ case OP_SQUARE_SUM:
+ mStack[sp - 1] = mStack[sp - 1] * mStack[sp - 1] + mStack[sp] * mStack[sp];
+ return sp - 1;
+ case OP_STEP:
+ System.out.println(mStack[sp] + " > " + mStack[sp - 1]);
+ mStack[sp - 1] = (mStack[sp - 1] > mStack[sp]) ? 1f : 0f;
+ return sp - 1;
+ case OP_SQUARE:
+ mStack[sp] = mStack[sp] * mStack[sp];
+ return sp;
+ case OP_DUP:
+ mStack[sp + 1] = mStack[sp];
+ return sp + 1;
+ case OP_HYPOT:
+ mStack[sp - 1] = (float) Math.hypot(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ case OP_SWAP:
+ float swap = mStack[sp - 1];
+ mStack[sp - 1] = mStack[sp];
+ mStack[sp] = swap;
+ return sp;
+ case OP_LERP:
+ float tmp1 = mStack[sp - 2];
+ float tmp2 = mStack[sp - 1];
+ float tmp3 = mStack[sp];
+ mStack[sp - 2] = tmp1 + (tmp2 - tmp1) * tmp3;
+ return sp - 2;
+ case OP_SMOOTH_STEP:
+ float val3 = mStack[sp - 2];
+ float max2 = mStack[sp - 1];
+ float min1 = mStack[sp];
+ System.out.println("val3 = " + val3 + " min1 = " + min1 + " max2 = " + max2);
+ if (val3 < min1) {
+ mStack[sp - 2] = 0f;
+ System.out.println("below min ");
+ } else if (val3 > max2) {
+ mStack[sp - 2] = 1f;
+ System.out.println("above max ");
+
+ } else {
+ float v = (val3 - min1) / (max2 - min1);
+ System.out.println("v = " + v);
+ mStack[sp - 2] = v * v * (3 - 2 * v);
+ }
+ return sp - 2;
case OP_FIRST_VAR:
mStack[sp] = mVar[0];
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
index d8bc83eb8a2e..2b5368297dae 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
@@ -19,24 +19,58 @@ package com.android.internal.widget.remotecompose.core.operations.utilities.easi
public abstract class Easing {
int mType;
- /** get the value at point x */
+ /**
+ * get the value at point x
+ *
+ * @param x the position at which to get the slope
+ * @return the value at the point
+ */
public abstract float get(float x);
- /** get the slope of the easing function at at x */
+ /**
+ * get the slope of the easing function at at x
+ *
+ * @param x the position at which to get the slope
+ * @return the slope
+ */
public abstract float getDiff(float x);
+ /**
+ * get the type of easing function
+ *
+ * @return the type of easing function
+ */
public int getType() {
return mType;
}
+ /** cubic Easing function that accelerates and decelerates */
public static final int CUBIC_STANDARD = 1;
+
+ /** cubic Easing function that accelerates */
public static final int CUBIC_ACCELERATE = 2;
+
+ /** cubic Easing function that decelerates */
public static final int CUBIC_DECELERATE = 3;
+
+ /** cubic Easing function that just linearly interpolates */
public static final int CUBIC_LINEAR = 4;
+
+ /** cubic Easing function that goes bacwards and then accelerates */
public static final int CUBIC_ANTICIPATE = 5;
+
+ /** cubic Easing function that overshoots and then goes back */
public static final int CUBIC_OVERSHOOT = 6;
+
+ /** cubic Easing function that you customize */
public static final int CUBIC_CUSTOM = 11;
+
+ /** a monotonic spline Easing function that you customize */
public static final int SPLINE_CUSTOM = 12;
+
+ /** a bouncing Easing function */
public static final int EASE_OUT_BOUNCE = 13;
+
+ /** a elastic Easing function */
public static final int EASE_OUT_ELASTIC = 14;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
index 465c95d06726..65472c262206 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
@@ -52,16 +52,25 @@ public class FloatAnimation extends Easing {
return str;
}
- public FloatAnimation() {
- mType = CUBIC_STANDARD;
- mEasingCurve = new CubicEasing(mType);
- }
-
+ /**
+ * Create an animation based on a float encoding of the animation
+ *
+ * @param description the float encoding of the animation
+ */
public FloatAnimation(@NonNull float... description) {
mType = CUBIC_STANDARD;
setAnimationDescription(description);
}
+ /**
+ * Create an animation based on the parameters
+ *
+ * @param type The type of animation
+ * @param duration The duration of the animation
+ * @param description The float parameters describing the animation
+ * @param initialValue The initial value of the float (NaN if none)
+ * @param wrap The wrap value of the animation NaN if it does not wrap
+ */
public FloatAnimation(
int type,
float duration,
@@ -139,8 +148,8 @@ public class FloatAnimation extends Easing {
/**
* Useful to debug the packed form of an animation string
*
- * @param description
- * @return
+ * @param description the float encoding of the animation
+ * @return a string describing the animation
*/
public static String unpackAnimationToString(float[] description) {
float[] mSpec = description;
@@ -223,7 +232,7 @@ public class FloatAnimation extends Easing {
/**
* Create an animation based on a float encoding of the animation
*
- * @param description
+ * @param description the float encoding of the animation
*/
public void setAnimationDescription(@NonNull float[] description) {
mSpec = description;
@@ -288,7 +297,7 @@ public class FloatAnimation extends Easing {
/**
* Set the initial Value
*
- * @param value
+ * @param value the value to set
*/
public void setInitialValue(float value) {
@@ -321,7 +330,7 @@ public class FloatAnimation extends Easing {
/**
* Set the target value to interpolate to
*
- * @param value
+ * @param value the value to set
*/
public void setTargetValue(float value) {
mTargetValue = value;
@@ -342,6 +351,11 @@ public class FloatAnimation extends Easing {
setScaleOffset();
}
+ /**
+ * Get the target value
+ *
+ * @return the target value
+ */
public float getTargetValue() {
return mTargetValue;
}
@@ -369,6 +383,11 @@ public class FloatAnimation extends Easing {
return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue);
}
+ /**
+ * Get the initial value
+ *
+ * @return the initial value
+ */
public float getInitialValue() {
return mInitialValue;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
index 06969ccd1b10..960eff2e7242 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
@@ -25,13 +25,18 @@ public class GeneralEasing extends Easing {
/**
* Set the curve based on the float encoding of it
*
- * @param data
+ * @param data the float encoding of the curve
*/
public void setCurveSpecification(@NonNull float[] data) {
mEasingData = data;
createEngine();
}
+ /**
+ * Get the float encoding of the curve
+ *
+ * @return the float encoding of the curve
+ */
public @NonNull float[] getCurveSpecification() {
return mEasingData;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
index f4579a24fd44..01d64dff10f9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
@@ -76,10 +76,10 @@ public class MonotonicCurveFit {
}
/**
- * Get the position of all curves at time t
+ * Get the position of all curves at position t
*
- * @param t
- * @param v
+ * @param t the point on the spline
+ * @param v the array to fill (for multiple curves)
*/
public void getPos(double t, @NonNull double[] v) {
final int n = mT.length;
@@ -136,10 +136,10 @@ public class MonotonicCurveFit {
}
/**
- * Get the position of all curves at time t
+ * Get the position of all curves at position t
*
- * @param t
- * @param v
+ * @param t the point on the spline
+ * @param v the array to fill
*/
public void getPos(double t, @NonNull float[] v) {
final int n = mT.length;
@@ -196,11 +196,11 @@ public class MonotonicCurveFit {
}
/**
- * Get the position of the jth curve at time t
+ * Get the position of the jth curve at position t
*
- * @param t
- * @param j
- * @return
+ * @param t the position
+ * @param j the curve to get
+ * @return the position
*/
public double getPos(double t, int j) {
final int n = mT.length;
@@ -240,8 +240,8 @@ public class MonotonicCurveFit {
/**
* Get the slope of all the curves at position t
*
- * @param t
- * @param v
+ * @param t the position
+ * @param v the array to fill
*/
public void getSlope(double t, @NonNull double[] v) {
final int n = mT.length;
@@ -271,9 +271,9 @@ public class MonotonicCurveFit {
/**
* Get the slope of the j curve at position t
*
- * @param t
- * @param j
- * @return
+ * @param t the position
+ * @param j the curve to get the value at
+ * @return the slope
*/
public double getSlope(double t, int j) {
final int n = mT.length;
@@ -297,6 +297,11 @@ public class MonotonicCurveFit {
return 0; // should never reach here
}
+ /**
+ * Get the time point used to create the curve
+ *
+ * @return the time points used to create the curve
+ */
public @NonNull double[] getTimePoints() {
return mT;
}
@@ -332,7 +337,12 @@ public class MonotonicCurveFit {
+ h * t1;
}
- /** This builds a monotonic spline to be used as a wave function */
+ /**
+ * This builds a monotonic spline to be used as a wave function
+ *
+ * @param configString the configuration string
+ * @return the curve
+ */
@NonNull
public static MonotonicCurveFit buildWave(@NonNull String configString) {
// done this way for efficiency
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java
index 23a664336c5f..8bb7dae2fd6a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java
@@ -76,6 +76,11 @@ public class MonotonicSpline {
mTangent = tangent;
}
+ /**
+ * Get the value point used in the interpolator.
+ *
+ * @return the value points
+ */
public float[] getArray() {
return mY;
}
@@ -83,7 +88,7 @@ public class MonotonicSpline {
/**
* Get the position of all curves at time t
*
- * @param t
+ * @param t the position along spline
* @return position at t
*/
public float getPos(float t) {
@@ -139,7 +144,7 @@ public class MonotonicSpline {
/**
* Get the slope of the curve at position t
*
- * @param t
+ * @param t the position along spline
* @return slope at t
*/
public float getSlope(float t) {
@@ -167,6 +172,11 @@ public class MonotonicSpline {
return v;
}
+ /**
+ * Get the time points used in the interpolator.
+ *
+ * @return the time points
+ */
public float[] getTimePoints() {
return mT;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java
index 03e45031e515..2f1379b3e9fc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java
@@ -42,9 +42,9 @@ public class SpringStopEngine {
private float mStopThreshold;
private int mBoundaryMode = 0;
- public String debug(String desc, float time) {
- return null;
- }
+ // public String debug(String desc, float time) {
+ // return null;
+ // }
void log(String str) {
StackTraceElement s = new Throwable().getStackTrace()[1];
@@ -53,20 +53,41 @@ public class SpringStopEngine {
System.out.println(line + str);
}
+ /** */
public SpringStopEngine() {}
+ /**
+ * get the value the sping is pulling towards
+ *
+ * @return the value the sping is pulling towards
+ */
public float getTargetValue() {
return (float) mTargetPos;
}
+ /**
+ * get the value the sping is starting from
+ *
+ * @param v the value the sping is starting from
+ */
public void setInitialValue(float v) {
mPos = v;
}
+ /**
+ * set the value the sping is pulling towards
+ *
+ * @param v the value the sping is pulling towards
+ */
public void setTargetValue(float v) {
mTargetPos = v;
}
+ /**
+ * Create a sping engine with the parameters encoded as an array of floats
+ *
+ * @param parameters the parameters to use
+ */
public SpringStopEngine(float[] parameters) {
if (parameters[0] != 0) {
throw new RuntimeException(" parameter[0] should be 0");
@@ -83,9 +104,9 @@ public class SpringStopEngine {
/**
* Config the spring starting conditions
*
- * @param currentPos
- * @param target
- * @param currentVelocity
+ * @param currentPos the current position of the spring
+ * @param target the target position of the spring
+ * @param currentVelocity the current velocity of the spring
*/
public void springStart(float currentPos, float target, float currentVelocity) {
mTargetPos = target;
@@ -115,10 +136,22 @@ public class SpringStopEngine {
mLastTime = 0;
}
+ /**
+ * get the velocity of the spring at a time
+ *
+ * @param time the time to get the velocity at
+ * @return the velocity of the spring at a time
+ */
public float getVelocity(float time) {
return (float) mV;
}
+ /**
+ * get the position of the spring at a time
+ *
+ * @param time the time to get the position at
+ * @return the position of the spring at a time
+ */
public float get(float time) {
compute(time - mLastTime);
mLastTime = time;
@@ -128,6 +161,11 @@ public class SpringStopEngine {
return (float) mPos;
}
+ /**
+ * get the acceleration of the spring
+ *
+ * @return the acceleration of the spring
+ */
public float getAcceleration() {
double k = mStiffness;
double c = mDamping;
@@ -135,10 +173,20 @@ public class SpringStopEngine {
return (float) (-k * x - c * mV) / mMass;
}
+ /**
+ * get the velocity of the spring
+ *
+ * @return the velocity of the spring
+ */
public float getVelocity() {
return 0;
}
+ /**
+ * is the spring stopped
+ *
+ * @return true if the spring is stopped
+ */
public boolean isStopped() {
double x = (mPos - mTargetPos);
double k = mStiffness;
@@ -149,6 +197,11 @@ public class SpringStopEngine {
return max_def <= mStopThreshold;
}
+ /**
+ * increment the spring position over time dt
+ *
+ * @param dt the time to increment the spring position over
+ */
private void compute(double dt) {
if (dt <= 0) {
// Nothing to compute if there's no time difference
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
index b1eb8041b0b3..376e1e9179f4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
@@ -26,6 +26,13 @@ public class StepCurve extends Easing {
// private static final boolean DEBUG = false;
@NonNull private final MonotonicCurveFit mCurveFit;
+ /**
+ * Create a step curve from a series of values
+ *
+ * @param params the series of values to ease over
+ * @param offset the offset into the array
+ * @param len the length of the array to use
+ */
public StepCurve(@NonNull float[] params, int offset, int len) {
mCurveFit = genSpline(params, offset, len);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 5de11a19799d..b17e3dc82d50 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -66,6 +66,28 @@ public class RemoteComposePlayer extends FrameLayout {
}
/**
+ * @inheritDoc
+ */
+ public void requestLayout() {
+ super.requestLayout();
+
+ if (mInner != null) {
+ mInner.requestLayout();
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public void invalidate() {
+ super.invalidate();
+
+ if (mInner != null) {
+ mInner.invalidate();
+ }
+ }
+
+ /**
* Returns true if the document supports drag touch events
*
* @return true if draggable content, false otherwise
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 970cc4a44672..334ba62636ff 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -48,7 +48,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
boolean mHasClickAreas = false;
Point mActionDownPoint = new Point(0, 0);
AndroidRemoteContext mARContext = new AndroidRemoteContext();
- float mDensity = 1f;
+ float mDensity = Float.NaN;
long mStart = System.nanoTime();
long mLastFrameDelay = 1;
@@ -68,24 +68,18 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
public RemoteComposeCanvas(Context context) {
super(context);
- if (USE_VIEW_AREA_CLICK) {
- addOnAttachStateChangeListener(this);
- }
+ addOnAttachStateChangeListener(this);
}
public RemoteComposeCanvas(Context context, AttributeSet attrs) {
super(context, attrs);
- if (USE_VIEW_AREA_CLICK) {
- addOnAttachStateChangeListener(this);
- }
+ addOnAttachStateChangeListener(this);
}
public RemoteComposeCanvas(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setBackgroundColor(Color.WHITE);
- if (USE_VIEW_AREA_CLICK) {
- addOnAttachStateChangeListener(this);
- }
+ addOnAttachStateChangeListener(this);
}
public void setDebug(int value) {
@@ -124,6 +118,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mChoreographer.postFrameCallback(mFrameCallback);
}
mDensity = getContext().getResources().getDisplayMetrics().density;
+ mARContext.setDensity(mDensity);
if (mDocument == null) {
return;
}
diff --git a/core/jni/android_os_PerfettoTrace.cpp b/core/jni/android_os_PerfettoTrace.cpp
index 988aea722be3..962aefc482e4 100644
--- a/core/jni/android_os_PerfettoTrace.cpp
+++ b/core/jni/android_os_PerfettoTrace.cpp
@@ -23,6 +23,7 @@
#include <nativehelper/scoped_local_ref.h>
#include <nativehelper/scoped_primitive_array.h>
#include <nativehelper/scoped_utf_chars.h>
+#include <nativehelper/utils.h>
#include <tracing_sdk.h>
namespace android {
@@ -36,30 +37,6 @@ inline static jlong toJLong(T* ptr) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr));
}
-static const char* fromJavaString(JNIEnv* env, jstring jstr) {
- if (!jstr) return "";
- ScopedUtfChars chars(env, jstr);
-
- if (!chars.c_str()) {
- ALOGE("Failed extracting string");
- return "";
- }
-
- return chars.c_str();
-}
-
-static void android_os_PerfettoTrace_event(JNIEnv* env, jclass, jint type, jlong cat_ptr,
- jstring name, jlong extra_ptr) {
- ScopedUtfChars name_utf(env, name);
- if (!name_utf.c_str()) {
- ALOGE("Failed extracting string");
- }
-
- tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(cat_ptr);
- tracing_perfetto::trace_event(type, category->get(), name_utf.c_str(),
- toPointer<tracing_perfetto::Extra>(extra_ptr));
-}
-
static jlong android_os_PerfettoTrace_get_process_track_uuid() {
return tracing_perfetto::get_process_track_uuid();
}
@@ -70,20 +47,18 @@ static jlong android_os_PerfettoTrace_get_thread_track_uuid(jlong tid) {
static void android_os_PerfettoTrace_activate_trigger(JNIEnv* env, jclass, jstring name,
jint ttl_ms) {
- ScopedUtfChars name_utf(env, name);
- if (!name_utf.c_str()) {
- ALOGE("Failed extracting string");
- return;
- }
-
- tracing_perfetto::activate_trigger(name_utf.c_str(), static_cast<uint32_t>(ttl_ms));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN_VOID(env, name);
+ tracing_perfetto::activate_trigger(name_chars.c_str(), static_cast<uint32_t>(ttl_ms));
}
static jlong android_os_PerfettoTraceCategory_init(JNIEnv* env, jclass, jstring name, jstring tag,
jstring severity) {
- return toJLong(new tracing_perfetto::Category(fromJavaString(env, name),
- fromJavaString(env, tag),
- fromJavaString(env, severity)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ ScopedUtfChars tag_chars = GET_UTF_OR_RETURN(env, tag);
+ ScopedUtfChars severity_chars = GET_UTF_OR_RETURN(env, severity);
+
+ return toJLong(new tracing_perfetto::Category(name_chars.c_str(), tag_chars.c_str(),
+ severity_chars.c_str()));
}
static jlong android_os_PerfettoTraceCategory_delete() {
@@ -121,8 +96,7 @@ static const JNINativeMethod gCategoryMethods[] = {
};
static const JNINativeMethod gTraceMethods[] =
- {{"native_event", "(IJLjava/lang/String;J)V", (void*)android_os_PerfettoTrace_event},
- {"native_get_process_track_uuid", "()J",
+ {{"native_get_process_track_uuid", "()J",
(void*)android_os_PerfettoTrace_get_process_track_uuid},
{"native_get_thread_track_uuid", "(J)J",
(void*)android_os_PerfettoTrace_get_thread_track_uuid},
@@ -132,10 +106,11 @@ static const JNINativeMethod gTraceMethods[] =
int register_android_os_PerfettoTrace(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace", gTraceMethods,
NELEM(gTraceMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register perfetto native methods.");
res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace$Category", gCategoryMethods,
NELEM(gCategoryMethods));
- LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register category native methods.");
return 0;
}
diff --git a/core/jni/android_os_PerfettoTrackEventExtra.cpp b/core/jni/android_os_PerfettoTrackEventExtra.cpp
index 9adad7bca940..b8bdc8c29199 100644
--- a/core/jni/android_os_PerfettoTrackEventExtra.cpp
+++ b/core/jni/android_os_PerfettoTrackEventExtra.cpp
@@ -20,6 +20,7 @@
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/scoped_utf_chars.h>
+#include <nativehelper/utils.h>
#include <tracing_sdk.h>
static constexpr ssize_t kMaxStrLen = 4096;
@@ -34,32 +35,24 @@ inline static jlong toJLong(T* ptr) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr));
}
-static const char* fromJavaString(JNIEnv* env, jstring jstr) {
- if (!jstr) return "";
- ScopedUtfChars chars(env, jstr);
-
- if (!chars.c_str()) {
- ALOGE("Failed extracting string");
- return "";
- }
-
- return chars.c_str();
-}
-
static jlong android_os_PerfettoTrackEventExtraArgInt64_init(JNIEnv* env, jclass, jstring name) {
- return toJLong(new tracing_perfetto::DebugArg<int64_t>(fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::DebugArg<int64_t>(name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraArgBool_init(JNIEnv* env, jclass, jstring name) {
- return toJLong(new tracing_perfetto::DebugArg<bool>(fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::DebugArg<bool>(name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraArgDouble_init(JNIEnv* env, jclass, jstring name) {
- return toJLong(new tracing_perfetto::DebugArg<double>(fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::DebugArg<double>(name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraArgString_init(JNIEnv* env, jclass, jstring name) {
- return toJLong(new tracing_perfetto::DebugArg<const char*>(fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::DebugArg<const char*>(name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraArgInt64_delete() {
@@ -116,9 +109,11 @@ static void android_os_PerfettoTrackEventExtraArgDouble_set_value(jlong ptr, jdo
static void android_os_PerfettoTrackEventExtraArgString_set_value(JNIEnv* env, jclass, jlong ptr,
jstring val) {
+ ScopedUtfChars val_chars = GET_UTF_OR_RETURN_VOID(env, val);
+
tracing_perfetto::DebugArg<const char*>* arg =
toPointer<tracing_perfetto::DebugArg<const char*>>(ptr);
- arg->set_value(strdup(fromJavaString(env, val)));
+ arg->set_value(strdup(val_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraFieldInt64_init() {
@@ -191,9 +186,11 @@ static void android_os_PerfettoTrackEventExtraFieldDouble_set_value(jlong ptr, j
static void android_os_PerfettoTrackEventExtraFieldString_set_value(JNIEnv* env, jclass, jlong ptr,
jlong id, jstring val) {
+ ScopedUtfChars val_chars = GET_UTF_OR_RETURN_VOID(env, val);
+
tracing_perfetto::ProtoField<const char*>* field =
toPointer<tracing_perfetto::ProtoField<const char*>>(ptr);
- field->set_value(id, strdup(fromJavaString(env, val)));
+ field->set_value(id, strdup(val_chars.c_str()));
}
static void android_os_PerfettoTrackEventExtraFieldNested_add_field(jlong field_ptr,
@@ -234,7 +231,8 @@ static jlong android_os_PerfettoTrackEventExtraFlow_get_extra_ptr(jlong ptr) {
static jlong android_os_PerfettoTrackEventExtraNamedTrack_init(JNIEnv* env, jclass, jlong id,
jstring name, jlong parent_uuid) {
- return toJLong(new tracing_perfetto::NamedTrack(id, parent_uuid, fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::NamedTrack(id, parent_uuid, name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraNamedTrack_delete() {
@@ -248,8 +246,9 @@ static jlong android_os_PerfettoTrackEventExtraNamedTrack_get_extra_ptr(jlong pt
static jlong android_os_PerfettoTrackEventExtraCounterTrack_init(JNIEnv* env, jclass, jstring name,
jlong parent_uuid) {
- return toJLong(
- new tracing_perfetto::RegisteredTrack(1, parent_uuid, fromJavaString(env, name), true));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+
+ return toJLong(new tracing_perfetto::RegisteredTrack(1, parent_uuid, name_chars.c_str(), true));
}
static jlong android_os_PerfettoTrackEventExtraCounterTrack_delete() {
@@ -317,6 +316,15 @@ static void android_os_PerfettoTrackEventExtra_clear_args(jlong ptr) {
extra->clear_extras();
}
+static void android_os_PerfettoTrackEventExtra_emit(JNIEnv* env, jclass, jint type, jlong cat_ptr,
+ jstring name, jlong extra_ptr) {
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN_VOID(env, name);
+
+ tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(cat_ptr);
+ tracing_perfetto::trace_event(type, category->get(), name_chars.c_str(),
+ toPointer<tracing_perfetto::Extra>(extra_ptr));
+}
+
static jlong android_os_PerfettoTrackEventExtraProto_init() {
return toJLong(new tracing_perfetto::Proto());
}
@@ -344,7 +352,9 @@ static const JNINativeMethod gExtraMethods[] =
{{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtra_init},
{"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtra_delete},
{"native_add_arg", "(JJ)V", (void*)android_os_PerfettoTrackEventExtra_add_arg},
- {"native_clear_args", "(J)V", (void*)android_os_PerfettoTrackEventExtra_clear_args}};
+ {"native_clear_args", "(J)V", (void*)android_os_PerfettoTrackEventExtra_clear_args},
+ {"native_emit", "(IJLjava/lang/String;J)V",
+ (void*)android_os_PerfettoTrackEventExtra_emit}};
static const JNINativeMethod gProtoMethods[] =
{{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraProto_init},
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/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 5d0b340ac839..69c812c6fb41 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -109,6 +109,7 @@ message SecureSettingsProto {
optional SettingProto em_value = 61 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Settings for accessibility autoclick
optional SettingProto autoclick_cursor_area_size = 62 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto autoclick_ignore_minor_cursor_movement = 63 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
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/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index 292f7500479b..ad28383689af 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -112,15 +112,14 @@ public class PerfettoTraceTest {
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(FOO_CATEGORY, "event")
.addFlow(2)
.addTerminatingFlow(3)
.addArg("long_val", 10000000000L)
.addArg("bool_val", true)
.addArg("double_val", 3.14)
.addArg("string_val", FOO)
- .build();
- PerfettoTrace.instant(FOO_CATEGORY, "event", extra);
+ .emit();
byte[] traceBytes = nativeStopTracing(ptr);
@@ -163,12 +162,12 @@ public class PerfettoTraceTest {
@Test
@RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
- public void testDebugAnnotationsWithLamda() throws Exception {
+ public void testDebugAnnotationsWithLambda() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrace.instant(FOO_CATEGORY, "event", e -> e.addArg("long_val", 123L));
+ PerfettoTrace.instant(FOO_CATEGORY, "event").addArg("long_val", 123L).emit();
byte[] traceBytes = nativeStopTracing(ptr);
@@ -203,15 +202,14 @@ public class PerfettoTraceTest {
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra beginExtra = PerfettoTrackEventExtra.builder()
- .usingNamedTrack(FOO, PerfettoTrace.getProcessTrackUuid())
- .build();
- PerfettoTrace.begin(FOO_CATEGORY, "event", beginExtra);
+ PerfettoTrace.begin(FOO_CATEGORY, "event")
+ .usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), FOO)
+ .emit();
- PerfettoTrackEventExtra endExtra = PerfettoTrackEventExtra.builder()
- .usingNamedTrack("bar", PerfettoTrace.getThreadTrackUuid(Process.myTid()))
- .build();
- PerfettoTrace.end(FOO_CATEGORY, endExtra);
+
+ PerfettoTrace.end(FOO_CATEGORY)
+ .usingNamedTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()), "bar")
+ .emit();
Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
@@ -242,26 +240,67 @@ public class PerfettoTraceTest {
assertThat(hasTrackUuid).isTrue();
assertThat(mCategoryNames).contains(FOO);
assertThat(mTrackNames).contains(FOO);
+ assertThat(mTrackNames).contains("bar");
}
@Test
@RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
- public void testCounter() throws Exception {
+ public void testProcessThreadNamedTrack() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra intExtra = PerfettoTrackEventExtra.builder()
- .usingCounterTrack(FOO, PerfettoTrace.getProcessTrackUuid())
- .setCounter(16)
- .build();
- PerfettoTrace.counter(FOO_CATEGORY, intExtra);
+ PerfettoTrace.begin(FOO_CATEGORY, "event")
+ .usingProcessNamedTrack(FOO)
+ .emit();
- PerfettoTrackEventExtra doubleExtra = PerfettoTrackEventExtra.builder()
- .usingCounterTrack("bar", PerfettoTrace.getProcessTrackUuid())
- .setCounter(3.14)
- .build();
- PerfettoTrace.counter(FOO_CATEGORY, doubleExtra);
+
+ PerfettoTrace.end(FOO_CATEGORY)
+ .usingThreadNamedTrack(Process.myTid(), "%s-%s", "bar", "stool")
+ .emit();
+
+ Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+
+ boolean hasTrackEvent = false;
+ boolean hasTrackUuid = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_SLICE_BEGIN.equals(event.getType())
+ && event.hasTrackUuid()) {
+ hasTrackUuid = true;
+ }
+
+ if (TrackEvent.Type.TYPE_SLICE_END.equals(event.getType())
+ && event.hasTrackUuid()) {
+ hasTrackUuid &= true;
+ }
+ }
+
+ collectInternedData(packet);
+ collectTrackNames(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasTrackUuid).isTrue();
+ assertThat(mCategoryNames).contains(FOO);
+ assertThat(mTrackNames).contains(FOO);
+ assertThat(mTrackNames).contains("bar-stool");
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testCounterSimple() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrace.counter(FOO_CATEGORY, 16, FOO).emit();
+
+ PerfettoTrace.counter(FOO_CATEGORY, 3.14, "bar").emit();
Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
@@ -297,12 +336,102 @@ public class PerfettoTraceTest {
@Test
@RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testCounter() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrace.counter(FOO_CATEGORY, 16)
+ .usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), FOO).emit();
+
+ PerfettoTrace.counter(FOO_CATEGORY, 3.14)
+ .usingCounterTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()),
+ "%s-%s", "bar", "stool").emit();
+
+ Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+
+ boolean hasTrackEvent = false;
+ boolean hasCounterValue = false;
+ boolean hasDoubleCounterValue = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getCounterValue() == 16) {
+ hasCounterValue = true;
+ }
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getDoubleCounterValue() == 3.14) {
+ hasDoubleCounterValue = true;
+ }
+ }
+
+ collectTrackNames(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasCounterValue).isTrue();
+ assertThat(hasDoubleCounterValue).isTrue();
+ assertThat(mTrackNames).contains(FOO);
+ assertThat(mTrackNames).contains("bar-stool");
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testProcessThreadCounter() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrace.counter(FOO_CATEGORY, 16).usingProcessCounterTrack(FOO).emit();
+
+ PerfettoTrace.counter(FOO_CATEGORY, 3.14)
+ .usingThreadCounterTrack(Process.myTid(), "%s-%s", "bar", "stool").emit();
+
+ Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+
+ boolean hasTrackEvent = false;
+ boolean hasCounterValue = false;
+ boolean hasDoubleCounterValue = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getCounterValue() == 16) {
+ hasCounterValue = true;
+ }
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getDoubleCounterValue() == 3.14) {
+ hasDoubleCounterValue = true;
+ }
+ }
+
+ collectTrackNames(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasCounterValue).isTrue();
+ assertThat(hasDoubleCounterValue).isTrue();
+ assertThat(mTrackNames).contains(FOO);
+ assertThat(mTrackNames).contains("bar-stool");
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
public void testProto() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra extra5 = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(FOO_CATEGORY, "event_proto")
.beginProto()
.beginNested(33L)
.addField(4L, 2L)
@@ -310,8 +439,7 @@ public class PerfettoTraceTest {
.endNested()
.addField(2001, "AIDL::IActivityManager")
.endProto()
- .build();
- PerfettoTrace.instant(FOO_CATEGORY, "event_proto", extra5);
+ .emit();
byte[] traceBytes = nativeStopTracing(ptr);
@@ -351,7 +479,7 @@ public class PerfettoTraceTest {
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra extra6 = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(FOO_CATEGORY, "event_proto_nested")
.beginProto()
.beginNested(29L)
.beginNested(4L)
@@ -364,8 +492,7 @@ public class PerfettoTraceTest {
.endNested()
.endNested()
.endProto()
- .build();
- PerfettoTrace.instant(FOO_CATEGORY, "event_proto_nested", extra6);
+ .emit();
byte[] traceBytes = nativeStopTracing(ptr);
@@ -413,8 +540,7 @@ public class PerfettoTraceTest {
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder().build();
- PerfettoTrace.instant(FOO_CATEGORY, "event_trigger", extra);
+ PerfettoTrace.instant(FOO_CATEGORY, "event_trigger").emit();
PerfettoTrace.activateTrigger(FOO, 1000);
@@ -439,49 +565,21 @@ public class PerfettoTraceTest {
@Test
@RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
- public void testMultipleExtras() throws Exception {
- boolean hasException = false;
- try {
- PerfettoTrackEventExtra.builder();
-
- // Unclosed extra will throw an exception here
- PerfettoTrackEventExtra.builder();
- } catch (Exception e) {
- hasException = true;
- }
-
- try {
- PerfettoTrackEventExtra.builder().build();
-
- // Closed extra but unused (reset hasn't been called internally) will throw an exception
- // here.
- PerfettoTrackEventExtra.builder();
- } catch (Exception e) {
- hasException &= true;
- }
-
- assertThat(hasException).isTrue();
- }
-
- @Test
- @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
public void testRegister() throws Exception {
TraceConfig traceConfig = getTraceConfig(BAR);
Category barCategory = new Category(BAR);
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra beforeExtra = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(barCategory, "event")
.addArg("before", 1)
- .build();
- PerfettoTrace.instant(barCategory, "event", beforeExtra);
+ .emit();
barCategory.register();
- PerfettoTrackEventExtra afterExtra = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(barCategory, "event")
.addArg("after", 1)
- .build();
- PerfettoTrace.instant(barCategory, "event", afterExtra);
+ .emit();
byte[] traceBytes = nativeStopTracing(ptr);
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/core/tests/utiltests/src/android/util/proto/ProtoFieldFilterTest.java b/core/tests/utiltests/src/android/util/proto/ProtoFieldFilterTest.java
new file mode 100644
index 000000000000..53407f0ef5b8
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/proto/ProtoFieldFilterTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.proto;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+
+/**
+ * Unit tests for {@link android.util.proto.ProtoFieldFilter}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ProtoFieldFilterTest
+ *
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProtoFieldFilterTest {
+
+ private static final class FieldTypes {
+ static final long INT64 = ProtoStream.FIELD_TYPE_INT64 | ProtoStream.FIELD_COUNT_SINGLE;
+ static final long FIXED64 = ProtoStream.FIELD_TYPE_FIXED64 | ProtoStream.FIELD_COUNT_SINGLE;
+ static final long BYTES = ProtoStream.FIELD_TYPE_BYTES | ProtoStream.FIELD_COUNT_SINGLE;
+ static final long FIXED32 = ProtoStream.FIELD_TYPE_FIXED32 | ProtoStream.FIELD_COUNT_SINGLE;
+ static final long MESSAGE = ProtoStream.FIELD_TYPE_MESSAGE | ProtoStream.FIELD_COUNT_SINGLE;
+ static final long INT32 = ProtoStream.FIELD_TYPE_INT32 | ProtoStream.FIELD_COUNT_SINGLE;
+ }
+
+ private ProtoOutputStream createBasicTestProto() {
+ ProtoOutputStream out = new ProtoOutputStream();
+
+ out.writeInt64(ProtoStream.makeFieldId(1, FieldTypes.INT64), 12345L);
+ out.writeFixed64(ProtoStream.makeFieldId(2, FieldTypes.FIXED64), 0x1234567890ABCDEFL);
+ out.writeBytes(ProtoStream.makeFieldId(3, FieldTypes.BYTES), new byte[]{1, 2, 3, 4, 5});
+ out.writeFixed32(ProtoStream.makeFieldId(4, FieldTypes.FIXED32), 0xDEADBEEF);
+
+ return out;
+ }
+
+ private byte[] filterProto(byte[] input, ProtoFieldFilter filter) throws IOException {
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ filter.filter(inputStream, outputStream);
+ return outputStream.toByteArray();
+ }
+
+ @Test
+ public void testNoFieldsFiltered() throws IOException {
+ byte[] input = createBasicTestProto().getBytes();
+ byte[] output = filterProto(input, new ProtoFieldFilter(fieldNumber -> true));
+ assertArrayEquals("No fields should be filtered out", input, output);
+ }
+
+ @Test
+ public void testAllFieldsFiltered() throws IOException {
+ byte[] input = createBasicTestProto().getBytes();
+ byte[] output = filterProto(input, new ProtoFieldFilter(fieldNumber -> false));
+
+ assertEquals("All fields should be filtered out", 0, output.length);
+ }
+
+ @Test
+ public void testSpecificFieldsFiltered() throws IOException {
+
+ ProtoOutputStream out = createBasicTestProto();
+ byte[] output = filterProto(out.getBytes(), new ProtoFieldFilter(n -> n != 2));
+
+ ProtoInputStream in = new ProtoInputStream(output);
+ boolean[] fieldsFound = new boolean[5];
+
+ int fieldNumber;
+ while ((fieldNumber = in.nextField()) != ProtoInputStream.NO_MORE_FIELDS) {
+ fieldsFound[fieldNumber] = true;
+ switch (fieldNumber) {
+ case 1:
+ assertEquals(12345L, in.readLong(ProtoStream.makeFieldId(1, FieldTypes.INT64)));
+ break;
+ case 2:
+ fail("Field 2 should be filtered out");
+ break;
+ case 3:
+ assertArrayEquals(new byte[]{1, 2, 3, 4, 5},
+ in.readBytes(ProtoStream.makeFieldId(3, FieldTypes.BYTES)));
+ break;
+ case 4:
+ assertEquals(0xDEADBEEF,
+ in.readInt(ProtoStream.makeFieldId(4, FieldTypes.FIXED32)));
+ break;
+ default:
+ fail("Unexpected field number: " + fieldNumber);
+ }
+ }
+
+ assertTrue("Field 1 should be present", fieldsFound[1]);
+ assertFalse("Field 2 should be filtered", fieldsFound[2]);
+ assertTrue("Field 3 should be present", fieldsFound[3]);
+ assertTrue("Field 4 should be present", fieldsFound[4]);
+ }
+
+ @Test
+ public void testDifferentWireTypes() throws IOException {
+ ProtoOutputStream out = new ProtoOutputStream();
+
+ out.writeInt64(ProtoStream.makeFieldId(1, FieldTypes.INT64), 12345L);
+ out.writeFixed64(ProtoStream.makeFieldId(2, FieldTypes.FIXED64), 0x1234567890ABCDEFL);
+ out.writeBytes(ProtoStream.makeFieldId(3, FieldTypes.BYTES), new byte[]{10, 20, 30});
+
+ long token = out.start(ProtoStream.makeFieldId(4, FieldTypes.MESSAGE));
+ out.writeInt32(ProtoStream.makeFieldId(1, FieldTypes.INT32), 42);
+ out.end(token);
+
+ out.writeFixed32(ProtoStream.makeFieldId(5, FieldTypes.FIXED32), 0xDEADBEEF);
+
+ byte[] output = filterProto(out.getBytes(), new ProtoFieldFilter(fieldNumber -> true));
+
+ ProtoInputStream in = new ProtoInputStream(output);
+ boolean[] fieldsFound = new boolean[6];
+
+ int fieldNumber;
+ while ((fieldNumber = in.nextField()) != ProtoInputStream.NO_MORE_FIELDS) {
+ fieldsFound[fieldNumber] = true;
+ switch (fieldNumber) {
+ case 1:
+ assertEquals(12345L, in.readLong(ProtoStream.makeFieldId(1, FieldTypes.INT64)));
+ break;
+ case 2:
+ assertEquals(0x1234567890ABCDEFL,
+ in.readLong(ProtoStream.makeFieldId(2, FieldTypes.FIXED64)));
+ break;
+ case 3:
+ assertArrayEquals(new byte[]{10, 20, 30},
+ in.readBytes(ProtoStream.makeFieldId(3, FieldTypes.BYTES)));
+ break;
+ case 4:
+ token = in.start(ProtoStream.makeFieldId(4, FieldTypes.MESSAGE));
+ assertTrue(in.nextField() == 1);
+ assertEquals(42, in.readInt(ProtoStream.makeFieldId(1, FieldTypes.INT32)));
+ assertTrue(in.nextField() == ProtoInputStream.NO_MORE_FIELDS);
+ in.end(token);
+ break;
+ case 5:
+ assertEquals(0xDEADBEEF,
+ in.readInt(ProtoStream.makeFieldId(5, FieldTypes.FIXED32)));
+ break;
+ default:
+ fail("Unexpected field number: " + fieldNumber);
+ }
+ }
+
+ assertTrue("All fields should be present",
+ fieldsFound[1] && fieldsFound[2] && fieldsFound[3]
+ && fieldsFound[4] && fieldsFound[5]);
+ }
+ @Test
+ public void testNestedMessagesUnfiltered() throws IOException {
+ ProtoOutputStream out = new ProtoOutputStream();
+
+ out.writeInt64(ProtoStream.makeFieldId(1, FieldTypes.INT64), 12345L);
+
+ long token = out.start(ProtoStream.makeFieldId(2, FieldTypes.MESSAGE));
+ out.writeInt32(ProtoStream.makeFieldId(1, FieldTypes.INT32), 6789);
+ out.writeFixed32(ProtoStream.makeFieldId(2, FieldTypes.FIXED32), 0xCAFEBABE);
+ out.end(token);
+
+ byte[] output = filterProto(out.getBytes(), new ProtoFieldFilter(n -> n != 2));
+
+ // Verify output
+ ProtoInputStream in = new ProtoInputStream(output);
+ boolean[] fieldsFound = new boolean[3];
+
+ int fieldNumber;
+ while ((fieldNumber = in.nextField()) != ProtoInputStream.NO_MORE_FIELDS) {
+ fieldsFound[fieldNumber] = true;
+ if (fieldNumber == 1) {
+ assertEquals(12345L, in.readLong(ProtoStream.makeFieldId(1, FieldTypes.INT64)));
+ } else {
+ fail("Unexpected field number: " + fieldNumber);
+ }
+ }
+
+ assertTrue("Field 1 should be present", fieldsFound[1]);
+ assertFalse("Field 2 should be filtered out", fieldsFound[2]);
+ }
+
+ @Test
+ public void testRepeatedFields() throws IOException {
+
+ ProtoOutputStream out = new ProtoOutputStream();
+ long fieldId = ProtoStream.makeFieldId(1,
+ ProtoStream.FIELD_TYPE_INT32 | ProtoStream.FIELD_COUNT_REPEATED);
+
+ out.writeRepeatedInt32(fieldId, 100);
+ out.writeRepeatedInt32(fieldId, 200);
+ out.writeRepeatedInt32(fieldId, 300);
+
+ byte[] input = out.getBytes();
+
+ byte[] output = filterProto(input, new ProtoFieldFilter(fieldNumber -> true));
+
+ assertArrayEquals("Repeated fields should be preserved", input, output);
+ }
+
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a30570a4cce5..b8059d08756a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -613,6 +613,10 @@ applications that come with the platform
<permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
<!-- Permission required for CTS test - CtsContentProviderMultiUserTest -->
<permission name="android.permission.RESOLVE_COMPONENT_FOR_UID"/>
+ <!-- Permission required for CTS test - MediaQualityTest -->
+ <permission name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"/>
+ <permission name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"/>
+ <permission name="android.permission.READ_COLOR_ZONES"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
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 5629a1719739..dd387b382dc6 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 a9996adcbdb4..f1ba0423b422 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 be7636787301..3b1d0a9a5106 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.android.wm.shell.taskview.TaskViewTransitions
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 d3a1f888ac8b..2e1d1e9b8a05 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 5111f289a592..a6492476176b 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..0974930adfa2 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"],
@@ -77,3 +78,17 @@ java_library {
"com.android.window.flags.window-aconfig-java",
],
}
+
+// Things that can be shared with launcher3
+java_library {
+ name: "WindowManager-Shell-shared-AOSP",
+
+ sdk_version: "current",
+
+ srcs: [
+ "src/com/android/wm/shell/shared/bubbles/BubbleAnythingFlagHelper.java",
+ ],
+ static_libs: [
+ "com_android_wm_shell_flags_lib",
+ ],
+}
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/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index bcd40a9a9765..c4696d5f44f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -192,15 +192,22 @@ public final class SyncTransactionQueue {
throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
+ mInFlight.mId + " - " + mInFlight.mWCT);
}
- mInFlight = this;
if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
- if (mLegacyTransition != null) {
- mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
- mLegacyTransition.getAdapter(), this, mWCT);
- } else {
- mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
+ try {
+ if (mLegacyTransition != null) {
+ mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
+ mLegacyTransition.getAdapter(), this, mWCT);
+ } else {
+ mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
+ }
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Send failed", e);
+ // Finish current sync callback immediately.
+ onTransactionReady(mId, new SurfaceControl.Transaction());
+ return;
}
if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
+ mInFlight = this;
mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
index e77987963b48..37779077f9b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
@@ -16,6 +16,7 @@
package com.android.wm.shell.common.pip;
+import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.view.SurfaceControl;
import android.content.ComponentName;
@@ -41,9 +42,8 @@ interface IPip {
bounds
* @return destination bounds the PiP window should land into
*/
- Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
- in PictureInPictureParams pictureInPictureParams,
- int launcherRotation, in Rect hotseatKeepClearArea) = 1;
+ Rect startSwipePipToHome(in ActivityManager.RunningTaskInfo taskInfo, int launcherRotation,
+ in Rect hotseatKeepClearArea) = 1;
/**
* Notifies the swiping Activity to PiP onto home transition is finished
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index d1ec1eb7fdf1..999c879051e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -1157,9 +1157,12 @@ public abstract class WMShellModule {
Context context,
ShellInit shellInit,
Transitions transitions,
- DesktopModeEventLogger desktopModeEventLogger) {
+ DesktopModeEventLogger desktopModeEventLogger,
+ Optional<DesktopTasksLimiter> desktopTasksLimiter,
+ ShellTaskOrganizer shellTaskOrganizer) {
return new DesktopModeLoggerTransitionObserver(
- context, shellInit, transitions, desktopModeEventLogger);
+ context, shellInit, transitions, desktopModeEventLogger,
+ desktopTasksLimiter, shellTaskOrganizer);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index c8d0dab39837..793bdf0b5614 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -83,13 +83,14 @@ public abstract class Pip2Module {
@NonNull PipTransitionState pipStackListenerController,
@NonNull PipDisplayLayoutState pipDisplayLayoutState,
@NonNull PipUiStateChangeController pipUiStateChangeController,
+ DisplayController displayController,
Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
Optional<DesktopWallpaperActivityTokenProvider>
desktopWallpaperActivityTokenProviderOptional) {
return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
pipBoundsState, null, pipBoundsAlgorithm, pipTaskListener,
pipScheduler, pipStackListenerController, pipDisplayLayoutState,
- pipUiStateChangeController, desktopUserRepositoriesOptional,
+ pipUiStateChangeController, displayController, desktopUserRepositoriesOptional,
desktopWallpaperActivityTokenProviderOptional);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index e8f9a789bb98..68bdbd1758b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -467,9 +467,13 @@ class DesktopModeEventLogger {
FrameworkStatsLog
.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_TASK_LIMIT
),
- MINIMIZE_BUTTON( // TODO(b/356843241): use this enum value
+ MINIMIZE_BUTTON(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_BUTTON
),
+ KEY_GESTURE(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_KEY_GESTURE
+ ),
}
// Default value used when the task was not unminimized.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
index 1ddb834399cb..9334898fdb93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
@@ -30,6 +30,7 @@ import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
@@ -125,7 +126,9 @@ class DesktopModeKeyGestureHandler(
KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> {
logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled")
getGloballyFocusedFreeformTask()?.let {
- mainExecutor.execute { desktopTasksController.get().minimizeTask(it) }
+ mainExecutor.execute {
+ desktopTasksController.get().minimizeTask(it, MinimizeReason.KEY_GESTURE)
+ }
}
return true
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index c09504ee3725..2dd89c790b58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -36,6 +36,7 @@ import androidx.core.util.isNotEmpty
import androidx.core.util.plus
import androidx.core.util.putAll
import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
@@ -52,6 +53,8 @@ import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
/**
* A [Transitions.TransitionObserver] that observes transitions and the proposed changes to log
@@ -63,6 +66,8 @@ class DesktopModeLoggerTransitionObserver(
shellInit: ShellInit,
private val transitions: Transitions,
private val desktopModeEventLogger: DesktopModeEventLogger,
+ private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
) : Transitions.TransitionObserver {
init {
@@ -141,6 +146,7 @@ class DesktopModeLoggerTransitionObserver(
// identify if we need to log any changes and update the state of visible freeform tasks
identifyLogEventAndUpdateState(
+ transition = transition,
transitionInfo = info,
preTransitionVisibleFreeformTasks = visibleFreeformTaskInfos,
postTransitionVisibleFreeformTasks = postTransitionVisibleFreeformTasks,
@@ -227,6 +233,7 @@ class DesktopModeLoggerTransitionObserver(
* state and update it
*/
private fun identifyLogEventAndUpdateState(
+ transition: IBinder,
transitionInfo: TransitionInfo,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
@@ -238,6 +245,7 @@ class DesktopModeLoggerTransitionObserver(
) {
// Sessions is finishing, log task updates followed by an exit event
identifyAndLogTaskUpdates(
+ transition,
transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
@@ -255,6 +263,7 @@ class DesktopModeLoggerTransitionObserver(
desktopModeEventLogger.logSessionEnter(getEnterReason(transitionInfo))
identifyAndLogTaskUpdates(
+ transition,
transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
@@ -262,6 +271,7 @@ class DesktopModeLoggerTransitionObserver(
} else if (isSessionActive) {
// Session is neither starting, nor finishing, log task updates if there are any
identifyAndLogTaskUpdates(
+ transition,
transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
@@ -275,6 +285,7 @@ class DesktopModeLoggerTransitionObserver(
/** Compare the old and new state of taskInfos and identify and log the changes */
private fun identifyAndLogTaskUpdates(
+ transition: IBinder,
transitionInfo: TransitionInfo,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
@@ -310,12 +321,9 @@ class DesktopModeLoggerTransitionObserver(
// find old tasks that were removed
preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
- val minimizeReason =
- if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) {
- MinimizeReason.MINIMIZE_BUTTON
- } else {
- null
- }
+ // The task is no longer visible, it might have been minimized, get the minimize
+ // reason (if any)
+ val minimizeReason = getMinimizeReason(transition, transitionInfo, taskInfo)
val taskUpdate =
buildTaskUpdateForTask(
taskInfo,
@@ -336,6 +344,21 @@ class DesktopModeLoggerTransitionObserver(
}
}
+ private fun getMinimizeReason(
+ transition: IBinder,
+ transitionInfo: TransitionInfo,
+ taskInfo: TaskInfo,
+ ): MinimizeReason? {
+ if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) {
+ return MinimizeReason.MINIMIZE_BUTTON
+ }
+ val minimizingTask = desktopTasksLimiter.getOrNull()?.getMinimizingTask(transition)
+ if (minimizingTask?.taskId == taskInfo.taskId) {
+ return minimizingTask.minimizeReason
+ }
+ return null
+ }
+
private fun buildTaskUpdateForTask(
taskInfo: TaskInfo,
visibleTasks: Int,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index c975533abf24..fa696682de28 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -24,8 +24,8 @@ import android.util.SparseArray
import android.view.Display.INVALID_DISPLAY
import android.window.DesktopModeFlags
import androidx.core.util.forEach
-import androidx.core.util.keyIterator
import androidx.core.util.valueIterator
+import com.android.internal.annotations.VisibleForTesting
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
@@ -43,26 +43,36 @@ class DesktopRepository(
@ShellMainThread private val mainCoroutineScope: CoroutineScope,
val userId: Int,
) {
+ /** A display that supports desktops. */
+ private data class DesktopDisplay(
+ val displayId: Int,
+ val orderedDesks: MutableSet<Desk> = mutableSetOf(),
+ // TODO: b/389960283 - update on desk activation / deactivation.
+ var activeDeskId: Int? = null,
+ )
+
/**
- * Task data tracked per desktop.
+ * Task data tracked per desk.
*
- * @property activeTasks task ids of active tasks currently or previously visible in Desktop
- * mode session. Tasks become inactive when task closes or when desktop mode session ends.
+ * @property activeTasks task ids of active tasks currently or previously visible in the desk.
+ * Tasks become inactive when task closes or when the desk becomes inactive.
* @property visibleTasks task ids for active freeform tasks that are currently visible. There
- * might be other active tasks in desktop mode that are not visible.
+ * might be other active tasks in a desk that are not visible.
* @property minimizedTasks task ids for active freeform tasks that are currently minimized.
* @property closingTasks task ids for tasks that are going to close, but are currently visible.
* @property freeformTasksInZOrder list of current freeform task ids ordered from top to bottom
- * @property fullImmersiveTaskId the task id of the desktop task that is in full-immersive mode.
+ * @property fullImmersiveTaskId the task id of the desk's task that is in full-immersive mode.
* @property topTransparentFullscreenTaskId the task id of any current top transparent
- * fullscreen task launched on top of Desktop Mode. Cleared when the transparent task is
- * closed or sent to back. (top is at index 0).
+ * fullscreen task launched on top of the desk. Cleared when the transparent task is closed or
+ * sent to back. (top is at index 0).
* @property pipTaskId the task id of PiP task entered while in Desktop Mode.
- * @property pipShouldKeepDesktopActive whether an active PiP window should keep the Desktop
- * Mode session active. Only false when we are explicitly exiting Desktop Mode (via user
- * action) while there is an active PiP window.
+ * @property pipShouldKeepDesktopActive whether an active PiP window should keep the desk
+ * active. Only false when we are explicitly exiting Desktop Mode (via user action) while
+ * there is an active PiP window.
*/
- private data class DesktopTaskData(
+ private data class Desk(
+ val deskId: Int,
+ val displayId: Int,
val activeTasks: ArraySet<Int> = ArraySet(),
val visibleTasks: ArraySet<Int> = ArraySet(),
val minimizedTasks: ArraySet<Int> = ArraySet(),
@@ -72,10 +82,13 @@ class DesktopRepository(
var fullImmersiveTaskId: Int? = null,
var topTransparentFullscreenTaskId: Int? = null,
var pipTaskId: Int? = null,
+ // TODO: b/389960283 - consolidate this with [DesktopDisplay#activeDeskId].
var pipShouldKeepDesktopActive: Boolean = true,
) {
- fun deepCopy(): DesktopTaskData =
- DesktopTaskData(
+ fun deepCopy(): Desk =
+ Desk(
+ deskId = deskId,
+ displayId = displayId,
activeTasks = ArraySet(activeTasks),
visibleTasks = ArraySet(visibleTasks),
minimizedTasks = ArraySet(minimizedTasks),
@@ -87,6 +100,8 @@ class DesktopRepository(
pipShouldKeepDesktopActive = pipShouldKeepDesktopActive,
)
+ // TODO: b/362720497 - remove when multi-desktops is enabled where instances aren't
+ // reusable.
fun clear() {
activeTasks.clear()
visibleTasks.clear()
@@ -121,11 +136,11 @@ class DesktopRepository(
private var desktopGestureExclusionListener: Consumer<Region>? = null
private var desktopGestureExclusionExecutor: Executor? = null
- private val desktopTaskDataByDisplayId =
- object : SparseArray<DesktopTaskData>() {
- /** Gets [DesktopTaskData] for existing [displayId] or creates a new one. */
- fun getOrCreate(displayId: Int): DesktopTaskData =
- this[displayId] ?: DesktopTaskData().also { this[displayId] = it }
+ private val desktopData: DesktopData =
+ if (Flags.enableMultipleDesktopsBackend()) {
+ MultiDesktopData()
+ } else {
+ SingleDesktopData()
}
/** Adds [activeTasksListener] to be notified of updates to active tasks. */
@@ -136,10 +151,16 @@ class DesktopRepository(
/** Adds [visibleTasksListener] to be notified of updates to visible tasks. */
fun addVisibleTasksListener(visibleTasksListener: VisibleTasksListener, executor: Executor) {
visibleTasksListeners[visibleTasksListener] = executor
- desktopTaskDataByDisplayId.keyIterator().forEach {
- val visibleTaskCount = getVisibleTaskCount(it)
- executor.execute { visibleTasksListener.onTasksVisibilityChanged(it, visibleTaskCount) }
- }
+ desktopData
+ .desksSequence()
+ .groupBy { it.displayId }
+ .keys
+ .forEach { displayId ->
+ val visibleTaskCount = getVisibleTaskCount(displayId)
+ executor.execute {
+ visibleTasksListener.onTasksVisibilityChanged(displayId, visibleTaskCount)
+ }
+ }
}
/** Updates tasks changes on all the active task listeners for given display id. */
@@ -147,9 +168,8 @@ class DesktopRepository(
activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) }
}
- /** Returns a list of all [DesktopTaskData] in the repository. */
- private fun desktopTaskDataSequence(): Sequence<DesktopTaskData> =
- desktopTaskDataByDisplayId.valueIterator().asSequence()
+ /** Returns a list of all [Desk]s in the repository. */
+ private fun desksSequence(): Sequence<Desk> = desktopData.desksSequence()
/** Adds [regionListener] to inform about changes to exclusion regions for all Desktop tasks. */
fun setExclusionRegionListener(regionListener: Consumer<Region>, executor: Executor) {
@@ -179,99 +199,183 @@ class DesktopRepository(
visibleTasksListeners.remove(visibleTasksListener)
}
- /** Adds task with [taskId] to the list of freeform tasks on [displayId]. */
+ /** Adds the given desk under the given display. */
+ fun addDesk(displayId: Int, deskId: Int) {
+ desktopData.getOrCreateDesk(displayId, deskId)
+ }
+
+ /** Returns the default desk in the given display. */
+ fun getDefaultDesk(displayId: Int): Int? = desktopData.getDefaultDesk(displayId)?.deskId
+
+ /** Sets the given desk as the active one in the given display. */
+ fun setActiveDesk(displayId: Int, deskId: Int) {
+ desktopData.setActiveDesk(displayId = displayId, deskId = deskId)
+ }
+
+ /**
+ * Adds task with [taskId] to the list of freeform tasks on [displayId]'s active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun addTask(displayId: Int, taskId: Int, isVisible: Boolean) {
addOrMoveFreeformTaskToTop(displayId, taskId)
addActiveTask(displayId, taskId)
updateTask(displayId, taskId, isVisible)
}
- /** Adds task with [taskId] to the list of active tasks on [displayId]. */
+ /**
+ * Adds task with [taskId] to the list of active tasks on [displayId]'s active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
private fun addActiveTask(displayId: Int, taskId: Int) {
- // Removes task if it is active on another display excluding [displayId].
- removeActiveTask(taskId, excludedDisplayId = displayId)
+ val activeDeskId =
+ desktopData.getActiveDesk(displayId)?.deskId
+ ?: error("Expected active desk in display: $displayId")
+
+ // Removes task if it is active on another desk excluding [activeDesk].
+ removeActiveTask(taskId, excludedDeskId = activeDeskId)
- if (desktopTaskDataByDisplayId.getOrCreate(displayId).activeTasks.add(taskId)) {
- logD("Adds active task=%d displayId=%d", taskId, displayId)
+ if (desktopData.getOrCreateDesk(displayId, activeDeskId).activeTasks.add(taskId)) {
+ logD("Adds active task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId)
updateActiveTasksListeners(displayId)
}
}
- /** Removes task from active task list of displays excluding the [excludedDisplayId]. */
- fun removeActiveTask(taskId: Int, excludedDisplayId: Int? = null) {
- desktopTaskDataByDisplayId.forEach { displayId, desktopTaskData ->
- if ((displayId != excludedDisplayId) && desktopTaskData.activeTasks.remove(taskId)) {
- logD("Removed active task=%d displayId=%d", taskId, displayId)
- updateActiveTasksListeners(displayId)
+ /** Removes task from active task list of desks excluding the [excludedDeskId]. */
+ @VisibleForTesting
+ fun removeActiveTask(taskId: Int, excludedDeskId: Int? = null) {
+ val affectedDisplays = mutableSetOf<Int>()
+ desktopData.forAllDesks { displayId, desk ->
+ if (desk.deskId != excludedDeskId && desk.activeTasks.remove(taskId)) {
+ logD(
+ "Removed active task=%d displayId=%d deskId=%d",
+ taskId,
+ displayId,
+ desk.deskId,
+ )
+ affectedDisplays.add(displayId)
}
}
+ affectedDisplays.forEach { displayId -> updateActiveTasksListeners(displayId) }
}
- /** Adds given task to the closing task list for [displayId]. */
+ /**
+ * Adds given task to the closing task list for [displayId]'s active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun addClosingTask(displayId: Int, taskId: Int) {
- if (desktopTaskDataByDisplayId.getOrCreate(displayId).closingTasks.add(taskId)) {
- logD("Added closing task=%d displayId=%d", taskId, displayId)
+ val activeDeskId =
+ desktopData.getActiveDesk(displayId)?.deskId
+ ?: error("Expected active desk in display: $displayId")
+ if (desktopData.getOrCreateDesk(displayId, activeDeskId).closingTasks.add(taskId)) {
+ logD("Added closing task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId)
} else {
// If the task hasn't been removed from closing list after it disappeared.
- logW("Task with taskId=%d displayId=%d is already closing", taskId, displayId)
+ logW(
+ "Task with taskId=%d displayId=%d deskId=%d is already closing",
+ taskId,
+ displayId,
+ activeDeskId,
+ )
}
}
- /** Removes task from the list of closing tasks for [displayId]. */
+ /** Removes task from the list of closing tasks for all desks. */
fun removeClosingTask(taskId: Int) {
- desktopTaskDataByDisplayId.forEach { displayId, taskInfo ->
- if (taskInfo.closingTasks.remove(taskId)) {
- logD("Removed closing task=%d displayId=%d", taskId, displayId)
+ desktopData.forAllDesks { desk ->
+ if (desk.closingTasks.remove(taskId)) {
+ logD("Removed closing task=%d deskId=%d", taskId, desk.deskId)
}
}
}
- fun isActiveTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.activeTasks }
+ fun isActiveTask(taskId: Int) = desksSequence().any { taskId in it.activeTasks }
- fun isClosingTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.closingTasks }
+ fun isClosingTask(taskId: Int) = desksSequence().any { taskId in it.closingTasks }
- fun isVisibleTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.visibleTasks }
+ fun isVisibleTask(taskId: Int) = desksSequence().any { taskId in it.visibleTasks }
- fun isMinimizedTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.minimizedTasks }
+ fun isMinimizedTask(taskId: Int) = desksSequence().any { taskId in it.minimizedTasks }
- /** Checks if a task is the only visible, non-closing, non-minimized task on its display. */
+ /**
+ * Checks if a task is the only visible, non-closing, non-minimized task on the active desk of
+ * the given display, or any display's active desk if [displayId] is [INVALID_DISPLAY].
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun isOnlyVisibleNonClosingTask(taskId: Int, displayId: Int = INVALID_DISPLAY): Boolean {
- val seq =
+ val activeDesks =
if (displayId != INVALID_DISPLAY) {
- sequenceOf(desktopTaskDataByDisplayId[displayId]).filterNotNull()
+ setOfNotNull(desktopData.getActiveDesk(displayId))
} else {
- desktopTaskDataSequence()
+ desktopData.getAllActiveDesks()
}
- return seq.any {
- it.visibleTasks.subtract(it.closingTasks).subtract(it.minimizedTasks).singleOrNull() ==
- taskId
+ return activeDesks.any { desk ->
+ desk.visibleTasks
+ .subtract(desk.closingTasks)
+ .subtract(desk.minimizedTasks)
+ .singleOrNull() == taskId
}
}
+ /**
+ * Returns the active tasks in the given display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
+ @VisibleForTesting
fun getActiveTasks(displayId: Int): ArraySet<Int> =
- ArraySet(desktopTaskDataByDisplayId[displayId]?.activeTasks)
+ ArraySet(desktopData.getActiveDesk(displayId)?.activeTasks)
+ /**
+ * Returns the minimized tasks in the given display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getMinimizedTasks(displayId: Int): ArraySet<Int> =
- ArraySet(desktopTaskDataByDisplayId[displayId]?.minimizedTasks)
+ ArraySet(desktopData.getActiveDesk(displayId)?.minimizedTasks)
- /** Returns all active non-minimized tasks for [displayId] ordered from top to bottom. */
+ /**
+ * Returns all active non-minimized tasks for [displayId] ordered from top to bottom.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getExpandedTasksOrdered(displayId: Int): List<Int> =
getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) }
- /** Returns the count of active non-minimized tasks for [displayId]. */
+ /**
+ * Returns the count of active non-minimized tasks for [displayId].
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getExpandedTaskCount(displayId: Int): Int {
return getActiveTasks(displayId).count { !isMinimizedTask(it) }
}
- /** Returns a list of freeform tasks, ordered from top-bottom (top at index 0). */
+ /**
+ * Returns a list of freeform tasks, ordered from top-bottom (top at index 0).
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
+ @VisibleForTesting
fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> =
- ArrayList(desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder ?: emptyList())
+ ArrayList(desktopData.getActiveDesk(displayId)?.freeformTasksInZOrder ?: emptyList())
+
+ /** Returns the tasks inside the given desk. */
+ fun getActiveTaskIdsInDesk(deskId: Int): Set<Int> =
+ desktopData.getDesk(deskId)?.activeTasks?.toSet()
+ ?: run {
+ logW("getTasksInDesk: could not find desk: deskId=%d", deskId)
+ emptySet()
+ }
/** Removes task from visible tasks of all displays except [excludedDisplayId]. */
private fun removeVisibleTask(taskId: Int, excludedDisplayId: Int? = null) {
- desktopTaskDataByDisplayId.forEach { displayId, data ->
- if ((displayId != excludedDisplayId) && data.visibleTasks.remove(taskId)) {
- notifyVisibleTaskListeners(displayId, data.visibleTasks.size)
+ desktopData.forAllDesks { displayId, desk ->
+ if (displayId != excludedDisplayId && desk.visibleTasks.remove(taskId)) {
+ notifyVisibleTaskListeners(displayId, desk.visibleTasks.size)
}
}
}
@@ -281,6 +385,8 @@ class DesktopRepository(
*
* If task was visible on a different display with a different [displayId], removes from the set
* of visible tasks on that display and notifies listeners.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
*/
fun updateTask(displayId: Int, taskId: Int, isVisible: Boolean) {
logD("updateTask taskId=%d, displayId=%d, isVisible=%b", taskId, displayId, isVisible)
@@ -295,10 +401,11 @@ class DesktopRepository(
}
val prevCount = getVisibleTaskCount(displayId)
if (isVisible) {
- desktopTaskDataByDisplayId.getOrCreate(displayId).visibleTasks.add(taskId)
+ desktopData.getActiveDesk(displayId)?.visibleTasks?.add(taskId)
+ ?: error("Expected non-null active desk in display $displayId")
unminimizeTask(displayId, taskId)
} else {
- desktopTaskDataByDisplayId[displayId]?.visibleTasks?.remove(taskId)
+ desktopData.getActiveDesk(displayId)?.visibleTasks?.remove(taskId)
}
val newCount = getVisibleTaskCount(displayId)
if (prevCount != newCount) {
@@ -316,57 +423,94 @@ class DesktopRepository(
}
}
- /** Set whether the given task is the Desktop-entered PiP task in this display. */
+ /**
+ * Set whether the given task is the Desktop-entered PiP task in this display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun setTaskInPip(displayId: Int, taskId: Int, enterPip: Boolean) {
- val desktopData = desktopTaskDataByDisplayId.getOrCreate(displayId)
+ val activeDesk =
+ desktopData.getActiveDesk(displayId)
+ ?: error("Expected active desk in display: $displayId")
if (enterPip) {
- desktopData.pipTaskId = taskId
- desktopData.pipShouldKeepDesktopActive = true
+ activeDesk.pipTaskId = taskId
+ activeDesk.pipShouldKeepDesktopActive = true
} else {
- desktopData.pipTaskId =
- if (desktopData.pipTaskId == taskId) null
+ activeDesk.pipTaskId =
+ if (activeDesk.pipTaskId == taskId) null
else {
logW(
- "setTaskInPip: taskId=$taskId did not match saved taskId=${desktopData.pipTaskId}"
+ "setTaskInPip: taskId=%d did not match saved taskId=%d",
+ taskId,
+ activeDesk.pipTaskId,
)
- desktopData.pipTaskId
+ activeDesk.pipTaskId
}
}
notifyVisibleTaskListeners(displayId, getVisibleTaskCount(displayId))
}
- /** Returns whether there is a PiP that was entered/minimized from Desktop in this display. */
+ /**
+ * Returns whether there is a PiP that was entered/minimized from Desktop in this display's
+ * active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun isMinimizedPipPresentInDisplay(displayId: Int): Boolean =
- desktopTaskDataByDisplayId.getOrCreate(displayId).pipTaskId != null
+ desktopData.getActiveDesk(displayId)?.pipTaskId != null
- /** Returns whether the given task is the Desktop-entered PiP task in this display. */
+ /**
+ * Returns whether the given task is the Desktop-entered PiP task in this display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun isTaskMinimizedPipInDisplay(displayId: Int, taskId: Int): Boolean =
- desktopTaskDataByDisplayId.getOrCreate(displayId).pipTaskId == taskId
+ desktopData.getActiveDesk(displayId)?.pipTaskId == taskId
- /** Returns whether Desktop session should be active in this display due to active PiP. */
+ /**
+ * Returns whether a desk should be active in this display due to active PiP.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun shouldDesktopBeActiveForPip(displayId: Int): Boolean =
Flags.enableDesktopWindowingPip() &&
isMinimizedPipPresentInDisplay(displayId) &&
- desktopTaskDataByDisplayId.getOrCreate(displayId).pipShouldKeepDesktopActive
+ (desktopData.getActiveDesk(displayId)?.pipShouldKeepDesktopActive ?: false)
- /** Saves whether a PiP window should keep Desktop session active in this display. */
+ /**
+ * Saves whether a PiP window should keep Desktop session active in this display.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun setPipShouldKeepDesktopActive(displayId: Int, keepActive: Boolean) {
- desktopTaskDataByDisplayId.getOrCreate(displayId).pipShouldKeepDesktopActive = keepActive
+ desktopData.getActiveDesk(displayId)?.pipShouldKeepDesktopActive = keepActive
}
- /** Saves callback to handle a pending PiP transition being aborted. */
- fun setOnPipAbortedCallback(callbackIfPipAborted: ((Int, Int) -> Unit)?) {
+ /**
+ * Saves callback to handle a pending PiP transition being aborted.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
+ fun setOnPipAbortedCallback(callbackIfPipAborted: ((displayId: Int, pipTaskId: Int) -> Unit)?) {
onPipAbortedCallback = callbackIfPipAborted
}
- /** Invokes callback to handle a pending PiP transition with the given task id being aborted. */
+ /**
+ * Invokes callback to handle a pending PiP transition with the given task id being aborted.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun onPipAborted(displayId: Int, pipTaskId: Int) {
onPipAbortedCallback?.invoke(displayId, pipTaskId)
}
- /** Set whether the given task is the full-immersive task in this display. */
+ /**
+ * Set whether the given task is the full-immersive task in this display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun setTaskInFullImmersiveState(displayId: Int, taskId: Int, immersive: Boolean) {
- val desktopData = desktopTaskDataByDisplayId.getOrCreate(displayId)
+ val desktopData = desktopData.getActiveDesk(displayId) ?: return
if (immersive) {
desktopData.fullImmersiveTaskId = taskId
} else {
@@ -378,25 +522,41 @@ class DesktopRepository(
/* Whether the task is in full-immersive state. */
fun isTaskInFullImmersiveState(taskId: Int): Boolean {
- return desktopTaskDataSequence().any { taskId == it.fullImmersiveTaskId }
+ return desksSequence().any { taskId == it.fullImmersiveTaskId }
}
- /** Returns the task that is currently in immersive mode in this display, or null. */
+ /**
+ * Returns the task that is currently in immersive mode in this display, or null.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getTaskInFullImmersiveState(displayId: Int): Int? =
- desktopTaskDataByDisplayId.getOrCreate(displayId).fullImmersiveTaskId
+ desktopData.getActiveDesk(displayId)?.fullImmersiveTaskId
- /** Sets the top transparent fullscreen task id for a given display. */
+ /**
+ * Sets the top transparent fullscreen task id for a given display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun setTopTransparentFullscreenTaskId(displayId: Int, taskId: Int) {
- desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId = taskId
+ desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = taskId
}
- /** Returns the top transparent fullscreen task id for a given display, or null. */
+ /**
+ * Returns the top transparent fullscreen task id for a given display's active desk, or null.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getTopTransparentFullscreenTaskId(displayId: Int): Int? =
- desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId
+ desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId
- /** Clears the top transparent fullscreen task id info for a given display. */
+ /**
+ * Clears the top transparent fullscreen task id info for a given display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun clearTopTransparentFullscreenTaskId(displayId: Int) {
- desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId = null
+ desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = null
}
private fun notifyVisibleTaskListeners(displayId: Int, visibleTasksCount: Int) {
@@ -409,22 +569,35 @@ class DesktopRepository(
}
}
- /** Gets number of visible freeform tasks on given [displayId] */
+ /**
+ * Gets number of visible freeform tasks on given [displayId]'s active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getVisibleTaskCount(displayId: Int): Int =
- desktopTaskDataByDisplayId[displayId]?.visibleTasks?.size
- ?: 0.also { logD("getVisibleTaskCount=$it") }
+ (desktopData.getActiveDesk(displayId)?.visibleTasks?.size ?: 0).also {
+ logD("getVisibleTaskCount=$it")
+ }
/**
* Adds task (or moves if it already exists) to the top of the ordered list.
*
* Unminimizes the task if it is minimized.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
*/
private fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) {
- logD("Add or move task to top: display=%d taskId=%d", taskId, displayId)
- desktopTaskDataByDisplayId.forEach { _, value ->
- value.freeformTasksInZOrder.remove(taskId)
- }
- desktopTaskDataByDisplayId.getOrCreate(displayId).freeformTasksInZOrder.add(0, taskId)
+ val activeDesk =
+ desktopData.getActiveDesk(displayId)
+ ?: error("Expected a desk to be active in display: $displayId")
+ logD(
+ "Add or move task to top: display=%d taskId=%d deskId=%d",
+ taskId,
+ displayId,
+ activeDesk.deskId,
+ )
+ desktopData.forAllDesks { _, desk -> desk.freeformTasksInZOrder.remove(taskId) }
+ activeDesk.freeformTasksInZOrder.add(0, taskId)
// Unminimize the task if it is minimized.
unminimizeTask(displayId, taskId)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
@@ -432,7 +605,11 @@ class DesktopRepository(
}
}
- /** Minimizes the task for [taskId] and [displayId] */
+ /**
+ * Minimizes the task for [taskId] and [displayId]'s active display.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun minimizeTask(displayId: Int, taskId: Int) {
if (displayId == INVALID_DISPLAY) {
// When a task vanishes it doesn't have a displayId. Find the display of the task and
@@ -441,7 +618,8 @@ class DesktopRepository(
?: logW("Minimize task: No display id found for task: taskId=%d", taskId)
} else {
logD("Minimize Task: display=%d, task=%d", displayId, taskId)
- desktopTaskDataByDisplayId.getOrCreate(displayId).minimizedTasks.add(taskId)
+ desktopData.getActiveDesk(displayId)?.minimizedTasks?.add(taskId)
+ ?: logD("Minimize task: No active desk found for task: taskId=%d", taskId)
}
updateTask(displayId, taskId, isVisible = false)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
@@ -449,26 +627,42 @@ class DesktopRepository(
}
}
- /** Unminimizes the task for [taskId] and [displayId] */
+ /**
+ * Unminimizes the task for [taskId] and [displayId].
+ *
+ * TODO: b/389960283 - consider adding an explicit [deskId] argument.
+ */
fun unminimizeTask(displayId: Int, taskId: Int) {
logD("Unminimize Task: display=%d, task=%d", displayId, taskId)
- desktopTaskDataByDisplayId[displayId]?.minimizedTasks?.remove(taskId)
- ?: logW("Unminimize Task: display=%d, task=%d, no task data", displayId, taskId)
+ var removed = false
+ desktopData.forAllDesks(displayId) { desk ->
+ if (desk.minimizedTasks.remove(taskId)) {
+ removed = true
+ }
+ }
+ if (!removed) {
+ logW("Unminimize Task: display=%d, task=%d, no task data", displayId, taskId)
+ }
}
private fun getDisplayIdForTask(taskId: Int): Int? {
- desktopTaskDataByDisplayId.forEach { displayId, data ->
- if (taskId in data.freeformTasksInZOrder) {
- return displayId
+ var displayForTask: Int? = null
+ desktopData.forAllDesks { displayId, desk ->
+ if (taskId in desk.freeformTasksInZOrder) {
+ displayForTask = displayId
}
}
- logW("No display id found for task: taskId=%d", taskId)
- return null
+ if (displayForTask == null) {
+ logW("No display id found for task: taskId=%d", taskId)
+ }
+ return displayForTask
}
/**
* Removes [taskId] from the respective display. If [INVALID_DISPLAY], the original display id
* will be looked up from the task id.
+ *
+ * TODO: b/389960283 - consider adding an explicit [deskId] argument.
*/
fun removeTask(displayId: Int, taskId: Int) {
logD("Removes freeform task: taskId=%d", taskId)
@@ -483,13 +677,17 @@ class DesktopRepository(
/** Removes given task from a valid [displayId] and updates the repository state. */
private fun removeTaskFromDisplay(displayId: Int, taskId: Int) {
logD("Removes freeform task: taskId=%d, displayId=%d", taskId, displayId)
- desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.remove(taskId)
+ desktopData.forAllDesks(displayId) { desk ->
+ if (desk.freeformTasksInZOrder.remove(taskId)) {
+ logD(
+ "Remaining freeform tasks in desk: %d, tasks: %s",
+ desk.deskId,
+ desk.freeformTasksInZOrder.toDumpString(),
+ )
+ }
+ }
boundsBeforeMaximizeByTaskId.remove(taskId)
boundsBeforeFullImmersiveByTaskId.remove(taskId)
- logD(
- "Remaining freeform tasks: %s",
- desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.toDumpString(),
- )
// Remove task from unminimized task if it is minimized.
unminimizeTask(displayId, taskId)
// Mark task as not in immersive if it was immersive.
@@ -502,15 +700,18 @@ class DesktopRepository(
}
/**
- * Removes the desktop for the given [displayId] and returns the active tasks on that desktop.
+ * Removes the active desk for the given [displayId] and returns the active tasks on that desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
*/
- fun removeDesktop(displayId: Int): ArraySet<Int> {
- if (!desktopTaskDataByDisplayId.contains(displayId)) {
- logW("Could not find desktop to remove: displayId=%d", displayId)
+ fun removeDesk(displayId: Int): ArraySet<Int> {
+ val desk = desktopData.getActiveDesk(displayId)
+ if (desk == null) {
+ logW("Could not find desk to remove: displayId=%d", displayId)
return ArraySet()
}
- val activeTasks = ArraySet(desktopTaskDataByDisplayId[displayId].activeTasks)
- desktopTaskDataByDisplayId[displayId].clear()
+ val activeTasks = ArraySet(desk.activeTasks)
+ desktopData.remove(desk.deskId)
return activeTasks
}
@@ -564,19 +765,20 @@ class DesktopRepository(
fun saveBoundsBeforeFullImmersive(taskId: Int, bounds: Rect) =
boundsBeforeFullImmersiveByTaskId.set(taskId, Rect(bounds))
+ /** TODO: b/389960283 - consider updating only the changing desks. */
private fun updatePersistentRepository(displayId: Int) {
- // Create a deep copy of the data
- desktopTaskDataByDisplayId[displayId]?.deepCopy()?.let { desktopTaskDataByDisplayIdCopy ->
- mainCoroutineScope.launch {
+ val desks = desktopData.desksSequence(displayId).map { desk -> desk.deepCopy() }.toList()
+ mainCoroutineScope.launch {
+ desks.forEach { desk ->
try {
persistentRepository.addOrUpdateDesktop(
- // Use display id as desktop id for now since only once desktop per display
+ // Use display id as desk id for now since only once desk per display
// is supported.
userId = userId,
- desktopId = displayId,
- visibleTasks = desktopTaskDataByDisplayIdCopy.visibleTasks,
- minimizedTasks = desktopTaskDataByDisplayIdCopy.minimizedTasks,
- freeformTasksInZOrder = desktopTaskDataByDisplayIdCopy.freeformTasksInZOrder,
+ desktopId = desk.deskId,
+ visibleTasks = desk.visibleTasks,
+ minimizedTasks = desk.minimizedTasks,
+ freeformTasksInZOrder = desk.freeformTasksInZOrder,
)
} catch (exception: Exception) {
logE(
@@ -598,20 +800,27 @@ class DesktopRepository(
private fun dumpDesktopTaskData(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
- desktopTaskDataByDisplayId.forEach { displayId, data ->
- pw.println("${prefix}Display $displayId:")
- pw.println("${innerPrefix}activeTasks=${data.activeTasks.toDumpString()}")
- pw.println("${innerPrefix}visibleTasks=${data.visibleTasks.toDumpString()}")
- pw.println(
- "${innerPrefix}freeformTasksInZOrder=${data.freeformTasksInZOrder.toDumpString()}"
- )
- pw.println("${innerPrefix}minimizedTasks=${data.minimizedTasks.toDumpString()}")
- pw.println("${innerPrefix}fullImmersiveTaskId=${data.fullImmersiveTaskId}")
- pw.println(
- "${innerPrefix}topTransparentFullscreenTaskId=" +
- "${data.topTransparentFullscreenTaskId}"
- )
- }
+ desktopData
+ .desksSequence()
+ .groupBy { it.displayId }
+ .forEach { (displayId, desks) ->
+ pw.println("${prefix}Display #$displayId:")
+ desks.forEach { desk ->
+ pw.println("${innerPrefix}Desk #${desk.deskId}:")
+ pw.print("$innerPrefix activeTasks=")
+ pw.println(desk.activeTasks.toDumpString())
+ pw.print("$innerPrefix visibleTasks=")
+ pw.println(desk.visibleTasks.toDumpString())
+ pw.print("$innerPrefix freeformTasksInZOrder=")
+ pw.println(desk.freeformTasksInZOrder.toDumpString())
+ pw.print("$innerPrefix minimizedTasks=")
+ pw.println(desk.minimizedTasks.toDumpString())
+ pw.print("$innerPrefix fullImmersiveTaskId=")
+ pw.println(desk.fullImmersiveTaskId)
+ pw.print("$innerPrefix topTransparentFullscreenTaskId=")
+ pw.println(desk.topTransparentFullscreenTaskId)
+ }
+ }
}
/** Listens to changes for active tasks in desktop mode. */
@@ -624,6 +833,227 @@ class DesktopRepository(
fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {}
}
+ /** An interface for the desktop hierarchy's data managed by this repository. */
+ private interface DesktopData {
+ /**
+ * Returns the existing desk or creates a new entry if needed.
+ *
+ * TODO: 389787966 - consider removing this as it cannot be assumed a desk can be created in
+ * all devices / form-factors.
+ */
+ fun getOrCreateDesk(displayId: Int, deskId: Int): Desk
+
+ /** Returns the desk with the given id, or null if it does not exist. */
+ fun getDesk(deskId: Int): Desk?
+
+ /** Returns the active desk in this diplay, or null if none are active. */
+ fun getActiveDesk(displayId: Int): Desk?
+
+ /** Sets the given desk as the active desk in the given display. */
+ fun setActiveDesk(displayId: Int, deskId: Int)
+
+ /**
+ * Returns the default desk in the given display. Useful when the system wants to activate a
+ * desk but doesn't care about which one it activates (e.g. when putting a window into a
+ * desk using the App Handle). May return null if the display does not support desks.
+ *
+ * TODO: 389787966 - consider removing or renaming. In practice, this is needed for
+ * soon-to-be deprecated IDesktopMode APIs, adb commands or entry-points into the only
+ * desk (single-desk devices) or the most-recent desk (multi-desk devices).
+ */
+ fun getDefaultDesk(displayId: Int): Desk?
+
+ /** Returns all the active desks of all displays. */
+ fun getAllActiveDesks(): Set<Desk>
+
+ /** Returns the number of desks in the given display. */
+ fun getNumberOfDesks(displayId: Int): Int
+
+ /** Applies a function to all desks. */
+ fun forAllDesks(consumer: (Desk) -> Unit)
+
+ /** Applies a function to all desks. */
+ fun forAllDesks(consumer: (displayId: Int, Desk) -> Unit)
+
+ /** Applies a function to all desks under the given display. */
+ fun forAllDesks(displayId: Int, consumer: (Desk) -> Unit)
+
+ /** Returns a sequence of all desks. */
+ fun desksSequence(): Sequence<Desk>
+
+ /** Returns a sequence of all desks under the given display. */
+ fun desksSequence(displayId: Int): Sequence<Desk>
+
+ /** Remove an existing desk if it exists. */
+ fun remove(deskId: Int)
+
+ /** Returns the id of the display where the given desk is located. */
+ fun getDisplayForDesk(deskId: Int): Int
+ }
+
+ /**
+ * A [DesktopData] implementation that only supports one desk per display.
+ *
+ * Internally, it reuses the displayId as that display's single desk's id.
+ */
+ private class SingleDesktopData : DesktopData {
+ private val deskByDisplayId =
+ object : SparseArray<Desk>() {
+ /** Gets [Desk] for existing [displayId] or creates a new one. */
+ fun getOrCreate(displayId: Int): Desk =
+ this[displayId]
+ ?: Desk(deskId = displayId, displayId = displayId).also {
+ this[displayId] = it
+ }
+ }
+
+ override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk {
+ check(displayId == deskId)
+ return deskByDisplayId.getOrCreate(displayId)
+ }
+
+ override fun getDesk(deskId: Int): Desk = getOrCreateDesk(deskId, deskId)
+
+ override fun getActiveDesk(displayId: Int): Desk {
+ // TODO: 389787966 - consider migrating to an "active" state instead of checking the
+ // number of visible active tasks, PIP in desktop, and empty desktop logic. In
+ // practice, existing single-desktop devices are ok with this function returning the
+ // only desktop, even if it's not active.
+ return deskByDisplayId.getOrCreate(displayId)
+ }
+
+ override fun setActiveDesk(displayId: Int, deskId: Int) {
+ // No-op, in single-desk setups, which desktop is "active" is determined by the
+ // existence of visible desktop windows, among other factors.
+ }
+
+ override fun getDefaultDesk(displayId: Int): Desk = getOrCreateDesk(displayId, displayId)
+
+ override fun getAllActiveDesks(): Set<Desk> =
+ deskByDisplayId.valueIterator().asSequence().toSet()
+
+ override fun getNumberOfDesks(displayId: Int): Int = 1
+
+ override fun forAllDesks(consumer: (Desk) -> Unit) {
+ deskByDisplayId.forEach { _, desk -> consumer(desk) }
+ }
+
+ override fun forAllDesks(consumer: (Int, Desk) -> Unit) {
+ deskByDisplayId.forEach { displayId, desk -> consumer(displayId, desk) }
+ }
+
+ override fun forAllDesks(displayId: Int, consumer: (Desk) -> Unit) {
+ consumer(getOrCreateDesk(displayId, displayId))
+ }
+
+ override fun desksSequence(): Sequence<Desk> = deskByDisplayId.valueIterator().asSequence()
+
+ override fun desksSequence(displayId: Int): Sequence<Desk> =
+ deskByDisplayId[displayId]?.let { sequenceOf(it) } ?: emptySequence()
+
+ override fun remove(deskId: Int) {
+ deskByDisplayId[deskId]?.clear()
+ }
+
+ override fun getDisplayForDesk(deskId: Int): Int = deskId
+ }
+
+ /** A [DesktopData] implementation that supports multiple desks. */
+ private class MultiDesktopData : DesktopData {
+ private val desktopDisplays = SparseArray<DesktopDisplay>()
+
+ override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk {
+ val display =
+ desktopDisplays[displayId]
+ ?: DesktopDisplay(displayId).also { desktopDisplays[displayId] = it }
+ val desk =
+ display.orderedDesks.find { desk -> desk.deskId == deskId }
+ ?: Desk(deskId = deskId, displayId = displayId).also {
+ display.orderedDesks.add(it)
+ }
+ return desk
+ }
+
+ override fun getDesk(deskId: Int): Desk? {
+ desktopDisplays.forEach { _, display ->
+ val desk = display.orderedDesks.find { desk -> desk.deskId == deskId }
+ if (desk != null) {
+ return desk
+ }
+ }
+ return null
+ }
+
+ override fun getActiveDesk(displayId: Int): Desk? {
+ val display = desktopDisplays[displayId] ?: return null
+ if (display.activeDeskId == null) return null
+ return display.orderedDesks.find { it.deskId == display.activeDeskId }
+ }
+
+ override fun setActiveDesk(displayId: Int, deskId: Int) {
+ val display =
+ desktopDisplays[displayId] ?: error("Expected display#$displayId to exist")
+ val desk = display.orderedDesks.single { it.deskId == deskId }
+ display.activeDeskId = desk.deskId
+ }
+
+ override fun getDefaultDesk(displayId: Int): Desk? {
+ val display = desktopDisplays[displayId] ?: return null
+ return display.orderedDesks.firstOrNull()
+ }
+
+ override fun getAllActiveDesks(): Set<Desk> {
+ return desktopDisplays
+ .valueIterator()
+ .asSequence()
+ .filter { display -> display.activeDeskId != null }
+ .map { display ->
+ display.orderedDesks.single { it.deskId == display.activeDeskId }
+ }
+ .toSet()
+ }
+
+ override fun getNumberOfDesks(displayId: Int): Int =
+ desktopDisplays[displayId]?.orderedDesks?.size ?: 0
+
+ override fun forAllDesks(consumer: (Desk) -> Unit) {
+ desktopDisplays.forEach { _, display -> display.orderedDesks.forEach { consumer(it) } }
+ }
+
+ override fun forAllDesks(consumer: (Int, Desk) -> Unit) {
+ desktopDisplays.forEach { _, display ->
+ display.orderedDesks.forEach { consumer(display.displayId, it) }
+ }
+ }
+
+ override fun forAllDesks(displayId: Int, consumer: (Desk) -> Unit) {
+ desktopDisplays
+ .valueIterator()
+ .asSequence()
+ .filter { display -> display.displayId == displayId }
+ .flatMap { display -> display.orderedDesks.asSequence() }
+ .forEach { desk -> consumer(desk) }
+ }
+
+ override fun desksSequence(): Sequence<Desk> =
+ desktopDisplays.valueIterator().asSequence().flatMap { display ->
+ display.orderedDesks.asSequence()
+ }
+
+ override fun desksSequence(displayId: Int): Sequence<Desk> =
+ desktopDisplays[displayId]?.orderedDesks?.asSequence() ?: emptySequence()
+
+ override fun remove(deskId: Int) {
+ desktopDisplays.forEach { _, display ->
+ display.orderedDesks.removeIf { it.deskId == deskId }
+ }
+ }
+
+ override fun getDisplayForDesk(deskId: Int): Int =
+ getAllActiveDesks().find { it.deskId == deskId }?.displayId
+ ?: error("Display for desk=$deskId not found")
+ }
+
private fun logD(msg: String, vararg arguments: Any?) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
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..172410d0482c 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
@@ -86,6 +87,7 @@ import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
import com.android.wm.shell.compatui.isTransparentTask
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState
@@ -465,7 +467,9 @@ class DesktopTasksController(
desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
FREEFORM_ANIMATION_DURATION
)
- taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+ taskIdToMinimize?.let {
+ addPendingMinimizeTransition(transition, it, MinimizeReason.TASK_LIMIT)
+ }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
return true
}
@@ -511,7 +515,9 @@ class DesktopTasksController(
desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
FREEFORM_ANIMATION_DURATION
)
- taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+ taskIdToMinimize?.let {
+ addPendingMinimizeTransition(transition, it, MinimizeReason.TASK_LIMIT)
+ }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
}
@@ -572,7 +578,9 @@ class DesktopTasksController(
DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS.toInt()
)
transition?.let {
- taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) }
+ taskIdToMinimize?.let { taskId ->
+ addPendingMinimizeTransition(it, taskId, MinimizeReason.TASK_LIMIT)
+ }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
}
}
@@ -621,7 +629,7 @@ class DesktopTasksController(
?.runOnTransitionStart
}
- fun minimizeTask(taskInfo: RunningTaskInfo) {
+ fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
val wct = WindowContainerTransaction()
val isMinimizingToPip = taskInfo.pictureInPictureParams?.isAutoEnterEnabled() ?: false
@@ -641,16 +649,16 @@ class DesktopTasksController(
freeformTaskTransitionStarter.startPipTransition(wct)
taskRepository.setTaskInPip(taskInfo.displayId, taskInfo.taskId, enterPip = true)
taskRepository.setOnPipAbortedCallback { displayId, taskId ->
- minimizeTaskInner(shellTaskOrganizer.getRunningTaskInfo(taskId)!!)
+ minimizeTaskInner(shellTaskOrganizer.getRunningTaskInfo(taskId)!!, minimizeReason)
taskRepository.setTaskInPip(displayId, taskId, enterPip = false)
}
return
}
- minimizeTaskInner(taskInfo)
+ minimizeTaskInner(taskInfo, minimizeReason)
}
- private fun minimizeTaskInner(taskInfo: RunningTaskInfo) {
+ private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
val taskId = taskInfo.taskId
val displayId = taskInfo.displayId
val wct = WindowContainerTransaction()
@@ -670,6 +678,7 @@ class DesktopTasksController(
transition = transition,
displayId = displayId,
taskId = taskId,
+ minimizeReason = minimizeReason,
)
}
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
@@ -825,7 +834,7 @@ class DesktopTasksController(
minimizingTaskId = taskIdToMinimize,
exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask,
)
- taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
+ taskIdToMinimize?.let { addPendingMinimizeTransition(t, it, MinimizeReason.TASK_LIMIT) }
exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
@@ -845,7 +854,7 @@ class DesktopTasksController(
)
val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
remoteTransitionHandler.setTransition(t)
- taskIdToMinimize.let { addPendingMinimizeTransition(t, it) }
+ taskIdToMinimize.let { addPendingMinimizeTransition(t, it, MinimizeReason.TASK_LIMIT) }
exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
@@ -884,6 +893,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].
@@ -1867,7 +1906,7 @@ class DesktopTasksController(
val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
if (taskIdToMinimize != null) {
- addPendingMinimizeTransition(transition, taskIdToMinimize)
+ addPendingMinimizeTransition(transition, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
return wct
}
if (!wct.isEmpty) {
@@ -1901,7 +1940,9 @@ class DesktopTasksController(
// Desktop Mode is already showing and we're launching a new Task - we might need to
// minimize another Task.
val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
- taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+ taskIdToMinimize?.let {
+ addPendingMinimizeTransition(transition, it, MinimizeReason.TASK_LIMIT)
+ }
addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
desktopImmersiveController.exitImmersiveIfApplicable(
transition,
@@ -2149,13 +2190,18 @@ class DesktopTasksController(
.addAndGetMinimizeTaskChanges(displayId, wct, newTaskId, launchingNewIntent)
}
- private fun addPendingMinimizeTransition(transition: IBinder, taskIdToMinimize: Int) {
+ private fun addPendingMinimizeTransition(
+ transition: IBinder,
+ taskIdToMinimize: Int,
+ minimizeReason: MinimizeReason,
+ ) {
val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
desktopTasksLimiter.ifPresent {
it.addPendingMinimizeChange(
transition = transition,
displayId = taskToMinimize?.displayId ?: DEFAULT_DISPLAY,
taskId = taskIdToMinimize,
+ minimizeReason = minimizeReason,
)
}
}
@@ -2185,7 +2231,7 @@ class DesktopTasksController(
fun removeDesktop(displayId: Int) {
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
- val tasksToRemove = taskRepository.removeDesktop(displayId)
+ val tasksToRemove = taskRepository.removeDesk(displayId)
val wct = WindowContainerTransaction()
tasksToRemove.forEach {
val task = shellTaskOrganizer.getRunningTaskInfo(it)
@@ -2425,6 +2471,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 +2950,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/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index e4a28e9efe60..204b39645248 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -30,6 +30,7 @@ import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.sysui.UserChangeListener
@@ -67,12 +68,21 @@ class DesktopTasksLimiter(
logV("Starting limiter with a maximum of %d tasks", maxTasksLimit)
}
- private data class TaskDetails(
+ data class TaskDetails(
val displayId: Int,
val taskId: Int,
- var transitionInfo: TransitionInfo?,
+ var transitionInfo: TransitionInfo? = null,
+ val minimizeReason: MinimizeReason? = null,
)
+ /**
+ * Returns the task being minimized in the given transition if that transition is a pending or
+ * active minimize transition.
+ */
+ fun getMinimizingTask(transition: IBinder): TaskDetails? {
+ return minimizeTransitionObserver.getMinimizingTask(transition)
+ }
+
// TODO(b/333018485): replace this observer when implementing the minimize-animation
private inner class MinimizeTransitionObserver : TransitionObserver {
private val pendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
@@ -82,6 +92,11 @@ class DesktopTasksLimiter(
pendingTransitionTokensAndTasks[transition] = taskDetails
}
+ fun getMinimizingTask(transition: IBinder): TaskDetails? {
+ return pendingTransitionTokensAndTasks[transition]
+ ?: activeTransitionTokensAndTasks[transition]
+ }
+
override fun onTransitionReady(
transition: IBinder,
info: TransitionInfo,
@@ -89,6 +104,14 @@ class DesktopTasksLimiter(
finishTransaction: SurfaceControl.Transaction,
) {
val taskRepository = desktopUserRepositories.current
+ handleMinimizeTransition(taskRepository, transition, info)
+ }
+
+ private fun handleMinimizeTransition(
+ taskRepository: DesktopRepository,
+ transition: IBinder,
+ info: TransitionInfo,
+ ) {
val taskToMinimize = pendingTransitionTokensAndTasks.remove(transition) ?: return
if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return
if (!isTaskReadyForMinimize(info, taskToMinimize)) {
@@ -241,10 +264,15 @@ class DesktopTasksLimiter(
* Add a pending minimize transition change to update the list of minimized apps once the
* transition goes through.
*/
- fun addPendingMinimizeChange(transition: IBinder, displayId: Int, taskId: Int) {
+ fun addPendingMinimizeChange(
+ transition: IBinder,
+ displayId: Int,
+ taskId: Int,
+ minimizeReason: MinimizeReason,
+ ) {
minimizeTransitionObserver.addPendingTransitionToken(
transition,
- TaskDetails(displayId, taskId, transitionInfo = null),
+ TaskDetails(displayId, taskId, transitionInfo = null, minimizeReason = minimizeReason),
)
}
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/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 9c3e815b389d..912d3839fae7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -1317,14 +1317,14 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
- public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
- PictureInPictureParams pictureInPictureParams, int launcherRotation,
- Rect keepClearArea) {
+ public Rect startSwipePipToHome(ActivityManager.RunningTaskInfo taskInfo,
+ int launcherRotation, Rect keepClearArea) {
Rect[] result = new Rect[1];
executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome",
(controller) -> {
- result[0] = controller.startSwipePipToHome(componentName, activityInfo,
- pictureInPictureParams, launcherRotation, keepClearArea);
+ result[0] = controller.startSwipePipToHome(taskInfo.topActivity,
+ taskInfo.topActivityInfo, taskInfo.pictureInPictureParams,
+ launcherRotation, keepClearArea);
}, true /* blocking */);
return result[0];
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
index 63c151268bdb..a033b824aa28 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
@@ -50,6 +50,11 @@ public class PipAlphaAnimator extends ValueAnimator {
private final SurfaceControl mLeash;
private final SurfaceControl.Transaction mStartTransaction;
+ private final SurfaceControl.Transaction mFinishTransaction;
+
+ private final int mDirection;
+ private final int mCornerRadius;
+ private final int mShadowRadius;
private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
@@ -59,6 +64,7 @@ public class PipAlphaAnimator extends ValueAnimator {
mAnimationStartCallback.run();
}
if (mStartTransaction != null) {
+ onAlphaAnimationUpdate(getStartAlphaValue(), mStartTransaction);
mStartTransaction.apply();
}
}
@@ -66,6 +72,10 @@ public class PipAlphaAnimator extends ValueAnimator {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
+ if (mFinishTransaction != null) {
+ onAlphaAnimationUpdate(getEndAlphaValue(), mFinishTransaction);
+ mFinishTransaction.apply();
+ }
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
}
@@ -77,8 +87,9 @@ public class PipAlphaAnimator extends ValueAnimator {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
final float alpha = (Float) animation.getAnimatedValue();
- mSurfaceControlTransactionFactory.getTransaction()
- .setAlpha(mLeash, alpha).apply();
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ onAlphaAnimationUpdate(alpha, tx);
}
};
@@ -91,19 +102,21 @@ public class PipAlphaAnimator extends ValueAnimator {
public PipAlphaAnimator(Context context,
SurfaceControl leash,
- SurfaceControl.Transaction tx,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
@Fade int direction) {
mLeash = leash;
- mStartTransaction = tx;
- if (direction == FADE_IN) {
- setFloatValues(0f, 1f);
- } else { // direction == FADE_OUT
- setFloatValues(1f, 0f);
- }
+ mStartTransaction = startTransaction;
+ mFinishTransaction = finishTransaction;
+
+ mDirection = direction;
+ setFloatValues(getStartAlphaValue(), getEndAlphaValue());
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
final int enterAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
+ mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
+ mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
setDuration(enterAnimationDuration);
addListener(mAnimatorListener);
addUpdateListener(mAnimatorUpdateListener);
@@ -117,6 +130,21 @@ public class PipAlphaAnimator extends ValueAnimator {
mAnimationEndCallback = runnable;
}
+ private void onAlphaAnimationUpdate(float alpha, SurfaceControl.Transaction tx) {
+ tx.setAlpha(mLeash, alpha)
+ .setCornerRadius(mLeash, mCornerRadius)
+ .setShadowRadius(mLeash, mShadowRadius);
+ tx.apply();
+ }
+
+ private float getStartAlphaValue() {
+ return mDirection == FADE_IN ? 0f : 1f;
+ }
+
+ private float getEndAlphaValue() {
+ return mDirection == FADE_IN ? 1f : 0f;
+ }
+
@VisibleForTesting
void setSurfaceControlTransactionFactory(
@NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 562b26014bf3..b1984ccef4cb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -40,6 +40,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.Preconditions;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayChangeController;
@@ -358,10 +359,21 @@ public class PipController implements ConfigurationChangeListener,
//
private Rect getSwipePipToHomeBounds(ComponentName componentName, ActivityInfo activityInfo,
- PictureInPictureParams pictureInPictureParams,
+ int displayId, PictureInPictureParams pictureInPictureParams,
int launcherRotation, Rect hotseatKeepClearArea) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"getSwipePipToHomeBounds: %s", componentName);
+
+ // If PiP is enabled on Connected Displays, update PipDisplayLayoutState to have the correct
+ // display info that PiP is entering in.
+ if (Flags.enableConnectedDisplaysPip()) {
+ final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
+ if (displayLayout != null) {
+ mPipDisplayLayoutState.setDisplayId(displayId);
+ mPipDisplayLayoutState.setDisplayLayout(displayLayout);
+ }
+ }
+
// Preemptively add the keep clear area for Hotseat, so that it is taken into account
// when calculating the entry destination bounds of PiP window.
mPipBoundsState.setNamedUnrestrictedKeepClearArea(
@@ -592,14 +604,14 @@ public class PipController implements ConfigurationChangeListener,
}
@Override
- public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
- PictureInPictureParams pictureInPictureParams, int launcherRotation,
- Rect keepClearArea) {
+ public Rect startSwipePipToHome(ActivityManager.RunningTaskInfo taskInfo,
+ int launcherRotation, Rect keepClearArea) {
Rect[] result = new Rect[1];
executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome",
(controller) -> {
- result[0] = controller.getSwipePipToHomeBounds(componentName, activityInfo,
- pictureInPictureParams, launcherRotation, keepClearArea);
+ result[0] = controller.getSwipePipToHomeBounds(taskInfo.topActivity,
+ taskInfo.topActivityInfo, taskInfo.displayId,
+ taskInfo.pictureInPictureParams, launcherRotation, keepClearArea);
}, true /* blocking */);
return result[0];
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index ed532cad0523..21b0820f523a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -294,7 +294,8 @@ public class PipScheduler {
interface PipAlphaAnimatorSupplier {
PipAlphaAnimator get(@NonNull Context context,
SurfaceControl leash,
- SurfaceControl.Transaction tx,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
@PipAlphaAnimator.Fade int direction);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 4902455cae16..8cba076d28f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -59,6 +59,8 @@ import com.android.internal.util.Preconditions;
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ComponentUtils;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
@@ -112,6 +114,7 @@ public class PipTransition extends PipTransitionController implements
private final PipScheduler mPipScheduler;
private final PipTransitionState mPipTransitionState;
private final PipDisplayLayoutState mPipDisplayLayoutState;
+ private final DisplayController mDisplayController;
private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
private final Optional<DesktopWallpaperActivityTokenProvider>
mDesktopWallpaperActivityTokenProviderOptional;
@@ -151,6 +154,7 @@ public class PipTransition extends PipTransitionController implements
PipTransitionState pipTransitionState,
PipDisplayLayoutState pipDisplayLayoutState,
PipUiStateChangeController pipUiStateChangeController,
+ DisplayController displayController,
Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
Optional<DesktopWallpaperActivityTokenProvider>
desktopWallpaperActivityTokenProviderOptional) {
@@ -164,6 +168,7 @@ public class PipTransition extends PipTransitionController implements
mPipTransitionState = pipTransitionState;
mPipTransitionState.addPipTransitionStateChangedListener(this);
mPipDisplayLayoutState = pipDisplayLayoutState;
+ mDisplayController = displayController;
mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
mDesktopWallpaperActivityTokenProviderOptional =
desktopWallpaperActivityTokenProviderOptional;
@@ -513,7 +518,7 @@ public class PipTransition extends PipTransitionController implements
private void startOverlayFadeoutAnimation(@NonNull SurfaceControl overlayLeash,
@NonNull Runnable onAnimationEnd) {
PipAlphaAnimator animator = new PipAlphaAnimator(mContext, overlayLeash,
- null /* startTx */, PipAlphaAnimator.FADE_OUT);
+ null /* startTx */, null /* finishTx */, PipAlphaAnimator.FADE_OUT);
animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DELAY_MS);
animator.setAnimationEndCallback(onAnimationEnd);
animator.start();
@@ -604,7 +609,7 @@ public class PipTransition extends PipTransitionController implements
.setAlpha(pipLeash, 0f);
PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
- PipAlphaAnimator.FADE_IN);
+ finishTransaction, PipAlphaAnimator.FADE_IN);
// This should update the pip transition state accordingly after we stop playing.
animator.setAnimationEndCallback(this::finishTransition);
cacheAndStartTransitionAnimator(animator);
@@ -699,7 +704,7 @@ public class PipTransition extends PipTransitionController implements
finishTransaction.setAlpha(pipChange.getLeash(), 0f);
if (mPendingRemoveWithFadeout) {
PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipChange.getLeash(),
- startTransaction, PipAlphaAnimator.FADE_OUT);
+ startTransaction, finishTransaction, PipAlphaAnimator.FADE_OUT);
animator.setAnimationEndCallback(this::finishTransition);
animator.start();
} else {
@@ -824,6 +829,17 @@ public class PipTransition extends PipTransitionController implements
mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo,
pipParams, mPipBoundsAlgorithm);
+ // If PiP is enabled on Connected Displays, update PipDisplayLayoutState to have the correct
+ // display info that PiP is entering in.
+ if (Flags.enableConnectedDisplaysPip()) {
+ final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(
+ pipTask.displayId);
+ if (displayLayout != null) {
+ mPipDisplayLayoutState.setDisplayId(pipTask.displayId);
+ mPipDisplayLayoutState.setDisplayLayout(displayLayout);
+ }
+ }
+
// calculate the entry bounds and notify core to move task to pinned with final bounds
final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
mPipBoundsState.setBounds(entryBounds);
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/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index aeccd86e122c..36eaebdf4fff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -1148,9 +1148,12 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
change, layer, info, t, mLeashMap);
appearedTargets[nextTargetIdx++] = target;
// reparent into the original `mInfo` since that's where we are animating.
- final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
+ final TransitionInfo.Root root = TransitionUtil.getRootFor(change, mInfo);
final boolean wasClosing = closingIdx >= 0;
- t.reparent(target.leash, mInfo.getRoot(rootIdx).getLeash());
+ t.reparent(target.leash, root.getLeash());
+ t.setPosition(target.leash,
+ change.getStartAbsBounds().left - root.getOffset().x,
+ change.getStartAbsBounds().top - root.getOffset().y);
t.setLayer(target.leash, layer);
if (wasClosing) {
// App was previously visible and is closing
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index c9136b4ad18d..37c93518998a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -138,6 +138,7 @@ import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.split.OffscreenTouchZone;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
@@ -556,6 +557,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
+ if (PipUtils.isPip2ExperimentEnabled()
+ && request.getPipChange() != null && getSplitPosition(
+ request.getPipChange().getTaskInfo().taskId) != SPLIT_POSITION_UNDEFINED) {
+ // In PiP2, PiP-able task can also come in through the pip change request field.
+ return true;
+ }
+
// If one of the splitting tasks support auto-pip, wm-core might reparent the task to TDA
// and file a TRANSIT_PIP transition when finishing transitions.
// @see com.android.server.wm.RootWindowContainer#moveActivityToPinnedRootTask
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
index e61929fef312..2133275cde61 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -17,13 +17,14 @@
package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
-import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
-import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -36,6 +37,7 @@ import android.view.SurfaceControl;
import android.window.TransitionInfo;
import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -54,6 +56,7 @@ public class MixedTransitionHelper {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ "entering PIP while Split-Screen is foreground.");
TransitionInfo.Change pipChange = null;
+ TransitionInfo.Change pipActivityChange = null;
TransitionInfo.Change wallpaper = null;
final TransitionInfo everythingElse =
subCopy(info, TRANSIT_TO_BACK, true /* changes */);
@@ -68,6 +71,13 @@ public class MixedTransitionHelper {
pipChange = change;
// going backwards, so remove-by-index is fine.
everythingElse.getChanges().remove(i);
+ } else if (change.getTaskInfo() == null && change.getParent() != null
+ && pipChange != null && change.getParent().equals(pipChange.getContainer())) {
+ // Cache the PiP activity if it's a target and cached pip task change is its parent;
+ // note that we are bottom-to-top, so if such activity has a task
+ // that is also a target, then it must have been cached already as pipChange.
+ pipActivityChange = change;
+ everythingElse.getChanges().remove(i);
} else if (isHomeOpening(change)) {
homeIsOpening = true;
} else if (isWallpaper(change)) {
@@ -138,9 +148,19 @@ public class MixedTransitionHelper {
}
}
- pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
- pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
- finishCB);
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ TransitionInfo pipInfo = subCopy(info, TRANSIT_PIP, false /* withChanges */);
+ pipInfo.getChanges().add(pipChange);
+ if (pipActivityChange != null) {
+ pipInfo.getChanges().add(pipActivityChange);
+ }
+ pipHandler.startAnimation(mixed.mTransition, pipInfo, startTransaction,
+ finishTransaction, finishCB);
+ } else {
+ pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
+ pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
+ finishCB);
+ }
// make a new finishTransaction because pip's startEnterAnimation "consumes" it so
// we need a separate one to send over to launcher.
SurfaceControl.Transaction otherFinishT = new SurfaceControl.Transaction();
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/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
index 0d75e659d95c..7948eadb28f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -110,9 +110,6 @@ public abstract class CarWindowDecorViewModel
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- if (!shouldShowWindowDecor(taskInfo)) {
- return false;
- }
createWindowDecoration(taskInfo, taskSurface, startT, finishT);
return true;
}
@@ -125,12 +122,9 @@ public abstract class CarWindowDecorViewModel
return;
}
- if (!shouldShowWindowDecor(taskInfo)) {
- destroyWindowDecoration(taskInfo);
- return;
- }
-
- decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion);
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ decoration.relayout(taskInfo, t, t,
+ /* isCaptionVisible= */ shouldShowWindowDecor(taskInfo));
}
@Override
@@ -221,7 +215,8 @@ public abstract class CarWindowDecorViewModel
mWindowDecorViewHostSupplier,
new ButtonClickListener(taskInfo));
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
- windowDecoration.relayout(taskInfo, startT, finishT);
+ windowDecoration.relayout(taskInfo, startT, finishT,
+ /* isCaptionVisible= */ shouldShowWindowDecor(taskInfo));
}
private class ButtonClickListener implements View.OnClickListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
index 1ca82d23c830..39437845301e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
@@ -20,14 +20,17 @@ import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.Context;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.WindowInsets;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -44,6 +47,7 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
private WindowDecorLinearLayout mRootView;
private @ShellBackgroundThread final ShellExecutor mBgExecutor;
private final View.OnClickListener mClickListener;
+ private final RelayoutResult<WindowDecorLinearLayout> mResult = new RelayoutResult<>();
CarWindowDecoration(
Context context,
@@ -71,26 +75,32 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
@SuppressLint("MissingPermission")
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
+ relayout(taskInfo, startT, finishT, /* isCaptionVisible= */ true);
+ }
+
+ @SuppressLint("MissingPermission")
+ void relayout(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+ boolean isCaptionVisible) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
RelayoutParams relayoutParams = new RelayoutParams();
- RelayoutResult<WindowDecorLinearLayout> outResult = new RelayoutResult<>();
updateRelayoutParams(relayoutParams, taskInfo,
- mDisplayController.getInsetsState(taskInfo.displayId));
+ mDisplayController.getInsetsState(taskInfo.displayId), isCaptionVisible);
- relayout(relayoutParams, startT, finishT, wct, mRootView, outResult);
+ relayout(relayoutParams, startT, finishT, wct, mRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
- if (outResult.mRootView == null) {
+ if (mResult.mRootView == null) {
// This means something blocks the window decor from showing, e.g. the task is hidden.
// Nothing is set up in this case including the decoration surface.
return;
}
- if (mRootView != outResult.mRootView) {
- mRootView = outResult.mRootView;
- setupRootView(outResult.mRootView, mClickListener);
+ if (mRootView != mResult.mRootView) {
+ mRootView = mResult.mRootView;
+ setupRootView(mResult.mRootView, mClickListener);
}
}
@@ -108,18 +118,31 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
private void updateRelayoutParams(
RelayoutParams relayoutParams,
ActivityManager.RunningTaskInfo taskInfo,
- InsetsState displayInsetsState) {
+ @Nullable InsetsState displayInsetsState,
+ boolean isCaptionVisible) {
relayoutParams.reset();
relayoutParams.mRunningTaskInfo = taskInfo;
// todo(b/382071404): update to car specific UI
relayoutParams.mLayoutResId = R.layout.caption_window_decor;
relayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
- relayoutParams.mIsCaptionVisible = mIsStatusBarVisible && !mIsKeyguardVisibleAndOccluded;
- relayoutParams.mCaptionTopPadding = 0;
+ relayoutParams.mIsCaptionVisible =
+ isCaptionVisible && mIsStatusBarVisible && !mIsKeyguardVisibleAndOccluded;
+ if (displayInsetsState != null) {
+ relayoutParams.mCaptionTopPadding = getTopPadding(
+ taskInfo.getConfiguration().windowConfiguration.getBounds(),
+ displayInsetsState);
+ }
relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
relayoutParams.mApplyStartTransactionOnDraw = true;
}
+ private static int getTopPadding(Rect taskBounds, @NonNull InsetsState insetsState) {
+ Insets systemDecor = insetsState.calculateInsets(taskBounds,
+ WindowInsets.Type.systemBars() & ~WindowInsets.Type.captionBar(),
+ false /* ignoreVisibility */);
+ return systemDecor.top;
+ }
+
/**
* Sets up listeners when a new root view is created.
*/
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..eb3a698fb58e 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
@@ -34,6 +34,7 @@ import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HAND
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing;
import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod;
+import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason;
import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR;
@@ -99,6 +100,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;
@@ -979,7 +981,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
ToggleTaskSizeInteraction.AmbiguousSource.HEADER_BUTTON, mMotionEvent);
}
} else if (id == R.id.minimize_window) {
- mDesktopTasksController.minimizeTask(decoration.mTaskInfo);
+ mDesktopTasksController.minimizeTask(
+ decoration.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON);
}
}
@@ -1926,14 +1929,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/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
index 413e7bc5d1d6..016e04039b12 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
@@ -46,6 +46,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -294,7 +295,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
testExecutor.flushAll()
assertThat(result).isTrue()
- verify(desktopTasksController).minimizeTask(task)
+ verify(desktopTasksController).minimizeTask(task, MinimizeReason.KEY_GESTURE)
}
private fun setUpFreeformTask(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 4317143aebfe..a9ebcef9bd98 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -42,6 +42,7 @@ import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
@@ -62,6 +63,7 @@ import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_MINIMIZE
+import java.util.Optional
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Before
@@ -69,6 +71,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -102,6 +105,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
private val mockShellInit = mock<ShellInit>()
private val transitions = mock<Transitions>()
private val context = mock<Context>()
+ private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
+ private val desktopTasksLimiter = mock<DesktopTasksLimiter>()
private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
private lateinit var shellInit: ShellInit
@@ -119,6 +124,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
mockShellInit,
transitions,
desktopModeEventLogger,
+ Optional.of(desktopTasksLimiter),
+ shellTaskOrganizer,
)
val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
verify(mockShellInit)
@@ -755,6 +762,39 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
verify(desktopModeEventLogger, never()).logSessionExit(any())
}
+ @Test
+ fun onTransitionReady_taskIsBeingMinimized_logsTaskMinimized() {
+ transitionObserver.isSessionActive = true
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 1))
+ val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo2)
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_BACK, 0)
+ .addChange(createChange(TRANSIT_TO_BACK, taskInfo2))
+ .build()
+ `when`(desktopTasksLimiter.getMinimizingTask(any()))
+ .thenReturn(
+ DesktopTasksLimiter.TaskDetails(
+ taskInfo2.displayId,
+ taskInfo2.taskId,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+ )
+
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskRemoved(
+ eq(
+ DEFAULT_TASK_UPDATE.copy(
+ instanceId = 2,
+ visibleTaskCount = 1,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+ )
+ )
+ }
+
/** Simulate calling the onTransitionReady() method */
private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
val transition = mock<IBinder>()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 6003a219d4db..8d73f3f59afd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -1046,14 +1046,14 @@ class DesktopRepositoryTest : ShellTestCase() {
}
@Test
- fun removeDesktop_multipleTasks_removesAll() {
+ fun removeDesk_multipleTasks_removesAll() {
// The front-most task will be the one added last through `addTask`.
repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 3, isVisible = true)
repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true)
repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 1, isVisible = true)
repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
- val tasksBeforeRemoval = repo.removeDesktop(displayId = DEFAULT_DISPLAY)
+ val tasksBeforeRemoval = repo.removeDesk(displayId = DEFAULT_DISPLAY)
assertThat(tasksBeforeRemoval).containsExactly(1, 2, 3).inOrder()
assertThat(repo.getActiveTasks(displayId = DEFAULT_DISPLAY)).isEmpty()
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..6c4f043a4f39 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
@@ -102,6 +102,7 @@ import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopTasksController.DesktopModeEntryExitTransitionListener
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
@@ -257,6 +258,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 +318,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 +1147,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()
@@ -2144,7 +2163,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(pipTask)
+ controller.minimizeTask(pipTask, MinimizeReason.MINIMIZE_BUTTON)
verifyExitDesktopWCTNotExecuted()
taskRepository.setTaskInPip(DEFAULT_DISPLAY, pipTask.taskId, enterPip = false)
@@ -2164,7 +2183,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2180,7 +2199,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
verify(freeformTaskTransitionStarter).startPipTransition(any())
verify(freeformTaskTransitionStarter, never()).startMinimizedModeTransition(any())
@@ -2192,7 +2211,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(Binder())
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(any())
verify(freeformTaskTransitionStarter, never()).startPipTransition(any())
@@ -2205,7 +2224,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startPipTransition(captor.capture())
@@ -2221,7 +2240,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2237,7 +2256,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
.thenReturn(transition)
// The only active task is being minimized.
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2254,7 +2273,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
// The only active task is already minimized.
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2271,7 +2290,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
- controller.minimizeTask(task1)
+ controller.minimizeTask(task1, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2291,7 +2310,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
// task1 is the only visible task as task2 is minimized.
- controller.minimizeTask(task1)
+ controller.minimizeTask(task1, MinimizeReason.MINIMIZE_BUTTON)
// Adds remove wallpaper operation
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2306,7 +2325,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task), any())
}
@@ -2323,7 +2342,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
ExitResult.Exit(exitingTask = task.taskId, runOnTransitionStart = runOnTransit)
)
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
assertThat(runOnTransit.invocations).isEqualTo(1)
assertThat(runOnTransit.lastInvoked).isEqualTo(transition)
@@ -3267,7 +3286,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(pipTask)
+ controller.minimizeTask(pipTask, MinimizeReason.MINIMIZE_BUTTON)
verifyExitDesktopWCTNotExecuted()
freeformTask.isFocused = true
@@ -3588,6 +3607,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 +5220,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/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index c8214b3838e2..acfe1e9fd5a2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -20,6 +20,7 @@ import android.app.ActivityManager.RunningTaskInfo
import android.graphics.Rect
import android.os.Binder
import android.os.Handler
+import android.os.IBinder
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
@@ -43,6 +44,7 @@ import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGAT
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
@@ -180,7 +182,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val task = setUpFreeformTask()
markTaskHidden(task)
- desktopTasksLimiter.addPendingMinimizeChange(Binder(), displayId = 1, taskId = task.taskId)
+ addPendingMinimizeChange(Binder(), displayId = 1, taskId = task.taskId)
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
}
@@ -208,11 +210,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val taskTransition = Binder()
val task = setUpFreeformTask()
markTaskHidden(task)
- desktopTasksLimiter.addPendingMinimizeChange(
- pendingTransition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(pendingTransition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -231,11 +229,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val transition = Binder()
val task = setUpFreeformTask()
markTaskVisible(task)
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -254,11 +248,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val transition = Binder()
val task = setUpFreeformTask()
markTaskHidden(task)
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -276,11 +266,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun onTransitionReady_pendingTransition_changeTaskToBack_taskIsMinimized() {
val transition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -299,11 +285,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val bounds = Rect(0, 0, 200, 200)
val transition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
val change =
TransitionInfo.Change(task.token, mock(SurfaceControl::class.java)).apply {
@@ -330,11 +312,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val mergedTransition = Binder()
val newTransition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- mergedTransition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(mergedTransition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
.onTransitionMerged(mergedTransition, newTransition)
@@ -541,11 +519,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
(1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
val transition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -573,11 +547,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
(1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
val transition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -606,11 +576,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val mergedTransition = Binder()
val newTransition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- mergedTransition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(mergedTransition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -633,6 +599,60 @@ class DesktopTasksLimiterTest : ShellTestCase() {
verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
}
+ @Test
+ fun getMinimizingTask_noPendingTransition_returnsNull() {
+ val transition = Binder()
+
+ assertThat(desktopTasksLimiter.getMinimizingTask(transition)).isNull()
+ }
+
+ @Test
+ fun getMinimizingTask_pendingTaskTransition_returnsTask() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+ addPendingMinimizeChange(
+ transition,
+ taskId = task.taskId,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+
+ assertThat(desktopTasksLimiter.getMinimizingTask(transition))
+ .isEqualTo(
+ createTaskDetails(taskId = task.taskId, minimizeReason = MinimizeReason.TASK_LIMIT)
+ )
+ }
+
+ @Test
+ fun getMinimizingTask_activeTaskTransition_returnsTask() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+ addPendingMinimizeChange(
+ transition,
+ taskId = task.taskId,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build()
+
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
+ transition,
+ transitionInfo,
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
+ )
+
+ assertThat(desktopTasksLimiter.getMinimizingTask(transition))
+ .isEqualTo(
+ createTaskDetails(
+ taskId = task.taskId,
+ transitionInfo = transitionInfo,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+ )
+ }
+
private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createFreeformTask(displayId)
`when`(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
@@ -640,6 +660,20 @@ class DesktopTasksLimiterTest : ShellTestCase() {
return task
}
+ private fun createTaskDetails(
+ displayId: Int = DEFAULT_DISPLAY,
+ taskId: Int,
+ transitionInfo: TransitionInfo? = null,
+ minimizeReason: MinimizeReason? = null,
+ ) = DesktopTasksLimiter.TaskDetails(displayId, taskId, transitionInfo, minimizeReason)
+
+ fun addPendingMinimizeChange(
+ transition: IBinder,
+ displayId: Int = DEFAULT_DISPLAY,
+ taskId: Int,
+ minimizeReason: MinimizeReason = MinimizeReason.TASK_LIMIT,
+ ) = desktopTasksLimiter.addPendingMinimizeChange(transition, displayId, taskId, minimizeReason)
+
private fun markTaskVisible(task: RunningTaskInfo) {
desktopTaskRepo.updateTask(task.displayId, task.taskId, isVisible = true)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
index 9cc18ffdaed7..607e6a450883 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
@@ -19,6 +19,8 @@ package com.android.wm.shell.pip2.animation;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -33,6 +35,7 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import org.junit.Before;
@@ -48,6 +51,8 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class PipAlphaAnimatorTest {
+ private static final float TEST_CORNER_RADIUS = 1f;
+ private static final float TEST_SHADOW_RADIUS = 2f;
@Mock private Context mMockContext;
@@ -55,7 +60,9 @@ public class PipAlphaAnimatorTest {
@Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
- @Mock private SurfaceControl.Transaction mMockTransaction;
+ @Mock private SurfaceControl.Transaction mMockAnimateTransaction;
+ @Mock private SurfaceControl.Transaction mMockStartTransaction;
+ @Mock private SurfaceControl.Transaction mMockFinishTransaction;
@Mock private Runnable mMockStartCallback;
@@ -69,9 +76,15 @@ public class PipAlphaAnimatorTest {
MockitoAnnotations.initMocks(this);
when(mMockContext.getResources()).thenReturn(mMockResources);
when(mMockResources.getInteger(anyInt())).thenReturn(0);
- when(mMockFactory.getTransaction()).thenReturn(mMockTransaction);
- when(mMockTransaction.setAlpha(any(SurfaceControl.class), anyFloat()))
- .thenReturn(mMockTransaction);
+ when(mMockFactory.getTransaction()).thenReturn(mMockAnimateTransaction);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_corner_radius))
+ .thenReturn((int) TEST_CORNER_RADIUS);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_shadow_radius))
+ .thenReturn((int) TEST_SHADOW_RADIUS);
+
+ prepareTransaction(mMockAnimateTransaction);
+ prepareTransaction(mMockStartTransaction);
+ prepareTransaction(mMockFinishTransaction);
mTestLeash = new SurfaceControl.Builder()
.setContainerLayer()
@@ -82,8 +95,8 @@ public class PipAlphaAnimatorTest {
@Test
public void setAnimationStartCallback_fadeInAnimator_callbackStartCallback() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction,
- PipAlphaAnimator.FADE_IN);
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback);
mPipAlphaAnimator.setAnimationEndCallback(mMockEndCallback);
@@ -98,8 +111,8 @@ public class PipAlphaAnimatorTest {
@Test
public void setAnimationEndCallback_fadeInAnimator_callbackStartAndEndCallback() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction,
- PipAlphaAnimator.FADE_IN);
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback);
mPipAlphaAnimator.setAnimationEndCallback(mMockEndCallback);
@@ -109,36 +122,98 @@ public class PipAlphaAnimatorTest {
});
verify(mMockStartCallback).run();
- verify(mMockStartCallback).run();
+ verify(mMockEndCallback).run();
+ }
+
+ @Test
+ public void onAnimationStart_setCornerAndShadowRadii() {
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
+ mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipAlphaAnimator.start();
+ mPipAlphaAnimator.pause();
+ });
+
+ verify(mMockStartTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockStartTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
+ }
+
+ @Test
+ public void onAnimationUpdate_setCornerAndShadowRadii() {
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
+ mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipAlphaAnimator.start();
+ mPipAlphaAnimator.pause();
+ });
+
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
+ }
+
+ @Test
+ public void onAnimationEnd_setCornerAndShadowRadii() {
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
+ mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipAlphaAnimator.start();
+ mPipAlphaAnimator.end();
+ });
+
+ verify(mMockFinishTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockFinishTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
}
@Test
public void onAnimationEnd_fadeInAnimator_leashVisibleAtEnd() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction,
- PipAlphaAnimator.FADE_IN);
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mMockFinishTransaction, PipAlphaAnimator.FADE_IN);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mPipAlphaAnimator.start();
- clearInvocations(mMockTransaction);
+ clearInvocations(mMockAnimateTransaction);
mPipAlphaAnimator.end();
});
- verify(mMockTransaction).setAlpha(mTestLeash, 1.0f);
+ verify(mMockAnimateTransaction).setAlpha(mTestLeash, 1.0f);
}
@Test
public void onAnimationEnd_fadeOutAnimator_leashInvisibleAtEnd() {
- mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction,
- PipAlphaAnimator.FADE_OUT);
+ mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockStartTransaction,
+ mMockFinishTransaction, PipAlphaAnimator.FADE_OUT);
mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mPipAlphaAnimator.start();
- clearInvocations(mMockTransaction);
+ clearInvocations(mMockAnimateTransaction);
mPipAlphaAnimator.end();
});
- verify(mMockTransaction).setAlpha(mTestLeash, 0f);
+ verify(mMockAnimateTransaction).setAlpha(mTestLeash, 0f);
+ }
+
+
+ // set up transaction chaining
+ private void prepareTransaction(SurfaceControl.Transaction tx) {
+ when(tx.setAlpha(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
+ when(tx.setCornerRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
+ when(tx.setShadowRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
index aef44a40fa0f..bd857c7dcd45 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
@@ -111,7 +111,7 @@ public class PipSchedulerTest {
mRootTaskDisplayAreaOrganizer);
mPipScheduler.setPipTransitionController(mMockPipTransitionController);
mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory);
- mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, tx, direction) ->
+ mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, startTx, finishTx, direction) ->
mMockAlphaAnimator);
SurfaceControl testLeash = new SurfaceControl.Builder()
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/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 79e9b9c8cd77..b4791642663a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -61,6 +61,7 @@ import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.desktopmode.DesktopImmersiveController
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
@@ -272,7 +273,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
onClickListenerCaptor.value.onClick(view)
- verify(mockDesktopTasksController).minimizeTask(decor.mTaskInfo)
+ verify(mockDesktopTasksController).minimizeTask(decor.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON)
}
@Test
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/ActionButtonsPreference/res/color/settingslib_expressive_actionbutton_background.xml b/packages/SettingsLib/ActionButtonsPreference/res/color/settingslib_expressive_actionbutton_background.xml
new file mode 100644
index 000000000000..ec9ee2211259
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/color/settingslib_expressive_actionbutton_background.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/settingslib_materialColorSurface"/>
+ <item android:state_checked="true" android:color="?attr/colorContainerChecked"/>
+ <item android:state_checkable="true" android:color="?attr/colorContainerUnchecked"/>
+ <item android:color="@color/settingslib_materialColorPrimaryContainer" />
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/color/settingslib_expressive_actionbutton_content_color.xml b/packages/SettingsLib/ActionButtonsPreference/res/color/settingslib_expressive_actionbutton_content_color.xml
new file mode 100644
index 000000000000..0488cbaead22
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/color/settingslib_expressive_actionbutton_content_color.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="@dimen/material_emphasis_disabled" android:color="?attr/colorOnSurface"/>
+ <item android:state_checkable="true" android:state_checked="true"
+ android:color="?attr/colorOnContainerChecked"/>
+ <item android:state_checkable="true" android:color="?attr/colorOnContainerUnchecked"/>
+ <item android:color="@color/settingslib_materialColorOnPrimaryContainer"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml b/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml
index fd8cecb8536e..267c9f65e104 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml
@@ -17,9 +17,14 @@
<resources>
<style name="SettingsLibActionButton.Expressive" parent="SettingsLibButtonStyle.Expressive.Tonal">
- <item name="android:backgroundTint">@color/settingslib_materialColorPrimaryContainer</item>
- <item name="iconTint">@color/settingslib_materialColorOnPrimaryContainer</item>
- <item name="iconGravity">textTop</item>
+ <item name="android:backgroundTint">@color/settingslib_expressive_actionbutton_background</item>
+ <item name="android:textColor">@color/settingslib_expressive_actionbutton_content_color</item>
+ <item name="android:insetTop">@dimen/settingslib_expressive_space_none</item>
+ <item name="android:insetBottom">@dimen/settingslib_expressive_space_none</item>
+ <item name="iconTint">@color/settingslib_expressive_actionbutton_content_color</item>
+ <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
+ <item name="iconPadding">@dimen/settingslib_expressive_space_none</item>"
+ <item name="iconGravity">textStart</item>
</style>
<style name="SettingsLibActionButton.Expressive.Label" parent="">
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index 601e001f48c2..0027d632319b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -549,7 +549,7 @@ public class ActionButtonsPreference extends Preference implements GroupSectionD
((MaterialButton) mButton).setIcon(mIcon);
}
mButton.setEnabled(mIsEnabled);
- mActionLayout.setOnClickListener(mListener);
+ mButton.setOnClickListener(mListener);
mActionLayout.setEnabled(mIsEnabled);
mActionLayout.setContentDescription(mText);
} else {
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/SettingsLib/src/com/android/settingslib/notification/modes/ZenDurationDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/DndDurationDialogFactory.java
index 98c3edba8223..c5fa0aacbda1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenDurationDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/DndDurationDialogFactory.java
@@ -44,7 +44,12 @@ import com.android.settingslib.R;
import java.util.Arrays;
-public class ZenDurationDialog {
+/**
+ * This dialog configures the default behavior that the user prefers when enabling DND.
+ * Not to be confused with {@link EnableDndDialogFactory}, which is the dialog that will be shown
+ * when the user enables DND if the "Ask every time" option was selected in this dialog.
+ */
+public class DndDurationDialogFactory {
private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
@VisibleForTesting
protected static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
@@ -72,7 +77,7 @@ public class ZenDurationDialog {
@VisibleForTesting
protected LayoutInflater mLayoutInflater;
- public ZenDurationDialog(Context context) {
+ public DndDurationDialogFactory(Context context) {
mContext = context;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableDndDialogFactory.java
index c48694cecb4c..f0e7fb851d5f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableDndDialogFactory.java
@@ -54,8 +54,15 @@ import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Objects;
-public class EnableZenModeDialog {
- private static final String TAG = "EnableZenModeDialog";
+/**
+ * When enabling DND, if the user has the setting to "Ask every time" for the duration, we show
+ * this dialog to allow the user to select for how long they want DND to be enabled this time.
+ * Not to be confused with {@link DndDurationDialogFactory}, which is the dialog that allows the
+ * user to configure the default behavior for enabling DND (and in turn may lead to this dialog
+ * being shown, since it contains the said "Ask every time" option).
+ */
+public class EnableDndDialogFactory {
+ private static final String TAG = "EnableDndDialogFactory";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
@@ -74,7 +81,7 @@ public class EnableZenModeDialog {
private static final int MINUTES_MS = 60 * SECONDS_MS;
@Nullable
- private final ZenModeDialogMetricsLogger mMetricsLogger;
+ private final EnableDndDialogMetricsLogger mMetricsLogger;
@VisibleForTesting
protected Uri mForeverId;
@@ -101,17 +108,17 @@ public class EnableZenModeDialog {
@VisibleForTesting
protected LayoutInflater mLayoutInflater;
- public EnableZenModeDialog(Context context) {
+ public EnableDndDialogFactory(Context context) {
this(context, 0);
}
- public EnableZenModeDialog(Context context, int themeResId) {
+ public EnableDndDialogFactory(Context context, int themeResId) {
this(context, themeResId, false /* cancelIsNeutral */,
- new ZenModeDialogMetricsLogger(context));
+ new EnableDndDialogMetricsLogger(context));
}
- public EnableZenModeDialog(Context context, int themeResId, boolean cancelIsNeutral,
- ZenModeDialogMetricsLogger metricsLogger) {
+ public EnableDndDialogFactory(Context context, int themeResId, boolean cancelIsNeutral,
+ EnableDndDialogMetricsLogger metricsLogger) {
mContext = context;
mThemeResId = themeResId;
mCancelIsNeutral = cancelIsNeutral;
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeDialogMetricsLogger.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableDndDialogMetricsLogger.java
index 17695e396bef..552bf8d95dcf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeDialogMetricsLogger.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableDndDialogMetricsLogger.java
@@ -22,12 +22,12 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
/**
- * Logs ui events for {@link EnableZenModeDialog}.
+ * Logs ui events for {@link EnableDndDialogFactory}.
*/
-public class ZenModeDialogMetricsLogger {
+public class EnableDndDialogMetricsLogger {
private final Context mContext;
- public ZenModeDialogMetricsLogger(Context context) {
+ public EnableDndDialogMetricsLogger(Context context) {
mContext = context;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/DndDurationDialogFactoryTest.java
index 19845a04ea18..0b05a4f2dff0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenDurationDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/DndDurationDialogFactoryTest.java
@@ -16,6 +16,12 @@
package com.android.settingslib.notification.modes;
+import static com.android.settingslib.notification.modes.DndDurationDialogFactory.ALWAYS_ASK_CONDITION_INDEX;
+import static com.android.settingslib.notification.modes.DndDurationDialogFactory.COUNTDOWN_CONDITION_INDEX;
+import static com.android.settingslib.notification.modes.DndDurationDialogFactory.FOREVER_CONDITION_INDEX;
+import static com.android.settingslib.notification.modes.DndDurationDialogFactory.MAX_BUCKET_MINUTES;
+import static com.android.settingslib.notification.modes.DndDurationDialogFactory.MIN_BUCKET_MINUTES;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -32,6 +38,8 @@ import android.view.View;
import androidx.appcompat.app.AlertDialog;
+import com.android.settingslib.notification.modes.DndDurationDialogFactory.ConditionTag;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,8 +47,8 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
-public class ZenDurationDialogTest {
- private ZenDurationDialog mController;
+public class DndDurationDialogFactoryTest {
+ private DndDurationDialogFactory mController;
private Context mContext;
private LayoutInflater mLayoutInflater;
@@ -53,7 +61,7 @@ public class ZenDurationDialogTest {
mContentResolver = RuntimeEnvironment.application.getContentResolver();
mLayoutInflater = LayoutInflater.from(mContext);
- mController = spy(new ZenDurationDialog(mContext));
+ mController = spy(new DndDurationDialogFactory(mContext));
mController.mLayoutInflater = mLayoutInflater;
mController.getContentView();
mBuilder = new AlertDialog.Builder(mContext);
@@ -65,12 +73,9 @@ public class ZenDurationDialogTest {
Settings.Global.ZEN_DURATION_PROMPT);
mController.setupDialog(mBuilder);
- assertFalse(mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(ZenDurationDialog.COUNTDOWN_CONDITION_INDEX).rb
- .isChecked());
- assertTrue(mController.getConditionTagAt(
- ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(FOREVER_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.isChecked());
+ assertTrue(mController.getConditionTagAt(ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
}
@Test
@@ -79,12 +84,9 @@ public class ZenDurationDialogTest {
Settings.Secure.ZEN_DURATION_FOREVER);
mController.setupDialog(mBuilder);
- assertTrue(mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(ZenDurationDialog.COUNTDOWN_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(
- ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
+ assertTrue(mController.getConditionTagAt(FOREVER_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
}
@Test
@@ -92,12 +94,9 @@ public class ZenDurationDialogTest {
Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION, 45);
mController.setupDialog(mBuilder);
- assertFalse(mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb
- .isChecked());
- assertTrue(mController.getConditionTagAt(ZenDurationDialog.COUNTDOWN_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(
- ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(FOREVER_CONDITION_INDEX).rb.isChecked());
+ assertTrue(mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
}
@Test
@@ -106,8 +105,7 @@ public class ZenDurationDialogTest {
Settings.Secure.ZEN_DURATION_FOREVER);
mController.setupDialog(mBuilder);
- mController.getConditionTagAt(ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.setChecked(
- true);
+ mController.getConditionTagAt(ALWAYS_ASK_CONDITION_INDEX).rb.setChecked(true);
mController.updateZenDuration(Settings.Secure.ZEN_DURATION_FOREVER);
assertEquals(Settings.Secure.ZEN_DURATION_PROMPT, Settings.Secure.getInt(mContentResolver,
@@ -120,8 +118,7 @@ public class ZenDurationDialogTest {
Settings.Secure.ZEN_DURATION_PROMPT);
mController.setupDialog(mBuilder);
- mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb.setChecked(
- true);
+ mController.getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
mController.updateZenDuration(Settings.Secure.ZEN_DURATION_PROMPT);
assertEquals(Settings.Secure.ZEN_DURATION_FOREVER, Settings.Secure.getInt(mContentResolver,
@@ -134,8 +131,7 @@ public class ZenDurationDialogTest {
Settings.Secure.ZEN_DURATION_PROMPT);
mController.setupDialog(mBuilder);
- mController.getConditionTagAt(ZenDurationDialog.COUNTDOWN_CONDITION_INDEX).rb.setChecked(
- true);
+ mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
mController.updateZenDuration(Settings.Secure.ZEN_DURATION_PROMPT);
// countdown defaults to 60 minutes:
@@ -152,59 +148,50 @@ public class ZenDurationDialogTest {
// click time button starts at 60 minutes
// - 1 hour to MAX_BUCKET_MINUTES (12 hours), increments by 1 hour
// - 0-60 minutes increments by 15 minutes
- View view = mController.mZenRadioGroupContent.getChildAt(
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
- ZenDurationDialog.ConditionTag tag = mController.getConditionTagAt(
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
+ View view = mController.mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX);
+ ConditionTag tag = mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX);
// test incrementing up:
- mController.onClickTimeButton(view, tag, true, ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.onClickTimeButton(view, tag, true, COUNTDOWN_CONDITION_INDEX);
assertEquals(120, tag.countdownZenDuration); // goes from 1 hour to 2 hours
// try clicking up 50 times - should max out at ZenDurationDialog.MAX_BUCKET_MINUTES
for (int i = 0; i < 50; i++) {
- mController.onClickTimeButton(view, tag, true,
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.onClickTimeButton(view, tag, true, COUNTDOWN_CONDITION_INDEX);
}
- assertEquals(ZenDurationDialog.MAX_BUCKET_MINUTES, tag.countdownZenDuration);
+ assertEquals(MAX_BUCKET_MINUTES, tag.countdownZenDuration);
// reset, test incrementing down:
mController.mBucketIndex = -1; // reset current bucket index to reset countdownZenDuration
tag.countdownZenDuration = 60; // back to default
- mController.onClickTimeButton(view, tag, false,
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.onClickTimeButton(view, tag, false, COUNTDOWN_CONDITION_INDEX);
assertEquals(45, tag.countdownZenDuration); // goes from 60 minutes to 45 minutes
// try clicking down 50 times - should stop at MIN_BUCKET_MINUTES
for (int i = 0; i < 50; i++) {
- mController.onClickTimeButton(view, tag, false,
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.onClickTimeButton(view, tag, false, COUNTDOWN_CONDITION_INDEX);
}
- assertEquals(ZenDurationDialog.MIN_BUCKET_MINUTES, tag.countdownZenDuration);
+ assertEquals(MIN_BUCKET_MINUTES, tag.countdownZenDuration);
// reset countdownZenDuration to unbucketed number, should round change to nearest bucket
mController.mBucketIndex = -1;
tag.countdownZenDuration = 50;
- mController.onClickTimeButton(view, tag, false,
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.onClickTimeButton(view, tag, false, COUNTDOWN_CONDITION_INDEX);
assertEquals(45, tag.countdownZenDuration);
mController.mBucketIndex = -1;
tag.countdownZenDuration = 50;
- mController.onClickTimeButton(view, tag, true,
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.onClickTimeButton(view, tag, true, COUNTDOWN_CONDITION_INDEX);
assertEquals(60, tag.countdownZenDuration);
mController.mBucketIndex = -1;
tag.countdownZenDuration = 75;
- mController.onClickTimeButton(view, tag, false,
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.onClickTimeButton(view, tag, false, COUNTDOWN_CONDITION_INDEX);
assertEquals(60, tag.countdownZenDuration);
mController.mBucketIndex = -1;
tag.countdownZenDuration = 75;
- mController.onClickTimeButton(view, tag, true,
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.onClickTimeButton(view, tag, true, COUNTDOWN_CONDITION_INDEX);
assertEquals(120, tag.countdownZenDuration);
}
@@ -213,12 +200,9 @@ public class ZenDurationDialogTest {
Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
Settings.Secure.ZEN_DURATION_FOREVER);
mController.setupDialog(mBuilder);
- ZenDurationDialog.ConditionTag forever = mController.getConditionTagAt(
- ZenDurationDialog.FOREVER_CONDITION_INDEX);
- ZenDurationDialog.ConditionTag countdown = mController.getConditionTagAt(
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
- ZenDurationDialog.ConditionTag alwaysAsk = mController.getConditionTagAt(
- ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX);
+ ConditionTag forever = mController.getConditionTagAt(FOREVER_CONDITION_INDEX);
+ ConditionTag countdown = mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX);
+ ConditionTag alwaysAsk = mController.getConditionTagAt(ALWAYS_ASK_CONDITION_INDEX);
forever.rb.setChecked(true);
assertThat(forever.line1.getStateDescription().toString()).isEqualTo("selected");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/EnableDndDialogFactoryTest.java
index e397f97e0277..fc9fd8077fd0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/EnableZenModeDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/EnableDndDialogFactoryTest.java
@@ -16,6 +16,10 @@
package com.android.settingslib.notification.modes;
+import static com.android.settingslib.notification.modes.EnableDndDialogFactory.COUNTDOWN_ALARM_CONDITION_INDEX;
+import static com.android.settingslib.notification.modes.EnableDndDialogFactory.COUNTDOWN_CONDITION_INDEX;
+import static com.android.settingslib.notification.modes.EnableDndDialogFactory.FOREVER_CONDITION_INDEX;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
@@ -39,6 +43,8 @@ import android.net.Uri;
import android.service.notification.Condition;
import android.view.LayoutInflater;
+import com.android.settingslib.notification.modes.EnableDndDialogFactory.ConditionTag;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,8 +54,8 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
-public class EnableZenModeDialogTest {
- private EnableZenModeDialog mController;
+public class EnableDndDialogFactoryTest {
+ private EnableDndDialogFactory mController;
@Mock
private Context mContext;
@@ -74,7 +80,7 @@ public class EnableZenModeDialogTest {
when(mFragment.getContext()).thenReturn(mShadowContext);
mLayoutInflater = LayoutInflater.from(mShadowContext);
- mController = spy(new EnableZenModeDialog(mContext));
+ mController = spy(new EnableDndDialogFactory(mContext));
mController.mContext = mContext;
mController.mLayoutInflater = mLayoutInflater;
mController.mForeverId = Condition.newId(mContext).appendPath("forever").build();
@@ -101,36 +107,29 @@ public class EnableZenModeDialogTest {
Uri countdown = Condition.newId(mContext).appendPath("countdown").build();
mCountdownCondition = new Condition(countdown, "countdown", "", "", 0, 0, 0);
mController.bind(mCountdownCondition,
- mController.mZenRadioGroupContent.getChildAt(
- EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX),
- EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
+ COUNTDOWN_CONDITION_INDEX);
mController.bind(mAlarmCondition,
mController.mZenRadioGroupContent.getChildAt(
- EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX),
- EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX);
+ COUNTDOWN_ALARM_CONDITION_INDEX),
+ COUNTDOWN_ALARM_CONDITION_INDEX);
}
@Test
public void testForeverChecked() {
mController.bindConditions(mController.forever());
- assertTrue(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(
- EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
+ assertTrue(mController.getConditionTagAt(FOREVER_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
}
@Test
public void testNoneChecked() {
mController.bindConditions(null);
- assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(
- EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(FOREVER_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
}
@Test
@@ -139,12 +138,9 @@ public class EnableZenModeDialogTest {
doReturn(true).when(mController).isAlarm(mAlarmCondition);
mController.bindConditions(mAlarmCondition);
- assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
- .isChecked());
- assertTrue(mController.getConditionTagAt(
- EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(FOREVER_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.isChecked());
+ assertTrue(mController.getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
}
@Test
@@ -153,12 +149,9 @@ public class EnableZenModeDialogTest {
doReturn(true).when(mController).isCountdown(mCountdownCondition);
mController.bindConditions(mCountdownCondition);
- assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
- .isChecked());
- assertTrue(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
- .isChecked());
- assertFalse(mController.getConditionTagAt(
- EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(FOREVER_CONDITION_INDEX).rb.isChecked());
+ assertTrue(mController.getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.isChecked());
+ assertFalse(mController.getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
}
@Test
@@ -198,12 +191,12 @@ public class EnableZenModeDialogTest {
@Test
public void testAccessibility() {
mController.bindConditions(null);
- EnableZenModeDialog.ConditionTag forever = mController.getConditionTagAt(
- ZenDurationDialog.FOREVER_CONDITION_INDEX);
- EnableZenModeDialog.ConditionTag countdown = mController.getConditionTagAt(
- ZenDurationDialog.COUNTDOWN_CONDITION_INDEX);
- EnableZenModeDialog.ConditionTag alwaysAsk = mController.getConditionTagAt(
- ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX);
+ ConditionTag forever = mController.getConditionTagAt(
+ DndDurationDialogFactory.FOREVER_CONDITION_INDEX);
+ ConditionTag countdown = mController.getConditionTagAt(
+ DndDurationDialogFactory.COUNTDOWN_CONDITION_INDEX);
+ ConditionTag alwaysAsk = mController.getConditionTagAt(
+ DndDurationDialogFactory.ALWAYS_ASK_CONDITION_INDEX);
forever.rb.setChecked(true);
assertThat(forever.line1.getStateDescription().toString()).isEqualTo("selected");
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 7b4a2ca5de39..d367748d7dbf 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -92,6 +92,7 @@ public class SecureSettings {
Settings.Secure.KEY_REPEAT_DELAY_MS,
Settings.Secure.CAMERA_GESTURE_DISABLED,
Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT,
Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b0309a8fa5a5..242bdce0d79e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -142,6 +142,8 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.CAMERA_GESTURE_DISABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(
+ Secure.ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_AUTOCLICK_DELAY, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_LARGE_POINTER_ICON, BOOLEAN_VALIDATOR);
@@ -454,5 +456,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
new InclusiveIntegerRangeValidator(0, 1));
VALIDATORS.put(Secure.ADVANCED_PROTECTION_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1c6d6816e9b4..95059779ce3d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1718,6 +1718,9 @@ class SettingsProtoDumpUtil {
Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
SecureSettingsProto.Accessibility.AUTOCLICK_CURSOR_AREA_SIZE);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT,
+ SecureSettingsProto.Accessibility.AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
SecureSettingsProto.Accessibility.AUTOCLICK_ENABLED);
dumpSetting(s, p,
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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 4448000324d8..a044738d2e91 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -998,6 +998,11 @@
<!-- Permission required for CTS test - CtsContentProviderMultiUserTest -->
<uses-permission android:name="android.permission.RESOLVE_COMPONENT_FOR_UID" />
+ <!-- Permissions required for CTS test - MediaQualityTest -->
+ <uses-permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE" />
+ <uses-permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE" />
+ <uses-permission android:name="android.permission.READ_COLOR_ZONES" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index a935aacacf95..848ea0f077ba 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -531,6 +531,7 @@ android_library {
"-Adagger.fastInit=enabled",
"-Adagger.explicitBindingConflictsWithInject=ERROR",
"-Adagger.strictMultibindingValidation=enabled",
+ "-Adagger.useBindingGraphFix=ENABLED",
"-Aroom.schemaLocation=frameworks/base/packages/SystemUI/schemas",
],
kotlincflags: ["-Xjvm-default=all"],
@@ -757,6 +758,10 @@ android_library {
// TODO(b/352363800): Why do we need this?
"-J-Xmx8192M",
],
+ javacflags: [
+ "-Adagger.useBindingGraphFix=ENABLED",
+ ],
+
aaptflags: [
"--extra-packages",
"com.android.systemui",
@@ -847,7 +852,6 @@ android_robolectric_test {
"androidx.test.ext.truth",
],
-
instrumentation_for: "SystemUIRobo-stub",
java_resource_dirs: ["tests/robolectric/config"],
plugins: [
@@ -884,7 +888,6 @@ android_robolectric_test {
"androidx.test.ext.truth",
],
-
instrumentation_for: "SystemUIRobo-stub",
java_resource_dirs: ["tests/robolectric/config"],
plugins: [
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 07262533b81a..33e9919f06eb 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -40,6 +40,7 @@ florenceyang@google.com
gallmann@google.com
graciecheng@google.com
gwasserman@google.com
+helencheuk@google.com
hwwang@google.com
hyunyoungs@google.com
ikateryna@google.com
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2d68ab8ff451..df70b0337213 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -176,13 +176,6 @@ flag {
}
flag {
- name: "notifications_dismiss_pruned_summaries"
- namespace: "systemui"
- description: "NotifCollection.dismissNotifications will now dismiss summaries that are pruned from the shade."
- bug: "355967751"
-}
-
-flag {
name: "notification_transparent_header_fix"
namespace: "systemui"
description: "fix the transparent group header issue for async header inflation."
@@ -1930,3 +1923,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..3c0480d150e0 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,
)
}
}
@@ -1065,8 +1065,7 @@ private fun EmptyStateCta(contentPadding: PaddingValues, viewModel: BaseCommunal
) {
Icon(
imageVector = Icons.Default.Add,
- contentDescription =
- stringResource(R.string.label_for_button_in_empty_state_cta),
+ contentDescription = null,
modifier = Modifier.size(24.dp),
)
Spacer(Modifier.width(ButtonDefaults.IconSpacing))
@@ -1261,7 +1260,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 +1284,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 +1450,6 @@ private fun WidgetContent(
} else {
Modifier
}
-
Box(
modifier =
modifier
@@ -1539,7 +1537,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 +1702,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 +1789,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/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index 3295dde55238..bcd4d925814b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -28,6 +28,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
@@ -54,7 +55,7 @@ constructor(
VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN,
0,
null,
- viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isActive }
+ viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isActive },
)
val gravity = horizontalGravity or Gravity.BOTTOM
volumePanelPopup.show(expandable, gravity, { Title() }, { Content(it) })
@@ -95,14 +96,14 @@ constructor(
icon = { Icon(icon = buttonViewModel.button.icon) },
label = {
Text(
- modifier = Modifier.basicMarquee(),
text = label,
style = MaterialTheme.typography.labelMedium,
color = LocalContentColor.current,
textAlign = TextAlign.Center,
- maxLines = 2
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
)
- }
+ },
)
}
}
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/log/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
index 4b5e9de2cce7..72304a19c17d 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
@@ -75,6 +75,7 @@ constructor(
private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true,
+ private val systraceTrackName: String = DEFAULT_LOGBUFFER_TRACK_NAME,
) : MessageBuffer {
private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
@@ -244,10 +245,11 @@ constructor(
}
private fun echoToSystrace(level: LogLevel, tag: String, strMessage: String) {
+ if (!Trace.isEnabled()) return
Trace.instantForTrack(
Trace.TRACE_TAG_APP,
- "UI Events",
- "$name - ${level.shortString} $tag: $strMessage"
+ systraceTrackName,
+ "$name - ${level.shortString} $tag: $strMessage",
)
}
@@ -261,6 +263,10 @@ constructor(
LogLevel.WTF -> Log.wtf(message.tag, strMessage, message.exception)
}
}
+
+ companion object {
+ const val DEFAULT_LOGBUFFER_TRACK_NAME = "UI Events"
+ }
}
private const val TAG = "LogBuffer"
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/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index bf49186a7f01..451ebf32c367 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -27,7 +27,7 @@ import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.notification.modes.EnableZenModeDialog
+import com.android.settingslib.notification.modes.EnableDndDialogFactory
import com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
@@ -85,7 +85,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
@Mock private lateinit var zenModeController: ZenModeController
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var conditionUri: Uri
- @Mock private lateinit var enableZenModeDialog: EnableZenModeDialog
+ @Mock private lateinit var mEnableDndDialogFactory: EnableDndDialogFactory
@Captor private lateinit var spyZenMode: ArgumentCaptor<Int>
@Captor private lateinit var spyConditionId: ArgumentCaptor<Uri?>
@@ -105,7 +105,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
testDispatcher,
testScope.backgroundScope,
conditionUri,
- enableZenModeDialog,
+ mEnableDndDialogFactory,
)
}
@@ -322,7 +322,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
testScope.runTest {
val expandable: Expandable = mock()
secureSettingsRepository.setInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_PROMPT)
- whenever(enableZenModeDialog.createDialog()).thenReturn(mock())
+ whenever(mEnableDndDialogFactory.createDialog()).thenReturn(mock())
collectLastValue(underTest.lockScreenState)
runCurrent()
@@ -344,7 +344,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
whenever(zenModeController.isZenAvailable).thenReturn(true)
whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
settings.putInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_PROMPT)
- whenever(enableZenModeDialog.createDialog()).thenReturn(mock())
+ whenever(mEnableDndDialogFactory.createDialog()).thenReturn(mock())
collectLastValue(underTest.lockScreenState)
runCurrent()
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/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt
index 4936f8559bfb..d782d1e2612c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt
@@ -15,16 +15,25 @@
*/
package com.android.systemui.keyguard.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.transitions.blurConfig
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,17 +55,33 @@ class AodToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
transitionProgress = listOf(0.0f, 0.0f, 0.3f, 0.4f, 0.5f, 1.0f),
startValue = kosmos.blurConfig.maxBlurRadiusPx,
endValue = kosmos.blurConfig.maxBlurRadiusPx,
- transitionFactory = { value, state ->
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.PRIMARY_BOUNCER,
- value = value,
- transitionState = state,
- ownerName = "AodToPrimaryBouncerTransitionViewModelTest",
- )
- },
+ transitionFactory = ::step,
actualValuesProvider = { values },
checkInterpolatedValues = false,
)
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_BOUNCER_UI_REVAMP)
+ fun aodToPrimaryBouncerHidesLockscreen() =
+ testScope.runTest {
+ val lockscreenAlpha by collectValues(underTest.lockscreenAlpha)
+ val notificationAlpha by collectValues(underTest.notificationAlpha)
+
+ val transitionSteps = listOf(step(0.0f, STARTED), step(0.5f), step(1.0f, FINISHED))
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(transitionSteps, testScope)
+ runCurrent()
+
+ lockscreenAlpha.forEach { assertThat(it).isEqualTo(0.0f) }
+ notificationAlpha.forEach { assertThat(it).isEqualTo(0.0f) }
+ }
+
+ private fun step(value: Float, transitionState: TransitionState = RUNNING) =
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = value,
+ transitionState = transitionState,
+ ownerName = "AodToPrimaryBouncerTransitionViewModelTest",
+ )
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
index 0d487509a83f..4d58f7ab118e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
@@ -16,21 +16,26 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.transitions.blurConfig
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -85,6 +90,21 @@ class DozingToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
)
}
+ @Test
+ @EnableFlags(Flags.FLAG_BOUNCER_UI_REVAMP)
+ fun dozingToPrimaryBouncerHidesLockscreen() =
+ testScope.runTest {
+ val lockscreenAlpha by collectValues(underTest.lockscreenAlpha)
+ val notificationAlpha by collectValues(underTest.notificationAlpha)
+
+ val transitionSteps = listOf(step(0.0f, STARTED), step(0.5f), step(1.0f, FINISHED))
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(transitionSteps, testScope)
+ runCurrent()
+
+ lockscreenAlpha.forEach { assertThat(it).isEqualTo(0.0f) }
+ notificationAlpha.forEach { assertThat(it).isEqualTo(0.0f) }
+ }
+
private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
from = KeyguardState.DOZING,
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/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index a06353171c33..6a33b5f58820 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -54,7 +54,7 @@ class QSTileLoggerTest : SysuiTestCase() {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- whenever(logBufferFactory.create(any(), any(), any(), any())).thenReturn(logBuffer)
+ whenever(logBufferFactory.create(any(), any(), any(), any(), any())).thenReturn(logBuffer)
val tileSpec: TileSpec = TileSpec.create("chatty_tile")
underTest =
QSTileLogger(mapOf(tileSpec to chattyLogBuffer), logBufferFactory, statusBarController)
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/collection/NotifCollectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 4ef9792a2ad9..0df1073ca553 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -1383,7 +1383,6 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_NOTIFICATIONS_DISMISS_PRUNED_SUMMARIES)
public void testDismissNotificationsIncludesPrunedParents() {
// GIVEN a collection with 2 groups; one has a single child, one has two.
mCollection.addNotificationDismissInterceptor(mInterceptor1);
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/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index 92271198cac0..8d90d38a9eca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -212,7 +212,11 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
}
@Test
- @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ @EnableFlags(
+ PromotedNotificationUi.FLAG_NAME,
+ StatusBarNotifChips.FLAG_NAME,
+ android.app.Flags.FLAG_API_RICH_ONGOING,
+ )
fun extractContent_fromProgressStyle() {
val entry = createEntry {
setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75))
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/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 937f333b0065..a1c910d48cef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -24,6 +24,8 @@ import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChip
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import com.android.systemui.statusbar.pipeline.shared.ui.model.SystemInfoCombinedVisibilityModel
+import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -51,28 +53,15 @@ class FakeHomeStatusBarViewModel(
override val shouldShowOperatorNameView = MutableStateFlow(false)
override val isClockVisible =
- MutableStateFlow(
- HomeStatusBarViewModel.VisibilityModel(
- visibility = View.GONE,
- shouldAnimateChange = false,
- )
- )
+ MutableStateFlow(VisibilityModel(visibility = View.GONE, shouldAnimateChange = false))
override val isNotificationIconContainerVisible =
- MutableStateFlow(
- HomeStatusBarViewModel.VisibilityModel(
- visibility = View.GONE,
- shouldAnimateChange = false,
- )
- )
+ MutableStateFlow(VisibilityModel(visibility = View.GONE, shouldAnimateChange = false))
override val systemInfoCombinedVis =
MutableStateFlow(
- HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel(
- HomeStatusBarViewModel.VisibilityModel(
- visibility = View.GONE,
- shouldAnimateChange = false,
- ),
+ SystemInfoCombinedVisibilityModel(
+ VisibilityModel(visibility = View.GONE, shouldAnimateChange = false),
Idle,
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index 03abcf850d26..e74d009bb909 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -83,12 +83,11 @@ import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher
import com.android.systemui.statusbar.phone.data.repository.fakeDarkIconRepository
import com.android.systemui.statusbar.pipeline.shared.domain.interactor.setHomeStatusBarIconBlockList
import com.android.systemui.statusbar.pipeline.shared.domain.interactor.setHomeStatusBarInteractorShowOperatorName
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
+import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Before
@@ -423,8 +422,9 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
fun areNotificationsLightsOut_requiresFlagEnabled() =
kosmos.runTest {
assertLogsWtf {
- val flow = underTest.areNotificationsLightsOut
- assertThat(flow).isEqualTo(emptyFlow<Boolean>())
+ val latest by collectLastValue(underTest.areNotificationsLightsOut)
+ // Nothing is emitted
+ assertThat(latest).isNull()
}
}
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/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 55be9f79e598..ca98cbf20c3a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -140,6 +140,11 @@ public interface ActivityStarter {
void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityTransitionAnimator.Controller animationController,
@Nullable String customMessage);
+ /** Posts a start activity intent that dismisses keyguard. */
+ void postStartActivityDismissingKeyguard(Intent intent, int delay,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
+ @Nullable String customMessage,
+ @Nullable UserHandle userHandle);
void postStartActivityDismissingKeyguard(PendingIntent intent);
/**
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/layout/contextual_edu_dialog.xml b/packages/SystemUI/res/layout/contextual_edu_dialog.xml
index 09aa8daa217e..e83d4902d512 100644
--- a/packages/SystemUI/res/layout/contextual_edu_dialog.xml
+++ b/packages/SystemUI/res/layout/contextual_edu_dialog.xml
@@ -29,7 +29,7 @@
android:layout_height="wrap_content"
android:contentDescription="@null"
android:importantForAccessibility="no"
- android:paddingRight="16dp" />
+ android:paddingHorizontal="16dp" />
<TextView
android:id="@+id/edu_message"
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..d363e524a9f2 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: 38
oneway interface IOverviewProxy {
void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
@@ -137,4 +138,20 @@ 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;
+
+ /**
+ * Sent when {@link TaskbarDelegate#onDisplayReady} is called.
+ */
+ void onDisplayReady(int displayId) = 36;
+
+ /**
+ * Sent when {@link TaskbarDelegate#onDisplayRemoved} is called.
+ */
+ void onDisplayRemoved(int displayId) = 37;
}
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/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index 1b8baf657948..f11ebee46659 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -26,8 +26,8 @@ import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
import android.service.notification.ZenModeConfig
import android.util.Log
-import com.android.settingslib.notification.modes.EnableZenModeDialog
-import com.android.settingslib.notification.modes.ZenModeDialogMetricsLogger
+import com.android.settingslib.notification.modes.EnableDndDialogFactory
+import com.android.settingslib.notification.modes.EnableDndDialogMetricsLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -60,8 +60,7 @@ import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
@SysUISingleton
-class DoNotDisturbQuickAffordanceConfig
-constructor(
+class DoNotDisturbQuickAffordanceConfig(
private val context: Context,
private val controller: ZenModeController,
private val interactor: ZenModeInteractor,
@@ -70,7 +69,7 @@ constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
@Background private val backgroundScope: CoroutineScope,
private val testConditionId: Uri?,
- testDialog: EnableZenModeDialog?,
+ testDialogFactory: EnableDndDialogFactory?,
) : KeyguardQuickAffordanceConfig {
@Inject
@@ -118,13 +117,13 @@ constructor(
)
.id
- private val dialog: EnableZenModeDialog by lazy {
- testDialog
- ?: EnableZenModeDialog(
+ private val dialogFactory: EnableDndDialogFactory by lazy {
+ testDialogFactory
+ ?: EnableDndDialogFactory(
context,
R.style.Theme_SystemUI_Dialog,
true, /* cancelIsNeutral */
- ZenModeDialogMetricsLogger(context),
+ EnableDndDialogMetricsLogger(context),
)
}
@@ -224,7 +223,7 @@ constructor(
if (interactor.shouldAskForZenDuration(dnd)) {
// NOTE: The dialog handles turning on the mode itself.
return KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog(
- dialog.createDialog(),
+ dialogFactory.createDialog(),
expandable,
)
} else {
@@ -243,7 +242,7 @@ constructor(
settingsValue == ZEN_DURATION_PROMPT ->
KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog(
- dialog.createDialog(),
+ dialogFactory.createDialog(),
expandable,
)
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/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
index e3b55874de6f..26bf0bc258e8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -29,6 +30,7 @@ import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
/**
* Breaks down AOD->PRIMARY BOUNCER transition into discrete steps for corresponding views to
@@ -54,6 +56,12 @@ constructor(blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFl
override val windowBlurRadius: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+ val lockscreenAlpha: Flow<Float> =
+ if (Flags.bouncerUiRevamp()) transitionAnimation.immediatelyTransitionTo(0.0f)
+ else emptyFlow()
+
+ val notificationAlpha = lockscreenAlpha
+
override val notificationBlurRadius: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0.0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
index c937d5c6453d..d9ca267f9445 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_PRIMARY_BOUNCER_DURATION
import com.android.systemui.keyguard.shared.model.Edge
@@ -29,6 +30,7 @@ import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
/**
* Breaks down DOZING->PRIMARY BOUNCER transition into discrete steps for corresponding views to
@@ -64,6 +66,13 @@ constructor(private val blurConfig: BlurConfig, animationFlow: KeyguardTransitio
},
onFinish = { blurConfig.maxBlurRadiusPx },
)
+
+ val lockscreenAlpha: Flow<Float> =
+ if (Flags.bouncerUiRevamp()) transitionAnimation.immediatelyTransitionTo(0.0f)
+ else emptyFlow()
+
+ val notificationAlpha: Flow<Float> = lockscreenAlpha
+
override val notificationBlurRadius: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index eaba5d5a149c..e51e05b8ab61 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -96,9 +96,12 @@ constructor(
private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
+ private val aodToPrimaryBouncerTransitionViewModel: AodToPrimaryBouncerTransitionViewModel,
private val dozingToGoneTransitionViewModel: DozingToGoneTransitionViewModel,
private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel,
+ private val dozingToPrimaryBouncerTransitionViewModel:
+ DozingToPrimaryBouncerTransitionViewModel,
private val dreamingToAodTransitionViewModel: DreamingToAodTransitionViewModel,
private val dreamingToGoneTransitionViewModel: DreamingToGoneTransitionViewModel,
private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
@@ -243,9 +246,11 @@ constructor(
aodToGoneTransitionViewModel.lockscreenAlpha(viewState),
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ aodToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
dozingToGoneTransitionViewModel.lockscreenAlpha(viewState),
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ dozingToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
dreamingToAodTransitionViewModel.lockscreenAlpha,
dreamingToGoneTransitionViewModel.lockscreenAlpha,
dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
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/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
index 6351d7d28d07..c9d6f81dc79c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
@@ -18,6 +18,7 @@ package com.android.systemui.log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer.Companion.DEFAULT_LOGBUFFER_TRACK_NAME
import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
import com.android.systemui.log.echo.LogcatEchoTrackerAlways
import javax.inject.Inject
@@ -27,7 +28,7 @@ class LogBufferFactory
@Inject
constructor(
private val dumpManager: DumpManager,
- private val logcatEchoTracker: LogcatEchoTracker
+ private val logcatEchoTracker: LogcatEchoTracker,
) {
@JvmOverloads
fun create(
@@ -35,9 +36,11 @@ constructor(
maxSize: Int,
systrace: Boolean = true,
alwaysLogToLogcat: Boolean = false,
+ systraceTrackName: String = DEFAULT_LOGBUFFER_TRACK_NAME,
): LogBuffer {
val echoTracker = if (alwaysLogToLogcat) LogcatEchoTrackerAlways else logcatEchoTracker
- val buffer = LogBuffer(name, adjustMaxSize(maxSize), echoTracker, systrace)
+ val buffer =
+ LogBuffer(name, adjustMaxSize(maxSize), echoTracker, systrace, systraceTrackName)
dumpManager.registerBuffer(name, buffer)
return buffer
}
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/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 3f14b55e46a1..9270fff61c43 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -236,11 +236,29 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void onDisplayReady(int displayId) {
CommandQueue.Callbacks.super.onDisplayReady(displayId);
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onDisplayReady(displayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onDisplayReady() failed", e);
+ }
}
@Override
public void onDisplayRemoved(int displayId) {
CommandQueue.Callbacks.super.onDisplayRemoved(displayId);
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onDisplayRemoved(displayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onDisplayRemoved() failed", e);
+ }
}
// Separated into a method to keep setDependencies() clean/readable.
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/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 04f0b8736598..1e8ef359bb71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -42,7 +42,7 @@ import androidx.annotation.Nullable;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.notification.modes.EnableZenModeDialog;
+import com.android.settingslib.notification.modes.EnableDndDialogFactory;
import com.android.systemui.Prefs;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogTransitionAnimator;
@@ -59,7 +59,7 @@ import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger;
+import com.android.systemui.qs.tiles.dialog.QSEnableDndDialogMetricsLogger;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -84,7 +84,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
private final SharedPreferences mSharedPreferences;
private final UserSettingObserver mSettingZenDuration;
private final DialogTransitionAnimator mDialogTransitionAnimator;
- private final QSZenModeDialogMetricsLogger mQSZenDialogMetricsLogger;
+ private final QSEnableDndDialogMetricsLogger mQSDndDurationDialogLogger;
private boolean mListening;
@@ -121,7 +121,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
refreshState();
}
};
- mQSZenDialogMetricsLogger = new QSZenModeDialogMetricsLogger(mContext);
+ mQSDndDurationDialogLogger = new QSEnableDndDialogMetricsLogger(mContext);
}
public static void setVisible(Context context, boolean visible) {
@@ -201,9 +201,9 @@ public class DndTile extends QSTileImpl<BooleanState> {
}
private Dialog makeZenModeDialog() {
- AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog,
+ AlertDialog dialog = new EnableDndDialogFactory(mContext, R.style.Theme_SystemUI_Dialog,
true /* cancelIsNeutral */,
- mQSZenDialogMetricsLogger).createDialog();
+ mQSDndDurationDialogLogger).createDialog();
SystemUIDialog.applyFlags(dialog);
SystemUIDialog.setShowForAllUsers(dialog, true);
SystemUIDialog.registerDismissListener(dialog);
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/QSZenModeDialogMetricsLogger.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/QSEnableDndDialogMetricsLogger.java
index b3f66a6bf9dd..5196a22df29a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/QSZenModeDialogMetricsLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/QSEnableDndDialogMetricsLogger.java
@@ -19,7 +19,7 @@ package com.android.systemui.qs.tiles.dialog;
import android.content.Context;
import com.android.internal.logging.UiEventLogger;
-import com.android.settingslib.notification.modes.ZenModeDialogMetricsLogger;
+import com.android.settingslib.notification.modes.EnableDndDialogMetricsLogger;
import com.android.systemui.qs.QSDndEvent;
import com.android.systemui.qs.QSEvents;
@@ -30,10 +30,10 @@ import com.android.systemui.qs.QSEvents;
*
* Other names for DND (Do Not Disturb) include "Zen" and "Priority only".
*/
-public class QSZenModeDialogMetricsLogger extends ZenModeDialogMetricsLogger {
+public class QSEnableDndDialogMetricsLogger extends EnableDndDialogMetricsLogger {
private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
- public QSZenModeDialogMetricsLogger(Context context) {
+ public QSEnableDndDialogMetricsLogger(Context context) {
super(context);
}
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/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index cf9ee619b734..826329d5da1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -39,7 +39,6 @@ import static android.service.notification.NotificationListenerService.REASON_TI
import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
-import static com.android.systemui.Flags.notificationsDismissPrunedSummaries;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
@@ -278,9 +277,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
Assert.isMainThread();
checkForReentrantCall();
- if (notificationsDismissPrunedSummaries()) {
- entriesToDismiss = includeSummariesToDismiss(entriesToDismiss);
- }
+ entriesToDismiss = includeSummariesToDismiss(entriesToDismiss);
final int entryCount = entriesToDismiss.size();
final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>();
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/logging/dagger/NotificationsLogModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt
index d3359d39e959..6bcce3e21998 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.logging.dagger
+import com.android.app.tracing.TrackGroupUtils.trackGroup
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
@@ -44,7 +45,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotificationHeadsUpLog
fun provideNotificationHeadsUpLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifHeadsUpLog", 1000)
+ return factory.create(
+ "NotifHeadsUpLog",
+ 1000,
+ systraceTrackName = notifPipelineTrack("NotifHeadsUpLog"),
+ )
}
/** Provides a logging buffer for logs related to inflation of notifications. */
@@ -52,7 +57,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotifInflationLog
fun provideNotifInflationLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifInflationLog", 250)
+ return factory.create(
+ "NotifInflationLog",
+ 250,
+ systraceTrackName = notifPipelineTrack("NotifInflationLog"),
+ )
}
/** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -60,7 +69,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotifInteractionLog
fun provideNotifInteractionLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifInteractionLog", 50)
+ return factory.create(
+ "NotifInteractionLog",
+ 50,
+ systraceTrackName = notifPipelineTrack("NotifInteractionLog"),
+ )
}
/** Provides a logging buffer for notification interruption calculations. */
@@ -68,7 +81,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotificationInterruptLog
fun provideNotificationInterruptLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifInterruptLog", 100)
+ return factory.create(
+ "NotifInterruptLog",
+ 100,
+ systraceTrackName = notifPipelineTrack("NotifInterruptLog"),
+ )
}
/** Provides a logging buffer for all logs related to notifications on the lockscreen. */
@@ -91,7 +108,12 @@ object NotificationsLogModule {
if (Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()) {
maxSize *= 10
}
- return factory.create("NotifLog", maxSize, Compile.IS_DEBUG /* systrace */)
+ return factory.create(
+ "NotifLog",
+ maxSize,
+ /* systrace= */ Compile.IS_DEBUG,
+ systraceTrackName = notifPipelineTrack("NotifLog"),
+ )
}
/** Provides a logging buffer for all logs related to remote input controller. */
@@ -107,7 +129,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotificationRenderLog
fun provideNotificationRenderLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifRenderLog", 100)
+ return factory.create(
+ "NotifRenderLog",
+ 100,
+ systraceTrackName = notifPipelineTrack("NotifRenderLog"),
+ )
}
/** Provides a logging buffer for all logs related to managing notification sections. */
@@ -150,3 +176,13 @@ object NotificationsLogModule {
return factory.create("VisualStabilityLog", 50, /* maxSize */ false /* systrace */)
}
}
+
+private const val NOTIF_PIPELINE_TRACK_GROUP_NAME = "Notification pipeline"
+
+/**
+ * This generates a track name that is hierarcically collapsed inside
+ * [NOTIF_PIPELINE_TRACK_GROUP_NAME] in perfetto traces.
+ */
+private fun notifPipelineTrack(trackName: String): String {
+ return trackGroup(NOTIF_PIPELINE_TRACK_GROUP_NAME, trackName)
+}
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/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 495b50869458..f57107141f61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -127,7 +127,6 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrim
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState;
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.Assert;
@@ -282,11 +281,9 @@ public class NotificationStackScrollLayout
private boolean mExpandedInThisMotion;
private boolean mShouldShowShelfOnly;
protected boolean mScrollingEnabled;
- private boolean mIsCurrentUserSetup;
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
private boolean mClearAllInProgress;
- private FooterClearAllListener mFooterClearAllListener;
private boolean mFlingAfterUpEvent;
/**
* Was the scroller scrolled to the top when the down motion was observed?
@@ -467,7 +464,6 @@ public class NotificationStackScrollLayout
boolean mHeadsUpAnimatingAway;
private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
private int mStatusBarState;
- private int mUpcomingStatusBarState;
private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
private final Runnable mReflingAndAnimateScroll = this::animateScroll;
private int mCornerRadius;
@@ -498,7 +494,6 @@ public class NotificationStackScrollLayout
private float mLastSentExpandedHeight;
private boolean mWillExpand;
private int mGapHeight;
- private boolean mIsRemoteInputActive;
/**
* The extra inset during the full shade transition
@@ -572,10 +567,8 @@ public class NotificationStackScrollLayout
private boolean mDismissUsingRowTranslationX = true;
private ExpandableNotificationRow mTopHeadsUpRow;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
- private final ScreenOffAnimationController mScreenOffAnimationController;
private boolean mShouldUseSplitNotificationShade;
private boolean mShouldSkipTopPaddingAnimationAfterFold = false;
- private boolean mHasFilteredOutSeenNotifications;
@Nullable private SplitShadeStateController mSplitShadeStateController = null;
private boolean mIsSmallLandscapeLockscreenEnabled = false;
private boolean mSuppressHeightUpdates;
@@ -636,9 +629,6 @@ public class NotificationStackScrollLayout
};
@Nullable
- private OnClickListener mManageButtonClickListener;
-
- @Nullable
private WallpaperInteractor mWallpaperInteractor;
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
@@ -650,8 +640,6 @@ public class NotificationStackScrollLayout
mDebugLines = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
- mScreenOffAnimationController =
- Dependency.get(ScreenOffAnimationController.class);
mSectionsManager.initialize(this);
mSections = mSectionsManager.createSectionsForBuckets();
@@ -5403,7 +5391,6 @@ public class NotificationStackScrollLayout
println(pw, "suppressChildrenMeasureLayout", mSuppressChildrenMeasureAndLayout);
println(pw, "scrollY", mAmbientState.getScrollY());
println(pw, "showShelfOnly", mShouldShowShelfOnly);
- println(pw, "isCurrentUserSetup", mIsCurrentUserSetup);
println(pw, "hideAmount", mAmbientState.getHideAmount());
println(pw, "ambientStateSwipingUp", mAmbientState.isSwipingUp());
println(pw, "maxDisplayedNotifications", mMaxDisplayedNotifications);
@@ -6793,10 +6780,6 @@ public class NotificationStackScrollLayout
void onClearAll(@SelectedRows int selectedRows);
}
- interface FooterClearAllListener {
- void onClearAll();
- }
-
interface ClearAllAnimationListener {
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index f0455fc3a22b..c1d022600559 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -49,9 +49,11 @@ import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
@@ -131,9 +133,12 @@ constructor(
private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
+ private val aodToPrimaryBouncerTransitionViewModel: AodToPrimaryBouncerTransitionViewModel,
dozingToGlanceableHubTransitionViewModel: DozingToGlanceableHubTransitionViewModel,
private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel,
+ private val dozingToPrimaryBouncerTransitionViewModel:
+ DozingToPrimaryBouncerTransitionViewModel,
private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
private val glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel,
@@ -554,8 +559,10 @@ constructor(
aodToGoneTransitionViewModel.notificationAlpha(viewState),
aodToLockscreenTransitionViewModel.notificationAlpha,
aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ aodToPrimaryBouncerTransitionViewModel.notificationAlpha,
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ dozingToPrimaryBouncerTransitionViewModel.notificationAlpha,
dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
goneToAodTransitionViewModel.notificationAlpha,
goneToDreamingTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 4751293a16cc..5a63c0cd84e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -312,6 +312,25 @@ constructor(
}
}
+ override fun postStartActivityDismissingKeyguard(
+ intent: Intent,
+ delay: Int,
+ animationController: ActivityTransitionAnimator.Controller?,
+ customMessage: String?,
+ userHandle: UserHandle?,
+ ) {
+ postOnUiThread(delay) {
+ activityStarterInternal.startActivityDismissingKeyguard(
+ intent = intent,
+ onlyProvisioned = true,
+ dismissShade = true,
+ animationController = animationController,
+ customMessage = customMessage,
+ userHandle = userHandle,
+ )
+ }
+ }
+
override fun dismissKeyguardThenExecute(
action: OnDismissAction,
cancel: Runnable?,
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/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 8daa8037c367..7e76d77abe61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -41,8 +41,8 @@ import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ConnectedD
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
+import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index b1cc208e9b43..9c1171fd1ebc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -57,6 +57,7 @@ import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarIconBlockListBinder
import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder
import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener
+import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.HomeStatusBarViewModelFactory
import javax.inject.Inject
@@ -304,11 +305,7 @@ fun StatusBarRoot(
fun Disambiguation(viewModel: HomeStatusBarViewModel) {
val clockVisibilityModel =
viewModel.isClockVisible.collectAsStateWithLifecycle(
- initialValue =
- HomeStatusBarViewModel.VisibilityModel(
- visibility = View.GONE,
- shouldAnimateChange = false,
- )
+ initialValue = VisibilityModel(visibility = View.GONE, shouldAnimateChange = false)
)
if (clockVisibilityModel.value.visibility == View.VISIBLE) {
Box(modifier = Modifier.fillMaxSize().alpha(0.5f), contentAlignment = Alignment.Center) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/SystemInfoCombinedVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/SystemInfoCombinedVisibilityModel.kt
new file mode 100644
index 000000000000..e27225270633
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/SystemInfoCombinedVisibilityModel.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.ui.model
+
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
+
+/** The combined visibility + animation state for the system info status bar area */
+data class SystemInfoCombinedVisibilityModel(
+ val baseVisibility: VisibilityModel,
+ val animationState: SystemEventAnimationState,
+) : Diffable<SystemInfoCombinedVisibilityModel> {
+ override fun logDiffs(prevVal: SystemInfoCombinedVisibilityModel, row: TableRowLogger) {
+ if (animationState != prevVal.animationState) {
+ row.logChange(COL_ANIM, animationState.name)
+ }
+
+ baseVisibility.logDiffs(prevVal.baseVisibility, row)
+ }
+
+ override fun logFull(row: TableRowLogger) {
+ row.logChange(COL_ANIM, animationState.name)
+ baseVisibility.logFull(row)
+ }
+
+ companion object {
+ const val COL_ANIM = "animState"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/VisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/VisibilityModel.kt
new file mode 100644
index 000000000000..7b39ada72c64
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/VisibilityModel.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.ui.model
+
+import android.view.View
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.util.visibilityString
+
+/** Models the current visibility for a specific child view of status bar. */
+data class VisibilityModel(
+ @View.Visibility val visibility: Int,
+ /** True if a visibility change should be animated. */
+ val shouldAnimateChange: Boolean,
+) : Diffable<VisibilityModel> {
+ override fun logDiffs(prevVal: VisibilityModel, row: TableRowLogger) {
+ if (visibility != prevVal.visibility) {
+ row.logChange(COL_VIS, visibilityString(visibility))
+ }
+
+ if (shouldAnimateChange != prevVal.shouldAnimateChange) {
+ row.logChange(COL_ANIMATE, shouldAnimateChange)
+ }
+ }
+
+ override fun logFull(row: TableRowLogger) {
+ row.logChange(COL_VIS, visibilityString(visibility))
+ row.logChange(COL_ANIMATE, shouldAnimateChange)
+ }
+
+ companion object {
+ const val COL_VIS = "vis"
+ const val COL_ANIMATE = "animate"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index dcd2dbf57b42..5acedf129184 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -28,6 +28,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
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.TransitionState
+import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -39,7 +41,6 @@ import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChip
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
-import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.StatusBarPopupChipsViewModel
@@ -53,7 +54,8 @@ import com.android.systemui.statusbar.phone.domain.interactor.DarkIconInteractor
import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
import com.android.systemui.statusbar.pipeline.shared.domain.interactor.HomeStatusBarIconBlockListInteractor
import com.android.systemui.statusbar.pipeline.shared.domain.interactor.HomeStatusBarInteractor
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
+import com.android.systemui.statusbar.pipeline.shared.ui.model.SystemInfoCombinedVisibilityModel
+import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -155,19 +157,6 @@ interface HomeStatusBarViewModel {
*/
val areaTint: Flow<StatusBarTintColor>
- /** Models the current visibility for a specific child view of status bar. */
- data class VisibilityModel(
- @View.Visibility val visibility: Int,
- /** True if a visibility change should be animated. */
- val shouldAnimateChange: Boolean,
- )
-
- /** The combined visibility + animation state for the system info status bar area */
- data class SystemInfoCombinedVisibilityModel(
- val baseVisibility: VisibilityModel,
- val animationState: SystemEventAnimationState,
- )
-
/** Interface for the assisted factory, to allow for providing a fake in tests */
interface HomeStatusBarViewModelFactory {
fun create(displayId: Int): HomeStatusBarViewModel
@@ -178,6 +167,7 @@ class HomeStatusBarViewModelImpl
@AssistedInject
constructor(
@Assisted thisDisplayId: Int,
+ tableLoggerFactory: TableLogBufferFactory,
homeStatusBarInteractor: HomeStatusBarInteractor,
homeStatusBarIconBlockListInteractor: HomeStatusBarIconBlockListInteractor,
lightsOutInteractor: LightsOutInteractor,
@@ -196,9 +186,19 @@ constructor(
statusBarContentInsetsViewModelStore: StatusBarContentInsetsViewModelStore,
@Application coroutineScope: CoroutineScope,
) : HomeStatusBarViewModel {
+
+ val tableLogger = tableLoggerFactory.getOrCreate(tableLogBufferName(thisDisplayId), 200)
+
override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
keyguardTransitionInteractor
.isInTransition(Edge.create(from = LOCKSCREEN, to = OCCLUDED))
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogger,
+ columnPrefix = "",
+ columnName = COL_LOCK_TO_OCCLUDED,
+ initialValue = false,
+ )
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
override val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> =
@@ -225,20 +225,33 @@ constructor(
// which lives elsewhere.)
currentScene == Scenes.Gone || isOccluded
}
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogger,
+ columnPrefix = "",
+ columnName = COL_ALLOWED_BY_SCENE,
+ initialValue = false,
+ )
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
override val areNotificationsLightsOut: Flow<Boolean> =
if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) {
- emptyFlow()
- } else {
- combine(
- notificationsInteractor.areAnyNotificationsPresent,
- lightsOutInteractor.isLowProfile(thisDisplayId) ?: flowOf(false),
- ) { hasNotifications, isLowProfile ->
- hasNotifications && isLowProfile
- }
- .distinctUntilChanged()
- }
+ emptyFlow()
+ } else {
+ combine(
+ notificationsInteractor.areAnyNotificationsPresent,
+ lightsOutInteractor.isLowProfile(thisDisplayId) ?: flowOf(false),
+ ) { hasNotifications, isLowProfile ->
+ hasNotifications && isLowProfile
+ }
+ .distinctUntilChanged()
+ }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogger,
+ columnPrefix = "",
+ columnName = COL_NOTIF_LIGHTS_OUT,
+ initialValue = false,
+ )
override val areaTint: Flow<StatusBarTintColor> =
darkIconInteractor
@@ -277,19 +290,26 @@ constructor(
override val shouldHomeStatusBarBeVisible =
combine(
- isHomeStatusBarAllowed,
- keyguardInteractor.isSecureCameraActive,
- headsUpNotificationInteractor.statusBarHeadsUpStatus,
- ) { isHomeStatusBarAllowed, isSecureCameraActive, headsUpState ->
- // When launching the camera over the lockscreen, the status icons would typically
- // become visible momentarily before animating out, since we're not yet aware that the
- // launching camera activity is fullscreen. Even once the activity finishes launching,
- // it takes a short time before WM decides that the top app wants to hide the icons and
- // tells us to hide them.
- // To ensure that this high-visibility animation is smooth, keep the icons hidden during
- // a camera launch. See b/257292822.
- headsUpState.isPinned || (isHomeStatusBarAllowed && !isSecureCameraActive)
- }
+ isHomeStatusBarAllowed,
+ keyguardInteractor.isSecureCameraActive,
+ headsUpNotificationInteractor.statusBarHeadsUpStatus,
+ ) { isHomeStatusBarAllowed, isSecureCameraActive, headsUpState ->
+ // When launching the camera over the lockscreen, the status icons would typically
+ // become visible momentarily before animating out, since we're not yet aware that
+ // the launching camera activity is fullscreen. Even once the activity finishes
+ // launching, it takes a short time before WM decides that the top app wants to hide
+ // the icons and tells us to hide them. To ensure that this high-visibility
+ // animation is smooth, keep the icons hidden during a camera launch. See
+ // b/257292822.
+ headsUpState.isPinned || (isHomeStatusBarAllowed && !isSecureCameraActive)
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogger,
+ columnPrefix = "",
+ columnName = COL_VISIBLE,
+ initialValue = false,
+ )
private val isAnyChipVisible =
if (StatusBarNotifChips.isEnabled) {
@@ -313,53 +333,73 @@ constructor(
override val shouldShowOperatorNameView: Flow<Boolean> =
combine(
- shouldHomeStatusBarBeVisible,
- hideStartSideContentForHeadsUp,
- homeStatusBarInteractor.visibilityViaDisableFlags,
- homeStatusBarInteractor.shouldShowOperatorName,
- ) {
- shouldStatusBarBeVisible,
- hideStartSideContentForHeadsUp,
- visibilityViaDisableFlags,
- shouldShowOperator ->
- shouldStatusBarBeVisible &&
- !hideStartSideContentForHeadsUp &&
- visibilityViaDisableFlags.isSystemInfoAllowed &&
- shouldShowOperator
- }
+ shouldHomeStatusBarBeVisible,
+ hideStartSideContentForHeadsUp,
+ homeStatusBarInteractor.visibilityViaDisableFlags,
+ homeStatusBarInteractor.shouldShowOperatorName,
+ ) {
+ shouldStatusBarBeVisible,
+ hideStartSideContentForHeadsUp,
+ visibilityViaDisableFlags,
+ shouldShowOperator ->
+ shouldStatusBarBeVisible &&
+ !hideStartSideContentForHeadsUp &&
+ visibilityViaDisableFlags.isSystemInfoAllowed &&
+ shouldShowOperator
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogger,
+ columnPrefix = "",
+ columnName = COL_SHOW_OPERATOR_NAME,
+ initialValue = false,
+ )
override val isClockVisible: Flow<VisibilityModel> =
combine(
- shouldHomeStatusBarBeVisible,
- hideStartSideContentForHeadsUp,
- homeStatusBarInteractor.visibilityViaDisableFlags,
- ) { shouldStatusBarBeVisible, hideStartSideContentForHeadsUp, visibilityViaDisableFlags ->
- val showClock =
- shouldStatusBarBeVisible &&
- visibilityViaDisableFlags.isClockAllowed &&
- !hideStartSideContentForHeadsUp
- // Always use View.INVISIBLE here, so that animations work
- VisibilityModel(showClock.toVisibleOrInvisible(), visibilityViaDisableFlags.animate)
- }
+ shouldHomeStatusBarBeVisible,
+ hideStartSideContentForHeadsUp,
+ homeStatusBarInteractor.visibilityViaDisableFlags,
+ ) { shouldStatusBarBeVisible, hideStartSideContentForHeadsUp, visibilityViaDisableFlags
+ ->
+ val showClock =
+ shouldStatusBarBeVisible &&
+ visibilityViaDisableFlags.isClockAllowed &&
+ !hideStartSideContentForHeadsUp
+ // Always use View.INVISIBLE here, so that animations work
+ VisibilityModel(showClock.toVisibleOrInvisible(), visibilityViaDisableFlags.animate)
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogger,
+ columnPrefix = COL_PREFIX_CLOCK,
+ initialValue = VisibilityModel(false.toVisibleOrInvisible(), false),
+ )
override val isNotificationIconContainerVisible: Flow<VisibilityModel> =
combine(
- shouldHomeStatusBarBeVisible,
- isAnyChipVisible,
- homeStatusBarInteractor.visibilityViaDisableFlags,
- ) { shouldStatusBarBeVisible, anyChipVisible, visibilityViaDisableFlags ->
- val showNotificationIconContainer =
- if (anyChipVisible) {
- false
- } else {
- shouldStatusBarBeVisible &&
- visibilityViaDisableFlags.areNotificationIconsAllowed
- }
- VisibilityModel(
- showNotificationIconContainer.toVisibleOrGone(),
- visibilityViaDisableFlags.animate,
+ shouldHomeStatusBarBeVisible,
+ isAnyChipVisible,
+ homeStatusBarInteractor.visibilityViaDisableFlags,
+ ) { shouldStatusBarBeVisible, anyChipVisible, visibilityViaDisableFlags ->
+ val showNotificationIconContainer =
+ if (anyChipVisible) {
+ false
+ } else {
+ shouldStatusBarBeVisible &&
+ visibilityViaDisableFlags.areNotificationIconsAllowed
+ }
+ VisibilityModel(
+ showNotificationIconContainer.toVisibleOrGone(),
+ visibilityViaDisableFlags.animate,
+ )
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogger,
+ columnPrefix = COL_PREFIX_NOTIF_CONTAINER,
+ initialValue = VisibilityModel(false.toVisibleOrInvisible(), false),
)
- }
private val isSystemInfoVisible =
combine(shouldHomeStatusBarBeVisible, homeStatusBarInteractor.visibilityViaDisableFlags) {
@@ -372,18 +412,19 @@ constructor(
override val systemInfoCombinedVis =
combine(isSystemInfoVisible, animations.animationState) { sysInfoVisible, animationState ->
- HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel(
- sysInfoVisible,
- animationState,
- )
+ SystemInfoCombinedVisibilityModel(sysInfoVisible, animationState)
}
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogger,
+ columnPrefix = COL_PREFIX_SYSTEM_INFO,
+ initialValue =
+ SystemInfoCombinedVisibilityModel(VisibilityModel(View.VISIBLE, false), Idle),
+ )
.stateIn(
coroutineScope,
SharingStarted.WhileSubscribed(),
- HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel(
- VisibilityModel(View.VISIBLE, false),
- Idle,
- ),
+ SystemInfoCombinedVisibilityModel(VisibilityModel(View.VISIBLE, false), Idle),
)
override val iconBlockList: Flow<List<String>> =
@@ -408,6 +449,19 @@ constructor(
HomeStatusBarViewModel.HomeStatusBarViewModelFactory {
override fun create(displayId: Int): HomeStatusBarViewModelImpl
}
+
+ companion object {
+ private const val COL_LOCK_TO_OCCLUDED = "Lock->Occluded"
+ private const val COL_ALLOWED_BY_SCENE = "allowedByScene"
+ private const val COL_NOTIF_LIGHTS_OUT = "notifLightsOut"
+ private const val COL_SHOW_OPERATOR_NAME = "showOperatorName"
+ private const val COL_VISIBLE = "visible"
+ private const val COL_PREFIX_CLOCK = "clock"
+ private const val COL_PREFIX_NOTIF_CONTAINER = "notifContainer"
+ private const val COL_PREFIX_SYSTEM_INFO = "systemInfo"
+
+ fun tableLogBufferName(displayId: Int) = "HomeStatusBarViewModel[$displayId]"
+ }
}
/** Lookup the color for a given view in the status bar */
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..db5f1301823b 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.EnableDndDialogFactory
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.QSEnableDndDialogMetricsLogger
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 dndDurationDialogLogger by lazy { QSEnableDndDialogMetricsLogger(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 =
+ EnableDndDialogFactory(
+ context,
+ R.style.Theme_SystemUI_Dialog,
+ /* cancelIsNeutral= */ true,
+ dndDurationDialogLogger,
+ )
+ .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/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
index 9cf02f26c9f7..ef147c741bec 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.graphics.drawable.Drawable
import android.media.AudioManager
import androidx.annotation.DrawableRes
+import com.android.settingslib.R as SettingsR
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.RingerMode
@@ -30,8 +31,10 @@ import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
+@SuppressLint("UseCompatLoadingForDrawables")
class VolumeDialogSliderIconProvider
@Inject
constructor(
@@ -40,7 +43,30 @@ constructor(
private val audioVolumeInteractor: AudioVolumeInteractor,
) {
- @SuppressLint("UseCompatLoadingForDrawables")
+ fun getAudioSharingIcon(isMuted: Boolean): Flow<Drawable> {
+ return flow {
+ val iconRes =
+ if (isMuted) {
+ R.drawable.ic_volume_media_bt_mute
+ } else {
+ R.drawable.ic_volume_media_bt
+ }
+ emit(context.getDrawable(iconRes)!!)
+ }
+ }
+
+ fun getCastIcon(isMuted: Boolean): Flow<Drawable> {
+ return flow {
+ val iconRes =
+ if (isMuted) {
+ SettingsR.drawable.ic_volume_remote_mute
+ } else {
+ SettingsR.drawable.ic_volume_remote
+ }
+ emit(context.getDrawable(iconRes)!!)
+ }
+ }
+
fun getStreamIcon(
stream: Int,
level: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index 89dd0352afa7..a752f1f78e74 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -57,12 +57,12 @@ private const val VOLUME_UPDATE_GRACE_PERIOD = 1000
class VolumeDialogSliderViewModel
@Inject
constructor(
+ private val sliderType: VolumeDialogSliderType,
private val interactor: VolumeDialogSliderInteractor,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
@VolumeDialog private val coroutineScope: CoroutineScope,
private val volumeDialogSliderIconProvider: VolumeDialogSliderIconProvider,
private val systemClock: SystemClock,
- private val sliderType: VolumeDialogSliderType,
private val logger: VolumeDialogLogger,
) {
@@ -82,14 +82,24 @@ constructor(
model
.flatMapLatest { streamModel ->
with(streamModel) {
- volumeDialogSliderIconProvider.getStreamIcon(
- stream = stream,
- level = level,
- levelMin = levelMin,
- levelMax = levelMax,
- isMuted = muteSupported && muted,
- isRoutedToBluetooth = routedToBluetooth,
- )
+ val isMuted = muteSupported && muted
+ when (sliderType) {
+ is VolumeDialogSliderType.Stream ->
+ volumeDialogSliderIconProvider.getStreamIcon(
+ stream = sliderType.audioStream,
+ level = level,
+ levelMin = levelMin,
+ levelMax = levelMax,
+ isMuted = isMuted,
+ isRoutedToBluetooth = routedToBluetooth,
+ )
+ is VolumeDialogSliderType.RemoteMediaStream -> {
+ volumeDialogSliderIconProvider.getCastIcon(isMuted)
+ }
+ is VolumeDialogSliderType.AudioSharingStream -> {
+ volumeDialogSliderIconProvider.getAudioSharingIcon(isMuted)
+ }
+ }
}
.map { icon -> streamModel.toStateModel(icon) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index 411e06ed1339..0c8dc11f4327 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -293,7 +293,7 @@ public class QuickAccessWalletController {
intent = getSysUiWalletIntent();
}
startQuickAccessViaIntent(intent, hasCard, activityStarter,
- animationController);
+ animationController, mQuickAccessWalletClient.getUser());
});
}
@@ -323,7 +323,8 @@ public class QuickAccessWalletController {
private void startQuickAccessViaIntent(Intent intent,
boolean hasCard,
ActivityStarter activityStarter,
- ActivityTransitionAnimator.Controller animationController) {
+ ActivityTransitionAnimator.Controller animationController,
+ UserHandle user) {
if (hasCard) {
activityStarter.startActivity(intent, true /* dismissShade */,
animationController, true /* showOverLockscreenWhenLocked */);
@@ -331,7 +332,9 @@ public class QuickAccessWalletController {
activityStarter.postStartActivityDismissingKeyguard(
intent,
/* delay= */ 0,
- animationController);
+ animationController,
+ null,
+ user);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 111492c3e227..18e1b6eb323d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -163,15 +163,13 @@ public class WalletActivity extends ComponentActivity implements
if (mKeyguardStateController.isUnlocked()) {
mUiEventLogger.log(WalletUiEvent.QAW_SHOW_ALL);
- mActivityStarter.startActivity(
- mWalletClient.createWalletIntent(), true);
+ startWalletActivity();
finish();
} else {
mUiEventLogger.log(WalletUiEvent.QAW_UNLOCK_FROM_SHOW_ALL_BUTTON);
mKeyguardDismissUtil.executeWhenUnlocked(() -> {
mUiEventLogger.log(WalletUiEvent.QAW_SHOW_ALL);
- mActivityStarter.startActivity(
- mWalletClient.createWalletIntent(), true);
+ startWalletActivity();
finish();
return false;
}, false, true);
@@ -193,6 +191,11 @@ public class WalletActivity extends ComponentActivity implements
});
}
+ private void startWalletActivity() {
+ mActivityStarter.startActivity(mWalletClient.createWalletIntent(), true,
+ null, true, mWalletClient.getUser());
+ }
+
@Override
protected void onStart() {
super.onStart();
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/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
index 733e2edaec84..8e97c86ba507 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.wallet.controller;
import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
@@ -35,6 +36,7 @@ import static org.mockito.Mockito.when;
import android.app.PendingIntent;
import android.app.role.RoleManager;
import android.content.Intent;
+import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
import android.service.quickaccesswallet.GetWalletCardsRequest;
import android.service.quickaccesswallet.QuickAccessWalletClient;
@@ -59,7 +61,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import java.util.List;
@@ -98,6 +99,7 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase {
when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true);
+ when(mQuickAccessWalletClient.getUser()).thenReturn(UserHandle.of(0));
mClock.setElapsedRealtime(100L);
doAnswer(invocation -> {
@@ -269,7 +271,8 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase {
public void getQuickAccessUiIntent_noCards_noPendingIntent_startsWalletActivity() {
mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, false);
verify(mActivityStarter).postStartActivityDismissingKeyguard(mIntentCaptor.capture(), eq(0),
- any(ActivityTransitionAnimator.Controller.class));
+ any(ActivityTransitionAnimator.Controller.class), eq(null),
+ eq(UserHandle.of(0)));
Intent intent = mIntentCaptor.getValue();
assertEquals(intent.getAction(), Intent.ACTION_VIEW);
assertEquals(
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/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index abbfa93edd17..1c0f97d294df 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -56,9 +56,11 @@ val Kosmos.keyguardRootViewModel by Fixture {
aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
+ aodToPrimaryBouncerTransitionViewModel = aodToPrimaryBouncerTransitionViewModel,
dozingToGoneTransitionViewModel = dozingToGoneTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel,
+ dozingToPrimaryBouncerTransitionViewModel = dozingToPrimaryBouncerTransitionViewModel,
dreamingToAodTransitionViewModel = dreamingToAodTransitionViewModel,
dreamingToGoneTransitionViewModel = dreamingToGoneTransitionViewModel,
dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
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/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 60e092c9709b..8461da77796d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -28,9 +28,11 @@ import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.aodToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dozingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dozingToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.dozingToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel
@@ -78,9 +80,11 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture {
aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
+ aodToPrimaryBouncerTransitionViewModel = aodToPrimaryBouncerTransitionViewModel,
dozingToGlanceableHubTransitionViewModel = dozingToGlanceableHubTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel,
+ dozingToPrimaryBouncerTransitionViewModel = dozingToPrimaryBouncerTransitionViewModel,
dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
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/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
index db7e31bb2cb6..f8bf3c3fbbd9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.table.tableLogBufferFactory
import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -39,6 +40,7 @@ var Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by
Kosmos.Fixture {
HomeStatusBarViewModelImpl(
testableContext.displayId,
+ tableLogBufferFactory,
homeStatusBarInteractor,
homeStatusBarIconBlockListInteractor,
lightsOutInteractor,
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/ravenwood/texts/ravenwood-standard-options.txt b/ravenwood/texts/ravenwood-standard-options.txt
index 3ec3e3ce2946..27223d8b72ff 100644
--- a/ravenwood/texts/ravenwood-standard-options.txt
+++ b/ravenwood/texts/ravenwood-standard-options.txt
@@ -5,6 +5,8 @@
# Keep all classes / methods / fields, but make the methods throw.
--default-throw
+--delete-finals
+
# Uncomment below lines to enable each feature.
#--default-method-call-hook
diff --git a/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
index 001943c18d6b..9c46a1646560 100644
--- a/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
+++ b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
@@ -2,6 +2,8 @@
--debug
+--delete-finals
+
# Uncomment below lines to enable each feature.
#--default-method-call-hook
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index cc704b2b32ed..985947575a86 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -411,6 +411,8 @@ class HostStubGen(val options: HostStubGenOptions) {
stats = stats,
enablePreTrace = options.enablePreTrace.get,
enablePostTrace = options.enablePostTrace.get,
+ deleteClassFinals = options.deleteFinals.get,
+ deleteMethodFinals = options.deleteFinals.get,
)
outVisitor = BaseAdapter.getVisitor(
classInternalName, classes, outVisitor, filter,
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 55e853e3e2fb..ae9276f711e1 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -106,6 +106,8 @@ class HostStubGenOptions(
var cleanUpOnError: SetOnce<Boolean> = SetOnce(false),
+ var deleteFinals: SetOnce<Boolean> = SetOnce(false),
+
var enableClassChecker: SetOnce<Boolean> = SetOnce(false),
var enablePreTrace: SetOnce<Boolean> = SetOnce(false),
var enablePostTrace: SetOnce<Boolean> = SetOnce(false),
@@ -218,6 +220,8 @@ class HostStubGenOptions(
"--gen-keep-all-file" ->
ret.inputJarAsKeepAllFile.set(nextArg())
+ "--delete-finals" -> ret.deleteFinals.set(true)
+
// Following options are for debugging.
"--enable-class-checker" -> ret.enableClassChecker.set(true)
"--no-class-checker" -> ret.enableClassChecker.set(false)
@@ -293,6 +297,7 @@ class HostStubGenOptions(
defaultMethodCallHook=$defaultMethodCallHook,
policyOverrideFiles=${policyOverrideFiles.toTypedArray().contentToString()},
defaultPolicy=$defaultPolicy,
+ deleteFinals=$deleteFinals,
cleanUpOnError=$cleanUpOnError,
enableClassChecker=$enableClassChecker,
enablePreTrace=$enablePreTrace,
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
index 261ef59c45c7..a08d1d605949 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
@@ -50,7 +50,13 @@ abstract class BaseAdapter(
val errors: HostStubGenErrors,
val stats: HostStubGenStats?,
val enablePreTrace: Boolean,
- val enablePostTrace: Boolean
+ val enablePostTrace: Boolean,
+ val deleteClassFinals: Boolean,
+ val deleteMethodFinals: Boolean,
+ // We don't remove finals from fields, because final fields have a stronger memory
+ // guarantee than non-final fields, see:
+ // https://docs.oracle.com/javase/specs/jls/se22/html/jls-17.html#jls-17.5
+ // i.e. changing a final field to non-final _could_ result in different behavior.
)
protected lateinit var currentPackageName: String
@@ -58,14 +64,33 @@ abstract class BaseAdapter(
protected var redirectionClass: String? = null
protected lateinit var classPolicy: FilterPolicyWithReason
+ private fun isEnum(access: Int): Boolean {
+ return (access and Opcodes.ACC_ENUM) != 0
+ }
+
+ protected fun modifyClassAccess(access: Int): Int {
+ if (options.deleteClassFinals && !isEnum(access)) {
+ return access and Opcodes.ACC_FINAL.inv()
+ }
+ return access
+ }
+
+ protected fun modifyMethodAccess(access: Int): Int {
+ if (options.deleteMethodFinals) {
+ return access and Opcodes.ACC_FINAL.inv()
+ }
+ return access
+ }
+
override fun visit(
version: Int,
- access: Int,
+ origAccess: Int,
name: String,
signature: String?,
superName: String?,
interfaces: Array<String>,
) {
+ val access = modifyClassAccess(origAccess)
super.visit(version, access, name, signature, superName, interfaces)
currentClassName = name
currentPackageName = getPackageNameFromFullClassName(name)
@@ -130,13 +155,14 @@ abstract class BaseAdapter(
}
}
- override fun visitMethod(
- access: Int,
+ final override fun visitMethod(
+ origAccess: Int,
name: String,
descriptor: String,
signature: String?,
exceptions: Array<String>?,
): MethodVisitor? {
+ val access = modifyMethodAccess(origAccess)
if (skipMemberModificationNestCount > 0) {
return super.visitMethod(access, name, descriptor, signature, exceptions)
}
@@ -176,6 +202,7 @@ abstract class BaseAdapter(
if (newAccess == NOT_COMPATIBLE) {
return null
}
+ newAccess = modifyMethodAccess(newAccess)
log.v(
"Emitting %s.%s%s as %s %s", currentClassName, name, descriptor,
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 567a69e43b58..70e7d46bb6cd 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -51,12 +51,13 @@ class ImplGeneratingAdapter(
override fun visit(
version: Int,
- access: Int,
+ origAccess: Int,
name: String,
signature: String?,
superName: String?,
interfaces: Array<String>
) {
+ val access = modifyClassAccess(origAccess)
super.visit(version, access, name, signature, superName, interfaces)
classLoadHooks = filter.getClassLoadHooks(currentClassName)
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 5e5ca62b49f0..b009b0957919 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -7,6 +7,8 @@ public interface android.hosttest.annotation.HostSideTestClassLoadHook extends j
this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
public abstract java.lang.String value();
descriptor: ()Ljava/lang/String;
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -30,6 +32,8 @@ public interface android.hosttest.annotation.HostSideTestIgnore extends java.lan
this_class: #x // android/hosttest/annotation/HostSideTestIgnore
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestIgnore.java"
RuntimeVisibleAnnotations:
@@ -50,6 +54,8 @@ public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.
this_class: #x // android/hosttest/annotation/HostSideTestKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
@@ -70,6 +76,8 @@ public interface android.hosttest.annotation.HostSideTestRedirect extends java.l
this_class: #x // android/hosttest/annotation/HostSideTestRedirect
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestRedirect.java"
RuntimeVisibleAnnotations:
@@ -90,6 +98,8 @@ public interface android.hosttest.annotation.HostSideTestRedirectionClass extend
this_class: #x // android/hosttest/annotation/HostSideTestRedirectionClass
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
public abstract java.lang.String value();
descriptor: ()Ljava/lang/String;
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -113,6 +123,8 @@ public interface android.hosttest.annotation.HostSideTestRemove extends java.lan
this_class: #x // android/hosttest/annotation/HostSideTestRemove
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
@@ -133,6 +145,8 @@ public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep e
this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestStaticInitializerKeep.java"
RuntimeVisibleAnnotations:
@@ -153,6 +167,8 @@ public interface android.hosttest.annotation.HostSideTestSubstitute extends java
this_class: #x // android/hosttest/annotation/HostSideTestSubstitute
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
public abstract java.lang.String suffix();
descriptor: ()Ljava/lang/String;
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -176,6 +192,8 @@ public interface android.hosttest.annotation.HostSideTestThrow extends java.lang
this_class: #x // android/hosttest/annotation/HostSideTestThrow
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
@@ -196,6 +214,8 @@ public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends
this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
@@ -216,6 +236,8 @@ public interface android.hosttest.annotation.tests.HostSideTestSuppress extends
this_class: #x // android/hosttest/annotation/tests/HostSideTestSuppress
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestSuppress.java"
RuntimeVisibleAnnotations:
@@ -232,6 +254,8 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Pro
this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -273,6 +297,8 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -314,6 +340,8 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 0, attributes: 3
+Constant pool:
+{
}
SourceFile: "IPretendingAidl.java"
NestMembers:
@@ -331,6 +359,8 @@ public class com.android.hoststubgen.test.tinyframework.R$Nested
this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 3
+Constant pool:
+{
public static int[] ARRAY;
descriptor: [I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -376,6 +406,8 @@ public class com.android.hoststubgen.test.tinyframework.R
this_class: #x // com/android/hoststubgen/test/tinyframework/R
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.R();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -396,13 +428,15 @@ InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
Compiled from "TinyFrameworkAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
minor version: 0
major version: 65
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 9, attributes: 2
+Constant pool:
+{
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -433,9 +467,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
x: #x()
android.hosttest.annotation.HostSideTestKeep
- public int addOne(int);
+ public final int addOne(int);
descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
x: iload_1
@@ -505,18 +539,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
0 4 1 value I
- public static native int nativeAddThree(int);
+ public static final native int nativeAddThree(int);
descriptor: (I)I
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ flags: (0x0119) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_NATIVE
RuntimeInvisibleAnnotations:
x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
- private static int nativeAddThree_host(int);
+ private static final int nativeAddThree_host(int);
descriptor: (I)I
- flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
Code:
stack=2, locals=1, args_size=1
x: iload_0
@@ -578,6 +612,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 3, attributes: 2
+Constant pool:
+{
public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
descriptor: Ljava/util/Set;
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
@@ -640,6 +676,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAn
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 6, attributes: 2
+Constant pool:
+{
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -764,6 +802,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 2, attributes: 2
+Constant pool:
+{
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -818,6 +858,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 2, attributes: 2
+Constant pool:
+{
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -878,6 +920,8 @@ public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumC
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
super_class: #x // java/lang/Enum
interfaces: 0, fields: 6, methods: 7, attributes: 3
+Constant pool:
+{
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
@@ -1081,6 +1125,8 @@ public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumS
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
super_class: #x // java/lang/Enum
interfaces: 0, fields: 3, methods: 5, attributes: 3
+Constant pool:
+{
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
@@ -1202,6 +1248,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1256,6 +1304,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 17, attributes: 1
+Constant pool:
+{
public int stub;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -1507,6 +1557,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nes
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 8, attributes: 5
+Constant pool:
+{
public final java.util.function.Supplier<java.lang.Integer> mSupplier;
descriptor: Ljava/util/function/Supplier;
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -1661,6 +1713,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 8, attributes: 5
+Constant pool:
+{
public final java.util.function.Supplier<java.lang.Integer> mSupplier;
descriptor: Ljava/util/function/Supplier;
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -1816,6 +1870,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallR
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 3, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1873,6 +1929,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallR
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 5, attributes: 5
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1984,6 +2042,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 14, attributes: 2
+Constant pool:
+{
int value;
descriptor: I
flags: (0x0000)
@@ -2157,6 +2217,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 7, attributes: 2
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2263,6 +2325,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
flags: (0x0000)
@@ -2321,6 +2385,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
descriptor: ()V
flags: (0x0000)
@@ -2375,6 +2441,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
flags: (0x0000)
@@ -2433,6 +2501,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
descriptor: ()V
flags: (0x0000)
@@ -2487,6 +2557,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2521,6 +2593,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2558,6 +2632,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
descriptor: ()V
flags: (0x0000)
@@ -2613,6 +2689,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2647,6 +2725,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 3
+Constant pool:
+{
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2694,6 +2774,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
interfaces: 0, fields: 0, methods: 1, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
@@ -2723,6 +2805,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 4, attributes: 4
+Constant pool:
+{
public final java.util.function.Supplier<java.lang.Integer> mSupplier;
descriptor: Ljava/util/function/Supplier;
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -2827,6 +2911,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedi
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2869,6 +2955,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClas
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2911,6 +2999,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 2
+Constant pool:
+{
private final int mValue;
descriptor: I
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
@@ -2958,6 +3048,8 @@ public class com.android.hoststubgen.test.tinyframework.packagetest.A
this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.packagetest.A();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2981,6 +3073,8 @@ public class com.android.hoststubgen.test.tinyframework.packagetest.B
this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/B
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.packagetest.B();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3004,6 +3098,8 @@ public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.packagetest.sub.A();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3027,6 +3123,8 @@ public class com.android.hoststubgen.test.tinyframework.packagetest.sub.B
this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/B
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.packagetest.sub.B();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3050,6 +3148,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.C1();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3073,6 +3173,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.C2();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3096,6 +3198,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.C3();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3119,6 +3223,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.CA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3142,6 +3248,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.CB();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3165,6 +3273,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3188,6 +3298,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3211,6 +3323,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3234,6 +3348,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3257,6 +3373,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3280,6 +3398,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3303,6 +3423,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3326,6 +3448,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3349,6 +3473,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3372,6 +3498,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3395,6 +3523,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3418,6 +3548,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3441,6 +3573,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3464,6 +3598,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3487,6 +3623,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3510,6 +3648,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3533,6 +3673,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_None
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_None
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_None();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3556,6 +3698,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "I1.java"
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
@@ -3567,6 +3711,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 exte
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "I2.java"
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
@@ -3578,6 +3724,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 exte
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "I3.java"
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
@@ -3589,6 +3737,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "IA.java"
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
@@ -3600,6 +3750,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "IB.java"
## Class: com/supported/UnsupportedClass.class
@@ -3611,6 +3763,8 @@ public class com.supported.UnsupportedClass
this_class: #x // com/supported/UnsupportedClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 2
+Constant pool:
+{
private final int mValue;
descriptor: I
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
@@ -3658,6 +3812,8 @@ public class com.unsupported.UnsupportedClass
this_class: #x // com/unsupported/UnsupportedClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
public com.unsupported.UnsupportedClass(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 103e152c7e39..ad413425801b 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -7,6 +7,8 @@ public interface android.hosttest.annotation.HostSideTestClassLoadHook extends j
this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
public abstract java.lang.String value();
descriptor: ()Ljava/lang/String;
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -30,6 +32,8 @@ public interface android.hosttest.annotation.HostSideTestIgnore extends java.lan
this_class: #x // android/hosttest/annotation/HostSideTestIgnore
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestIgnore.java"
RuntimeVisibleAnnotations:
@@ -50,6 +54,8 @@ public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.
this_class: #x // android/hosttest/annotation/HostSideTestKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
@@ -70,6 +76,8 @@ public interface android.hosttest.annotation.HostSideTestRedirect extends java.l
this_class: #x // android/hosttest/annotation/HostSideTestRedirect
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestRedirect.java"
RuntimeVisibleAnnotations:
@@ -90,6 +98,8 @@ public interface android.hosttest.annotation.HostSideTestRedirectionClass extend
this_class: #x // android/hosttest/annotation/HostSideTestRedirectionClass
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
public abstract java.lang.String value();
descriptor: ()Ljava/lang/String;
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -113,6 +123,8 @@ public interface android.hosttest.annotation.HostSideTestRemove extends java.lan
this_class: #x // android/hosttest/annotation/HostSideTestRemove
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
@@ -133,6 +145,8 @@ public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep e
this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestStaticInitializerKeep.java"
RuntimeVisibleAnnotations:
@@ -153,6 +167,8 @@ public interface android.hosttest.annotation.HostSideTestSubstitute extends java
this_class: #x // android/hosttest/annotation/HostSideTestSubstitute
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
public abstract java.lang.String suffix();
descriptor: ()Ljava/lang/String;
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -176,6 +192,8 @@ public interface android.hosttest.annotation.HostSideTestThrow extends java.lang
this_class: #x // android/hosttest/annotation/HostSideTestThrow
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
@@ -196,6 +214,8 @@ public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends
this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
@@ -216,6 +236,8 @@ public interface android.hosttest.annotation.tests.HostSideTestSuppress extends
this_class: #x // android/hosttest/annotation/tests/HostSideTestSuppress
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
}
SourceFile: "HostSideTestSuppress.java"
RuntimeVisibleAnnotations:
@@ -232,6 +254,8 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Pro
this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -273,6 +297,8 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -314,6 +340,8 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 0, attributes: 3
+Constant pool:
+{
}
SourceFile: "IPretendingAidl.java"
NestMembers:
@@ -331,6 +359,8 @@ public class com.android.hoststubgen.test.tinyframework.R$Nested
this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 3
+Constant pool:
+{
public static int[] ARRAY;
descriptor: [I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -376,6 +406,8 @@ public class com.android.hoststubgen.test.tinyframework.R
this_class: #x // com/android/hoststubgen/test/tinyframework/R
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.R();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -396,13 +428,15 @@ InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
Compiled from "TinyFrameworkAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
minor version: 0
major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 9, attributes: 2
+Constant pool:
+{
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -433,9 +467,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
x: #x()
android.hosttest.annotation.HostSideTestKeep
- public int addOne(int);
+ public final int addOne(int);
descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
x: iload_1
@@ -505,18 +539,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
0 4 1 value I
- public static native int nativeAddThree(int);
+ public static final native int nativeAddThree(int);
descriptor: (I)I
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ flags: (0x0119) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_NATIVE
RuntimeInvisibleAnnotations:
x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
- private static int nativeAddThree_host(int);
+ private static final int nativeAddThree_host(int);
descriptor: (I)I
- flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
Code:
stack=2, locals=1, args_size=1
x: iload_0
@@ -578,6 +612,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 3, attributes: 2
+Constant pool:
+{
public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
descriptor: Ljava/util/Set;
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
@@ -640,6 +676,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAn
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 6, attributes: 2
+Constant pool:
+{
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -764,6 +802,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 2, attributes: 2
+Constant pool:
+{
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -818,6 +858,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 2, attributes: 2
+Constant pool:
+{
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -878,6 +920,8 @@ public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumC
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
super_class: #x // java/lang/Enum
interfaces: 0, fields: 6, methods: 7, attributes: 3
+Constant pool:
+{
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
@@ -1081,6 +1125,8 @@ public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumS
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
super_class: #x // java/lang/Enum
interfaces: 0, fields: 3, methods: 5, attributes: 3
+Constant pool:
+{
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
@@ -1202,6 +1248,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1256,6 +1304,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 17, attributes: 1
+Constant pool:
+{
public int stub;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -1507,6 +1557,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nes
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 8, attributes: 5
+Constant pool:
+{
public final java.util.function.Supplier<java.lang.Integer> mSupplier;
descriptor: Ljava/util/function/Supplier;
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -1661,6 +1713,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 8, attributes: 5
+Constant pool:
+{
public final java.util.function.Supplier<java.lang.Integer> mSupplier;
descriptor: Ljava/util/function/Supplier;
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -1816,6 +1870,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallR
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 3, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1873,6 +1929,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallR
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 5, attributes: 5
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1984,6 +2042,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 14, attributes: 2
+Constant pool:
+{
int value;
descriptor: I
flags: (0x0000)
@@ -2157,6 +2217,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 7, attributes: 2
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2263,6 +2325,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
super_class: #x // java/lang/Object
interfaces: 1, fields: 1, methods: 3, attributes: 5
+Constant pool:
+{
final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
@@ -2328,6 +2392,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
descriptor: ()V
flags: (0x0000)
@@ -2382,6 +2448,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
super_class: #x // java/lang/Object
interfaces: 1, fields: 1, methods: 3, attributes: 5
+Constant pool:
+{
final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
@@ -2447,6 +2515,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
descriptor: ()V
flags: (0x0000)
@@ -2501,6 +2571,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2535,6 +2607,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 1, attributes: 3
+Constant pool:
+{
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2579,6 +2653,8 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
descriptor: ()V
flags: (0x0000)
@@ -2634,6 +2710,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2668,6 +2746,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 3
+Constant pool:
+{
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2715,6 +2795,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
interfaces: 0, fields: 0, methods: 1, attributes: 3
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
@@ -2744,6 +2826,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
super_class: #x // java/lang/Object
interfaces: 0, fields: 2, methods: 4, attributes: 4
+Constant pool:
+{
public final java.util.function.Supplier<java.lang.Integer> mSupplier;
descriptor: Ljava/util/function/Supplier;
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -2848,6 +2932,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedi
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2890,6 +2976,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClas
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2932,6 +3020,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 2
+Constant pool:
+{
private final int mValue;
descriptor: I
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
@@ -2979,6 +3069,8 @@ public class com.android.hoststubgen.test.tinyframework.packagetest.A
this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.packagetest.A();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3002,6 +3094,8 @@ public class com.android.hoststubgen.test.tinyframework.packagetest.B
this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/B
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.packagetest.B();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3025,6 +3119,8 @@ public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.packagetest.sub.A();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3048,6 +3144,8 @@ public class com.android.hoststubgen.test.tinyframework.packagetest.sub.B
this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/B
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.packagetest.sub.B();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3071,6 +3169,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.C1();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3094,6 +3194,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.C2();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3117,6 +3219,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.C3();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3140,6 +3244,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.CA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3163,6 +3269,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.CB();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3186,6 +3294,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3209,6 +3319,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3232,6 +3344,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3255,6 +3369,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3278,6 +3394,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB ex
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3301,6 +3419,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA
super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3324,6 +3444,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3347,6 +3469,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3370,6 +3494,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3393,6 +3519,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3416,6 +3544,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3439,6 +3569,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3462,6 +3594,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3485,6 +3619,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3508,6 +3644,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB im
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3531,6 +3669,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA
super_class: #x // java/lang/Object
interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3554,6 +3694,8 @@ public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_None
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_None
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
public com.android.hoststubgen.test.tinyframework.subclasstest.Class_None();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -3577,6 +3719,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "I1.java"
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
@@ -3588,6 +3732,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 exte
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "I2.java"
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
@@ -3599,6 +3745,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 exte
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "I3.java"
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
@@ -3610,6 +3758,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "IA.java"
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
@@ -3621,6 +3771,8 @@ public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
}
SourceFile: "IB.java"
## Class: com/supported/UnsupportedClass.class
@@ -3632,6 +3784,8 @@ public class com.supported.UnsupportedClass
this_class: #x // com/supported/UnsupportedClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 2
+Constant pool:
+{
private final int mValue;
descriptor: I
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
@@ -3679,6 +3833,8 @@ public class com.unsupported.UnsupportedClass
this_class: #x // com/unsupported/UnsupportedClass
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
public com.unsupported.UnsupportedClass(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
index 3415deb957ed..674937d15424 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
@@ -28,7 +28,7 @@ import android.hosttest.annotation.HostSideTestThrow;
@HostSideTestKeep
@HostSideTestClassLoadHook(
"com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
-public class TinyFrameworkAnnotations {
+public final class TinyFrameworkAnnotations {
@HostSideTestKeep
public TinyFrameworkAnnotations() {
}
@@ -42,7 +42,7 @@ public class TinyFrameworkAnnotations {
public int remove;
@HostSideTestKeep
- public int addOne(int value) {
+ public final int addOne(int value) {
return value + 1;
}
@@ -61,10 +61,10 @@ public class TinyFrameworkAnnotations {
}
@HostSideTestSubstitute(suffix = "_host")
- public static native int nativeAddThree(int value);
+ public final static native int nativeAddThree(int value);
// This method is private, but at runtime, it'll inherit the visibility of the original method
- private static int nativeAddThree_host(int value) {
+ private final static int nativeAddThree_host(int value) {
return value + 3;
}
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/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index b94fa2f59162..8b758d29a2ac 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -39,6 +39,8 @@ import android.view.MotionEvent.PointerProperties;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.VisibleForTesting;
+
/**
* Implements "Automatically click on mouse stop" feature.
*
@@ -69,10 +71,10 @@ public class AutoclickController extends BaseEventStreamTransformation {
private final int mUserId;
// Lazily created on the first mouse motion event.
- private ClickScheduler mClickScheduler;
- private AutoclickSettingsObserver mAutoclickSettingsObserver;
- private AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
- private AutoclickIndicatorView mAutoclickIndicatorView;
+ @VisibleForTesting ClickScheduler mClickScheduler;
+ @VisibleForTesting AutoclickSettingsObserver mAutoclickSettingsObserver;
+ @VisibleForTesting AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
+ @VisibleForTesting AutoclickIndicatorView mAutoclickIndicatorView;
private WindowManager mWindowManager;
public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
@@ -360,7 +362,8 @@ public class AutoclickController extends BaseEventStreamTransformation {
* moving. The click is first scheduled when a mouse movement is detected, and then further
* delayed on every sufficient mouse movement.
*/
- final private class ClickScheduler implements Runnable {
+ @VisibleForTesting
+ final class ClickScheduler implements Runnable {
/**
* Minimal distance pointer has to move relative to anchor in order for movement not to be
* discarded as noise. Anchor is the position of the last MOVE event that was not considered
@@ -474,6 +477,11 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
}
+ @VisibleForTesting
+ int getDelayForTesting() {
+ return mDelay;
+ }
+
/**
* Updates the time at which click sequence should occur.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
index bf5015176f8c..f87dcdb200bb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
@@ -28,6 +28,8 @@ import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.LinearInterpolator;
+import androidx.annotation.VisibleForTesting;
+
// A visual indicator for the autoclick feature.
public class AutoclickIndicatorView extends View {
private static final String TAG = AutoclickIndicatorView.class.getSimpleName();
@@ -37,7 +39,7 @@ public class AutoclickIndicatorView extends View {
static final int MINIMAL_ANIMATION_DURATION = 50;
- private float mRadius = AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
+ private int mRadius = AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
private final Paint mPaint;
@@ -112,6 +114,11 @@ public class AutoclickIndicatorView extends View {
mRadius = radius;
}
+ @VisibleForTesting
+ int getRadiusForTesting() {
+ return mRadius;
+ }
+
public void redrawIndicator() {
showIndicator = true;
invalidate();
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 0cbbf6da022b..2c106d31ae59 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -16,8 +16,6 @@
package com.android.server.accessibility.gestures;
-import static android.accessibilityservice.AccessibilityTrace.FLAGS_GESTURE;
-import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_HOVER_ENTER;
@@ -86,8 +84,6 @@ import java.util.List;
public class TouchExplorer extends BaseEventStreamTransformation
implements GestureManifold.Listener {
- private static final long LOGGING_FLAGS = FLAGS_GESTURE | FLAGS_INPUT_FILTER;
-
// Tag for logging received events.
private static final String LOG_TAG = "TouchExplorer";
@@ -261,10 +257,6 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
- mAms.getTraceManager().logTrace(LOG_TAG + ".onMotionEvent", LOGGING_FLAGS,
- "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
- }
if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
super.onMotionEvent(event, rawEvent, policyFlags);
return;
@@ -323,9 +315,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
- if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
- mAms.getTraceManager().logTrace(LOG_TAG + ".onAccessibilityEvent",
- LOGGING_FLAGS, "event=" + event);
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Received A11y Event. event=" + event);
}
final int eventType = event.getEventType();
@@ -383,9 +374,9 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
- mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTapAndHold", LOGGING_FLAGS,
- "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Double tap and hold. event="
+ + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
}
if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
@@ -403,9 +394,9 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
- mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTap", LOGGING_FLAGS,
- "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Double tap. event="
+ + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
}
mAms.onTouchInteractionEnd();
// Remove pending event deliveries.
@@ -463,8 +454,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onGestureStarted() {
- if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
- mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureStarted", LOGGING_FLAGS);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Gesture started.");
}
// We have to perform gesture detection, so
// clear the current state and try to detect.
@@ -479,9 +470,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) {
- if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
- mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCompleted",
- LOGGING_FLAGS, "event=" + gestureEvent);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Gesture completed. gestureEvent=" + gestureEvent);
}
endGestureDetection(true);
mSendTouchInteractionEndDelayed.cancel();
@@ -491,10 +481,11 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
- mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCancelled", LOGGING_FLAGS,
- "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Gesture cancelled. event="
+ + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
}
+
if (mState.isGestureDetecting()) {
endGestureDetection(event.getActionMasked() == ACTION_UP);
return true;
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/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 0e2e50589217..a37b2b926c9c 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -612,7 +612,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public void enablePermissionsSync(int associationId) {
- if (getCallingUid() != SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
throw new SecurityException("Caller must be system UID");
}
mSystemDataTransferProcessor.enablePermissionsSync(associationId);
@@ -620,7 +620,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public void disablePermissionsSync(int associationId) {
- if (getCallingUid() != SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
throw new SecurityException("Caller must be system UID");
}
mSystemDataTransferProcessor.disablePermissionsSync(associationId);
@@ -628,7 +628,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
- if (getCallingUid() != SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
throw new SecurityException("Caller must be system UID");
}
return mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
@@ -704,7 +704,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public byte[] getBackupPayload(int userId) {
- if (getCallingUid() != SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
throw new SecurityException("Caller must be system");
}
return mBackupRestoreProcessor.getBackupPayload(userId);
@@ -712,7 +712,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public void applyRestoredPayload(byte[] payload, int userId) {
- if (getCallingUid() != SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
throw new SecurityException("Caller must be system");
}
mBackupRestoreProcessor.applyRestoredPayload(payload, userId);
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/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index abfb8268bd9a..df47c98d6433 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -98,6 +98,8 @@ public class ContextualSearchManagerService extends SystemService {
private static final int MSG_INVALIDATE_TOKEN = 1;
private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes
+ private static final boolean DEBUG = false;
+
private final Context mContext;
private final ActivityTaskManagerInternal mAtmInternal;
private final PackageManagerInternal mPackageManager;
@@ -121,6 +123,7 @@ public class ContextualSearchManagerService extends SystemService {
final Bundle data,
final int activityIndex,
final int activityCount) {
+
final IContextualSearchCallback callback;
synchronized (mLock) {
callback = mStateCallback;
@@ -160,7 +163,7 @@ public class ContextualSearchManagerService extends SystemService {
public ContextualSearchManagerService(@NonNull Context context) {
super(context);
- if (DEBUG_USER) Log.d(TAG, "ContextualSearchManagerService created");
+ if (DEBUG) Log.d(TAG, "ContextualSearchManagerService created");
mContext = context;
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
@@ -206,7 +209,7 @@ public class ContextualSearchManagerService extends SystemService {
mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_PACKAGE);
mTemporaryHandler = null;
}
- if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage reset.");
+ if (DEBUG) Log.d(TAG, "mTemporaryPackage reset.");
mTemporaryPackage = null;
updateSecureSetting();
}
@@ -239,7 +242,7 @@ public class ContextualSearchManagerService extends SystemService {
mTemporaryPackage = temporaryPackage;
updateSecureSetting();
mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_PACKAGE, durationMs);
- if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage set to " + mTemporaryPackage);
+ if (DEBUG) Log.d(TAG, "mTemporaryPackage set to " + mTemporaryPackage);
}
}
@@ -256,7 +259,7 @@ public class ContextualSearchManagerService extends SystemService {
+ durationMs + ")");
}
mTokenValidDurationMs = durationMs;
- if (DEBUG_USER) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs);
+ if (DEBUG) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs);
}
}
@@ -268,12 +271,12 @@ public class ContextualSearchManagerService extends SystemService {
private Intent getResolvedLaunchIntent(int userId) {
synchronized (this) {
- if(DEBUG_USER) Log.d(TAG, "Attempting to getResolvedLaunchIntent");
+ if(DEBUG) Log.d(TAG, "Attempting to getResolvedLaunchIntent");
// If mTemporaryPackage is not null, use it to get the ContextualSearch intent.
String csPkgName = getContextualSearchPackageName();
if (csPkgName.isEmpty()) {
// Return null if csPackageName is not specified.
- if (DEBUG_USER) Log.w(TAG, "getContextualSearchPackageName is empty");
+ if (DEBUG) Log.w(TAG, "getContextualSearchPackageName is empty");
return null;
}
Intent launchIntent = new Intent(
@@ -282,12 +285,12 @@ public class ContextualSearchManagerService extends SystemService {
ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(
launchIntent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
if (resolveInfo == null) {
- if (DEBUG_USER) Log.w(TAG, "resolveInfo is null");
+ if (DEBUG) Log.w(TAG, "resolveInfo is null");
return null;
}
ComponentName componentName = resolveInfo.getComponentInfo().getComponentName();
if (componentName == null) {
- if (DEBUG_USER) Log.w(TAG, "componentName is null");
+ if (DEBUG) Log.w(TAG, "componentName is null");
return null;
}
launchIntent.setComponent(componentName);
@@ -298,11 +301,11 @@ public class ContextualSearchManagerService extends SystemService {
private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
final Intent launchIntent = getResolvedLaunchIntent(userId);
if (launchIntent == null) {
- if (DEBUG_USER) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
+ if (DEBUG) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
return null;
}
- if (DEBUG_USER) Log.d(TAG, "Launch component: " + launchIntent.getComponent());
+ if (DEBUG) Log.d(TAG, "Launch component: " + launchIntent.getComponent());
launchIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION
| FLAG_ACTIVITY_NO_USER_ACTION | FLAG_ACTIVITY_CLEAR_TASK);
launchIntent.putExtra(
@@ -355,7 +358,7 @@ public class ContextualSearchManagerService extends SystemService {
TYPE_NAVIGATION_BAR_PANEL,
TYPE_POINTER));
} else {
- if (DEBUG_USER) Log.w(TAG, "Can't capture contextual screenshot: mWmInternal is null");
+ if (DEBUG) Log.w(TAG, "Can't capture contextual screenshot: mWmInternal is null");
shb = null;
}
final Bitmap bm = shb != null ? shb.asBitmap() : null;
@@ -429,7 +432,7 @@ public class ContextualSearchManagerService extends SystemService {
mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN);
mTokenHandler = null;
}
- if (DEBUG_USER) Log.d(TAG, "mToken invalidated.");
+ if (DEBUG) Log.d(TAG, "mToken invalidated.");
mToken = null;
}
}
@@ -459,7 +462,7 @@ public class ContextualSearchManagerService extends SystemService {
@Override
public void startContextualSearch(int entrypoint) {
synchronized (this) {
- if (DEBUG_USER) Log.d(TAG, "startContextualSearch entrypoint: " + entrypoint);
+ if (DEBUG) Log.d(TAG, "startContextualSearch entrypoint: " + entrypoint);
enforcePermission("startContextualSearch");
final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
@@ -474,7 +477,7 @@ public class ContextualSearchManagerService extends SystemService {
getContextualSearchIntent(entrypoint, callingUserId, mToken);
if (launchIntent != null) {
int result = invokeContextualSearchIntent(launchIntent, callingUserId);
- if (DEBUG_USER) Log.d(TAG, "Launch result: " + result);
+ if (DEBUG) Log.d(TAG, "Launch result: " + result);
}
});
}
@@ -484,11 +487,11 @@ public class ContextualSearchManagerService extends SystemService {
public void getContextualSearchState(
@NonNull IBinder token,
@NonNull IContextualSearchCallback callback) {
- if (DEBUG_USER) {
+ if (DEBUG) {
Log.i(TAG, "getContextualSearchState token: " + token + ", callback: " + callback);
}
if (mToken == null || !mToken.getToken().equals(token)) {
- if (DEBUG_USER) {
+ if (DEBUG) {
Log.e(TAG, "getContextualSearchState: invalid token, returning error");
}
try {
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 1588e0421675..7a5b8660ef7c 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -40,7 +40,9 @@ import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Slog;
import android.util.Xml;
+import android.util.proto.ProtoFieldFilter;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoParseException;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -49,10 +51,13 @@ import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.am.DropboxRateLimiter;
+import com.android.server.os.TombstoneProtos.Tombstone;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
@@ -64,6 +69,7 @@ import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -392,6 +398,129 @@ public class BootReceiver extends BroadcastReceiver {
writeTimestamps(timestamps);
}
+ /**
+ * Processes a tombstone file and adds it to the DropBox after filtering and applying
+ * rate limiting.
+ * Filtering removes memory sections from the tombstone proto to reduce size while preserving
+ * critical information. The filtered tombstone is then added to DropBox in both proto
+ * and text formats, with the text format derived from the filtered proto.
+ * Rate limiting is applied as it is the case with other crash types.
+ *
+ * @param ctx Context
+ * @param tombstone path to the tombstone
+ * @param processName the name of the process corresponding to the tombstone
+ * @param tmpFileLock the lock for reading/writing tmp files
+ */
+ public static void filterAndAddTombstoneToDropBox(
+ Context ctx, File tombstone, String processName, ReentrantLock tmpFileLock) {
+ final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+ if (db == null) {
+ Slog.e(TAG, "Can't log tombstone: DropBoxManager not available");
+ return;
+ }
+ File filteredProto = null;
+ // Check if we should rate limit and abort early if needed.
+ DropboxRateLimiter.RateLimitResult rateLimitResult =
+ sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE_PROTO_WITH_HEADERS, processName);
+ if (rateLimitResult.shouldRateLimit()) return;
+
+ HashMap<String, Long> timestamps = readTimestamps();
+ try {
+ tmpFileLock.lock();
+ Slog.i(TAG, "Filtering tombstone file: " + tombstone.getName());
+ // Create a temporary tombstone without memory sections.
+ filteredProto = createTempTombstoneWithoutMemory(tombstone);
+ Slog.i(TAG, "Generated tombstone file: " + filteredProto.getName());
+
+ if (recordFileTimestamp(tombstone, timestamps)) {
+ // We need to attach the count indicating the number of dropped dropbox entries
+ // due to rate limiting. Do this by enclosing the proto tombsstone in a
+ // container proto that has the dropped entry count and the proto tombstone as
+ // bytes (to avoid the complexity of reading and writing nested protos).
+ Slog.i(TAG, "Adding tombstone " + filteredProto.getName() + " to dropbox");
+ addAugmentedProtoToDropbox(filteredProto, db, rateLimitResult);
+ }
+ // Always add the text version of the tombstone to the DropBox, in order to
+ // match the previous behaviour.
+ Slog.i(TAG, "Adding text tombstone version of " + filteredProto.getName()
+ + " to dropbox");
+ addTextTombstoneFromProtoToDropbox(filteredProto, db, timestamps, rateLimitResult);
+
+ } catch (IOException | ProtoParseException e) {
+ Slog.e(TAG, "Failed to log tombstone '" + tombstone.getName()
+ + "' to DropBox. Error during processing or writing: " + e.getMessage(), e);
+ } finally {
+ if (filteredProto != null) {
+ filteredProto.delete();
+ }
+ tmpFileLock.unlock();
+ }
+ writeTimestamps(timestamps);
+ }
+
+ /**
+ * Creates a temporary tombstone file by filtering out memory mapping fields.
+ * This ensures that the unneeded memory mapping data is removed from the tombstone
+ * before adding it to Dropbox
+ *
+ * @param tombstone the original tombstone file to process
+ * @return a temporary file containing the filtered tombstone data
+ * @throws IOException if an I/O error occurs during processing
+ */
+ private static File createTempTombstoneWithoutMemory(File tombstone) throws IOException {
+ // Process the proto tombstone file and write it to a temporary file
+ File tombstoneProto =
+ File.createTempFile(tombstone.getName(), ".pb.tmp", TOMBSTONE_TMP_DIR);
+ ProtoFieldFilter protoFilter =
+ new ProtoFieldFilter(fieldNumber -> fieldNumber != (int) Tombstone.MEMORY_MAPPINGS);
+
+ try (FileInputStream fis = new FileInputStream(tombstone);
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ FileOutputStream fos = new FileOutputStream(tombstoneProto);
+ BufferedOutputStream bos = new BufferedOutputStream(fos)) {
+ protoFilter.filter(bis, bos);
+ return tombstoneProto;
+ }
+ }
+
+ private static void addTextTombstoneFromProtoToDropbox(File tombstone, DropBoxManager db,
+ HashMap<String, Long> timestamps, DropboxRateLimiter.RateLimitResult rateLimitResult) {
+ File tombstoneTextFile = null;
+
+ try {
+ tombstoneTextFile = File.createTempFile(tombstone.getName(),
+ ".pb.txt.tmp", TOMBSTONE_TMP_DIR);
+
+ // Create a ProcessBuilder to execute pbtombstone
+ ProcessBuilder pb = new ProcessBuilder("/system/bin/pbtombstone", tombstone.getPath());
+ pb.redirectOutput(tombstoneTextFile);
+ Process process = pb.start();
+
+ // Wait 10 seconds for the process to complete
+ if (!process.waitFor(10, TimeUnit.SECONDS)) {
+ Slog.e(TAG, "pbtombstone timed out");
+ process.destroyForcibly();
+ return;
+ }
+
+ int exitCode = process.exitValue();
+ if (exitCode != 0) {
+ Slog.e(TAG, "pbtombstone failed with exit code " + exitCode);
+ } else {
+ final String headers = getBootHeadersToLogAndUpdate()
+ + rateLimitResult.createHeader();
+ addFileToDropBox(db, timestamps, headers, tombstoneTextFile.getPath(), LOG_SIZE,
+ TAG_TOMBSTONE);
+ }
+ } catch (IOException | InterruptedException e) {
+ Slog.e(TAG, "Failed to process tombstone with pbtombstone", e);
+ } finally {
+ if (tombstoneTextFile != null) {
+ tombstoneTextFile.delete();
+ }
+ }
+ }
+
private static void addAugmentedProtoToDropbox(
File tombstone, DropBoxManager db,
DropboxRateLimiter.RateLimitResult rateLimitResult) throws IOException {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 2216f2769826..f7eaa159be30 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2553,13 +2553,12 @@ public final class ProcessList {
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
// We can't isolate app data and storage data as parent zygote already did that.
- startResult = appZygote.getProcess().start(entryPoint,
- app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+ startResult = appZygote.startProcess(entryPoint,
+ app.processName, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, null, app.info.packageName,
- /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
- app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
- false, false, false,
+ app.info.dataDir, app.info.packageName, isTopApp,
+ app.getDisabledCompatChanges(), pkgDataInfoMap,
+ allowlistedAppDataInfoMap,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else {
regularZygote = true;
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/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index ba391d0a9995..5aa2a6b60106 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -205,18 +205,8 @@ final class HistoricalRegistry {
mContext = context;
if (Flags.enableSqliteAppopsAccesses()) {
mDiscreteRegistry = new DiscreteOpsSqlRegistry(context);
- if (DiscreteOpsXmlRegistry.getDiscreteOpsDir().exists()) {
- DiscreteOpsSqlRegistry sqlRegistry = (DiscreteOpsSqlRegistry) mDiscreteRegistry;
- DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(context);
- DiscreteOpsMigrationHelper.migrateDiscreteOpsToSqlite(xmlRegistry, sqlRegistry);
- }
} else {
mDiscreteRegistry = new DiscreteOpsXmlRegistry(context);
- if (DiscreteOpsDbHelper.getDatabaseFile().exists()) { // roll-back sqlite
- DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(context);
- DiscreteOpsXmlRegistry xmlRegistry = (DiscreteOpsXmlRegistry) mDiscreteRegistry;
- DiscreteOpsMigrationHelper.migrateDiscreteOpsToXml(sqlRegistry, xmlRegistry);
- }
}
}
@@ -267,6 +257,19 @@ final class HistoricalRegistry {
}
}
}
+ if (Flags.enableSqliteAppopsAccesses()) {
+ if (DiscreteOpsXmlRegistry.getDiscreteOpsDir().exists()) {
+ DiscreteOpsSqlRegistry sqlRegistry = (DiscreteOpsSqlRegistry) mDiscreteRegistry;
+ DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(mContext);
+ DiscreteOpsMigrationHelper.migrateDiscreteOpsToSqlite(xmlRegistry, sqlRegistry);
+ }
+ } else {
+ if (DiscreteOpsDbHelper.getDatabaseFile().exists()) { // roll-back sqlite
+ DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(mContext);
+ DiscreteOpsXmlRegistry xmlRegistry = (DiscreteOpsXmlRegistry) mDiscreteRegistry;
+ DiscreteOpsMigrationHelper.migrateDiscreteOpsToXml(sqlRegistry, xmlRegistry);
+ }
+ }
}
private boolean isPersistenceInitializedMLocked() {
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/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index c19d2c9091c3..21c9b876ab46 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -16,6 +16,9 @@
package com.android.server.display;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -1181,6 +1184,14 @@ public class BrightnessTracker {
}
public RootTaskInfo getFocusedStack() throws RemoteException {
+ if (UserManager.isVisibleBackgroundUsersEnabled()) {
+ // In MUMD (Multiple Users on Multiple Displays) system, the top most focused stack
+ // could be on the secondary display with a user signed on its display so get the
+ // root task info only on the default display.
+ return ActivityTaskManager.getService().getRootTaskInfoOnDisplay(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_UNDEFINED,
+ Display.DEFAULT_DISPLAY);
+ }
return ActivityTaskManager.getService().getFocusedRootTaskInfo();
}
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/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ca001b9c7e6d..a37e9c3ac1b8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -4382,26 +4382,29 @@ public final class DisplayManagerService extends SystemService {
// would be unusual to do so. The method returns true on success.
// This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
public boolean dispatchPending() {
+ Event[] pending;
+ synchronized (mCallback) {
+ if (mPendingEvents == null || mPendingEvents.isEmpty() || !mAlive) {
+ return true;
+ }
+ if (!isReadyLocked()) {
+ return false;
+ }
+ pending = new Event[mPendingEvents.size()];
+ pending = mPendingEvents.toArray(pending);
+ mPendingEvents.clear();
+ }
try {
- synchronized (mCallback) {
- if (mPendingEvents == null || mPendingEvents.isEmpty() || !mAlive) {
- return true;
- }
- if (!isReadyLocked()) {
- return false;
- }
- for (int i = 0; i < mPendingEvents.size(); i++) {
- Event displayEvent = mPendingEvents.get(i);
- if (DEBUG) {
- Slog.d(TAG, "Send pending display event #" + i + " "
- + displayEvent.displayId + "/"
- + displayEvent.event + " to " + mUid + "/" + mPid);
- }
- transmitDisplayEvent(displayEvent.displayId, displayEvent.event);
+ for (int i = 0; i < pending.length; i++) {
+ Event displayEvent = pending[i];
+ if (DEBUG) {
+ Slog.d(TAG, "Send pending display event #" + i + " "
+ + displayEvent.displayId + "/"
+ + displayEvent.event + " to " + mUid + "/" + mPid);
}
- mPendingEvents.clear();
- return true;
+ transmitDisplayEvent(displayEvent.displayId, displayEvent.event);
}
+ return true;
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify process "
+ mPid + " that display topology changed, assuming it died.", ex);
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/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 2615a76ac279..2b0ca145372b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -334,7 +334,7 @@ public class ContextHubService extends IContextHubService.Stub {
if (Flags.offloadApi() && Flags.offloadImplementation()) {
HubInfoRegistry registry;
try {
- registry = new HubInfoRegistry(mContextHubWrapper);
+ registry = new HubInfoRegistry(mContext, mContextHubWrapper);
mEndpointManager =
new ContextHubEndpointManager(
mContext, mContextHubWrapper, registry, mTransactionManager);
@@ -821,6 +821,13 @@ public class ContextHubService extends IContextHubService.Stub {
mHubInfoRegistry.unregisterEndpointDiscoveryCallback(callback);
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @Override
+ public void onDiscoveryCallbackFinished() throws RemoteException {
+ super.onDiscoveryCallbackFinished_enforcePermission();
+ mHubInfoRegistry.onDiscoveryCallbackFinished();
+ }
+
private void checkEndpointDiscoveryPreconditions() {
if (mHubInfoRegistry == null) {
Log.e(TAG, "Hub endpoint registry failed to initialize");
diff --git a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
index bf54fd720d42..711383bbca37 100644
--- a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
+++ b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
@@ -16,12 +16,18 @@
package com.android.server.location.contexthub;
+import android.content.Context;
import android.hardware.contexthub.HubEndpointInfo;
import android.hardware.contexthub.HubServiceInfo;
import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback;
import android.hardware.location.HubInfo;
-import android.os.DeadObjectException;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.Process;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -34,10 +40,15 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycleCallback {
private static final String TAG = "HubInfoRegistry";
+
+ /** The duration of wakelocks acquired during discovery callbacks */
+ private static final long WAKELOCK_TIMEOUT_MILLIS = 5 * 1000;
+
private final Object mLock = new Object();
private final IContextHubWrapper mContextHubWrapper;
@@ -53,21 +64,37 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
* A wrapper class that is used to store arguments to
* ContextHubManager.registerEndpointCallback.
*/
- private static class DiscoveryCallback {
+ private static class DiscoveryCallback implements IBinder.DeathRecipient {
+ private final HubInfoRegistry mHubInfoRegistry;
private final IContextHubEndpointDiscoveryCallback mCallback;
private final Optional<Long> mEndpointId;
private final Optional<String> mServiceDescriptor;
- DiscoveryCallback(IContextHubEndpointDiscoveryCallback callback, long endpointId) {
+ // True if the binder death recipient fired
+ private final AtomicBoolean mBinderDied = new AtomicBoolean(false);
+
+ DiscoveryCallback(
+ HubInfoRegistry registry,
+ IContextHubEndpointDiscoveryCallback callback,
+ long endpointId)
+ throws RemoteException {
+ mHubInfoRegistry = registry;
mCallback = callback;
mEndpointId = Optional.of(endpointId);
mServiceDescriptor = Optional.empty();
+ attachDeathRecipient();
}
- DiscoveryCallback(IContextHubEndpointDiscoveryCallback callback, String serviceDescriptor) {
+ DiscoveryCallback(
+ HubInfoRegistry registry,
+ IContextHubEndpointDiscoveryCallback callback,
+ String serviceDescriptor)
+ throws RemoteException {
+ mHubInfoRegistry = registry;
mCallback = callback;
mEndpointId = Optional.empty();
mServiceDescriptor = Optional.of(serviceDescriptor);
+ attachDeathRecipient();
}
public IContextHubEndpointDiscoveryCallback getCallback() {
@@ -79,6 +106,10 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
* @return true if info matches
*/
public boolean isMatch(HubEndpointInfo info) {
+ if (mBinderDied.get()) {
+ Log.w(TAG, "Callback died, isMatch returning false");
+ return false;
+ }
if (mEndpointId.isPresent()) {
return mEndpointId.get() == info.getIdentifier().getEndpoint();
}
@@ -91,6 +122,17 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
}
return false;
}
+
+ @Override
+ public void binderDied() {
+ Log.d(TAG, "Binder died for discovery callback");
+ mBinderDied.set(true);
+ mHubInfoRegistry.unregisterEndpointDiscoveryCallback(mCallback);
+ }
+
+ private void attachDeathRecipient() throws RemoteException {
+ mCallback.asBinder().linkToDeath(this, 0 /* flags */);
+ }
}
/* The list of discovery callbacks registered with the service */
@@ -99,7 +141,11 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
private final Object mCallbackLock = new Object();
- HubInfoRegistry(IContextHubWrapper contextHubWrapper) throws InstantiationException {
+ /** Wakelock held while endpoint callbacks are being invoked */
+ private final WakeLock mWakeLock;
+
+ HubInfoRegistry(Context context, IContextHubWrapper contextHubWrapper)
+ throws InstantiationException {
mContextHubWrapper = contextHubWrapper;
try {
refreshCachedHubs();
@@ -109,6 +155,16 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
Log.e(TAG, error, e);
throw new InstantiationException(error);
}
+
+ PowerManager powerManager = context.getSystemService(PowerManager.class);
+ if (powerManager == null) {
+ String error = "PowerManager was null";
+ Log.e(TAG, error);
+ throw new InstantiationError(error);
+ }
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock.setWorkSource(new WorkSource(Process.myUid(), context.getPackageName()));
+ mWakeLock.setReferenceCounted(true);
}
/** Retrieve the list of hubs available. */
@@ -178,12 +234,7 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
try {
cb.onEndpointsStarted(infoList);
} catch (RemoteException e) {
- if (e instanceof DeadObjectException) {
- Log.w(TAG, "onEndpointStarted: callback died, unregistering");
- unregisterEndpointDiscoveryCallback(cb);
- } else {
- Log.e(TAG, "Exception while calling onEndpointsStarted", e);
- }
+ Log.e(TAG, "Exception while calling onEndpointsStarted", e);
}
});
}
@@ -208,12 +259,7 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
cb.onEndpointsStopped(
infoList, ContextHubServiceUtil.toAppHubEndpointReason(reason));
} catch (RemoteException e) {
- if (e instanceof DeadObjectException) {
- Log.w(TAG, "onEndpointStopped: callback died, unregistering");
- unregisterEndpointDiscoveryCallback(cb);
- } else {
- Log.e(TAG, "Exception while calling onEndpointsStopped", e);
- }
+ Log.e(TAG, "Exception while calling onEndpointsStopped", e);
}
});
}
@@ -254,7 +300,11 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
Objects.requireNonNull(callback, "callback cannot be null");
synchronized (mCallbackLock) {
checkCallbackAlreadyRegistered(callback);
- mEndpointDiscoveryCallbacks.add(new DiscoveryCallback(callback, endpointId));
+ try {
+ mEndpointDiscoveryCallbacks.add(new DiscoveryCallback(this, callback, endpointId));
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while adding discovery callback", e);
+ }
}
}
@@ -264,7 +314,12 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
Objects.requireNonNull(callback, "callback cannot be null");
synchronized (mCallbackLock) {
checkCallbackAlreadyRegistered(callback);
- mEndpointDiscoveryCallbacks.add(new DiscoveryCallback(callback, serviceDescriptor));
+ try {
+ mEndpointDiscoveryCallbacks.add(
+ new DiscoveryCallback(this, callback, serviceDescriptor));
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while adding discovery callback", e);
+ }
}
}
@@ -282,6 +337,11 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
}
}
+ /* package */
+ void onDiscoveryCallbackFinished() {
+ releaseWakeLock();
+ }
+
private void checkCallbackAlreadyRegistered(
IContextHubEndpointDiscoveryCallback callback) {
synchronized (mCallbackLock) {
@@ -315,6 +375,7 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
}
}
+ acquireWakeLock();
consumer.accept(
discoveryCallback.getCallback(),
infoList.toArray(new HubEndpointInfo[infoList.size()]));
@@ -322,6 +383,26 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
}
}
+ private void acquireWakeLock() {
+ Binder.withCleanCallingIdentity(
+ () -> {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
+ });
+ }
+
+ private void releaseWakeLock() {
+ Binder.withCleanCallingIdentity(
+ () -> {
+ if (mWakeLock.isHeld()) {
+ try {
+ mWakeLock.release();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Releasing the wakelock fails - ", e);
+ }
+ }
+ });
+ }
+
void dump(IndentingPrintWriter ipw) {
synchronized (mLock) {
dumpLocked(ipw);
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..6ad7ea7b768f 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,34 @@
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.SharedPreferences;
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;
@@ -36,12 +52,14 @@ import android.media.quality.SoundProfile;
import android.media.quality.SoundProfileHandle;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Environment;
import android.os.IBinder;
import android.os.PersistableBundle;
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;
@@ -53,6 +71,7 @@ import com.android.server.utils.Slogf;
import org.json.JSONException;
import org.json.JSONObject;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -60,6 +79,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;
@@ -71,24 +91,46 @@ public class MediaQualityService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "MediaQualityService";
+ private static final String ALLOWLIST = "allowlist";
+ private static final String PICTURE_PROFILE_PREFERENCE = "picture_profile_preference";
+ private static final String SOUND_PROFILE_PREFERENCE = "sound_profile_preference";
+ private static final String COMMA_DELIMITER = ",";
private static final int MAX_UUID_GENERATION_ATTEMPTS = 10;
private final Context mContext;
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;
+ private SharedPreferences mPictureProfileSharedPreference;
+ private SharedPreferences mSoundProfileSharedPreference;
public MediaQualityService(Context context) {
super(context);
mContext = context;
+ mHalAmbientBacklightCallback = new HalAmbientBacklightCallback();
mPackageManager = mContext.getPackageManager();
mPictureProfileTempIdMap = new BiMap<>();
mSoundProfileTempIdMap = new BiMap<>();
mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true);
mMediaQualityDbHelper.setIdleConnectionTimeout(30);
+
+ // The package info in the context isn't initialized in the way it is for normal apps,
+ // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
+ // build the path manually below using the same policy that appears in ContextImpl.
+ final Context deviceContext = mContext.createDeviceProtectedStorageContext();
+ final File pictureProfilePrefs = new File(Environment.getDataSystemDirectory(),
+ PICTURE_PROFILE_PREFERENCE);
+ mPictureProfileSharedPreference = deviceContext.getSharedPreferences(
+ pictureProfilePrefs, Context.MODE_PRIVATE);
+ final File soundProfilePrefs = new File(Environment.getDataSystemDirectory(),
+ SOUND_PROFILE_PREFERENCE);
+ mSoundProfileSharedPreference = deviceContext.getSharedPreferences(
+ soundProfilePrefs, Context.MODE_PRIVATE);
}
@Override
@@ -97,6 +139,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 +331,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 +750,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 +1219,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
@@ -937,6 +1313,11 @@ public class MediaQualityService extends SystemService {
notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
+ String allowlist = mPictureProfileSharedPreference.getString(ALLOWLIST, null);
+ if (allowlist != null) {
+ String[] stringArray = allowlist.split(COMMA_DELIMITER);
+ return new ArrayList<>(Arrays.asList(stringArray));
+ }
return new ArrayList<>();
}
@@ -946,6 +1327,9 @@ public class MediaQualityService extends SystemService {
notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
+ SharedPreferences.Editor editor = mPictureProfileSharedPreference.edit();
+ editor.putString(ALLOWLIST, String.join(COMMA_DELIMITER, packages));
+ editor.commit();
}
@Override
@@ -954,6 +1338,11 @@ public class MediaQualityService extends SystemService {
notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
+ String allowlist = mSoundProfileSharedPreference.getString(ALLOWLIST, null);
+ if (allowlist != null) {
+ String[] stringArray = allowlist.split(COMMA_DELIMITER);
+ return new ArrayList<>(Arrays.asList(stringArray));
+ }
return new ArrayList<>();
}
@@ -963,6 +1352,9 @@ public class MediaQualityService extends SystemService {
notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
+ SharedPreferences.Editor editor = mSoundProfileSharedPreference.edit();
+ editor.putString(ALLOWLIST, String.join(COMMA_DELIMITER, packages));
+ editor.commit();
}
@Override
@@ -979,10 +1371,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 +1384,10 @@ public class MediaQualityService extends SystemService {
public boolean isAutoPictureQualityEnabled(UserHandle user) {
try {
if (mMediaQuality != null) {
- return mMediaQuality.getAutoPqEnabled();
+ if (mMediaQuality.isAutoPqSupported()) {
+ return 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 +1403,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 +1416,12 @@ public class MediaQualityService extends SystemService {
public boolean isSuperResolutionEnabled(UserHandle user) {
try {
if (mMediaQuality != null) {
- return mMediaQuality.getAutoSrEnabled();
+ if (mMediaQuality.isAutoSrSupported()) {
+ return 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 +1435,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 +1448,12 @@ public class MediaQualityService extends SystemService {
public boolean isAutoSoundQualityEnabled(UserHandle user) {
try {
if (mMediaQuality != null) {
- return mMediaQuality.getAutoAqEnabled();
+ if (mMediaQuality.isAutoAqSupported()) {
+ return 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 +1511,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/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index f23d7823be94..33c122964d77 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -137,16 +137,26 @@ public final class NativeTombstoneManager {
return;
}
- String processName = "UNKNOWN";
final boolean isProtoFile = filename.endsWith(".pb");
- File protoPath = isProtoFile ? path : new File(path.getAbsolutePath() + ".pb");
- Optional<TombstoneFile> parsedTombstone = handleProtoTombstone(protoPath, isProtoFile);
- if (parsedTombstone.isPresent()) {
- processName = parsedTombstone.get().getProcessName();
+ // Only process the pb tombstone output, the text version will be generated in
+ // BootReceiver.filterAndAddTombstoneToDropBox through pbtombstone
+ if (Flags.protoTombstone() && !isProtoFile) {
+ return;
}
- BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName, mTmpFileLock);
+ File protoPath = isProtoFile ? path : new File(path.getAbsolutePath() + ".pb");
+
+ final String processName = handleProtoTombstone(protoPath, isProtoFile)
+ .map(TombstoneFile::getProcessName)
+ .orElse("UNKNOWN");
+
+ if (Flags.protoTombstone()) {
+ BootReceiver.filterAndAddTombstoneToDropBox(mContext, path, processName, mTmpFileLock);
+ } else {
+ BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile,
+ processName, mTmpFileLock);
+ }
// TODO(b/339371242): An optimizer on WearOS is misbehaving and this member is being garbage
// collected as it's never referenced inside this class outside of the constructor. But,
// it's a file watcher, and needs to stay alive to do its job. So, add a cheap check here to
diff --git a/services/core/java/com/android/server/os/core_os_flags.aconfig b/services/core/java/com/android/server/os/core_os_flags.aconfig
index efdc9b8c164f..5e35cf5f02d3 100644
--- a/services/core/java/com/android/server/os/core_os_flags.aconfig
+++ b/services/core/java/com/android/server/os/core_os_flags.aconfig
@@ -3,7 +3,7 @@ container: "system"
flag {
name: "proto_tombstone"
- namespace: "proto_tombstone_ns"
+ namespace: "stability"
description: "Use proto tombstones as source of truth for adding to dropbox"
bug: "323857385"
}
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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0226650ec560..42a47d4a037e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8440,8 +8440,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mTmpConfig.updateFrom(resolvedConfig);
newParentConfiguration = mTmpConfig;
}
-
- mAppCompatController.getAspectRatioPolicy().reset();
+ final AppCompatAspectRatioPolicy aspectRatioPolicy =
+ mAppCompatController.getAspectRatioPolicy();
+ aspectRatioPolicy.reset();
mIsEligibleForFixedOrientationLetterbox = false;
mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration,
isFixedRotationTransforming());
@@ -8472,12 +8473,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
// Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
- if (!mAppCompatController.getAspectRatioPolicy()
- .isLetterboxedForFixedOrientationAndAspectRatio()
- && !mAppCompatController.getAspectRatioOverrides()
- .hasFullscreenOverride()) {
- resolveAspectRatioRestriction(newParentConfiguration);
- }
+ aspectRatioPolicy.resolveAspectRatioRestrictionIfNeeded(newParentConfiguration);
final AppCompatDisplayInsets appCompatDisplayInsets = getAppCompatDisplayInsets();
final AppCompatSizeCompatModePolicy scmPolicy =
mAppCompatController.getSizeCompatModePolicy();
@@ -8509,8 +8505,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Fixed orientation letterboxing is possible on both large screen devices
// with ignoreOrientationRequest enabled and on phones in split screen even with
// ignoreOrientationRequest disabled.
- && (mAppCompatController.getAspectRatioPolicy()
- .isLetterboxedForFixedOrientationAndAspectRatio()
+ && (aspectRatioPolicy.isLetterboxedForFixedOrientationAndAspectRatio()
// Limiting check for aspect ratio letterboxing to devices with enabled
// ignoreOrientationRequest. This avoids affecting phones where apps may
// not expect the change of smallestScreenWidthDp after rotation which is
@@ -8518,7 +8513,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// accurate on phones shouldn't make the big difference and is expected
// to be already well-tested by apps.
|| (isIgnoreOrientationRequest
- && mAppCompatController.getAspectRatioPolicy().isAspectRatioApplied()))) {
+ && aspectRatioPolicy.isAspectRatioApplied()))) {
// TODO(b/264034555): Use mDisplayContent to calculate smallestScreenWidthDp from all
// rotations and only re-calculate if parent bounds have non-orientation size change.
resolvedConfig.smallestScreenWidthDp =
@@ -9007,37 +9002,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
new Rect(resolvedBounds));
}
- /**
- * Resolves aspect ratio restrictions for an activity. If the bounds are restricted by
- * aspect ratio, the position will be adjusted later in {@link #updateResolvedBoundsPosition
- * within parent's app bounds to balance the visual appearance. The policy of aspect ratio has
- * higher priority than the requested override bounds.
- */
- private void resolveAspectRatioRestriction(Configuration newParentConfiguration) {
- final Configuration resolvedConfig = getResolvedOverrideConfiguration();
- final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
- final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
- final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
- // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
- // restricted size (resolved bounds may be the requested override bounds).
- mTmpBounds.setEmpty();
- final AppCompatAspectRatioPolicy aspectRatioPolicy = mAppCompatController
- .getAspectRatioPolicy();
- aspectRatioPolicy.applyAspectRatioForLetterbox(mTmpBounds, parentAppBounds, parentBounds);
- // If the out bounds is not empty, it means the activity cannot fill parent's app bounds,
- // then they should be aligned later in #updateResolvedBoundsPosition()
- if (!mTmpBounds.isEmpty()) {
- resolvedBounds.set(mTmpBounds);
- }
- if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
- // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
- // restrict, the bounds should be the requested override bounds.
- mResolveConfigHint.mTmpOverrideDisplayInfo = getFixedRotationTransformDisplayInfo();
- computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
- aspectRatioPolicy.setLetterboxBoundsForAspectRatio(new Rect(resolvedBounds));
- }
- }
-
@Override
public Rect getBounds() {
// TODO(b/268458693): Refactor configuration inheritance in case of translucent activities
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index 4ecd0bec9880..ab1778a1a32e 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -56,6 +56,8 @@ class AppCompatAspectRatioPolicy {
@NonNull
private final AppCompatAspectRatioState mAppCompatAspectRatioState;
+ private final Rect mTmpBounds = new Rect();
+
AppCompatAspectRatioPolicy(@NonNull ActivityRecord activityRecord,
@NonNull TransparentPolicy transparentPolicy,
@NonNull AppCompatOverrides appCompatOverrides) {
@@ -222,6 +224,45 @@ class AppCompatAspectRatioPolicy {
return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
}
+ /**
+ * Resolves aspect ratio restrictions for an activity. If the bounds are restricted by
+ * aspect ratio, the position will be adjusted later in {@link #updateResolvedBoundsPosition}
+ * within parent's app bounds to balance the visual appearance. The policy of aspect ratio has
+ * higher priority than the requested override bounds.
+ */
+ void resolveAspectRatioRestrictionIfNeeded(@NonNull Configuration newParentConfiguration) {
+ // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
+ // are already calculated in resolveFixedOrientationConfiguration.
+ // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
+ if (isLetterboxedForFixedOrientationAndAspectRatio()
+ || getOverrides().hasFullscreenOverride()) {
+ return;
+ }
+ final Configuration resolvedConfig = mActivityRecord.getResolvedOverrideConfiguration();
+ final Rect parentAppBounds =
+ mActivityRecord.mResolveConfigHint.mParentAppBoundsOverride;
+ final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+ final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+ // Use tmp bounds to calculate aspect ratio so we can know whether the activity should
+ // use restricted size (resolved bounds may be the requested override bounds).
+ mTmpBounds.setEmpty();
+ applyAspectRatioForLetterbox(mTmpBounds, parentAppBounds, parentBounds);
+ // If the out bounds is not empty, it means the activity cannot fill parent's app
+ // bounds, then they should be aligned later in #updateResolvedBoundsPosition().
+ if (!mTmpBounds.isEmpty()) {
+ resolvedBounds.set(mTmpBounds);
+ }
+ if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
+ // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
+ // restrict, the bounds should be the requested override bounds.
+ // TODO(b/384473893): Improve ActivityRecord usage here.
+ mActivityRecord.mResolveConfigHint.mTmpOverrideDisplayInfo =
+ mActivityRecord.getFixedRotationTransformDisplayInfo();
+ mActivityRecord.computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
+ setLetterboxBoundsForAspectRatio(new Rect(resolvedBounds));
+ }
+ }
+
private boolean isParentFullscreenPortrait() {
final WindowContainer<?> parent = mActivityRecord.getParent();
return parent != null
@@ -364,6 +405,11 @@ class AppCompatAspectRatioPolicy {
&& !dc.getIgnoreOrientationRequest();
}
+ @NonNull
+ private AppCompatAspectRatioOverrides getOverrides() {
+ return mActivityRecord.mAppCompatController.getAspectRatioOverrides();
+ }
+
private static class AppCompatAspectRatioState {
// Whether the aspect ratio restrictions applied to the activity bounds
// in applyAspectRatio().
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..c6136f316c3e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5101,6 +5101,7 @@ class Task extends TaskFragment {
mTranslucentActivityWaiting = r;
mPendingConvertFromTranslucentActivity = r;
mUndrawnActivitiesBelowTopTranslucent.clear();
+ updateTaskDescription();
mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
}
@@ -5110,6 +5111,7 @@ class Task extends TaskFragment {
+ " but is " + r);
}
mPendingConvertFromTranslucentActivity = null;
+ updateTaskDescription();
}
/**
@@ -5297,40 +5299,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 +5337,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 +5396,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/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java
new file mode 100644
index 000000000000..acce813ff659
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java
@@ -0,0 +1,242 @@
+/*
+ * 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.accessibility;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.testutils.MockitoUtilsKt.eq;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertNull;
+
+import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+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;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Test cases for {@link AutoclickController}. */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class AutoclickControllerTest {
+
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Rule
+ public TestableContext mTestableContext =
+ new TestableContext(getInstrumentation().getContext());
+
+ private TestableLooper mTestableLooper;
+ @Mock private AccessibilityTraceManager mMockTrace;
+ @Mock private WindowManager mMockWindowManager;
+ private AutoclickController mController;
+
+ @Before
+ public void setUp() {
+ mTestableLooper = TestableLooper.get(this);
+ mTestableContext.addMockSystemService(Context.WINDOW_SERVICE, mMockWindowManager);
+ mController =
+ new AutoclickController(mTestableContext, mTestableContext.getUserId(), mMockTrace);
+ }
+
+ @After
+ public void tearDown() {
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ public void onMotionEvent_lazyInitClickScheduler() {
+ assertNull(mController.mClickScheduler);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mClickScheduler);
+ }
+
+ @Test
+ public void onMotionEvent_nonMouseSource_notInitClickScheduler() {
+ assertNull(mController.mClickScheduler);
+
+ injectFakeNonMouseActionDownEvent();
+
+ assertNull(mController.mClickScheduler);
+ }
+
+ @Test
+ public void onMotionEvent_lazyInitAutoclickSettingsObserver() {
+ assertNull(mController.mAutoclickSettingsObserver);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mAutoclickSettingsObserver);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorScheduler() {
+ assertNull(mController.mAutoclickIndicatorScheduler);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mAutoclickIndicatorScheduler);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOff_notInitAutoclickIndicatorScheduler() {
+ assertNull(mController.mAutoclickIndicatorScheduler);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNull(mController.mAutoclickIndicatorScheduler);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorView() {
+ assertNull(mController.mAutoclickIndicatorView);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mAutoclickIndicatorView);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOff_notInitAutoclickIndicatorView() {
+ assertNull(mController.mAutoclickIndicatorView);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNull(mController.mAutoclickIndicatorView);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_addAutoclickIndicatorViewToWindowManager() {
+ injectFakeMouseActionDownEvent();
+
+ verify(mMockWindowManager).addView(eq(mController.mAutoclickIndicatorView), any());
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onDestroy_flagOn_removeAutoclickIndicatorViewToWindowManager() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ verify(mMockWindowManager).removeView(mController.mAutoclickIndicatorView);
+ }
+
+ @Test
+ public void onMotionEvent_initClickSchedulerDelayFromSetting() {
+ injectFakeMouseActionDownEvent();
+
+ int delay =
+ Settings.Secure.getIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
+ AccessibilityManager.AUTOCLICK_DELAY_DEFAULT,
+ mTestableContext.getUserId());
+ assertEquals(delay, mController.mClickScheduler.getDelayForTesting());
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_initCursorAreaSizeFromSetting() {
+ injectFakeMouseActionDownEvent();
+
+ int size =
+ Settings.Secure.getIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
+ AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT,
+ mTestableContext.getUserId());
+ assertEquals(size, mController.mAutoclickIndicatorView.getRadiusForTesting());
+ }
+
+ @Test
+ public void onDestroy_clearClickScheduler() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ assertNull(mController.mClickScheduler);
+ }
+
+ @Test
+ public void onDestroy_clearAutoclickSettingsObserver() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ assertNull(mController.mAutoclickSettingsObserver);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onDestroy_flagOn_clearAutoclickIndicatorScheduler() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ assertNull(mController.mAutoclickIndicatorScheduler);
+ }
+
+ private void injectFakeMouseActionDownEvent() {
+ MotionEvent event = getFakeMotionDownEvent();
+ event.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(event, event, /* policyFlags= */ 0);
+ }
+
+ private void injectFakeNonMouseActionDownEvent() {
+ MotionEvent event = getFakeMotionDownEvent();
+ event.setSource(InputDevice.SOURCE_KEYBOARD);
+ mController.onMotionEvent(event, event, /* policyFlags= */ 0);
+ }
+
+ private MotionEvent getFakeMotionDownEvent() {
+ return MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ /* action= */ MotionEvent.ACTION_DOWN,
+ /* x= */ 0,
+ /* y= */ 0,
+ /* metaState= */ 0);
+ }
+}
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/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 38d3d2fec942..724d7e7c111c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -92,6 +92,7 @@ import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
+import android.view.WindowInsetsController;
import android.window.TaskFragmentOrganizer;
import androidx.test.filters.MediumTest;
@@ -2109,6 +2110,43 @@ public class TaskTests extends WindowTestsBase {
assertEquals(Color.RED, task.getTaskDescription().getBackgroundColor());
}
+ @Test
+ public void testUpdateTopOpaqueSystemBarsAppearanceWhenActivityBecomesTransparent() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activity = createActivityRecord(task);
+ final ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
+ td.setSystemBarsAppearance(
+ WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND);
+ activity.setTaskDescription(td);
+
+ assertEquals(WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+ task.getTaskDescription().getTopOpaqueSystemBarsAppearance());
+
+ activity.setOccludesParent(false);
+
+ assertEquals(0, task.getTaskDescription().getTopOpaqueSystemBarsAppearance());
+ }
+
+ @Test
+ public void testUpdateTopOpaqueSystemBarsAppearanceWhenActivityBecomesOpaque() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activity = createActivityRecord(task);
+ activity.setOccludesParent(false);
+
+ final ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
+ td.setSystemBarsAppearance(
+ WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND);
+ activity.setTaskDescription(td);
+
+ assertEquals(0, task.getTaskDescription().getTopOpaqueSystemBarsAppearance());
+
+ activity.setOccludesParent(true);
+
+ assertEquals(WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+ task.getTaskDescription().getTopOpaqueSystemBarsAppearance());
+
+ }
+
private Task getTestTask() {
return new TaskBuilder(mSupervisor).setCreateActivity(true).build();
}
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/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 24fb8c5da2d7..da4165553e57 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -180,7 +180,7 @@ import java.util.stream.IntStream;
* permission-protected. Your application cannot access the protected
* information unless it has the appropriate permissions declared in
* its manifest file. Where permissions apply, they are noted in the
- * the methods through which you access the protected information.
+ * methods through which you access the protected information.
*
* <p>TelephonyManager is intended for use on devices that implement
* {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
@@ -633,11 +633,14 @@ public class TelephonyManager {
}
/**
- * Returns the multi SIM variant
- * Returns DSDS for Dual SIM Dual Standby
- * Returns DSDA for Dual SIM Dual Active
- * Returns TSTS for Triple SIM Triple Standby
- * Returns UNKNOWN for others
+ * Returns the multi SIM variant.
+ *
+ * <ul>
+ * <li>Returns DSDS for Dual SIM Dual Standby.</li>
+ * <li>Returns DSDA for Dual SIM Dual Active.</li>
+ * <li>Returns TSTS for Triple SIM Triple Standby.</li>
+ * <li>Returns UNKNOWN for others.</li>
+ * </ul>
*/
/** {@hide} */
@UnsupportedAppUsage
@@ -657,10 +660,14 @@ public class TelephonyManager {
/**
* Returns the number of phones available.
- * Returns 0 if none of voice, sms, data is not supported
- * Returns 1 for Single standby mode (Single SIM functionality).
- * Returns 2 for Dual standby mode (Dual SIM functionality).
- * Returns 3 for Tri standby mode (Tri SIM functionality).
+ *
+ * <ul>
+ * <li>Returns 0 if none of voice, sms, data is supported.</li>
+ * <li>Returns 1 for Single standby mode (Single SIM functionality).</li>
+ * <li>Returns 2 for Dual standby mode (Dual SIM functionality).</li>
+ * <li>Returns 3 for Tri standby mode (Tri SIM functionality).</li>
+ * </ul>
+ *
* @deprecated Use {@link #getActiveModemCount} instead.
*/
@Deprecated
@@ -671,10 +678,12 @@ public class TelephonyManager {
/**
* Returns the number of logical modems currently configured to be activated.
*
- * Returns 0 if none of voice, sms, data is not supported
- * Returns 1 for Single standby mode (Single SIM functionality).
- * Returns 2 for Dual standby mode (Dual SIM functionality).
- * Returns 3 for Tri standby mode (Tri SIM functionality).
+ * <ul>
+ * <li>Returns 0 if none of voice, sms, data is supported.</li>
+ * <li>Returns 1 for Single standby mode (Single SIM functionality).</li>
+ * <li>Returns 2 for Dual standby mode (Dual SIM functionality).</li>
+ * <li>Returns 3 for Tri standby mode (Tri SIM functionality).</li>
+ * </ul>
*/
public int getActiveModemCount() {
int modemCount = 1;
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));
}