summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/uiautomator/library/Android.bp8
-rw-r--r--core/api/current.txt18
-rw-r--r--core/java/android/app/appfunctions/AppFunctionService.java69
-rw-r--r--core/java/android/app/notification.aconfig8
-rw-r--r--core/java/android/content/pm/TEST_MAPPING22
-rw-r--r--core/java/android/net/vcn/VcnGatewayConnectionConfig.java10
-rw-r--r--core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java10
-rw-r--r--core/java/android/os/BatteryManager.java71
-rw-r--r--core/java/android/os/BinderProxy.java17
-rw-r--r--core/java/android/os/IBinder.java43
-rw-r--r--core/java/android/os/flags.aconfig8
-rw-r--r--core/java/android/provider/Settings.java42
-rw-r--r--core/java/android/provider/flags.aconfig11
-rw-r--r--core/java/android/text/ClientFlags.java9
-rw-r--r--core/java/android/text/TextFlags.java5
-rw-r--r--core/java/android/text/flags/flags.aconfig41
-rw-r--r--core/java/android/view/InsetsState.java4
-rw-r--r--core/java/android/view/ViewRootImpl.java6
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.java3
-rw-r--r--core/java/android/widget/RemoteViews.java480
-rw-r--r--core/java/android/window/flags/DesktopModeFlags.java27
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java49
-rw-r--r--core/java/com/android/internal/policy/DecorView.java10
-rw-r--r--core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java8
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogConfigurationService.java424
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java454
-rw-r--r--core/java/com/android/internal/view/menu/ListMenuItemView.java7
-rw-r--r--core/java/com/android/internal/view/menu/StandardMenuPopup.java5
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/android_util_Binder.cpp6
-rw-r--r--core/jni/jni_wrappers.h40
-rw-r--r--core/proto/android/widget/remoteviews.proto54
-rw-r--r--core/res/res/layout/miniresolver.xml4
-rw-r--r--core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java15
-rw-r--r--core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java18
-rw-r--r--core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java54
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java1
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java4
-rw-r--r--graphics/java/android/graphics/Bitmap.java29
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt71
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/PooledWindowDecorViewHostSupplier.kt105
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/ReusableWindowDecorViewHost.kt161
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/SurfaceControlViewHostAdapter.kt111
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/Warmable.kt23
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt81
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/PooledWindowDecorViewHostSupplierTest.kt181
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/ReusableWindowDecorViewHostTest.kt182
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/SurfaceControlViewHostAdapterTest.kt144
-rw-r--r--libs/appfunctions/Android.bp31
-rw-r--r--libs/appfunctions/api/current.txt49
-rw-r--r--libs/appfunctions/api/removed.txt1
-rw-r--r--libs/appfunctions/api/system-current.txt1
-rw-r--r--libs/appfunctions/api/system-removed.txt1
-rw-r--r--libs/appfunctions/api/test-current.txt1
-rw-r--r--libs/appfunctions/api/test-removed.txt1
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java82
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java114
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java137
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java240
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java104
-rw-r--r--libs/hwui/hwui/Bitmap.cpp29
-rw-r--r--libs/hwui/hwui/Bitmap.h8
-rw-r--r--libs/hwui/hwui/DrawTextFunctor.h1
-rw-r--r--libs/hwui/platform/host/thread/ThreadBase.h2
-rw-r--r--libs/input/MouseCursorController.cpp44
-rw-r--r--libs/input/MouseCursorController.h2
-rw-r--r--libs/input/PointerController.cpp4
-rw-r--r--libs/input/PointerController.h4
-rw-r--r--location/java/android/location/flags/location.aconfig11
-rw-r--r--media/java/android/media/AudioManager.java9
-rw-r--r--media/java/android/media/projection/MediaProjection.java44
-rw-r--r--media/java/android/media/projection/MediaProjectionManager.java15
-rw-r--r--nfc/api/system-current.txt5
-rw-r--r--nfc/java/android/nfc/INfcAdapter.aidl2
-rw-r--r--nfc/java/android/nfc/NfcAdapter.java24
-rw-r--r--nfc/java/android/nfc/NfcOemExtension.java78
-rw-r--r--packages/PackageInstaller/Android.bp1
-rw-r--r--packages/PackageInstaller/TEST_MAPPING22
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt38
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java27
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig31
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt24
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt60
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt50
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt57
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt94
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/NavigationBarContextTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/buttons/NavigationBarContextTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/NearestTouchFrameTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/buttons/NearestTouchFrameTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/people/NotificationHelperTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/people/PeopleProviderTestable.java (renamed from packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/people/SharedPreferencesHelperTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/PagedTileLayoutTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSContainerImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseSceneContainerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseSceneContainerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/TileStateToProtoTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/TouchAnimatorTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileAdapterTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/CustomTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileColorPickerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileServiceManagerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NfcTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandlerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandlerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaOutputSwitcherViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt96
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarSimpleFragment.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java)84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt2
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt8
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java25
-rw-r--r--ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java32
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java54
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java26
-rw-r--r--services/companion/java/com/android/server/companion/virtual/OWNERS4
-rw-r--r--services/core/java/com/android/server/BatteryService.java8
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING9
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java45
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java25
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java10
-rw-r--r--services/core/java/com/android/server/am/OomAdjusterModernImpl.java6
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java3
-rw-r--r--services/core/java/com/android/server/cpu/CpuMonitorService.java2
-rw-r--r--services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java61
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java6
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java74
-rw-r--r--services/core/java/com/android/server/input/debug/TouchpadDebugView.java4
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java6
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsStorage.java2
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java11
-rw-r--r--services/core/java/com/android/server/notification/VibratorHelper.java10
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING32
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java23
-rw-r--r--services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java43
-rw-r--r--services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java13
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperData.java12
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperDataParser.java22
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java93
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java3
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java2
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeHelper.java6
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java1
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java39
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java12
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp27
-rw-r--r--services/java/com/android/server/SystemServer.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java18
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java9
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java42
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java10
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java4
-rw-r--r--telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl8
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java8
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java9
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt79
-rw-r--r--tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java57
-rw-r--r--tests/Tracing/Android.bp2
-rw-r--r--tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java5
-rw-r--r--tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java58
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt2
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleRequiresNoPermissionDetector.kt118
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/PermissionAnnotationDetectorTest.kt55
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleRequiresNoPermissionDetectorTest.kt244
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt44
-rw-r--r--wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java28
319 files changed, 5234 insertions, 2687 deletions
diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp
index 966bf13adfe4..5c5b220d20e9 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -28,9 +28,9 @@ droidstubs {
"testrunner-src/**/*.java",
],
libs: [
- "android.test.runner",
+ "android.test.runner.stubs.system",
"junit",
- "android.test.base",
+ "android.test.base.stubs.system",
"unsupportedappusage",
],
installable: false,
@@ -56,9 +56,9 @@ droiddoc {
":uiautomator-stubs",
],
libs: [
- "android.test.runner",
+ "android.test.runner.stubs",
"junit",
- "android.test.base",
+ "android.test.base.stubs",
],
sdk_version: "current",
installable: false,
diff --git a/core/api/current.txt b/core/api/current.txt
index 1667f2e4457a..542543d11318 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -32613,6 +32613,13 @@ package android.os {
method public boolean isCharging();
field public static final String ACTION_CHARGING = "android.os.action.CHARGING";
field public static final String ACTION_DISCHARGING = "android.os.action.DISCHARGING";
+ field @FlaggedApi("android.os.battery_part_status_api") public static final int BATTERY_CAPACITY_LEVEL_CRITICAL = 1; // 0x1
+ field @FlaggedApi("android.os.battery_part_status_api") public static final int BATTERY_CAPACITY_LEVEL_FULL = 5; // 0x5
+ field @FlaggedApi("android.os.battery_part_status_api") public static final int BATTERY_CAPACITY_LEVEL_HIGH = 4; // 0x4
+ field @FlaggedApi("android.os.battery_part_status_api") public static final int BATTERY_CAPACITY_LEVEL_LOW = 2; // 0x2
+ field @FlaggedApi("android.os.battery_part_status_api") public static final int BATTERY_CAPACITY_LEVEL_NORMAL = 3; // 0x3
+ field @FlaggedApi("android.os.battery_part_status_api") public static final int BATTERY_CAPACITY_LEVEL_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("android.os.battery_part_status_api") public static final int BATTERY_CAPACITY_LEVEL_UNSUPPORTED = -1; // 0xffffffff
field public static final int BATTERY_HEALTH_COLD = 7; // 0x7
field public static final int BATTERY_HEALTH_DEAD = 4; // 0x4
field public static final int BATTERY_HEALTH_GOOD = 2; // 0x2
@@ -32637,6 +32644,7 @@ package android.os {
field public static final int BATTERY_STATUS_NOT_CHARGING = 4; // 0x4
field public static final int BATTERY_STATUS_UNKNOWN = 1; // 0x1
field public static final String EXTRA_BATTERY_LOW = "battery_low";
+ field @FlaggedApi("android.os.battery_part_status_api") public static final String EXTRA_CAPACITY_LEVEL = "android.os.extra.CAPACITY_LEVEL";
field public static final String EXTRA_CHARGING_STATUS = "android.os.extra.CHARGING_STATUS";
field public static final String EXTRA_CYCLE_COUNT = "android.os.extra.CYCLE_COUNT";
field public static final String EXTRA_HEALTH = "health";
@@ -33228,6 +33236,7 @@ package android.os {
}
public interface IBinder {
+ method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default void addFrozenStateChangeCallback(@NonNull android.os.IBinder.FrozenStateChangeCallback) throws android.os.RemoteException;
method public void dump(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException;
method public void dumpAsync(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException;
method @Nullable public String getInterfaceDescriptor() throws android.os.RemoteException;
@@ -33236,6 +33245,7 @@ package android.os {
method public void linkToDeath(@NonNull android.os.IBinder.DeathRecipient, int) throws android.os.RemoteException;
method public boolean pingBinder();
method @Nullable public android.os.IInterface queryLocalInterface(@NonNull String);
+ method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default boolean removeFrozenStateChangeCallback(@NonNull android.os.IBinder.FrozenStateChangeCallback);
method public boolean transact(int, @NonNull android.os.Parcel, @Nullable android.os.Parcel, int) throws android.os.RemoteException;
method public boolean unlinkToDeath(@NonNull android.os.IBinder.DeathRecipient, int);
field public static final int DUMP_TRANSACTION = 1598311760; // 0x5f444d50
@@ -33253,6 +33263,12 @@ package android.os {
method public default void binderDied(@NonNull android.os.IBinder);
}
+ @FlaggedApi("android.os.binder_frozen_state_change_callback") public static interface IBinder.FrozenStateChangeCallback {
+ method public void onFrozenStateChanged(@NonNull android.os.IBinder, int);
+ field public static final int STATE_FROZEN = 0; // 0x0
+ field public static final int STATE_UNFROZEN = 1; // 0x1
+ }
+
public interface IInterface {
method public android.os.IBinder asBinder();
}
@@ -44070,7 +44086,7 @@ package android.telephony {
}
public static final class CarrierConfigManager.Gps {
- field @FlaggedApi("android.location.flags.enable_ni_supl_message_injection_by_carrier_config") public static final String KEY_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL = "gps.enable_ni_supl_message_injection_bool";
+ field @FlaggedApi("android.location.flags.enable_ni_supl_message_injection_by_carrier_config_bugfix") public static final String KEY_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL = "gps.enable_ni_supl_message_injection_bool";
field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool";
field public static final String KEY_PREFIX = "gps.";
}
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index c27141a1acbf..0d981ea5a679 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -26,6 +26,7 @@ import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
@@ -60,29 +61,53 @@ public abstract class AppFunctionService extends Service {
@NonNull
public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
- private final Binder mBinder =
- new IAppFunctionService.Stub() {
- @Override
- public void executeAppFunction(
- @NonNull ExecuteAppFunctionRequest request,
- @NonNull IExecuteAppFunctionCallback callback) {
- if (AppFunctionService.this.checkCallingPermission(BIND_APP_FUNCTION_SERVICE)
- == PERMISSION_DENIED) {
- throw new SecurityException("Can only be called by the system server.");
- }
- SafeOneTimeExecuteAppFunctionCallback safeCallback =
- new SafeOneTimeExecuteAppFunctionCallback(callback);
- try {
- AppFunctionService.this.onExecuteFunction(request, safeCallback::onResult);
- } catch (Exception ex) {
- // Apps should handle exceptions. But if they don't, report the error on
- // behalf of them.
- safeCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- getResultCode(ex), ex.getMessage(), /* extras= */ null));
- }
+ /**
+ * Functional interface to represent the execution logic of an app function.
+ *
+ * @hide
+ */
+ @FunctionalInterface
+ public interface OnExecuteFunction {
+ /**
+ * Performs the semantic of executing the function specified by the provided request and
+ * return the response through the provided callback.
+ */
+ void perform(
+ @NonNull ExecuteAppFunctionRequest request,
+ @NonNull Consumer<ExecuteAppFunctionResponse> callback);
+ }
+
+ /** @hide */
+ @NonNull
+ public static Binder createBinder(
+ @NonNull Context context, @NonNull OnExecuteFunction onExecuteFunction) {
+ return new IAppFunctionService.Stub() {
+ @Override
+ public void executeAppFunction(
+ @NonNull ExecuteAppFunctionRequest request,
+ @NonNull IExecuteAppFunctionCallback callback) {
+ if (context.checkCallingPermission(BIND_APP_FUNCTION_SERVICE)
+ == PERMISSION_DENIED) {
+ throw new SecurityException("Can only be called by the system server.");
+ }
+ SafeOneTimeExecuteAppFunctionCallback safeCallback =
+ new SafeOneTimeExecuteAppFunctionCallback(callback);
+ try {
+ onExecuteFunction.perform(request, safeCallback::onResult);
+ } catch (Exception ex) {
+ // Apps should handle exceptions. But if they don't, report the error on
+ // behalf of them.
+ safeCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ getResultCode(ex), ex.getMessage(), /* extras= */ null));
}
- };
+ }
+ };
+ }
+
+ private final Binder mBinder = createBinder(
+ AppFunctionService.this,
+ AppFunctionService.this::onExecuteFunction);
@NonNull
@Override
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 9891e8930936..9b06adf4e894 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -236,4 +236,12 @@ flag {
namespace: "systemui"
description: "Guards new android.app.richongoingnotification api"
bug: "337261753"
+}
+
+flag {
+ name: "ui_rich_ongoing"
+ is_exported: true
+ namespace: "systemui"
+ description: "Guards new android.app.richongoingnotification promotion and new uis"
+ bug: "337261753"
} \ No newline at end of file
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index ffadd1e4083c..2cdae21bde92 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -206,6 +206,17 @@
]
},
{
+ "name": "CtsPackageInstallerCUJDeviceAdminTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJInstallationTestCases",
"options":[
{
@@ -217,6 +228,17 @@
]
},
{
+ "name": "CtsPackageInstallerCUJMultiUsersTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJUninstallationTestCases",
"options":[
{
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 6f11d3ae661c..af93c964a8ba 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -35,7 +35,6 @@ import android.os.PersistableBundle;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.vcn.util.PersistableBundleUtils;
@@ -434,7 +433,14 @@ public final class VcnGatewayConnectionConfig {
@NonNull
public int[] getExposedCapabilities() {
// Sorted set guarantees ordering
- return ArrayUtils.convertToIntArray(new ArrayList<>(mExposedCapabilities));
+ final int[] caps = new int[mExposedCapabilities.size()];
+
+ int i = 0;
+ for (int c : mExposedCapabilities) {
+ caps[i++] = c;
+ }
+
+ return caps;
}
/**
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java b/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
index a97563724e50..e1d1b3c65c99 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
@@ -24,7 +24,6 @@ import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.util.ArrayUtils;
import java.util.Arrays;
import java.util.Objects;
@@ -114,8 +113,13 @@ public final class VcnUnderlyingNetworkSpecifier extends NetworkSpecifier implem
@Override
public boolean canBeSatisfiedBy(NetworkSpecifier other) {
if (other instanceof TelephonyNetworkSpecifier) {
- return ArrayUtils.contains(
- mSubIds, ((TelephonyNetworkSpecifier) other).getSubscriptionId());
+ final int targetSubId = ((TelephonyNetworkSpecifier) other).getSubscriptionId();
+ for (int subId : mSubIds) {
+ if (targetSubId == subId) {
+ return true;
+ }
+ }
+ return false;
}
// TODO(b/180140053): Allow matching against WifiNetworkAgentSpecifier
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index f3efd89d4bc3..8b267bf28c7e 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -141,6 +141,7 @@ public class BatteryManager {
/**
* Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
* integer containing the charge counter present in the battery.
+ * It shows the available battery power in µAh
* {@hide}
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -166,6 +167,76 @@ public class BatteryManager {
public static final String EXTRA_CHARGING_STATUS = "android.os.extra.CHARGING_STATUS";
/**
+ * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+ * Int value representing the battery's capacity level. These constants are key indicators of
+ * battery status and system capabilities, guiding power management decisions for both the
+ * system and apps:
+ * {@link #BATTERY_CAPACITY_LEVEL_UNSUPPORTED}: Feature not supported on this device.
+ * {@link #BATTERY_CAPACITY_LEVEL_UNKNOWN}: Battery status is unavailable or uninitialized.
+ * {@link #BATTERY_CAPACITY_LEVEL_CRITICAL}: Battery is critically low and the Android
+ * framework has been notified to schedule a shutdown by this value
+ * {@link #BATTERY_CAPACITY_LEVEL_LOW}: Android framework must limit background jobs to
+ * avoid impacting charging speed
+ * {@link #BATTERY_CAPACITY_LEVEL_NORMAL}: Battery level and charging rates are normal,
+ * battery temperature is within normal range and adapter power is enough to charge the
+ * battery at an acceptable rate. Android framework can run light background tasks without
+ * affecting charging performance severely.
+ * {@link #BATTERY_CAPACITY_LEVEL_HIGH}: Battery level is high, battery temperature is
+ * within normal range and adapter power is enough to charge the battery at an acceptable
+ * rate while running background loads. Android framework can run background tasks without
+ * affecting charging or battery performance.
+ * {@link #BATTERY_CAPACITY_LEVEL_FULL}: The battery is full, battery temperature is
+ * within normal range and adapter power is enough to sustain running background loads.
+ * Android framework can run background tasks without affecting the battery level or
+ * battery performance.
+ */
+
+ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+ public static final String EXTRA_CAPACITY_LEVEL = "android.os.extra.CAPACITY_LEVEL";
+
+ /**
+ * Battery capacity level is unsupported. @see EXTRA_CAPACITY_LEVEL
+ */
+ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+ public static final int BATTERY_CAPACITY_LEVEL_UNSUPPORTED = -1;
+
+ /**
+ * Battery capacity level is unknown. @see EXTRA_CAPACITY_LEVEL
+ */
+ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+ public static final int BATTERY_CAPACITY_LEVEL_UNKNOWN = 0;
+
+ /**
+ * Battery capacity level is critical. @see EXTRA_CAPACITY_LEVEL
+ */
+ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+ public static final int BATTERY_CAPACITY_LEVEL_CRITICAL = 1;
+
+ /**
+ * Battery capacity level is low. @see EXTRA_CAPACITY_LEVEL
+ */
+ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+ public static final int BATTERY_CAPACITY_LEVEL_LOW = 2;
+
+ /**
+ * Battery capacity level is normal. @see EXTRA_CAPACITY_LEVEL
+ */
+ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+ public static final int BATTERY_CAPACITY_LEVEL_NORMAL = 3;
+
+ /**
+ * Battery capacity level is high. @see EXTRA_CAPACITY_LEVEL
+ */
+ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+ public static final int BATTERY_CAPACITY_LEVEL_HIGH = 4;
+
+ /**
+ * Battery capacity level is full. @see EXTRA_CAPACITY_LEVEL
+ */
+ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+ public static final int BATTERY_CAPACITY_LEVEL_FULL = 5;
+
+ /**
* Extra for {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}:
* Contains list of Bundles representing battery events
* @hide
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index c22f46cdc2b5..80546cd6770f 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -650,13 +650,13 @@ public final class BinderProxy implements IBinder {
* weakly referenced by JNI so the strong references here are needed to keep the callbacks
* around until the proxy is GC'ed.
*/
- private List<IFrozenStateChangeCallback> mFrozenStateChangeCallbacks =
+ private List<FrozenStateChangeCallback> mFrozenStateChangeCallbacks =
Collections.synchronizedList(new ArrayList<>());
/**
- * See {@link IBinder#addFrozenStateChangeCallback(IFrozenStateChangeCallback)}
+ * See {@link IBinder#addFrozenStateChangeCallback(FrozenStateChangeCallback)}
*/
- public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+ public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback)
throws RemoteException {
addFrozenStateChangeCallbackNative(callback);
mFrozenStateChangeCallbacks.add(callback);
@@ -665,16 +665,16 @@ public final class BinderProxy implements IBinder {
/**
* See {@link IBinder#removeFrozenStateChangeCallback}
*/
- public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+ public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback) {
mFrozenStateChangeCallbacks.remove(callback);
return removeFrozenStateChangeCallbackNative(callback);
}
- private native void addFrozenStateChangeCallbackNative(IFrozenStateChangeCallback callback)
+ private native void addFrozenStateChangeCallbackNative(FrozenStateChangeCallback callback)
throws RemoteException;
private native boolean removeFrozenStateChangeCallbackNative(
- IFrozenStateChangeCallback callback);
+ FrozenStateChangeCallback callback);
/**
* Perform a dump on the remote object
@@ -762,10 +762,9 @@ public final class BinderProxy implements IBinder {
}
private static void invokeFrozenStateChangeCallback(
- IFrozenStateChangeCallback callback, IBinder binderProxy, int stateIndex) {
+ FrozenStateChangeCallback callback, IBinder binderProxy, int stateIndex) {
try {
- callback.onFrozenStateChanged(binderProxy,
- IFrozenStateChangeCallback.State.values()[stateIndex]);
+ callback.onFrozenStateChanged(binderProxy, stateIndex);
} catch (RuntimeException exc) {
Log.w("BinderNative", "Uncaught exception from frozen state change callback",
exc);
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 8185e8e542e1..a997f4c86704 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -16,11 +16,15 @@
package android.os;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import java.io.FileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Base interface for a remotable object, the core part of a lightweight
@@ -377,9 +381,24 @@ public interface IBinder {
*/
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);
- /** @hide */
- interface IFrozenStateChangeCallback {
- enum State {FROZEN, UNFROZEN};
+ /**
+ * A callback interface for receiving frozen state change events.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ interface FrozenStateChangeCallback {
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"STATE_"}, value = {
+ STATE_FROZEN,
+ STATE_UNFROZEN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface State {
+ }
+
+ int STATE_FROZEN = 0;
+ int STATE_UNFROZEN = 1;
/**
* Interface for receiving a callback when the process hosting an IBinder
@@ -387,13 +406,13 @@ public interface IBinder {
* @param who The IBinder whose hosting process has changed state.
* @param state The latest state.
*/
- void onFrozenStateChanged(@NonNull IBinder who, State state);
+ void onFrozenStateChanged(@NonNull IBinder who, @State int state);
}
/**
- * {@link addFrozenStateChangeCallback} provides a callback mechanism to notify about process
- * frozen/unfrozen events. Upon registration and any subsequent state changes, the callback is
- * invoked with the latest process frozen state.
+ * This method provides a callback mechanism to notify about process frozen/unfrozen events.
+ * Upon registration and any subsequent state changes, the callback is invoked with the latest
+ * process frozen state.
*
* <p>If the listener process (the one using this API) is itself frozen, state change events
* might be combined into a single one with the latest frozen state. This single event would
@@ -410,19 +429,19 @@ public interface IBinder {
*
* <p>@throws {@link UnsupportedOperationException} if the kernel binder driver does not support
* this feature.
- * @hide
*/
- default void addFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback)
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ default void addFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback)
throws RemoteException {
throw new UnsupportedOperationException();
}
/**
- * Unregister a {@link IFrozenStateChangeCallback}. The callback will no longer be invoked when
+ * Unregister a {@link FrozenStateChangeCallback}. The callback will no longer be invoked when
* the hosting process changes its frozen state.
- * @hide
*/
- default boolean removeFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback) {
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ default boolean removeFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback) {
throw new UnsupportedOperationException();
}
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 39bd15c968d7..738d12978aed 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -153,6 +153,14 @@ flag {
}
flag {
+ name: "binder_frozen_state_change_callback"
+ is_exported: true
+ namespace: "system_performance"
+ description: "Guards the frozen state change callback API."
+ bug: "361157077"
+}
+
+flag {
name: "message_queue_tail_tracking"
namespace: "system_performance"
description: "track tail of message queue."
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e32625e1f7a8..0ada9934482c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -111,6 +111,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -3022,6 +3023,9 @@ public final class Settings {
/** @hide - Private call() method to query the 'configuration' table */
public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
+ /** @hide - Private call() method to query the 'configuration' tables' namespaces */
+ public static final String CALL_METHOD_LIST_NAMESPACES_CONFIG = "LIST_namespaces_config";
+
/** @hide - Private call() method to disable / re-enable syncs to the 'configuration' table */
public static final String CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG =
"SET_SYNC_DISABLED_MODE_config";
@@ -20458,6 +20462,10 @@ public final class Settings {
*
* The keys take the form {@code namespace/flag}, and the values are the flag values.
*
+ * Note: this API is _not_ performant, and may make a large number of
+ * Binder calls. It is intended for use in testing and debugging, and
+ * should not be used in performance-sensitive code.
+ *
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -20469,13 +20477,33 @@ public final class Settings {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, resolver.getUserId());
IContentProvider cp = sProviderHolder.getProvider(resolver);
- Bundle b = cp.call(resolver.getAttributionSource(),
- sProviderHolder.mUri.getAuthority(), CALL_METHOD_LIST_CONFIG, null, arg);
- if (b != null) {
- Map<String, String> flagsToValues =
- (HashMap) b.getSerializable(Settings.NameValueTable.VALUE,
- java.util.HashMap.class);
- allFlags.putAll(flagsToValues);
+
+ if (Flags.reduceBinderTransactionSizeForGetAllProperties()) {
+ Bundle b = cp.call(resolver.getAttributionSource(),
+ sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_LIST_NAMESPACES_CONFIG, null, arg);
+ if (b != null) {
+ HashSet<String> namespaces =
+ (HashSet) b.getSerializable(Settings.NameValueTable.VALUE,
+ java.util.HashSet.class);
+ for (String namespace : namespaces) {
+ Map<String, String> keyValues =
+ getStrings(namespace, new ArrayList());
+ for (String key : keyValues.keySet()) {
+ allFlags.put(namespace + "/" + key, keyValues.get(key));
+ }
+ }
+ }
+ } else {
+ Bundle b = cp.call(resolver.getAttributionSource(),
+ sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_LIST_CONFIG, null, arg);
+ if (b != null) {
+ Map<String, String> flagsToValues =
+ (HashMap) b.getSerializable(Settings.NameValueTable.VALUE,
+ java.util.HashMap.class);
+ allFlags.putAll(flagsToValues);
+ }
}
} catch (RemoteException e) {
Log.w(TAG, "Can't query configuration table for " + CONTENT_URI, e);
diff --git a/core/java/android/provider/flags.aconfig b/core/java/android/provider/flags.aconfig
index 5c0f8737ca27..4c636735b5ce 100644
--- a/core/java/android/provider/flags.aconfig
+++ b/core/java/android/provider/flags.aconfig
@@ -52,3 +52,14 @@ flag {
description: "Enable the new ContactsContract Default Account APIs."
bug: "359957527"
}
+
+flag {
+ name: "reduce_binder_transaction_size_for_get_all_properties"
+ namespace: "core_experiments_team_internal"
+ description: "Reduce Binder transaction size in getAllProperties calls"
+ bug: "362652574"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
index c2ad508c2b44..ca887646b3aa 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/text/ClientFlags.java
@@ -16,21 +16,14 @@
package android.text;
-import com.android.text.flags.Flags;
-
/**
* An aconfig feature flags that can be accessible from application process without
* ContentProvider IPCs.
*
* When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
*
+ * TODO(nona): Remove this class.
* @hide
*/
public class ClientFlags {
- /**
- * @see Flags#fixMisalignedContextMenu()
- */
- public static boolean fixMisalignedContextMenu() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU);
- }
}
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 076721f629ed..f69a333ff81f 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -19,11 +19,10 @@ package android.text;
import android.annotation.NonNull;
import android.app.AppGlobals;
-import com.android.text.flags.Flags;
-
/**
* Flags in the "text" namespace.
*
+ * TODO(nona): Remove this class.
* @hide
*/
public final class TextFlags {
@@ -55,7 +54,6 @@ public final class TextFlags {
* List of text flags to be transferred to the application process.
*/
public static final String[] TEXT_ACONFIGS_FLAGS = {
- Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU,
};
/**
@@ -64,7 +62,6 @@ public final class TextFlags {
* The order must be the same to the TEXT_ACONFIG_FLAGS.
*/
public static final boolean[] TEXT_ACONFIG_DEFAULT_VALUE = {
- Flags.fixMisalignedContextMenu(),
};
/**
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 3c61f4f5a33c..c83285a5c889 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -84,27 +84,6 @@ flag {
}
flag {
- name: "fix_font_update_failure"
- namespace: "text"
- description: "There was a bug of updating system font from Android 13 to 14. This flag for fixing the migration failure."
- is_fixed_read_only: true
- bug: "331717791"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "fix_misaligned_context_menu"
- namespace: "text"
- description: "Fix the context menu misalignment and incosistent icon size."
- bug: "332542108"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "missing_getter_apis"
namespace: "text"
description: "Fix the lint warning about missing getters."
@@ -154,26 +133,6 @@ flag {
}
flag {
- name: "portuguese_hyphenator"
- namespace: "text"
- description: "Portuguese taiored hyphenator"
- bug: "344656282"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "dont_break_email_in_nobreak_tag"
- namespace: "text"
- description: "Prevent line break inside email."
- bug: "350691716"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "handwriting_gesture_with_transformation"
namespace: "text"
description: "Fix handwriting gesture is not working when view has transformation."
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 6b4340a02edc..15a4715bd059 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -38,6 +38,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -55,7 +56,6 @@ import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.Objects;
@@ -146,7 +146,7 @@ public class InsetsState implements Parcelable {
forceConsumingTypes |= type;
}
- if (Flags.enableCaptionCompatInsetForceConsumptionAlways()
+ if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isEnabled()
&& (flags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) {
forceConsumingOpaqueCaptionBar = true;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9ff503171a3f..33e79059c7e5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -125,11 +125,11 @@ import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
+import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme;
import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay;
-import static com.android.window.flags.Flags.enableCaptionCompatInsetForceConsumption;
import static com.android.window.flags.Flags.insetsControlChangedItem;
import static com.android.window.flags.Flags.insetsControlSeq;
import static com.android.window.flags.Flags.setScPropertiesInClient;
@@ -3214,10 +3214,10 @@ public final class ViewRootImpl implements ViewParent,
typesToShow |= Type.navigationBars();
}
if (captionIsHiddenByFlags && !captionWasHiddenByFlags
- && enableCaptionCompatInsetForceConsumption()) {
+ && ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()) {
typesToHide |= Type.captionBar();
} else if (!captionIsHiddenByFlags && captionWasHiddenByFlags
- && enableCaptionCompatInsetForceConsumption()) {
+ && ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()) {
typesToShow |= Type.captionBar();
}
if (typesToHide != 0) {
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index b9751c8cab52..d90455ab971d 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -920,7 +920,8 @@ public interface ImeTracker {
final Configuration.Builder builder = Configuration.Builder.withSurface(
cujType,
jankContext.getDisplayContext(),
- jankContext.getTargetSurfaceControl())
+ jankContext.getTargetSurfaceControl(),
+ jankContext.getDisplayContext().getMainThreadHandler())
.setTag(String.format(Locale.US, "%d@%d@%s", animType,
useSeparatedThread ? 0 : 1, jankContext.getHostPackageName()));
InteractionJankMonitor.getInstance().begin(builder);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e1154ca0701c..06820cd4c2ce 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1003,6 +1003,55 @@ public class RemoteViews implements Parcelable, Filter {
public int getActionTag() {
return SET_EMPTY_VIEW_ACTION_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.SET_EMPTY_VIEW_ACTION);
+ out.write(RemoteViewsProto.SetEmptyViewAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.SET_EMPTY_VIEW_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.SetEmptyViewAction.VIEW_ID:
+ values.put(RemoteViewsProto.SetEmptyViewAction.VIEW_ID,
+ in.readString(RemoteViewsProto.SetEmptyViewAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID:
+ values.put(RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID,
+ in.readString(RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.SetEmptyViewAction.VIEW_ID,
+ RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.SetEmptyViewAction.VIEW_ID);
+ int emptyViewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID);
+ return new SetEmptyView(viewId, emptyViewId);
+ };
+ }
}
private static class SetPendingIntentTemplate extends Action {
@@ -1243,6 +1292,68 @@ public class RemoteViews implements Parcelable, Filter {
mItems.visitUris(visitor);
}
+
+ @Override
+ public boolean canWriteToProto() {
+ // Skip actions that do not contain items (intent only actions)
+ return mItems != null;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ if (mItems == null) return;
+ final long token = out.start(
+ RemoteViewsProto.Action.SET_REMOTE_COLLECTION_ITEM_LIST_ADAPTER_ACTION);
+ out.write(RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ final long itemsToken = out.start(
+ RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS);
+ mItems.writeToProto(context, out, /* attached= */ true);
+ out.end(itemsToken);
+ out.end(token);
+ }
+ }
+
+ private PendingResources<Action> createSetRemoteCollectionItemListAdapterActionFromProto(
+ ProtoInputStream in) throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(
+ RemoteViewsProto.Action.SET_REMOTE_COLLECTION_ITEM_LIST_ADAPTER_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID:
+ values.put(RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID,
+ in.readString(
+ RemoteViewsProto
+ .SetRemoteCollectionItemListAdapterAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS:
+ final long itemsToken = in.start(
+ RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS);
+ values.put(RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS,
+ RemoteCollectionItems.createFromProto(in));
+ in.end(itemsToken);
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values,
+ new long[]{RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID,
+ RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID);
+ return new SetRemoteCollectionItemListAdapterAction(viewId,
+ ((PendingResources<RemoteCollectionItems>) values.get(
+ RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS))
+ .create(context, resources, rootData, depth));
+ };
}
/**
@@ -2036,6 +2147,68 @@ public class RemoteViews implements Parcelable, Filter {
public int getActionTag() {
return SET_DRAWABLE_TINT_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.SET_DRAWABLE_TINT_ACTION);
+ out.write(RemoteViewsProto.SetDrawableTintAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER, mColorFilter);
+ out.write(RemoteViewsProto.SetDrawableTintAction.FILTER_MODE,
+ PorterDuff.modeToInt(mFilterMode));
+ out.write(RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND, mTargetBackground);
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.SET_DRAWABLE_TINT_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.SetDrawableTintAction.VIEW_ID:
+ values.put(RemoteViewsProto.SetDrawableTintAction.VIEW_ID,
+ in.readString(RemoteViewsProto.SetDrawableTintAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND:
+ values.put(RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND,
+ in.readBoolean(
+ RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND));
+ break;
+ case (int) RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER:
+ values.put(RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER,
+ in.readInt(RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER));
+ break;
+ case (int) RemoteViewsProto.SetDrawableTintAction.FILTER_MODE:
+ values.put(RemoteViewsProto.SetDrawableTintAction.FILTER_MODE,
+ PorterDuff.intToMode(in.readInt(
+ RemoteViewsProto.SetDrawableTintAction.FILTER_MODE)));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.SetDrawableTintAction.VIEW_ID});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.SetDrawableTintAction.VIEW_ID);
+ return new SetDrawableTint(viewId, (boolean) values.get(
+ RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND, false),
+ (int) values.get(RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER, 0),
+ (PorterDuff.Mode) values.get(
+ RemoteViewsProto.SetDrawableTintAction.FILTER_MODE));
+ };
+ }
}
/**
@@ -2047,7 +2220,7 @@ public class RemoteViews implements Parcelable, Filter {
* target {@link View#getBackground()}.
* <p>
*/
- private class SetRippleDrawableColor extends Action {
+ private static class SetRippleDrawableColor extends Action {
ColorStateList mColorStateList;
SetRippleDrawableColor(@IdRes int id, ColorStateList colorStateList) {
@@ -2082,6 +2255,58 @@ public class RemoteViews implements Parcelable, Filter {
public int getActionTag() {
return SET_RIPPLE_DRAWABLE_COLOR_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.SET_RIPPLE_DRAWABLE_COLOR_ACTION);
+ out.write(RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ writeColorStateListToProto(out, mColorStateList,
+ RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST);
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.SET_RIPPLE_DRAWABLE_COLOR_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID:
+ values.put(RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID,
+ in.readString(
+ RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST:
+ values.put(RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST,
+ createColorStateListFromProto(in,
+ RemoteViewsProto
+ .SetRippleDrawableColorAction.COLOR_STATE_LIST));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values,
+ new long[]{RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID,
+ RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID);
+ return new SetRippleDrawableColor(viewId, (ColorStateList) values.get(
+ RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST));
+ };
+ }
}
/**
@@ -2987,6 +3212,82 @@ public class RemoteViews implements Parcelable, Filter {
public int getActionTag() {
return RESOURCE_REFLECTION_ACTION_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.RESOURCE_REFLECTION_ACTION);
+ out.write(RemoteViewsProto.ResourceReflectionAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.ResourceReflectionAction.METHOD_NAME, mMethodName);
+ out.write(RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE, mType);
+ out.write(RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE, mResourceType);
+ if (mResId != 0) {
+ out.write(RemoteViewsProto.ResourceReflectionAction.RES_ID,
+ appResources.getResourceName(mResId));
+ }
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.RESOURCE_REFLECTION_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.ResourceReflectionAction.VIEW_ID:
+ values.put(RemoteViewsProto.ResourceReflectionAction.VIEW_ID,
+ in.readString(RemoteViewsProto.ResourceReflectionAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.ResourceReflectionAction.METHOD_NAME:
+ values.put(RemoteViewsProto.ResourceReflectionAction.METHOD_NAME,
+ in.readString(
+ RemoteViewsProto.ResourceReflectionAction.METHOD_NAME));
+ break;
+ case (int) RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE:
+ values.put(RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE,
+ in.readInt(
+ RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE));
+ break;
+ case (int) RemoteViewsProto.ResourceReflectionAction.RES_ID:
+ values.put(RemoteViewsProto.ResourceReflectionAction.RES_ID,
+ in.readString(RemoteViewsProto.ResourceReflectionAction.RES_ID));
+ break;
+ case (int) RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE:
+ values.put(RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE,
+ in.readInt(
+ RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.ResourceReflectionAction.VIEW_ID,
+ RemoteViewsProto.ResourceReflectionAction.METHOD_NAME,
+ RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.ResourceReflectionAction.VIEW_ID);
+
+ int resId = (values.indexOfKey(RemoteViewsProto.ResourceReflectionAction.RES_ID)
+ >= 0) ? getAsIdentifier(resources, values,
+ RemoteViewsProto.ResourceReflectionAction.RES_ID) : 0;
+ return new ResourceReflectionAction(viewId,
+ (String) values.get(RemoteViewsProto.ResourceReflectionAction.METHOD_NAME),
+ (int) values.get(RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE),
+ (int) values.get(RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE,
+ 0), resId);
+ };
+ }
}
private static final class AttributeReflectionAction extends BaseReflectionAction {
@@ -4593,6 +4894,61 @@ public class RemoteViews implements Parcelable, Filter {
public int getActionTag() {
return SET_INT_TAG_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.SET_INT_TAG_ACTION);
+ out.write(RemoteViewsProto.SetIntTagAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.SetIntTagAction.KEY,
+ appResources.getResourceName(mKey)); // rebase
+ out.write(RemoteViewsProto.SetIntTagAction.TAG, mTag);
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.SET_INT_TAG_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.SetIntTagAction.VIEW_ID:
+ values.put(RemoteViewsProto.SetIntTagAction.VIEW_ID,
+ in.readString(RemoteViewsProto.SetIntTagAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.SetIntTagAction.KEY:
+ values.put(RemoteViewsProto.SetIntTagAction.KEY,
+ in.readString(RemoteViewsProto.SetIntTagAction.KEY));
+ break;
+ case (int) RemoteViewsProto.SetIntTagAction.TAG:
+ values.put(RemoteViewsProto.SetIntTagAction.TAG,
+ in.readInt(RemoteViewsProto.SetIntTagAction.TAG));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.SetIntTagAction.VIEW_ID,
+ RemoteViewsProto.SetIntTagAction.KEY});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.SetIntTagAction.VIEW_ID);
+ int keyId = getAsIdentifier(resources, values,
+ RemoteViewsProto.SetIntTagAction.KEY);
+ return new SetIntTagAction(viewId, keyId,
+ (int) values.get(RemoteViewsProto.SetIntTagAction.TAG, 0));
+ };
+ }
}
private static class SetCompoundButtonCheckedAction extends Action {
@@ -4643,6 +4999,56 @@ public class RemoteViews implements Parcelable, Filter {
public int getActionTag() {
return SET_COMPOUND_BUTTON_CHECKED_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(
+ RemoteViewsProto.Action.SET_COMPOUND_BUTTON_CHECKED_ACTION);
+ out.write(RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED, mChecked);
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.SET_COMPOUND_BUTTON_CHECKED_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID:
+ values.put(RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID,
+ in.readString(
+ RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED:
+ values.put(RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED,
+ in.readBoolean(
+ RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values,
+ new long[]{RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID);
+ return new SetCompoundButtonCheckedAction(viewId, (boolean) values.get(
+ RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED, false));
+ };
+ }
}
private static class SetRadioGroupCheckedAction extends Action {
@@ -4707,6 +5113,61 @@ public class RemoteViews implements Parcelable, Filter {
public int getActionTag() {
return SET_RADIO_GROUP_CHECKED;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.SET_RADIO_GROUP_CHECKED_ACTION);
+ out.write(RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ if (mCheckedId != -1) {
+ out.write(RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID,
+ appResources.getResourceName(mCheckedId));
+ }
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.SET_RADIO_GROUP_CHECKED_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID:
+ values.put(RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID,
+ in.readString(RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID:
+ values.put(RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID,
+ in.readString(
+ RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values,
+ new long[]{RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID);
+
+ int checkedId = (values.indexOfKey(
+ RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID) >= 0)
+ ? getAsIdentifier(resources, values,
+ RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID) : -1;
+ return new SetRadioGroupCheckedAction(viewId, checkedId);
+ };
+ }
}
private static class SetViewOutlinePreferredRadiusAction extends Action {
@@ -8450,6 +8911,7 @@ public class RemoteViews implements Parcelable, Filter {
public static PendingResources<RemoteCollectionItems> createFromProto(ProtoInputStream in)
throws Exception {
final LongSparseArray<Object> values = new LongSparseArray<>();
+
values.put(RemoteViewsProto.RemoteCollectionItems.IDS, new ArrayList<Long>());
values.put(RemoteViewsProto.RemoteCollectionItems.VIEWS,
new ArrayList<PendingResources<RemoteViews>>());
@@ -9207,6 +9669,22 @@ public class RemoteViews implements Parcelable, Filter {
return ReflectionAction.createFromProto(in);
case (int) RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION:
return RemoveFromParentAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.RESOURCE_REFLECTION_ACTION:
+ return ResourceReflectionAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.SET_COMPOUND_BUTTON_CHECKED_ACTION:
+ return SetCompoundButtonCheckedAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.SET_DRAWABLE_TINT_ACTION:
+ return SetDrawableTint.createFromProto(in);
+ case (int) RemoteViewsProto.Action.SET_EMPTY_VIEW_ACTION:
+ return SetEmptyView.createFromProto(in);
+ case (int) RemoteViewsProto.Action.SET_INT_TAG_ACTION:
+ return SetIntTagAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.SET_RADIO_GROUP_CHECKED_ACTION:
+ return SetRadioGroupCheckedAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.SET_REMOTE_COLLECTION_ITEM_LIST_ADAPTER_ACTION:
+ return rv.createSetRemoteCollectionItemListAdapterActionFromProto(in);
+ case (int) RemoteViewsProto.Action.SET_RIPPLE_DRAWABLE_COLOR_ACTION:
+ return SetRippleDrawableColor.createFromProto(in);
default:
throw new RuntimeException("Unhandled field while reading Action proto!\n"
+ ProtoUtils.currentFieldToString(in));
diff --git a/core/java/android/window/flags/DesktopModeFlags.java b/core/java/android/window/flags/DesktopModeFlags.java
index 5c53d66e49fe..701b6be06e72 100644
--- a/core/java/android/window/flags/DesktopModeFlags.java
+++ b/core/java/android/window/flags/DesktopModeFlags.java
@@ -17,7 +17,9 @@
package android.window.flags;
import android.annotation.Nullable;
-import android.content.Context;
+import android.app.ActivityThread;
+import android.app.Application;
+import android.content.ContentResolver;
import android.provider.Settings;
import android.util.Log;
@@ -39,9 +41,13 @@ import java.util.function.Supplier;
*/
public enum DesktopModeFlags {
// All desktop mode related flags to be overridden by developer option toggle will be added here
- DESKTOP_WINDOWING_MODE(
+ ENABLE_DESKTOP_WINDOWING_MODE(
Flags::enableDesktopWindowingMode, /* shouldOverrideByDevOption= */ true),
- DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false);
+ ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false),
+ ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION(
+ Flags::enableCaptionCompatInsetForceConsumption, true),
+ ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS(
+ Flags::enableCaptionCompatInsetForceConsumptionAlways, true);
private static final String TAG = "DesktopModeFlagsUtil";
// Function called to obtain aconfig flag value.
@@ -62,14 +68,15 @@ public enum DesktopModeFlags {
* Determines state of flag based on the actual flag and desktop mode developer option
* overrides.
*/
- public boolean isEnabled(Context context) {
+ public boolean isEnabled() {
+ Application application = ActivityThread.currentApplication();
if (!Flags.showDesktopWindowingDevOption()
|| !mShouldOverrideByDevOption
- || context.getContentResolver() == null) {
+ || application == null) {
return mFlagFunction.get();
} else {
boolean shouldToggleBeEnabledByDefault = Flags.enableDesktopWindowingMode();
- return switch (getToggleOverride(context)) {
+ return switch (getToggleOverride(application.getContentResolver())) {
case OVERRIDE_UNSET -> mFlagFunction.get();
// When toggle override matches its default state, don't override flags. This
// helps users reset their feature overrides.
@@ -79,14 +86,14 @@ public enum DesktopModeFlags {
}
}
- private ToggleOverride getToggleOverride(Context context) {
+ private ToggleOverride getToggleOverride(ContentResolver contentResolver) {
// If cached, return it
if (sCachedToggleOverride != null) {
return sCachedToggleOverride;
}
// Otherwise, fetch and cache it
- ToggleOverride override = getToggleOverrideFromSystem(context);
+ ToggleOverride override = getToggleOverrideFromSystem(contentResolver);
sCachedToggleOverride = override;
Log.d(TAG, "Toggle override initialized to: " + override);
return override;
@@ -95,9 +102,9 @@ public enum DesktopModeFlags {
/**
* Returns {@link ToggleOverride} from Settings.Global set by toggle.
*/
- private ToggleOverride getToggleOverrideFromSystem(Context context) {
+ private ToggleOverride getToggleOverrideFromSystem(ContentResolver contentResolver) {
int settingValue = Settings.Global.getInt(
- context.getContentResolver(),
+ contentResolver,
Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
ToggleOverride.OVERRIDE_UNSET.getSetting()
);
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index c7e1fba66d7f..ef08e49ce6d9 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -330,9 +330,10 @@ public class InteractionJankMonitor {
* @param cujType the specific {@link Cuj.CujType}.
* @return boolean true if the tracker is started successfully, false otherwise.
*/
- public boolean begin(SurfaceControl surface, Context context, @Cuj.CujType int cujType) {
+ public boolean begin(SurfaceControl surface, Context context, Handler handler,
+ @Cuj.CujType int cujType) {
try {
- return begin(Configuration.Builder.withSurface(cujType, context, surface));
+ return begin(Configuration.Builder.withSurface(cujType, context, surface, handler));
} catch (IllegalArgumentException ex) {
Log.d(TAG, "Build configuration failed!", ex);
return false;
@@ -348,11 +349,12 @@ public class InteractionJankMonitor {
* @param tag a tag containing extra information about the interaction.
* @return boolean true if the tracker is started successfully, false otherwise.
*/
- public boolean begin(SurfaceControl surface, Context context, @Cuj.CujType int cujType,
+ public boolean begin(SurfaceControl surface, Context context, Handler handler,
+ @Cuj.CujType int cujType,
String tag) {
try {
final Configuration.Builder builder =
- Configuration.Builder.withSurface(cujType, context, surface);
+ Configuration.Builder.withSurface(cujType, context, surface, handler);
if (!TextUtils.isEmpty(tag)) {
builder.setTag(tag);
}
@@ -689,20 +691,23 @@ public class InteractionJankMonitor {
private SurfaceControl mAttrSurfaceControl;
private final @Cuj.CujType int mAttrCujType;
private boolean mAttrDeferMonitor = true;
+ private Handler mHandler = null;
/**
* Creates a builder which instruments only surface.
* @param cuj The enum defined in {@link Cuj.CujType}.
* @param context context
* @param surfaceControl surface control
+ * @param uiThreadHandler UI thread for that surface
* @return builder
*/
public static Builder withSurface(@Cuj.CujType int cuj, @NonNull Context context,
- @NonNull SurfaceControl surfaceControl) {
+ @NonNull SurfaceControl surfaceControl, @NonNull Handler uiThreadHandler) {
return new Builder(cuj)
.setContext(context)
.setSurfaceControl(surfaceControl)
- .setSurfaceOnly(true);
+ .setSurfaceOnly(true)
+ .setHandler(uiThreadHandler);
}
/**
@@ -722,6 +727,18 @@ public class InteractionJankMonitor {
}
/**
+ * Specifies the UI thread handler. If not provided, the View's one will be used.
+ * If only a surface is provided without handler, the app main thread will be used.
+ *
+ * @param uiThreadHandler handler associated to the cuj UI thread
+ * @return builder
+ */
+ public Builder setHandler(Handler uiThreadHandler) {
+ mHandler = uiThreadHandler;
+ return this;
+ }
+
+ /**
* Specifies a view, must be set if {@link #setSurfaceOnly(boolean)} is set to false.
* @param view an attached view
* @return builder
@@ -798,13 +815,13 @@ public class InteractionJankMonitor {
return new Configuration(
mAttrCujType, mAttrView, mAttrTag, mAttrTimeout,
mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl,
- mAttrDeferMonitor);
+ mAttrDeferMonitor, mHandler);
}
}
private Configuration(@Cuj.CujType int cuj, View view, @NonNull String tag, long timeout,
boolean surfaceOnly, Context context, SurfaceControl surfaceControl,
- boolean deferMonitor) {
+ boolean deferMonitor, Handler handler) {
mCujType = cuj;
mTag = tag;
mSessionName = generateSessionName(Cuj.getNameOfCuj(cuj), tag);
@@ -816,8 +833,16 @@ public class InteractionJankMonitor {
: (view != null ? view.getContext().getApplicationContext() : null);
mSurfaceControl = surfaceControl;
mDeferMonitor = deferMonitor;
+ if (handler != null) {
+ mHandler = handler;
+ } else if (mSurfaceOnly) {
+ Log.w(TAG, "No UIThread provided for " + mSessionName
+ + " (surface only). Defaulting to app main thread.");
+ mHandler = mContext.getMainThreadHandler();
+ } else {
+ mHandler = mView.getHandler();
+ }
validate();
- mHandler = mSurfaceOnly ? mContext.getMainThreadHandler() : mView.getHandler();
}
@VisibleForTesting
@@ -858,6 +883,12 @@ public class InteractionJankMonitor {
shouldThrow = true;
msg.append("Must pass in a valid surface control if only instrument surface; ");
}
+ if (mHandler == null) {
+ shouldThrow = true;
+ msg.append(
+ "Must pass a UI thread handler when only a surface control is "
+ + "provided.");
+ }
} else {
if (!hasValidView()) {
shouldThrow = true;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 4708be8108c2..84dfc497dc84 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -38,6 +38,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATIO
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.flags.Flags.customizableWindowHeaders;
+import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
+import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
@@ -114,7 +116,6 @@ import com.android.internal.view.menu.MenuHelper;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
-import com.android.window.flags.Flags;
import java.util.List;
import java.util.concurrent.Executor;
@@ -1217,14 +1218,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
final boolean hideCaptionBar = fullscreen
|| (requestedVisibleTypes & WindowInsets.Type.captionBar()) == 0;
- final boolean consumingCaptionBar = Flags.enableCaptionCompatInsetForceConsumption()
- && ((mLastForceConsumingTypes & WindowInsets.Type.captionBar()) != 0
+ final boolean consumingCaptionBar =
+ ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()
+ && ((mLastForceConsumingTypes & WindowInsets.Type.captionBar()) != 0
&& hideCaptionBar);
final boolean isOpaqueCaptionBar = customizableWindowHeaders()
&& (appearance & APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) == 0;
final boolean consumingOpaqueCaptionBar =
- Flags.enableCaptionCompatInsetForceConsumptionAlways()
+ ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isEnabled()
&& mLastForceConsumingOpaqueCaptionBar
&& isOpaqueCaptionBar;
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index e440dc9053fd..4db9ddf128da 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -223,17 +223,17 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
"ServiceManager returned a null ProtoLog Configuration Service");
try {
- var args = new ProtoLogConfigurationService.RegisterClientArgs();
+ var args = new ProtoLogConfigurationServiceImpl.RegisterClientArgs();
if (viewerConfigFilePath != null) {
args.setViewerConfigFile(viewerConfigFilePath);
}
final var groupArgs = Stream.of(groups)
- .map(group -> new ProtoLogConfigurationService.RegisterClientArgs
+ .map(group -> new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(group.name(), group.isLogToLogcat()))
- .toArray(
- ProtoLogConfigurationService.RegisterClientArgs.GroupConfig[]::new);
+ .toArray(ProtoLogConfigurationServiceImpl
+ .RegisterClientArgs.GroupConfig[]::new);
args.setGroups(groupArgs);
mProtoLogConfigurationService.registerClient(this, args);
diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
index 7031d694f09c..d65aaae7deaa 100644
--- a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
+++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
@@ -16,434 +16,32 @@
package com.android.internal.protolog;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.TAG;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
-
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemService;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
-import android.tracing.perfetto.DataSourceParams;
-import android.tracing.perfetto.InitArguments;
-import android.tracing.perfetto.Producer;
-import android.util.Log;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-/**
- * The ProtoLog service is responsible for orchestrating centralized actions of the protolog tracing
- * system. Currently this service has the following roles:
- * - Handle shell commands to toggle logging ProtoLog messages for specified groups to logcat.
- * - Handle viewer config dumping (the mapping from message hash to message string) for all protolog
- * clients. This is for two reasons: firstly, because client processes might be frozen so might
- * not response to the request to dump their viewer config when the trace is stopped; secondly,
- * multiple processes might be running the same code with the same viewer config, this centralized
- * service ensures we don't dump the same viewer config multiple times across processes.
- * <p>
- * {@link com.android.internal.protolog.IProtoLogClient ProtoLog clients} register themselves to
- * this service on initialization.
- * <p>
- * This service is intended to run on the system server, such that it never gets frozen.
- */
-@SystemService(Context.PROTOLOG_CONFIGURATION_SERVICE)
-public final class ProtoLogConfigurationService extends IProtoLogConfigurationService.Stub {
- private static final String LOG_TAG = "ProtoLogConfigurationService";
-
- private final ProtoLogDataSource mDataSource;
-
- /**
- * Keeps track of how many of each viewer config file is currently registered.
- * Use to keep track of which viewer config files are actively being used in tracing and might
- * need to be dumped on flush.
- */
- private final Map<String, Integer> mConfigFileCounts = new HashMap<>();
- /**
- * Keeps track of the viewer config file of each client if available.
- */
- private final Map<IProtoLogClient, String> mClientConfigFiles = new HashMap<>();
-
- /**
- * Keeps track of all the protolog groups that have been registered by clients and are still
- * being actively traced.
- */
- private final Set<String> mRegisteredGroups = new HashSet<>();
- /**
- * Keeps track of all the clients that are actively tracing a given protolog group.
- */
- private final Map<String, Set<IProtoLogClient>> mGroupToClients = new HashMap<>();
-
- /**
- * Keeps track of whether or not a given group should be logged to logcat.
- * True when logging to logcat, false otherwise.
- */
- private final Map<String, Boolean> mLogGroupToLogcatStatus = new TreeMap<>();
-
- /**
- * Keeps track of all the tracing instance ids that are actively running for ProtoLog.
- */
- private final Set<Integer> mRunningInstances = new HashSet<>();
-
- private final ViewerConfigFileTracer mViewerConfigFileTracer;
-
- public ProtoLogConfigurationService() {
- this(ProtoLogDataSource::new, ProtoLogConfigurationService::dumpTransitionTraceConfig);
- }
-
- @VisibleForTesting
- public ProtoLogConfigurationService(@NonNull ProtoLogDataSourceBuilder dataSourceBuilder) {
- this(dataSourceBuilder, ProtoLogConfigurationService::dumpTransitionTraceConfig);
- }
-
- @VisibleForTesting
- public ProtoLogConfigurationService(@NonNull ViewerConfigFileTracer tracer) {
- this(ProtoLogDataSource::new, tracer);
- }
-
- @VisibleForTesting
- public ProtoLogConfigurationService(
- @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
- @NonNull ViewerConfigFileTracer tracer) {
- mDataSource = dataSourceBuilder.build(
- this::onTracingInstanceStart,
- this::onTracingInstanceFlush,
- this::onTracingInstanceStop
- );
-
- // Initialize the Perfetto producer and register the Perfetto ProtoLog datasource to be
- // receive the lifecycle callbacks of the datasource and write the viewer configs if and
- // when required to the datasource.
- Producer.init(InitArguments.DEFAULTS);
- final var params = new DataSourceParams.Builder()
- .setBufferExhaustedPolicy(DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
- .build();
- mDataSource.register(params);
-
- mViewerConfigFileTracer = tracer;
- }
-
- public static class RegisterClientArgs extends IRegisterClientArgs.Stub {
- /**
- * The viewer config file to be registered for this client ProtoLog process.
- */
- @Nullable
- private String mViewerConfigFile = null;
- /**
- * The list of all groups that this client protolog process supports and might trace.
- */
- @NonNull
- private String[] mGroups = new String[0];
- /**
- * The default logcat status of the ProtoLog client. True is logging to logcat, false
- * otherwise. The indices should match the indices in {@link mGroups}.
- */
- @NonNull
- private boolean[] mLogcatStatus = new boolean[0];
-
- public record GroupConfig(@NonNull String group, boolean logToLogcat) {}
-
- /**
- * Specify groups to register with this client that will be used for protologging in this
- * process.
- * @param groups to register with this client.
- * @return self
- */
- public RegisterClientArgs setGroups(GroupConfig... groups) {
- mGroups = new String[groups.length];
- mLogcatStatus = new boolean[groups.length];
-
- for (int i = 0; i < groups.length; i++) {
- mGroups[i] = groups[i].group;
- mLogcatStatus[i] = groups[i].logToLogcat;
- }
-
- return this;
- }
-
- /**
- * Set the viewer config file that the logs in this process are using.
- * @param viewerConfigFile The file path of the viewer config.
- * @return self
- */
- public RegisterClientArgs setViewerConfigFile(@NonNull String viewerConfigFile) {
- mViewerConfigFile = viewerConfigFile;
-
- return this;
- }
-
- @Override
- @NonNull
- public String[] getGroups() {
- return mGroups;
- }
-
- @Override
- @NonNull
- public boolean[] getGroupsDefaultLogcatStatus() {
- return mLogcatStatus;
- }
-
- @Nullable
- @Override
- public String getViewerConfigFile() {
- return mViewerConfigFile;
- }
- }
-
- @FunctionalInterface
- public interface ViewerConfigFileTracer {
- /**
- * Write the viewer config data to the trace buffer.
- *
- * @param dataSource The target datasource to write the viewer config to.
- * @param viewerConfigFilePath The path of the viewer config file which contains the data we
- * want to write to the trace buffer.
- * @throws FileNotFoundException if the viewerConfigFilePath is invalid.
- */
- void trace(@NonNull ProtoLogDataSource dataSource, @NonNull String viewerConfigFilePath);
- }
-
- @Override
- public void registerClient(@NonNull IProtoLogClient client, @NonNull IRegisterClientArgs args)
- throws RemoteException {
- client.asBinder().linkToDeath(() -> onClientBinderDeath(client), /* flags */ 0);
-
- final String viewerConfigFile = args.getViewerConfigFile();
- if (viewerConfigFile != null) {
- registerViewerConfigFile(client, viewerConfigFile);
- }
-
- registerGroups(client, args.getGroups(), args.getGroupsDefaultLogcatStatus());
- }
-
- @Override
- public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
- @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback,
- @NonNull ResultReceiver resultReceiver) throws RemoteException {
- new ProtoLogCommandHandler(this)
- .exec(this, in, out, err, args, callback, resultReceiver);
- }
+public interface ProtoLogConfigurationService extends IProtoLogConfigurationService {
/**
* Get the list of groups clients have registered to the protolog service.
* @return The list of ProtoLog groups registered with this service.
*/
@NonNull
- public String[] getGroups() {
- return mRegisteredGroups.toArray(new String[0]);
- }
+ String[] getGroups();
+
+ /**
+ * Check if a group is logging to logcat
+ * @param group The group we want to check for
+ * @return True iff we are logging this group to logcat.
+ */
+ boolean isLoggingToLogcat(@NonNull String group);
/**
* Enable logging target groups to logcat.
* @param groups we want to enable logging them to logcat for.
*/
- public void enableProtoLogToLogcat(String... groups) {
- toggleProtoLogToLogcat(true, groups);
- }
+ void enableProtoLogToLogcat(@NonNull String... groups);
/**
* Disable logging target groups to logcat.
* @param groups we want to disable from being logged to logcat.
*/
- public void disableProtoLogToLogcat(String... groups) {
- toggleProtoLogToLogcat(false, groups);
- }
-
- /**
- * Check if a group is logging to logcat
- * @param group The group we want to check for
- * @return True iff we are logging this group to logcat.
- */
- public boolean isLoggingToLogcat(@NonNull String group) {
- final Boolean isLoggingToLogcat = mLogGroupToLogcatStatus.get(group);
-
- if (isLoggingToLogcat == null) {
- throw new RuntimeException(
- "Trying to get logcat logging status of non-registered group " + group);
- }
-
- return isLoggingToLogcat;
- }
-
- private void registerViewerConfigFile(
- @NonNull IProtoLogClient client, @NonNull String viewerConfigFile) {
- final var count = mConfigFileCounts.getOrDefault(viewerConfigFile, 0);
- mConfigFileCounts.put(viewerConfigFile, count + 1);
- mClientConfigFiles.put(client, viewerConfigFile);
- }
-
- private void registerGroups(@NonNull IProtoLogClient client, @NonNull String[] groups,
- @NonNull boolean[] logcatStatuses) throws RemoteException {
- if (groups.length != logcatStatuses.length) {
- throw new RuntimeException(
- "Expected groups and logcatStatuses to have the same length, "
- + "but groups has length " + groups.length
- + " and logcatStatuses has length " + logcatStatuses.length);
- }
-
- for (int i = 0; i < groups.length; i++) {
- String group = groups[i];
- boolean logcatStatus = logcatStatuses[i];
-
- mRegisteredGroups.add(group);
-
- mGroupToClients.putIfAbsent(group, new HashSet<>());
- mGroupToClients.get(group).add(client);
-
- if (!mLogGroupToLogcatStatus.containsKey(group)) {
- mLogGroupToLogcatStatus.put(group, logcatStatus);
- }
-
- boolean requestedLogToLogcat = mLogGroupToLogcatStatus.get(group);
- if (requestedLogToLogcat != logcatStatus) {
- client.toggleLogcat(requestedLogToLogcat, new String[] { group });
- }
- }
- }
-
- private void toggleProtoLogToLogcat(boolean enabled, @NonNull String[] groups) {
- final var clientToGroups = new HashMap<IProtoLogClient, Set<String>>();
-
- for (String group : groups) {
- final var clients = mGroupToClients.get(group);
-
- if (clients == null) {
- // No clients associated to this group
- Log.w(LOG_TAG, "Attempting to toggle log to logcat for group " + group
- + " with no registered clients.");
- continue;
- }
-
- for (IProtoLogClient client : clients) {
- clientToGroups.putIfAbsent(client, new HashSet<>());
- clientToGroups.get(client).add(group);
- }
- }
-
- for (IProtoLogClient client : clientToGroups.keySet()) {
- try {
- client.toggleLogcat(enabled, clientToGroups.get(client).toArray(new String[0]));
- } catch (RemoteException e) {
- throw new RuntimeException(
- "Failed to toggle logcat status for groups on client", e);
- }
- }
-
- for (String group : groups) {
- mLogGroupToLogcatStatus.put(group, enabled);
- }
- }
-
- private void onTracingInstanceStart(int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
- mRunningInstances.add(instanceIdx);
- }
-
- private void onTracingInstanceFlush() {
- for (String fileName : mConfigFileCounts.keySet()) {
- mViewerConfigFileTracer.trace(mDataSource, fileName);
- }
- }
-
- private void onTracingInstanceStop(int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
- mRunningInstances.remove(instanceIdx);
- }
-
- private static void dumpTransitionTraceConfig(@NonNull ProtoLogDataSource dataSource,
- @NonNull String viewerConfigFilePath) {
- Utils.dumpViewerConfig(dataSource, () -> {
- try {
- return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
- } catch (FileNotFoundException e) {
- throw new RuntimeException(
- "Failed to load viewer config file " + viewerConfigFilePath, e);
- }
- });
- }
-
- private void onClientBinderDeath(@NonNull IProtoLogClient client) {
- // Dump the tracing config now if no other client is going to dump the same config file.
- String configFile = mClientConfigFiles.get(client);
- if (configFile != null) {
- final var newCount = mConfigFileCounts.get(configFile) - 1;
- mConfigFileCounts.put(configFile, newCount);
- boolean lastProcessWithViewerConfig = newCount == 0;
- if (lastProcessWithViewerConfig) {
- mViewerConfigFileTracer.trace(mDataSource, configFile);
- }
- }
- }
-
- private static void writeViewerConfigGroup(
- @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
- final long inGroupToken = pis.start(GROUPS);
- final long outGroupToken = os.start(GROUPS);
-
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) ID -> {
- int id = pis.readInt(ID);
- os.write(ID, id);
- }
- case (int) NAME -> {
- String name = pis.readString(NAME);
- os.write(NAME, name);
- }
- case (int) TAG -> {
- String tag = pis.readString(TAG);
- os.write(TAG, tag);
- }
- default ->
- throw new RuntimeException(
- "Unexpected field id " + pis.getFieldNumber());
- }
- }
-
- pis.end(inGroupToken);
- os.end(outGroupToken);
- }
-
- private static void writeViewerConfigMessage(
- @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
- final long inMessageToken = pis.start(MESSAGES);
- final long outMessagesToken = os.start(MESSAGES);
-
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) MESSAGE_ID -> os.write(MESSAGE_ID,
- pis.readLong(MESSAGE_ID));
- case (int) MESSAGE -> os.write(MESSAGE, pis.readString(MESSAGE));
- case (int) LEVEL -> os.write(LEVEL, pis.readInt(LEVEL));
- case (int) GROUP_ID -> os.write(GROUP_ID, pis.readInt(GROUP_ID));
- case (int) LOCATION -> os.write(LOCATION, pis.readString(LOCATION));
- default ->
- throw new RuntimeException(
- "Unexpected field id " + pis.getFieldNumber());
- }
- }
-
- pis.end(inMessageToken);
- os.end(outMessagesToken);
- }
+ void disableProtoLogToLogcat(@NonNull String... groups);
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java
new file mode 100644
index 000000000000..e382ac1513e0
--- /dev/null
+++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java
@@ -0,0 +1,454 @@
+/*
+ * 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.protolog;
+
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.TAG;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.tracing.perfetto.DataSourceParams;
+import android.tracing.perfetto.InitArguments;
+import android.tracing.perfetto.Producer;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * The ProtoLog service is responsible for orchestrating centralized actions of the protolog tracing
+ * system. Currently this service has the following roles:
+ * - Handle shell commands to toggle logging ProtoLog messages for specified groups to logcat.
+ * - Handle viewer config dumping (the mapping from message hash to message string) for all protolog
+ * clients. This is for two reasons: firstly, because client processes might be frozen so might
+ * not response to the request to dump their viewer config when the trace is stopped; secondly,
+ * multiple processes might be running the same code with the same viewer config, this centralized
+ * service ensures we don't dump the same viewer config multiple times across processes.
+ * <p>
+ * {@link com.android.internal.protolog.IProtoLogClient ProtoLog clients} register themselves to
+ * this service on initialization.
+ * <p>
+ * This service is intended to run on the system server, such that it never gets frozen.
+ */
+@SystemService(Context.PROTOLOG_CONFIGURATION_SERVICE)
+public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationService.Stub
+ implements ProtoLogConfigurationService {
+ private static final String LOG_TAG = "ProtoLogConfigurationService";
+
+ private final ProtoLogDataSource mDataSource;
+
+ /**
+ * Keeps track of how many of each viewer config file is currently registered.
+ * Use to keep track of which viewer config files are actively being used in tracing and might
+ * need to be dumped on flush.
+ */
+ private final Map<String, Integer> mConfigFileCounts = new HashMap<>();
+ /**
+ * Keeps track of the viewer config file of each client if available.
+ */
+ private final Map<IProtoLogClient, String> mClientConfigFiles = new HashMap<>();
+
+ /**
+ * Keeps track of all the protolog groups that have been registered by clients and are still
+ * being actively traced.
+ */
+ private final Set<String> mRegisteredGroups = new HashSet<>();
+ /**
+ * Keeps track of all the clients that are actively tracing a given protolog group.
+ */
+ private final Map<String, Set<IProtoLogClient>> mGroupToClients = new HashMap<>();
+
+ /**
+ * Keeps track of whether or not a given group should be logged to logcat.
+ * True when logging to logcat, false otherwise.
+ */
+ private final Map<String, Boolean> mLogGroupToLogcatStatus = new TreeMap<>();
+
+ /**
+ * Keeps track of all the tracing instance ids that are actively running for ProtoLog.
+ */
+ private final Set<Integer> mRunningInstances = new HashSet<>();
+
+ private final ViewerConfigFileTracer mViewerConfigFileTracer;
+
+ public ProtoLogConfigurationServiceImpl() {
+ this(ProtoLogDataSource::new, ProtoLogConfigurationServiceImpl::dumpTransitionTraceConfig);
+ }
+
+ @VisibleForTesting
+ public ProtoLogConfigurationServiceImpl(@NonNull ProtoLogDataSourceBuilder dataSourceBuilder) {
+ this(dataSourceBuilder, ProtoLogConfigurationServiceImpl::dumpTransitionTraceConfig);
+ }
+
+ @VisibleForTesting
+ public ProtoLogConfigurationServiceImpl(@NonNull ViewerConfigFileTracer tracer) {
+ this(ProtoLogDataSource::new, tracer);
+ }
+
+ @VisibleForTesting
+ public ProtoLogConfigurationServiceImpl(
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @NonNull ViewerConfigFileTracer tracer) {
+ mDataSource = dataSourceBuilder.build(
+ this::onTracingInstanceStart,
+ this::onTracingInstanceFlush,
+ this::onTracingInstanceStop
+ );
+
+ // Initialize the Perfetto producer and register the Perfetto ProtoLog datasource to be
+ // receive the lifecycle callbacks of the datasource and write the viewer configs if and
+ // when required to the datasource.
+ Producer.init(InitArguments.DEFAULTS);
+ final var params = new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
+ .build();
+ mDataSource.register(params);
+
+ mViewerConfigFileTracer = tracer;
+ }
+
+ public static class RegisterClientArgs extends IRegisterClientArgs.Stub {
+ /**
+ * The viewer config file to be registered for this client ProtoLog process.
+ */
+ @Nullable
+ private String mViewerConfigFile = null;
+ /**
+ * The list of all groups that this client protolog process supports and might trace.
+ */
+ @NonNull
+ private String[] mGroups = new String[0];
+ /**
+ * The default logcat status of the ProtoLog client. True is logging to logcat, false
+ * otherwise. The indices should match the indices in {@link mGroups}.
+ */
+ @NonNull
+ private boolean[] mLogcatStatus = new boolean[0];
+
+ public record GroupConfig(@NonNull String group, boolean logToLogcat) {}
+
+ /**
+ * Specify groups to register with this client that will be used for protologging in this
+ * process.
+ * @param groups to register with this client.
+ * @return self
+ */
+ public RegisterClientArgs setGroups(GroupConfig... groups) {
+ mGroups = new String[groups.length];
+ mLogcatStatus = new boolean[groups.length];
+
+ for (int i = 0; i < groups.length; i++) {
+ mGroups[i] = groups[i].group;
+ mLogcatStatus[i] = groups[i].logToLogcat;
+ }
+
+ return this;
+ }
+
+ /**
+ * Set the viewer config file that the logs in this process are using.
+ * @param viewerConfigFile The file path of the viewer config.
+ * @return self
+ */
+ public RegisterClientArgs setViewerConfigFile(@NonNull String viewerConfigFile) {
+ mViewerConfigFile = viewerConfigFile;
+
+ return this;
+ }
+
+ @Override
+ @NonNull
+ public String[] getGroups() {
+ return mGroups;
+ }
+
+ @Override
+ @NonNull
+ public boolean[] getGroupsDefaultLogcatStatus() {
+ return mLogcatStatus;
+ }
+
+ @Nullable
+ @Override
+ public String getViewerConfigFile() {
+ return mViewerConfigFile;
+ }
+ }
+
+ @FunctionalInterface
+ public interface ViewerConfigFileTracer {
+ /**
+ * Write the viewer config data to the trace buffer.
+ *
+ * @param dataSource The target datasource to write the viewer config to.
+ * @param viewerConfigFilePath The path of the viewer config file which contains the data we
+ * want to write to the trace buffer.
+ * @throws FileNotFoundException if the viewerConfigFilePath is invalid.
+ */
+ void trace(@NonNull ProtoLogDataSource dataSource, @NonNull String viewerConfigFilePath);
+ }
+
+ @Override
+ public void registerClient(@NonNull IProtoLogClient client, @NonNull IRegisterClientArgs args)
+ throws RemoteException {
+ client.asBinder().linkToDeath(() -> onClientBinderDeath(client), /* flags */ 0);
+
+ final String viewerConfigFile = args.getViewerConfigFile();
+ if (viewerConfigFile != null) {
+ registerViewerConfigFile(client, viewerConfigFile);
+ }
+
+ registerGroups(client, args.getGroups(), args.getGroupsDefaultLogcatStatus());
+ }
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ new ProtoLogCommandHandler(this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ /**
+ * Get the list of groups clients have registered to the protolog service.
+ * @return The list of ProtoLog groups registered with this service.
+ */
+ @Override
+ @NonNull
+ public String[] getGroups() {
+ return mRegisteredGroups.toArray(new String[0]);
+ }
+
+ /**
+ * Enable logging target groups to logcat.
+ * @param groups we want to enable logging them to logcat for.
+ */
+ @Override
+ public void enableProtoLogToLogcat(@NonNull String... groups) {
+ toggleProtoLogToLogcat(true, groups);
+ }
+
+ /**
+ * Disable logging target groups to logcat.
+ * @param groups we want to disable from being logged to logcat.
+ */
+ @Override
+ public void disableProtoLogToLogcat(@NonNull String... groups) {
+ toggleProtoLogToLogcat(false, groups);
+ }
+
+ /**
+ * Check if a group is logging to logcat
+ * @param group The group we want to check for
+ * @return True iff we are logging this group to logcat.
+ */
+ @Override
+ public boolean isLoggingToLogcat(@NonNull String group) {
+ final Boolean isLoggingToLogcat = mLogGroupToLogcatStatus.get(group);
+
+ if (isLoggingToLogcat == null) {
+ throw new RuntimeException(
+ "Trying to get logcat logging status of non-registered group " + group);
+ }
+
+ return isLoggingToLogcat;
+ }
+
+ private void registerViewerConfigFile(
+ @NonNull IProtoLogClient client, @NonNull String viewerConfigFile) {
+ final var count = mConfigFileCounts.getOrDefault(viewerConfigFile, 0);
+ mConfigFileCounts.put(viewerConfigFile, count + 1);
+ mClientConfigFiles.put(client, viewerConfigFile);
+ }
+
+ private void registerGroups(@NonNull IProtoLogClient client, @NonNull String[] groups,
+ @NonNull boolean[] logcatStatuses) throws RemoteException {
+ if (groups.length != logcatStatuses.length) {
+ throw new RuntimeException(
+ "Expected groups and logcatStatuses to have the same length, "
+ + "but groups has length " + groups.length
+ + " and logcatStatuses has length " + logcatStatuses.length);
+ }
+
+ for (int i = 0; i < groups.length; i++) {
+ String group = groups[i];
+ boolean logcatStatus = logcatStatuses[i];
+
+ mRegisteredGroups.add(group);
+
+ mGroupToClients.putIfAbsent(group, new HashSet<>());
+ mGroupToClients.get(group).add(client);
+
+ if (!mLogGroupToLogcatStatus.containsKey(group)) {
+ mLogGroupToLogcatStatus.put(group, logcatStatus);
+ }
+
+ boolean requestedLogToLogcat = mLogGroupToLogcatStatus.get(group);
+ if (requestedLogToLogcat != logcatStatus) {
+ client.toggleLogcat(requestedLogToLogcat, new String[] { group });
+ }
+ }
+ }
+
+ private void toggleProtoLogToLogcat(boolean enabled, @NonNull String[] groups) {
+ final var clientToGroups = new HashMap<IProtoLogClient, Set<String>>();
+
+ for (String group : groups) {
+ final var clients = mGroupToClients.get(group);
+
+ if (clients == null) {
+ // No clients associated to this group
+ Log.w(LOG_TAG, "Attempting to toggle log to logcat for group " + group
+ + " with no registered clients.");
+ continue;
+ }
+
+ for (IProtoLogClient client : clients) {
+ clientToGroups.putIfAbsent(client, new HashSet<>());
+ clientToGroups.get(client).add(group);
+ }
+ }
+
+ for (IProtoLogClient client : clientToGroups.keySet()) {
+ try {
+ client.toggleLogcat(enabled, clientToGroups.get(client).toArray(new String[0]));
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "Failed to toggle logcat status for groups on client", e);
+ }
+ }
+
+ for (String group : groups) {
+ mLogGroupToLogcatStatus.put(group, enabled);
+ }
+ }
+
+ private void onTracingInstanceStart(int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
+ mRunningInstances.add(instanceIdx);
+ }
+
+ private void onTracingInstanceFlush() {
+ for (String fileName : mConfigFileCounts.keySet()) {
+ mViewerConfigFileTracer.trace(mDataSource, fileName);
+ }
+ }
+
+ private void onTracingInstanceStop(int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
+ mRunningInstances.remove(instanceIdx);
+ }
+
+ private static void dumpTransitionTraceConfig(@NonNull ProtoLogDataSource dataSource,
+ @NonNull String viewerConfigFilePath) {
+ Utils.dumpViewerConfig(dataSource, () -> {
+ try {
+ return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(
+ "Failed to load viewer config file " + viewerConfigFilePath, e);
+ }
+ });
+ }
+
+ private void onClientBinderDeath(@NonNull IProtoLogClient client) {
+ // Dump the tracing config now if no other client is going to dump the same config file.
+ String configFile = mClientConfigFiles.get(client);
+ if (configFile != null) {
+ final var newCount = mConfigFileCounts.get(configFile) - 1;
+ mConfigFileCounts.put(configFile, newCount);
+ boolean lastProcessWithViewerConfig = newCount == 0;
+ if (lastProcessWithViewerConfig) {
+ mViewerConfigFileTracer.trace(mDataSource, configFile);
+ }
+ }
+ }
+
+ private static void writeViewerConfigGroup(
+ @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
+ final long inGroupToken = pis.start(GROUPS);
+ final long outGroupToken = os.start(GROUPS);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) ID -> {
+ int id = pis.readInt(ID);
+ os.write(ID, id);
+ }
+ case (int) NAME -> {
+ String name = pis.readString(NAME);
+ os.write(NAME, name);
+ }
+ case (int) TAG -> {
+ String tag = pis.readString(TAG);
+ os.write(TAG, tag);
+ }
+ default ->
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inGroupToken);
+ os.end(outGroupToken);
+ }
+
+ private static void writeViewerConfigMessage(
+ @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
+ final long inMessageToken = pis.start(MESSAGES);
+ final long outMessagesToken = os.start(MESSAGES);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) MESSAGE_ID -> os.write(MESSAGE_ID,
+ pis.readLong(MESSAGE_ID));
+ case (int) MESSAGE -> os.write(MESSAGE, pis.readString(MESSAGE));
+ case (int) LEVEL -> os.write(LEVEL, pis.readInt(LEVEL));
+ case (int) GROUP_ID -> os.write(GROUP_ID, pis.readInt(GROUP_ID));
+ case (int) LOCATION -> os.write(LOCATION, pis.readString(LOCATION));
+ default ->
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inMessageToken);
+ os.end(outMessagesToken);
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index b73cacb77539..bdb33c4b151c 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -33,8 +33,6 @@ import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.TextView;
-import com.android.text.flags.Flags;
-
/**
* The item view for each item in the ListView-based MenuViews.
*/
@@ -283,10 +281,7 @@ public class ListMenuItemView extends LinearLayout
private void insertIconView() {
LayoutInflater inflater = getInflater();
- mIconView = (ImageView) inflater.inflate(
- !Flags.fixMisalignedContextMenu()
- ? com.android.internal.R.layout.list_menu_item_fixed_size_icon :
- com.android.internal.R.layout.list_menu_item_icon,
+ mIconView = (ImageView) inflater.inflate(com.android.internal.R.layout.list_menu_item_icon,
this, false);
addContentView(mIconView, 0);
}
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index c43a8c6ee7ee..8e2536a423b8 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -35,8 +35,6 @@ import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;
import android.widget.TextView;
-import com.android.text.flags.Flags;
-
import java.util.Objects;
/**
@@ -122,8 +120,7 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
mMenu = menu;
mOverflowOnly = overflowOnly;
final LayoutInflater inflater = LayoutInflater.from(context);
- mAdapter = new MenuAdapter(menu, inflater, mOverflowOnly,
- Flags.fixMisalignedContextMenu() ? ITEM_LAYOUT_MATERIAL : ITEM_LAYOUT);
+ mAdapter = new MenuAdapter(menu, inflater, mOverflowOnly, ITEM_LAYOUT_MATERIAL);
mPopupStyleAttr = popupStyleAttr;
mPopupStyleRes = popupStyleRes;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 90cb10aa62b2..9a4ff8fc264f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -290,7 +290,6 @@ cc_library_shared_for_libandroid_runtime {
"libasync_safe",
"libbinderthreadstateutils",
"libdmabufinfo",
- "libgif",
"libgui_window_info_static",
"libkernelconfigs",
"libnativehelper_lazy",
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index f2c70b5f41d4..8003bb7d442b 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1747,9 +1747,9 @@ static const JNINativeMethod gBinderProxyMethods[] = {
{"linkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
{"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
{"addFrozenStateChangeCallbackNative",
- "(Landroid/os/IBinder$IFrozenStateChangeCallback;)V", (void*)android_os_BinderProxy_addFrozenStateChangeCallback},
+ "(Landroid/os/IBinder$FrozenStateChangeCallback;)V", (void*)android_os_BinderProxy_addFrozenStateChangeCallback},
{"removeFrozenStateChangeCallbackNative",
- "(Landroid/os/IBinder$IFrozenStateChangeCallback;)Z", (void*)android_os_BinderProxy_removeFrozenStateChangeCallback},
+ "(Landroid/os/IBinder$FrozenStateChangeCallback;)Z", (void*)android_os_BinderProxy_removeFrozenStateChangeCallback},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
{"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
};
@@ -1774,7 +1774,7 @@ static int int_register_android_os_BinderProxy(JNIEnv* env)
"(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
gBinderProxyOffsets.mInvokeFrozenStateChangeCallback =
GetStaticMethodIDOrDie(env, clazz, "invokeFrozenStateChangeCallback",
- "(Landroid/os/IBinder$IFrozenStateChangeCallback;Landroid/os/"
+ "(Landroid/os/IBinder$FrozenStateChangeCallback;Landroid/os/"
"IBinder;I)V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
diff --git a/core/jni/jni_wrappers.h b/core/jni/jni_wrappers.h
index 3b29e305e410..21b5b1308fcf 100644
--- a/core/jni/jni_wrappers.h
+++ b/core/jni/jni_wrappers.h
@@ -69,9 +69,47 @@ static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) {
return static_cast<T>(res);
}
+// Inline variable that specifies the method binding format.
+// The expected format is 'XX${method}XX', where ${method} represents the original method name.
+// This variable is shared across all translation units. This is treated as a global variable as
+// per C++ 17.
+inline std::string jniMethodFormat;
+
+inline static void setJniMethodFormat(std::string value) {
+ jniMethodFormat = value;
+}
+
+// Potentially translates the given JNINativeMethods if setJniMethodFormat has been set.
+// Has no effect otherwise
+inline const JNINativeMethod* maybeRenameJniMethods(const JNINativeMethod* gMethods,
+ int numMethods) {
+ if (jniMethodFormat.empty()) {
+ return gMethods;
+ }
+ // Make a copy of gMethods with reformatted method names.
+ JNINativeMethod* modifiedMethods = new JNINativeMethod[numMethods];
+ LOG_ALWAYS_FATAL_IF(!modifiedMethods, "Failed to allocate a copy of the JNI methods");
+
+ size_t methodNamePos = jniMethodFormat.find("${method}");
+ LOG_ALWAYS_FATAL_IF(methodNamePos == std::string::npos,
+ "Invalid jniMethodFormat: could not find '${method}' in pattern");
+
+ for (int i = 0; i < numMethods; i++) {
+ modifiedMethods[i] = gMethods[i];
+ std::string modifiedName = jniMethodFormat;
+ modifiedName.replace(methodNamePos, 9, gMethods[i].name);
+ char* modifiedNameChars = new char[modifiedName.length() + 1];
+ LOG_ALWAYS_FATAL_IF(!modifiedNameChars, "Failed to allocate the new method name");
+ std::strcpy(modifiedNameChars, modifiedName.c_str());
+ modifiedMethods[i].name = modifiedNameChars;
+ }
+ return modifiedMethods;
+}
+
static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods) {
- int res = jniRegisterNativeMethods(env, className, gMethods, numMethods);
+ const JNINativeMethod* modifiedMethods = maybeRenameJniMethods(gMethods, numMethods);
+ int res = jniRegisterNativeMethods(env, className, modifiedMethods, numMethods);
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
return res;
}
diff --git a/core/proto/android/widget/remoteviews.proto b/core/proto/android/widget/remoteviews.proto
index 47c97b08666b..f477d32cd915 100644
--- a/core/proto/android/widget/remoteviews.proto
+++ b/core/proto/android/widget/remoteviews.proto
@@ -299,6 +299,14 @@ message RemoteViewsProto {
NightModeReflectionAction night_mode_reflection_action = 5;
ReflectionAction reflection_action = 6;
RemoveFromParentAction remove_from_parent_action = 7;
+ ResourceReflectionAction resource_reflection_action = 8;
+ SetCompoundButtonCheckedAction set_compound_button_checked_action = 9;
+ SetDrawableTintAction set_drawable_tint_action = 10;
+ SetEmptyViewAction set_empty_view_action = 11;
+ SetIntTagAction set_int_tag_action = 12;
+ SetRadioGroupCheckedAction set_radio_group_checked_action = 13;
+ SetRemoteCollectionItemListAdapterAction set_remote_collection_item_list_adapter_action = 14;
+ SetRippleDrawableColorAction set_ripple_drawable_color_action = 15;
}
}
@@ -374,6 +382,52 @@ message RemoteViewsProto {
message RemoveFromParentAction {
optional string view_id = 1;
}
+
+ message ResourceReflectionAction {
+ optional string view_id = 1;
+ optional string method_name = 2;
+ optional int32 resource_type = 3;
+ optional string res_id = 4;
+ optional int32 parameter_type = 5;
+ }
+
+ message SetCompoundButtonCheckedAction {
+ optional string view_id = 1;
+ optional bool checked = 2;
+ }
+
+ message SetDrawableTintAction {
+ optional string view_id = 1;
+ optional bool target_background = 2;
+ optional int32 color_filter = 3;
+ optional int32 filter_mode = 4;
+ }
+
+ message SetEmptyViewAction {
+ optional string view_id = 1;
+ optional string empty_view_id = 2;
+ }
+
+ message SetIntTagAction {
+ optional string view_id = 1;
+ optional string key = 2;
+ optional int32 tag = 3;
+ }
+
+ message SetRadioGroupCheckedAction {
+ optional string view_id = 1;
+ optional string checked_id = 2;
+ }
+
+ message SetRemoteCollectionItemListAdapterAction {
+ optional string view_id = 1;
+ optional RemoteCollectionItems items = 2;
+ }
+
+ message SetRippleDrawableColorAction {
+ optional string view_id = 1;
+ optional android.content.res.ColorStateListProto color_state_list = 2;
+ }
}
diff --git a/core/res/res/layout/miniresolver.xml b/core/res/res/layout/miniresolver.xml
index e60e0b0079a9..db1c779d0087 100644
--- a/core/res/res/layout/miniresolver.xml
+++ b/core/res/res/layout/miniresolver.xml
@@ -122,7 +122,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
- android:maxLines="2"
+ android:layout_toStartOf="@id/button_open"
+ android:layout_marginEnd="8dp"
android:background="@drawable/resolver_outlined_button_bg"
style="?android:attr/borderlessButtonStyle"
android:paddingHorizontal="16dp"
@@ -136,7 +137,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
- android:maxLines="2"
android:paddingHorizontal="16dp"
android:background="@drawable/resolver_button_bg"
style="?android:attr/borderlessButtonStyle"
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
index 77e8a404a0ff..fe54aa8d87f0 100644
--- a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
@@ -30,31 +30,30 @@ import java.util.concurrent.TimeUnit;
public class BfsccTestAppCmdService extends Service {
private IBfsccTestAppCmdService.Stub mBinder = new IBfsccTestAppCmdService.Stub() {
- private final LinkedBlockingQueue<IBinder.IFrozenStateChangeCallback.State> mNotifications =
+ private final LinkedBlockingQueue<Integer> mNotifications =
new LinkedBlockingQueue<>();
@Override
public void listenTo(IBinder binder) throws RemoteException {
binder.addFrozenStateChangeCallback(
- (IBinder who, IBinder.IFrozenStateChangeCallback.State state)
- -> mNotifications.offer(state));
+ (IBinder who, int state) -> mNotifications.offer(state));
}
@Override
public boolean[] waitAndConsumeNotifications() {
List<Boolean> results = new ArrayList<>();
try {
- IBinder.IFrozenStateChangeCallback.State state =
- mNotifications.poll(5, TimeUnit.SECONDS);
+ Integer state = mNotifications.poll(5, TimeUnit.SECONDS);
if (state != null) {
- results.add(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ results.add(
+ state.intValue() == IBinder.FrozenStateChangeCallback.STATE_FROZEN);
}
} catch (InterruptedException e) {
return null;
}
while (mNotifications.size() > 0) {
- results.add(mNotifications.poll()
- == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ results.add(mNotifications.poll().intValue()
+ == IBinder.FrozenStateChangeCallback.STATE_FROZEN);
}
boolean[] convertedResults = new boolean[results.size()];
for (int i = 0; i < results.size(); i++) {
diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
index ee2e7e06081e..195a18a5f521 100644
--- a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
+++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
@@ -52,7 +52,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
- * Tests functionality of {@link android.os.IBinder.IFrozenStateChangeCallback}.
+ * Tests functionality of {@link android.os.IBinder.FrozenStateChangeCallback}.
*/
@RunWith(AndroidJUnit4.class)
@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
@@ -157,7 +157,7 @@ public class BinderFrozenStateChangeNotificationTest {
@Test
public void onStateChangeNotCalledAfterCallbackRemoved() throws Exception {
final LinkedBlockingQueue<Boolean> results = new LinkedBlockingQueue<>();
- IBinder.IFrozenStateChangeCallback callback;
+ IBinder.FrozenStateChangeCallback callback;
if ((callback = createCallback(mBfsccTestAppCmdService.asBinder(), results)) == null) {
return;
}
@@ -171,7 +171,7 @@ public class BinderFrozenStateChangeNotificationTest {
public void multipleCallbacks() throws Exception {
final LinkedBlockingQueue<Boolean> results1 = new LinkedBlockingQueue<>();
final LinkedBlockingQueue<Boolean> results2 = new LinkedBlockingQueue<>();
- IBinder.IFrozenStateChangeCallback callback1;
+ IBinder.FrozenStateChangeCallback callback1;
if ((callback1 = createCallback(mBfsccTestAppCmdService.asBinder(), results1)) == null) {
return;
}
@@ -197,8 +197,8 @@ public class BinderFrozenStateChangeNotificationTest {
public void onStateChangeCalledWithTheRightBinder() throws Exception {
final IBinder binder = mBfsccTestAppCmdService.asBinder();
final LinkedBlockingQueue<IBinder> results = new LinkedBlockingQueue<>();
- IBinder.IFrozenStateChangeCallback callback =
- (IBinder who, IBinder.IFrozenStateChangeCallback.State state) -> results.offer(who);
+ IBinder.FrozenStateChangeCallback callback =
+ (IBinder who, int state) -> results.offer(who);
try {
binder.addFrozenStateChangeCallback(callback);
} catch (UnsupportedOperationException e) {
@@ -221,12 +221,12 @@ public class BinderFrozenStateChangeNotificationTest {
}
}
- private IBinder.IFrozenStateChangeCallback createCallback(IBinder binder, Queue<Boolean> queue)
+ private IBinder.FrozenStateChangeCallback createCallback(IBinder binder, Queue<Boolean> queue)
throws RemoteException {
try {
- final IBinder.IFrozenStateChangeCallback callback =
- (IBinder who, IBinder.IFrozenStateChangeCallback.State state) ->
- queue.offer(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ final IBinder.FrozenStateChangeCallback callback =
+ (IBinder who, int state) ->
+ queue.offer(state == IBinder.FrozenStateChangeCallback.STATE_FROZEN);
binder.addFrozenStateChangeCallback(callback);
return callback;
} catch (UnsupportedOperationException e) {
diff --git a/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java b/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java
index 32345e606229..dd406955785b 100644
--- a/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java
+++ b/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java
@@ -16,7 +16,7 @@
package android.window.flags;
-import static android.window.flags.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
+import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
import static com.android.window.flags.Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS;
@@ -75,7 +75,7 @@ public class DesktopModeFlagsTest {
public void isEnabled_devOptionFlagDisabled_overrideOff_featureFlagOn_returnsTrue() {
setOverride(OVERRIDE_OFF_SETTING);
// In absence of dev options, follow flag
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
}
@@ -84,7 +84,7 @@ public class DesktopModeFlagsTest {
public void isEnabled_devOptionFlagDisabled_overrideOn_featureFlagOff_returnsFalse() {
setOverride(OVERRIDE_ON_SETTING);
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
}
@Test
@@ -93,7 +93,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_UNSET_SETTING);
// For overridableFlag, for unset overrides, follow flag
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
}
@Test
@@ -103,7 +103,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_UNSET_SETTING);
// For overridableFlag, for unset overrides, follow flag
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
}
@Test
@@ -112,7 +112,7 @@ public class DesktopModeFlagsTest {
setOverride(null);
// For overridableFlag, in absence of overrides, follow flag
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
}
@Test
@@ -122,7 +122,7 @@ public class DesktopModeFlagsTest {
setOverride(null);
// For overridableFlag, in absence of overrides, follow flag
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
}
@Test
@@ -131,7 +131,7 @@ public class DesktopModeFlagsTest {
setOverride(-2);
// For overridableFlag, for unrecognized overrides, follow flag
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
}
@Test
@@ -141,7 +141,7 @@ public class DesktopModeFlagsTest {
setOverride(-2);
// For overridableFlag, for unrecognizable overrides, follow flag
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
}
@Test
@@ -150,7 +150,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_OFF_SETTING);
// For overridableFlag, follow override if they exist
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
}
@Test
@@ -160,7 +160,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_ON_SETTING);
// For overridableFlag, follow override if they exist
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
}
@Test
@@ -169,12 +169,12 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_OFF_SETTING);
// For overridableFlag, follow override if they exist
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
setOverride(OVERRIDE_ON_SETTING);
// Keep overrides constant through the process
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
}
@Test
@@ -184,12 +184,12 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_ON_SETTING);
// For overridableFlag, follow override if they exist
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
setOverride(OVERRIDE_OFF_SETTING);
// Keep overrides constant through the process
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
}
@Test
@@ -199,7 +199,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
}
@Test
@@ -208,7 +208,7 @@ public class DesktopModeFlagsTest {
public void isEnabled_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() {
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
}
@Test
@@ -221,7 +221,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_ON_SETTING);
// When toggle override matches its default state (dw flag), don't override flags
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
}
@Test
@@ -231,7 +231,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_ON_SETTING);
// When toggle override matches its default state (dw flag), don't override flags
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
}
@Test
@@ -244,7 +244,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_OFF_SETTING);
// Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
}
@Test
@@ -254,7 +254,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_OFF_SETTING);
// Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
}
@Test
@@ -267,7 +267,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
}
@Test
@@ -280,7 +280,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
}
@Test
@@ -293,7 +293,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_ON_SETTING);
// Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
}
@Test
@@ -306,7 +306,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_ON_SETTING);
// Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
}
@Test
@@ -319,7 +319,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_OFF_SETTING);
// When toggle override matches its default state (dw flag), don't override flags
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
}
@Test
@@ -332,7 +332,7 @@ public class DesktopModeFlagsTest {
setOverride(OVERRIDE_OFF_SETTING);
// When toggle override matches its default state (dw flag), don't override flags
- assertThat(DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
+ assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
}
private void setOverride(Integer setting) {
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index 5af272c8bead..d6b2a782bc0c 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -117,6 +117,7 @@ public class InteractionJankMonitorTest {
// Simulate a trace session and see if begin / end are invoked.
assertThat(monitor.begin(mSurfaceControl, mActivity.getApplicationContext(),
+ mActivity.getMainThreadHandler(),
Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
verify(tracker).begin();
assertThat(monitor.end(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 397cdcf6acdd..67de25eede42 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -125,12 +125,12 @@ public class BinderDeathDispatcherTest {
}
@Override
- public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+ public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback)
throws RemoteException {
}
@Override
- public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+ public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback) {
return false;
}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 6d31578ac020..e07471cd64bc 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -128,6 +128,22 @@ public final class Bitmap implements Parcelable {
private static final WeakHashMap<Bitmap, Void> sAllBitmaps = new WeakHashMap<>();
/**
+ * @hide
+ */
+ private static NativeAllocationRegistry getRegistry(boolean malloc, long size) {
+ final long free = nativeGetNativeFinalizer();
+ if (com.android.libcore.Flags.nativeMetrics()) {
+ Class cls = Bitmap.class;
+ return malloc ? NativeAllocationRegistry.createMalloced(cls, free, size)
+ : NativeAllocationRegistry.createNonmalloced(cls, free, size);
+ } else {
+ ClassLoader loader = Bitmap.class.getClassLoader();
+ return malloc ? NativeAllocationRegistry.createMalloced(loader, free, size)
+ : NativeAllocationRegistry.createNonmalloced(loader, free, size);
+ }
+ }
+
+ /**
* Private constructor that must receive an already allocated native bitmap
* int (pointer).
*/
@@ -151,7 +167,6 @@ public final class Bitmap implements Parcelable {
mWidth = width;
mHeight = height;
mRequestPremultiplied = requestPremultiplied;
-
mNinePatchChunk = ninePatchChunk;
mNinePatchInsets = ninePatchInsets;
if (density >= 0) {
@@ -159,17 +174,9 @@ public final class Bitmap implements Parcelable {
}
mNativePtr = nativeBitmap;
-
final int allocationByteCount = getAllocationByteCount();
- NativeAllocationRegistry registry;
- if (fromMalloc) {
- registry = NativeAllocationRegistry.createMalloced(
- Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount);
- } else {
- registry = NativeAllocationRegistry.createNonmalloced(
- Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount);
- }
- registry.registerNativeAllocation(this, nativeBitmap);
+ getRegistry(fromMalloc, allocationByteCount).registerNativeAllocation(this, mNativePtr);
+
synchronized (Bitmap.class) {
sAllBitmaps.put(this, null);
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index dd86a1a0edbb..83619efefd3b 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -90,9 +90,6 @@ public class DesktopModeStatus {
/** The maximum override density allowed for tasks inside the desktop. */
private static final int DESKTOP_DENSITY_MAX = 1000;
- /** The number of [WindowDecorViewHost] instances to warm up on system start. */
- private static final int WINDOW_DECOR_PRE_WARM_SIZE = 2;
-
/**
* Sysprop declaring whether to enters desktop mode by default when the windowing mode of the
* display's root TaskDisplayArea is set to WINDOWING_MODE_FREEFORM.
@@ -115,14 +112,6 @@ public class DesktopModeStatus {
private static final String MAX_TASK_LIMIT_SYS_PROP = "persist.wm.debug.desktop_max_task_limit";
/**
- * Sysprop declaring the number of [WindowDecorViewHost] instances to warm up on system start.
- *
- * <p>If it is not defined, then [WINDOW_DECOR_PRE_WARM_SIZE] is used.
- */
- private static final String WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP =
- "persist.wm.debug.desktop_window_decor_pre_warm_size";
-
- /**
* Return {@code true} if veiled resizing is active. If false, fluid resizing is used.
*/
public static boolean isVeiledResizeEnabled() {
@@ -162,12 +151,6 @@ public class DesktopModeStatus {
context.getResources().getInteger(R.integer.config_maxDesktopWindowingActiveTasks));
}
- /** The number of [WindowDecorViewHost] instances to warm up on system start. */
- public static int getWindowDecorPreWarmSize() {
- return SystemProperties.getInt(WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP,
- WINDOW_DECOR_PRE_WARM_SIZE);
- }
-
/**
* Return {@code true} if the current device supports desktop mode.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index f478b4446cbe..3e5adf395cdd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -117,6 +117,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
*/
private static final long MAX_ANIMATION_DURATION = 2000;
private final LatencyTracker mLatencyTracker;
+ @ShellMainThread private final Handler mHandler;
/** True when a back gesture is ongoing */
private boolean mBackGestureStarted = false;
@@ -218,7 +219,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull BackAnimationBackground backAnimationBackground,
ShellBackAnimationRegistry shellBackAnimationRegistry,
ShellCommandHandler shellCommandHandler,
- Transitions transitions) {
+ Transitions transitions,
+ @ShellMainThread Handler handler) {
this(
shellInit,
shellController,
@@ -230,7 +232,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
backAnimationBackground,
shellBackAnimationRegistry,
shellCommandHandler,
- transitions);
+ transitions,
+ handler);
}
@VisibleForTesting
@@ -245,7 +248,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull BackAnimationBackground backAnimationBackground,
ShellBackAnimationRegistry shellBackAnimationRegistry,
ShellCommandHandler shellCommandHandler,
- Transitions transitions) {
+ Transitions transitions,
+ @NonNull @ShellMainThread Handler handler) {
mShellController = shellController;
mShellExecutor = shellExecutor;
mActivityTaskManager = activityTaskManager;
@@ -263,6 +267,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mTransitions = transitions;
mBackTransitionHandler = new BackTransitionHandler();
mTransitions.addHandler(mBackTransitionHandler);
+ mHandler = handler;
updateTouchableArea();
}
@@ -399,7 +404,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
- private static class IBackAnimationImpl extends IBackAnimation.Stub
+ private class IBackAnimationImpl extends IBackAnimation.Stub
implements ExternalInterfaceBinder {
private BackAnimationController mController;
@@ -417,7 +422,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
callback,
runner,
controller.mContext,
- CUJ_PREDICTIVE_BACK_HOME)));
+ CUJ_PREDICTIVE_BACK_HOME,
+ mHandler)));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
index e24df0bdc05d..9ca9b730fb06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
@@ -20,6 +20,7 @@ import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import android.annotation.NonNull;
import android.content.Context;
+import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.IRemoteAnimationFinishedCallback;
@@ -31,6 +32,7 @@ import android.window.IOnBackInvokedCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.Cuj.CujType;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
/**
* Used to register the animation callback and runner, it will trigger result if gesture was finish
@@ -45,6 +47,8 @@ public class BackAnimationRunner {
private final IRemoteAnimationRunner mRunner;
private final @CujType int mCujType;
private final Context mContext;
+ @ShellMainThread
+ private final Handler mHandler;
// Whether we are waiting to receive onAnimationStart
private boolean mWaitingAnimation;
@@ -56,18 +60,35 @@ public class BackAnimationRunner {
@NonNull IOnBackInvokedCallback callback,
@NonNull IRemoteAnimationRunner runner,
@NonNull Context context,
- @CujType int cujType) {
+ @CujType int cujType,
+ @ShellMainThread Handler handler) {
mCallback = callback;
mRunner = runner;
mCujType = cujType;
mContext = context;
+ mHandler = handler;
}
public BackAnimationRunner(
@NonNull IOnBackInvokedCallback callback,
@NonNull IRemoteAnimationRunner runner,
- @NonNull Context context) {
- this(callback, runner, context, NO_CUJ);
+ @NonNull Context context,
+ @ShellMainThread Handler handler
+ ) {
+ this(callback, runner, context, NO_CUJ, handler);
+ }
+
+ /**
+ * @deprecated Use {@link BackAnimationRunner} constructor providing an handler for the ui
+ * thread of the animation.
+ */
+ @Deprecated
+ public BackAnimationRunner(
+ @NonNull IOnBackInvokedCallback callback,
+ @NonNull IRemoteAnimationRunner runner,
+ @NonNull Context context
+ ) {
+ this(callback, runner, context, NO_CUJ, context.getMainThreadHandler());
}
/** Returns the registered animation runner */
@@ -100,7 +121,7 @@ public class BackAnimationRunner {
mWaitingAnimation = false;
if (shouldMonitorCUJ(apps)) {
interactionJankMonitor.begin(
- apps[0].leash, mContext, mCujType);
+ apps[0].leash, mContext, mHandler, mCujType);
}
try {
getRunner().onAnimationStart(TRANSIT_OLD_UNSET, apps, wallpapers,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 32e809a23a91..37339307f5b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -26,6 +26,7 @@ import android.graphics.Matrix
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
+import android.os.Handler
import android.os.RemoteException
import android.util.TimeUtils
import android.view.Choreographer
@@ -53,6 +54,7 @@ import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.shared.animation.Interpolators
+import com.android.wm.shell.shared.annotations.ShellMainThread
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -61,7 +63,8 @@ abstract class CrossActivityBackAnimation(
private val context: Context,
private val background: BackAnimationBackground,
private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
- protected val transaction: SurfaceControl.Transaction
+ protected val transaction: SurfaceControl.Transaction,
+ @ShellMainThread handler: Handler,
) : ShellBackAnimation() {
protected val startClosingRect = RectF()
@@ -80,7 +83,13 @@ abstract class CrossActivityBackAnimation(
private var statusbarHeight = SystemBarUtils.getStatusBarHeight(context)
private val backAnimationRunner =
- BackAnimationRunner(Callback(), Runner(), context, Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY)
+ BackAnimationRunner(
+ Callback(),
+ Runner(),
+ context,
+ Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY,
+ handler,
+ )
private val initialTouchPos = PointF()
private val transformMatrix = Matrix()
private val tmpFloat9 = FloatArray(9)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index 3fccecab4319..7a569799ab84 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -34,6 +34,7 @@ import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.Handler;
import android.os.RemoteException;
import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
@@ -52,6 +53,7 @@ import com.android.internal.policy.SystemBarUtils;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.shared.animation.Interpolators;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import javax.inject.Inject;
@@ -113,9 +115,10 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
private float mVerticalMargin;
@Inject
- public CrossTaskBackAnimation(Context context, BackAnimationBackground background) {
+ public CrossTaskBackAnimation(Context context, BackAnimationBackground background,
+ @ShellMainThread Handler handler) {
mBackAnimationRunner = new BackAnimationRunner(
- new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_TASK);
+ new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_TASK, handler);
mBackground = background;
mContext = context;
loadResources();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
index b02f97bf7784..2f7666b21882 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.back
import android.content.Context
import android.graphics.Rect
import android.graphics.RectF
+import android.os.Handler
import android.util.MathUtils
import android.view.SurfaceControl
import android.view.animation.Animation
@@ -30,6 +31,7 @@ import com.android.internal.policy.TransitionAnimation
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.annotations.ShellMainThread
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
@@ -40,13 +42,15 @@ class CustomCrossActivityBackAnimation(
background: BackAnimationBackground,
rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
transaction: SurfaceControl.Transaction,
- private val customAnimationLoader: CustomAnimationLoader
+ private val customAnimationLoader: CustomAnimationLoader,
+ @ShellMainThread handler: Handler,
) :
CrossActivityBackAnimation(
context,
background,
rootTaskDisplayAreaOrganizer,
- transaction
+ transaction,
+ handler
) {
private var enterAnimation: Animation? = null
@@ -59,7 +63,8 @@ class CustomCrossActivityBackAnimation(
constructor(
context: Context,
background: BackAnimationBackground,
- rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ @ShellMainThread handler: Handler,
) : this(
context,
background,
@@ -67,7 +72,8 @@ class CustomCrossActivityBackAnimation(
SurfaceControl.Transaction(),
CustomAnimationLoader(
TransitionAnimation(context, false /* debug */, "CustomCrossActivityBackAnimation")
- )
+ ),
+ handler,
)
override fun preparePreCommitClosingRectMovement(swipeEdge: Int) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
index 66d8a5f2eeb9..eecd7694009d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
@@ -16,11 +16,13 @@
package com.android.wm.shell.back
import android.content.Context
+import android.os.Handler
import android.view.SurfaceControl
import android.window.BackEvent
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.shared.animation.Interpolators
+import com.android.wm.shell.shared.annotations.ShellMainThread
import javax.inject.Inject
import kotlin.math.max
@@ -30,13 +32,15 @@ class DefaultCrossActivityBackAnimation
constructor(
context: Context,
background: BackAnimationBackground,
- rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ @ShellMainThread handler: Handler,
) :
CrossActivityBackAnimation(
context,
background,
rootTaskDisplayAreaOrganizer,
- SurfaceControl.Transaction()
+ SurfaceControl.Transaction(),
+ handler
) {
private val postCommitInterpolator = Interpolators.EMPHASIZED
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index c545d73734f0..af4a0c55f28d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1241,8 +1241,9 @@ public class BubbleController implements ConfigurationChangeListener,
mBubbleData.dismissBubbleWithKey(
bubbleKey, Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, timestamp);
}
- if (selectedBubbleKey != null && !selectedBubbleKey.equals(bubbleKey)) {
- // We did not remove the selected bubble. Expand it again
+ if (mBubbleData.hasBubbles()) {
+ // We still have bubbles, if we dragged an individual bubble to dismiss we were expanded
+ // so re-expand to whatever is selected.
showExpandedViewForBubbleBar();
}
}
@@ -2007,7 +2008,7 @@ public class BubbleController implements ConfigurationChangeListener,
@Override
public void selectionChanged(BubbleViewProvider selectedBubble) {
// Only need to update the layer view if we're currently expanded for selection changes.
- if (mLayerView != null && isStackExpanded()) {
+ if (mLayerView != null && mLayerView.isExpanded()) {
mLayerView.showExpandedView(selectedBubble);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 1c9c195cf718..1367b7e24bc7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -186,6 +186,10 @@ public class BubbleBarLayerView extends FrameLayout
if (expandedView == null) {
return;
}
+ if (mExpandedBubble != null && mIsExpanded && b.getKey().equals(mExpandedBubble.getKey())) {
+ // Already showing this bubble, skip animating
+ return;
+ }
if (mExpandedBubble != null && !b.getKey().equals(mExpandedBubble.getKey())) {
removeView(mExpandedView);
mExpandedView = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index b8aa1b189f7e..4b55fd0f5527 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -48,6 +48,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Handler;
import android.view.Display;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -75,6 +76,7 @@ import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.animation.Interpolators;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
import com.android.wm.shell.shared.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
@@ -116,6 +118,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
new PathInterpolator(0.2f, 0f, 0f, 1f);
private static final Interpolator GROW_INTERPOLATOR =
new PathInterpolator(0.45f, 0f, 0.5f, 1f);
+ @ShellMainThread
+ private final Handler mHandler;
private int mDividerWindowWidth;
private int mDividerInsets;
@@ -166,7 +170,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
DisplayController displayController, DisplayImeController displayImeController,
- ShellTaskOrganizer taskOrganizer, int parallaxType) {
+ ShellTaskOrganizer taskOrganizer, int parallaxType, @ShellMainThread Handler handler) {
+ mHandler = handler;
mContext = context.createConfigurationContext(configuration);
mOrientation = configuration.orientation;
mRotation = configuration.windowConfiguration.getRotation();
@@ -598,7 +603,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
void onStartDragging() {
- mInteractionJankMonitor.begin(getDividerLeash(), mContext, CUJ_SPLIT_SCREEN_RESIZE);
+ mInteractionJankMonitor.begin(getDividerLeash(), mContext, mHandler,
+ CUJ_SPLIT_SCREEN_RESIZE);
}
void onDraggingCancelled() {
@@ -756,7 +762,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public void onAnimationStart(Animator animation) {
mInteractionJankMonitor.begin(getDividerLeash(),
- mContext, CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER);
+ mContext, mHandler, CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 4adea233b734..722fe1f59388 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -444,7 +444,9 @@ public abstract class WMShellBaseModule {
BackAnimationBackground backAnimationBackground,
Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry,
ShellCommandHandler shellCommandHandler,
- Transitions transitions) {
+ Transitions transitions,
+ @ShellMainThread Handler handler
+ ) {
if (BackAnimationController.IS_ENABLED) {
return shellBackAnimationRegistry.map(
(animations) ->
@@ -457,7 +459,8 @@ public abstract class WMShellBaseModule {
backAnimationBackground,
animations,
shellCommandHandler,
- transitions));
+ transitions,
+ handler));
}
return Optional.empty();
}
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 05a70d8ae197..2f4d77baae97 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
@@ -116,8 +116,6 @@ import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import com.android.wm.shell.windowdecor.viewhost.DefaultWindowDecorViewHostSupplier;
-import com.android.wm.shell.windowdecor.viewhost.PooledWindowDecorViewHostSupplier;
-import com.android.wm.shell.windowdecor.viewhost.ReusableWindowDecorViewHost;
import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
import dagger.Binds;
@@ -355,7 +353,8 @@ public abstract class WMShellModule {
@ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor,
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ @ShellMainThread Handler handler) {
return new FreeformTaskTransitionHandler(
shellInit,
transitions,
@@ -365,7 +364,8 @@ public abstract class WMShellModule {
mainExecutor,
animExecutor,
desktopModeTaskRepository,
- interactionJankMonitor);
+ interactionJankMonitor,
+ handler);
}
@WMSingleton
@@ -382,19 +382,8 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
static WindowDecorViewHostSupplier provideWindowDecorViewHostSupplier(
- @NonNull Context context,
- @ShellMainThread @NonNull CoroutineScope mainScope,
- @NonNull ShellInit shellInit) {
- if (DesktopModeStatus.canEnterDesktopMode(context)
- && Flags.enableDesktopWindowingScvhCache()) {
- final int maxPoolSize = DesktopModeStatus.getMaxTaskLimit(context);
- final int preWarmSize = DesktopModeStatus.getWindowDecorPreWarmSize();
- return new PooledWindowDecorViewHostSupplier(
- context, mainScope, shellInit,
- ReusableWindowDecorViewHost.DefaultFactory.INSTANCE, maxPoolSize, preWarmSize);
- } else {
- return new DefaultWindowDecorViewHostSupplier(mainScope);
- }
+ @ShellMainThread @NonNull CoroutineScope mainScope) {
+ return new DefaultWindowDecorViewHostSupplier(mainScope);
}
//
@@ -617,6 +606,7 @@ public abstract class WMShellModule {
RecentsTransitionHandler recentsTransitionHandler,
MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
Optional<RecentTasksController> recentTasksController,
InteractionJankMonitor interactionJankMonitor) {
@@ -629,7 +619,7 @@ public abstract class WMShellModule {
dragToDesktopTransitionHandler, desktopModeTaskRepository,
desktopModeLoggerTransitionObserver, launchAdjacentController,
recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter,
- recentTasksController.orElse(null), interactionJankMonitor);
+ recentTasksController.orElse(null), interactionJankMonitor, mainHandler);
}
@WMSingleton
@@ -639,7 +629,8 @@ public abstract class WMShellModule {
Transitions transitions,
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
ShellTaskOrganizer shellTaskOrganizer,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ @ShellMainThread Handler handler) {
int maxTaskLimit = DesktopModeStatus.getMaxTaskLimit(context);
if (!DesktopModeStatus.canEnterDesktopMode(context)
|| !ENABLE_DESKTOP_WINDOWING_TASK_LIMIT.isEnabled(context)
@@ -653,7 +644,8 @@ public abstract class WMShellModule {
shellTaskOrganizer,
maxTaskLimit,
interactionJankMonitor,
- context)
+ context,
+ handler)
);
}
@@ -700,9 +692,10 @@ public abstract class WMShellModule {
static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler(
Transitions transitions,
Context context,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ @ShellMainThread Handler handler) {
return new ExitDesktopTaskTransitionHandler(
- transitions, context, interactionJankMonitor);
+ transitions, context, interactionJankMonitor, handler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 037fbb235bd4..3a4764d45f2c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -101,7 +101,8 @@ public abstract class Pip1Module {
DisplayInsetsController displayInsetsController,
TabletopModeController pipTabletopController,
Optional<OneHandedController> oneHandedController,
- @ShellMainThread ShellExecutor mainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler handler) {
return Optional.ofNullable(PipController.create(
context, shellInit, shellCommandHandler, shellController,
displayController, pipAnimationController, pipAppOpsListener,
@@ -111,7 +112,7 @@ public abstract class Pip1Module {
pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
displayInsetsController, pipTabletopController, oneHandedController,
- mainExecutor));
+ mainExecutor, handler));
}
// Handler is used by Icon.loadDrawableAsync
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 e1d14e5e6209..b8ebbcdbfb9d 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.Handler
import android.os.IBinder
import android.os.SystemProperties
import android.util.Size
@@ -136,7 +137,8 @@ class DesktopTasksController(
@ShellMainThread private val mainExecutor: ShellExecutor,
private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
private val recentTasksController: RecentTasksController?,
- private val interactionJankMonitor: InteractionJankMonitor
+ private val interactionJankMonitor: InteractionJankMonitor,
+ @ShellMainThread private val handler: Handler,
) :
RemoteCallable<DesktopTasksController>,
Transitions.TransitionHandler,
@@ -392,7 +394,7 @@ class DesktopTasksController(
taskSurface: SurfaceControl,
) {
logV("startDragToDesktop taskId=%d", taskInfo.taskId)
- interactionJankMonitor.begin(taskSurface, context,
+ interactionJankMonitor.begin(taskSurface, context, handler,
CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
dragToDesktopTransitionHandler.startDragToDesktopTransition(
taskInfo.taskId,
@@ -796,7 +798,7 @@ class DesktopTasksController(
releaseVisualIndicator()
if (!taskInfo.isResizeable && DesktopModeFlags.DISABLE_SNAP_RESIZE.isEnabled(context)) {
interactionJankMonitor.begin(
- taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_non_resizable"
+ taskSurface, context, handler, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_non_resizable"
)
// reposition non-resizable app back to its original position before being dragged
@@ -809,7 +811,7 @@ class DesktopTasksController(
)
} else {
interactionJankMonitor.begin(
- taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable"
+ taskSurface, context, handler, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable"
)
snapToHalfScreen(taskInfo, taskSurface, currentDragBounds, position)
}
@@ -1608,7 +1610,7 @@ class DesktopTasksController(
when (indicatorType) {
IndicatorType.TO_DESKTOP_INDICATOR -> {
// Start a new jank interaction for the drag release to desktop window animation.
- interactionJankMonitor.begin(taskSurface, context,
+ interactionJankMonitor.begin(taskSurface, context, handler,
CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE, "to_desktop")
finalizeDragToDesktop(taskInfo)
}
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 597637d3fbfc..dae37f4926f5 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
@@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode
import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
+import android.os.Handler
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_BACK
@@ -29,6 +30,7 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionObserver
@@ -45,7 +47,8 @@ class DesktopTasksLimiter (
private val shellTaskOrganizer: ShellTaskOrganizer,
private val maxTasksLimit: Int,
private val interactionJankMonitor: InteractionJankMonitor,
- private val context: Context
+ private val context: Context,
+ @ShellMainThread private val handler: Handler,
) {
private val minimizeTransitionObserver = MinimizeTransitionObserver()
@VisibleForTesting
@@ -125,7 +128,7 @@ class DesktopTasksLimiter (
if (mActiveTaskDetails != null && mActiveTaskDetails.transitionInfo != null) {
// Begin minimize window CUJ instrumentation.
interactionJankMonitor.begin(
- mActiveTaskDetails.transitionInfo?.rootLeash, context,
+ mActiveTaskDetails.transitionInfo?.rootLeash, context, handler,
CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
index e87be520cc91..dedd44f3950a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -29,6 +29,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.IBinder;
import android.util.DisplayMetrics;
import android.view.SurfaceControl;
@@ -44,6 +45,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.Cuj;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.transition.Transitions;
@@ -63,6 +65,8 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH
private final Context mContext;
private final Transitions mTransitions;
private final InteractionJankMonitor mInteractionJankMonitor;
+ @ShellMainThread
+ private final Handler mHandler;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback;
private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
@@ -71,20 +75,24 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH
public ExitDesktopTaskTransitionHandler(
Transitions transitions,
Context context,
- InteractionJankMonitor interactionJankMonitor
- ) {
- this(transitions, SurfaceControl.Transaction::new, context, interactionJankMonitor);
+ InteractionJankMonitor interactionJankMonitor,
+ @ShellMainThread Handler handler
+ ) {
+ this(transitions, SurfaceControl.Transaction::new, context, interactionJankMonitor,
+ handler);
}
private ExitDesktopTaskTransitionHandler(
Transitions transitions,
Supplier<SurfaceControl.Transaction> supplier,
Context context,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ @ShellMainThread Handler handler) {
mTransitions = transitions;
mTransactionSupplier = supplier;
mContext = context;
mInteractionJankMonitor = interactionJankMonitor;
+ mHandler = handler;
}
/**
@@ -154,7 +162,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH
final SurfaceControl sc = change.getLeash();
final Rect endBounds = change.getEndAbsBounds();
mInteractionJankMonitor
- .begin(sc, mContext, Cuj.CUJ_DESKTOP_MODE_EXIT_MODE);
+ .begin(sc, mContext, mHandler, Cuj.CUJ_DESKTOP_MODE_EXIT_MODE);
// Hide the first (fullscreen) frame because the animation will start from the freeform
// size.
startT.hide(sc)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 832e2d2bc77b..517e20910f6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -28,6 +28,7 @@ import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.IBinder;
import android.util.ArrayMap;
import android.view.SurfaceControl;
@@ -43,6 +44,7 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -65,6 +67,8 @@ public class FreeformTaskTransitionHandler
private final InteractionJankMonitor mInteractionJankMonitor;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
+ @ShellMainThread
+ private final Handler mHandler;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
@@ -79,7 +83,8 @@ public class FreeformTaskTransitionHandler
ShellExecutor mainExecutor,
ShellExecutor animExecutor,
DesktopModeTaskRepository desktopModeTaskRepository,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ @ShellMainThread Handler handler) {
mTransitions = transitions;
mContext = context;
mWindowDecorViewModel = windowDecorViewModel;
@@ -88,6 +93,7 @@ public class FreeformTaskTransitionHandler
mInteractionJankMonitor = interactionJankMonitor;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mHandler = handler;
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
shellInit.addInitCallback(this::onInit, this);
}
@@ -267,7 +273,7 @@ public class FreeformTaskTransitionHandler
change.getTaskInfo().displayId) == 1) {
// Starting the jank trace if closing the last window in desktop mode.
mInteractionJankMonitor.begin(
- sc, mContext, CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE);
+ sc, mContext, mHandler, CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE);
}
animator.addListener(
new AnimatorListenerAdapter() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 1827923aad90..15472ebc149b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -55,6 +55,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.shared.annotations.ExternalThread;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -203,7 +204,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
DisplayController displayController, DisplayLayout displayLayout,
TaskStackListenerImpl taskStackListener,
InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger,
- ShellExecutor mainExecutor, Handler mainHandler) {
+ ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) {
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
@@ -217,7 +218,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
context, displayLayout, settingsUtil, animationController, tutorialHandler,
- jankMonitor, mainExecutor);
+ jankMonitor, mainExecutor, mainHandler);
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
return new OneHandedController(context, shellInit, shellCommandHandler, shellController,
displayController, organizer, touchHandler, tutorialHandler, settingsUtil,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index d157ca837608..95e633d0b5ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -23,6 +23,7 @@ import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSI
import android.content.Context;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -42,6 +43,7 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -70,6 +72,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
private final InteractionJankMonitor mJankMonitor;
private final Context mContext;
+ @ShellMainThread
+ private final Handler mHandler;
private boolean mIsReady;
private float mLastVisualOffset = 0;
@@ -136,9 +140,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
OneHandedAnimationController animationController,
OneHandedTutorialHandler tutorialHandler,
InteractionJankMonitor jankMonitor,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor,
+ @ShellMainThread Handler handler) {
super(mainExecutor);
mContext = context;
+ mHandler = handler;
setDisplayLayout(displayLayout);
mOneHandedSettingsUtil = oneHandedSettingsUtil;
mAnimationController = animationController;
@@ -333,7 +339,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
getDisplayAreaTokenMap().entrySet().iterator().next();
final InteractionJankMonitor.Configuration.Builder builder =
InteractionJankMonitor.Configuration.Builder.withSurface(
- cujType, mContext, firstEntry.getValue());
+ cujType, mContext, firstEntry.getValue(), mHandler);
if (!TextUtils.isEmpty(tag)) {
builder.setTag(tag);
}
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 deb7691f2d4d..af6844262771 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
@@ -44,6 +44,7 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Pair;
@@ -93,6 +94,7 @@ import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -146,6 +148,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private Optional<OneHandedController> mOneHandedController;
private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
+ @ShellMainThread
+ private final Handler mHandler;
protected final PipImpl mImpl;
private final Rect mTmpInsetBounds = new Rect();
@@ -405,7 +409,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
DisplayInsetsController displayInsetsController,
TabletopModeController pipTabletopController,
Optional<OneHandedController> oneHandedController,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor,
+ Handler handler) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Device doesn't support Pip feature", TAG);
@@ -418,7 +423,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
pipDisplayLayoutState, pipMotionHelper, pipMediaController, phonePipMenuController,
pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- displayInsetsController, pipTabletopController, oneHandedController, mainExecutor)
+ displayInsetsController, pipTabletopController, oneHandedController, mainExecutor,
+ handler)
.mImpl;
}
@@ -446,11 +452,13 @@ public class PipController implements PipTransitionController.PipTransitionCallb
DisplayInsetsController displayInsetsController,
TabletopModeController tabletopModeController,
Optional<OneHandedController> oneHandedController,
- ShellExecutor mainExecutor
+ ShellExecutor mainExecutor,
+ @ShellMainThread Handler handler
) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
+ mHandler = handler;
mImpl = new PipImpl();
mWindowManagerShellWrapper = windowManagerShellWrapper;
mDisplayController = displayController;
@@ -1047,7 +1055,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
// Begin InteractionJankMonitor with PIP transition CUJs
final InteractionJankMonitor.Configuration.Builder builder =
InteractionJankMonitor.Configuration.Builder.withSurface(
- CUJ_PIP_TRANSITION, mContext, mPipTaskOrganizer.getSurfaceControl())
+ CUJ_PIP_TRANSITION, mContext, mPipTaskOrganizer.getSurfaceControl(),
+ mHandler)
.setTag(getTransitionTag(direction))
.setTimeout(2000);
InteractionJankMonitor.getInstance().begin(builder);
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 4ba84eeb2e6d..e8eb10c984af 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
@@ -1654,7 +1654,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mRootTaskInfo.configuration, this, mParentContainerCallbacks,
mDisplayController, mDisplayImeController, mTaskOrganizer,
- PARALLAX_ALIGN_CENTER /* parallaxType */);
+ PARALLAX_ALIGN_CENTER /* parallaxType */, mMainHandler);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
@@ -2456,6 +2456,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final StageChangeRecord record = new StageChangeRecord();
final int transitType = info.getType();
TransitionInfo.Change pipChange = null;
+ int closingSplitTaskId = -1;
for (int iC = 0; iC < info.getChanges().size(); ++iC) {
final TransitionInfo.Change change = info.getChanges().get(iC);
if (change.getMode() == TRANSIT_CHANGE
@@ -2516,21 +2517,31 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+ " with " + taskInfo.taskId + " before startAnimation().");
}
}
+ if (isClosingType(change.getMode()) &&
+ getStageOfTask(change.getTaskInfo().taskId) != STAGE_TYPE_UNDEFINED) {
+ // If either one of the 2 stages is closing we're assuming we'll break split
+ closingSplitTaskId = change.getTaskInfo().taskId;
+ }
}
if (pipChange != null) {
TransitionInfo.Change pipReplacingChange = getPipReplacingChange(info, pipChange,
mMainStage.mRootTaskInfo.taskId, mSideStage.mRootTaskInfo.taskId,
getSplitItemStage(pipChange.getLastParent()));
- if (pipReplacingChange != null) {
+ boolean keepSplitWithPip = pipReplacingChange != null && closingSplitTaskId == -1;
+ if (keepSplitWithPip) {
// Set an enter transition for when startAnimation gets called again
mSplitTransitions.setEnterTransition(transition, /*remoteTransition*/ null,
TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, /*resizeAnim*/ false);
+ } else {
+ int finalClosingTaskId = closingSplitTaskId;
+ mRecentTasks.ifPresent(recentTasks ->
+ recentTasks.removeSplitPair(finalClosingTaskId));
+ logExit(EXIT_REASON_FULLSCREEN_REQUEST);
}
mMixedHandler.animatePendingEnterPipFromSplit(transition, info,
- startTransaction, finishTransaction, finishCallback,
- pipReplacingChange != null);
+ startTransaction, finishTransaction, finishCallback, keepSplitWithPip);
notifySplitAnimationFinished();
return true;
}
@@ -2821,8 +2832,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
callbackWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, false);
mWindowDecorViewModel.ifPresent(viewModel -> {
- viewModel.onTaskInfoChanged(finalMainChild.getTaskInfo());
- viewModel.onTaskInfoChanged(finalSideChild.getTaskInfo());
+ if (finalMainChild != null) {
+ viewModel.onTaskInfoChanged(finalMainChild.getTaskInfo());
+ }
+ if (finalSideChild != null) {
+ viewModel.onTaskInfoChanged(finalSideChild.getTaskInfo());
+ }
});
mPausingTasks.clear();
});
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 b14283f878a3..caac2f6bb03e 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
@@ -111,6 +111,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
@@ -154,7 +155,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final ShellTaskOrganizer mTaskOrganizer;
private final ShellController mShellController;
private final Context mContext;
- private final Handler mMainHandler;
+ private final @ShellMainThread Handler mMainHandler;
private final @ShellBackgroundThread ShellExecutor mBgExecutor;
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
@@ -214,7 +215,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
public DesktopModeWindowDecorViewModel(
Context context,
ShellExecutor shellExecutor,
- Handler mainHandler,
+ @ShellMainThread Handler mainHandler,
Choreographer mainChoreographer,
@ShellBackgroundThread ShellExecutor bgExecutor,
ShellInit shellInit,
@@ -270,7 +271,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
DesktopModeWindowDecorViewModel(
Context context,
ShellExecutor shellExecutor,
- Handler mainHandler,
+ @ShellMainThread Handler mainHandler,
Choreographer mainChoreographer,
@ShellBackgroundThread ShellExecutor bgExecutor,
ShellInit shellInit,
@@ -495,7 +496,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return;
}
mInteractionJankMonitor.begin(
- decoration.mTaskSurface, mContext, Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source);
+ decoration.mTaskSurface, mContext, mMainHandler,
+ Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source);
mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo);
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
@@ -512,7 +514,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
Toast.makeText(mContext,
R.string.desktop_mode_non_resizable_snap_text, Toast.LENGTH_SHORT).show();
} else {
- mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext,
+ mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext, mMainHandler,
Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable");
mDesktopTasksController.snapToHalfScreen(
decoration.mTaskInfo,
@@ -548,7 +550,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return;
}
final WindowContainerTransaction wct = new WindowContainerTransaction();
- mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext,
+ mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext, mMainHandler,
CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU);
// App sometimes draws before the insets from WindowDecoration#relayout have
// been added, so they must be added here
@@ -860,6 +862,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
handleCaptionThroughStatusBar(e, decoration);
final boolean wasDragging = mIsDragging;
updateDragStatus(e.getActionMasked());
+ final boolean upOrCancel = e.getActionMasked() == ACTION_UP
+ || e.getActionMasked() == ACTION_CANCEL;
+ if (wasDragging && upOrCancel) {
+ // When finishing a drag the event will be consumed, which means the pressed
+ // state of the App Handle must be manually reset to scale its drawable back to
+ // its original shape. This is necessary for drag gestures of the Handle that
+ // result in a cancellation (dragging back to the top).
+ v.setPressed(false);
+ }
// Only prevent onClick from receiving this event if it's a drag.
return wasDragging;
}
@@ -1180,8 +1191,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
: SPLIT_POSITION_TOP_OR_LEFT;
final RunningTaskInfo oppositeTaskInfo =
mSplitScreenController.getTaskInfo(oppositePosition);
- mWindowDecorByTaskId.get(oppositeTaskInfo.taskId)
- .disposeStatusBarInputLayer();
+ if (oppositeTaskInfo != null) {
+ mWindowDecorByTaskId.get(oppositeTaskInfo.taskId)
+ .disposeStatusBarInputLayer();
+ }
}
}
mMoveToDesktopAnimator = null;
@@ -1378,7 +1391,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDragStartListener,
mTransitions,
mInteractionJankMonitor,
- mTransactionFactory);
+ mTransactionFactory,
+ mMainHandler);
windowDecoration.setTaskDragResizer(taskPositioner);
final DesktopModeTouchEventListener touchEventListener =
@@ -1602,7 +1616,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
DragPositioningCallbackUtility.DragStartListener dragStartListener,
Transitions transitions,
InteractionJankMonitor interactionJankMonitor,
- Supplier<SurfaceControl.Transaction> transactionFactory) {
+ Supplier<SurfaceControl.Transaction> transactionFactory,
+ Handler handler) {
final TaskPositioner taskPositioner = DesktopModeStatus.isVeiledResizeEnabled()
? new VeiledResizeTaskPositioner(
taskOrganizer,
@@ -1610,7 +1625,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
displayController,
dragStartListener,
transitions,
- interactionJankMonitor)
+ interactionJankMonitor,
+ handler)
: new FluidResizeTaskPositioner(
taskOrganizer,
transitions,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index b1fc55f604d2..16036bee75b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -24,6 +24,8 @@ import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
+import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
+import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
import static com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON;
@@ -604,13 +606,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// their custom content.
relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
} else {
- if (Flags.enableCaptionCompatInsetForceConsumption()) {
+ if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()) {
// Force-consume the caption bar insets when the app tries to hide the caption.
// This improves app compatibility of immersive apps.
relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING;
}
}
- if (Flags.enableCaptionCompatInsetForceConsumptionAlways()) {
+ if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isEnabled()) {
// Always force-consume the caption bar insets for maximum app compatibility,
// including non-immersive apps that just don't handle caption insets properly.
relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 599815530f63..6f3f41191485 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -24,6 +24,7 @@ import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_RESIZE_WINDOW;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.IBinder;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -37,6 +38,7 @@ import androidx.annotation.Nullable;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.transition.Transitions;
import java.util.function.Supplier;
@@ -63,14 +65,17 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
private int mCtrlType;
private boolean mIsResizingOrAnimatingResize;
@Surface.Rotation private int mRotation;
+ @ShellMainThread
+ private final Handler mHandler;
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
DesktopModeWindowDecoration windowDecoration,
DisplayController displayController,
DragPositioningCallbackUtility.DragStartListener dragStartListener,
- Transitions transitions, InteractionJankMonitor interactionJankMonitor) {
+ Transitions transitions, InteractionJankMonitor interactionJankMonitor,
+ @ShellMainThread Handler handler) {
this(taskOrganizer, windowDecoration, displayController, dragStartListener,
- SurfaceControl.Transaction::new, transitions, interactionJankMonitor);
+ SurfaceControl.Transaction::new, transitions, interactionJankMonitor, handler);
}
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
@@ -78,7 +83,7 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
DisplayController displayController,
DragPositioningCallbackUtility.DragStartListener dragStartListener,
Supplier<SurfaceControl.Transaction> supplier, Transitions transitions,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor, @ShellMainThread Handler handler) {
mDesktopWindowDecoration = windowDecoration;
mTaskOrganizer = taskOrganizer;
mDisplayController = displayController;
@@ -86,6 +91,7 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
mTransactionSupplier = supplier;
mTransitions = transitions;
mInteractionJankMonitor = interactionJankMonitor;
+ mHandler = handler;
}
@Override
@@ -97,7 +103,7 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
if (isResizing()) {
// Capture CUJ for re-sizing window in DW mode.
mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface,
- mDesktopWindowDecoration.mContext, CUJ_DESKTOP_MODE_RESIZE_WINDOW);
+ mDesktopWindowDecoration.mContext, mHandler, CUJ_DESKTOP_MODE_RESIZE_WINDOW);
if (!mDesktopWindowDecoration.mTaskInfo.isFocused) {
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mDesktopWindowDecoration.mTaskInfo.token, true);
@@ -131,7 +137,7 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
} else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
// Begin window drag CUJ instrumentation only when drag position moves.
mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface,
- mDesktopWindowDecoration.mContext, CUJ_DESKTOP_MODE_DRAG_WINDOW);
+ mDesktopWindowDecoration.mContext, mHandler, CUJ_DESKTOP_MODE_DRAG_WINDOW);
final SurfaceControl.Transaction t = mTransactionSupplier.get();
DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration,
mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, x, y);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt
index 5156e47cfd13..139e6790b744 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt
@@ -19,33 +19,51 @@ import android.content.Context
import android.content.res.Configuration
import android.view.Display
import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
import android.view.View
import android.view.WindowManager
+import android.view.WindowlessWindowManager
import androidx.tracing.Trace
import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.shared.annotations.ShellMainThread
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
+typealias SurfaceControlViewHostFactory =
+ (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost
/**
- * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHostAdapter].
+ * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHost].
*
- * It supports asynchronously updating the view hierarchy using [updateViewAsync], in which
+ * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
+ * any attempts to do will throw, which means that once a [View] is added using [updateView] or
+ * [updateViewAsync], only its properties and binding may be changed, its children views may be
+ * added, removed or changed and its [WindowManager.LayoutParams] may be changed.
+ * It also supports asynchronously updating the view hierarchy using [updateViewAsync], in which
* case the update work will be posted on the [ShellMainThread] with no delay.
*/
class DefaultWindowDecorViewHost(
- context: Context,
+ private val context: Context,
@ShellMainThread private val mainScope: CoroutineScope,
- display: Display,
- @VisibleForTesting val viewHostAdapter: SurfaceControlViewHostAdapter =
- SurfaceControlViewHostAdapter(context, display)
+ private val display: Display,
+ private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
+ SurfaceControlViewHost(c, d, wwm, s)
+ }
) : WindowDecorViewHost {
+ private val rootSurface: SurfaceControl = SurfaceControl.Builder()
+ .setName("DefaultWindowDecorViewHost surface")
+ .setContainerLayer()
+ .setCallsite("DefaultWindowDecorViewHost#init")
+ .build()
+
+ private var wwm: WindowlessWindowManager? = null
+ @VisibleForTesting
+ var viewHost: SurfaceControlViewHost? = null
private var currentUpdateJob: Job? = null
override val surfaceControl: SurfaceControl
- get() = viewHostAdapter.rootSurface
+ get() = rootSurface
override fun updateView(
view: View,
@@ -74,7 +92,8 @@ class DefaultWindowDecorViewHost(
override fun release(t: SurfaceControl.Transaction) {
clearCurrentUpdateJob()
- viewHostAdapter.release(t)
+ viewHost?.release()
+ t.remove(rootSurface)
}
private fun updateViewHost(
@@ -83,15 +102,45 @@ class DefaultWindowDecorViewHost(
configuration: Configuration,
onDrawTransaction: SurfaceControl.Transaction?
) {
- viewHostAdapter.prepareViewHost(configuration)
+ Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost")
+ if (wwm == null) {
+ wwm = WindowlessWindowManager(configuration, rootSurface, null)
+ }
+ requireWindowlessWindowManager().setConfiguration(configuration)
+ if (viewHost == null) {
+ viewHost = surfaceControlViewHostFactory.invoke(
+ context,
+ display,
+ requireWindowlessWindowManager(),
+ "DefaultWindowDecorViewHost#updateViewHost"
+ )
+ }
onDrawTransaction?.let {
- viewHostAdapter.applyTransactionOnDraw(it)
+ requireViewHost().rootSurfaceControl.applyTransactionOnDraw(it)
+ }
+ if (requireViewHost().view == null) {
+ Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-setView")
+ requireViewHost().setView(view, attrs)
+ Trace.endSection()
+ } else {
+ check(requireViewHost().view == view) { "Changing view is not allowed" }
+ Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-relayout")
+ requireViewHost().relayout(attrs)
+ Trace.endSection()
}
- viewHostAdapter.updateView(view, attrs)
+ Trace.endSection()
}
private fun clearCurrentUpdateJob() {
currentUpdateJob?.cancel()
currentUpdateJob = null
}
+
+ private fun requireWindowlessWindowManager(): WindowlessWindowManager {
+ return wwm ?: error("Expected non-null windowless window manager")
+ }
+
+ private fun requireViewHost(): SurfaceControlViewHost {
+ return viewHost ?: error("Expected non-null view host")
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/PooledWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/PooledWindowDecorViewHostSupplier.kt
deleted file mode 100644
index b04188fa82a8..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/PooledWindowDecorViewHostSupplier.kt
+++ /dev/null
@@ -1,105 +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.wm.shell.windowdecor.viewhost
-
-import android.content.Context
-import android.os.Trace
-import android.util.Pools
-import android.view.Display
-import android.view.SurfaceControl
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import com.android.wm.shell.sysui.ShellInit
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-
-/**
- * A [WindowDecorViewHostSupplier] backed by a pool to allow recycling view hosts which may be
- * expensive to recreate for each new/updated window decoration.
- *
- * Callers can obtain [ReusableWindowDecorViewHost] using [acquire], which will return a pooled
- * object if available, or create a new instance and return it if needed. When done using a
- * [ReusableWindowDecorViewHost], it must be released using [release] to allow it to be sent back
- * into the pool and reused later on.
- *
- * This class also supports pre-warming [ReusableWindowDecorViewHost] instances, which will be put
- * into the pool immediately after creation.
- */
-class PooledWindowDecorViewHostSupplier(
- private val context: Context,
- @ShellMainThread private val mainScope: CoroutineScope,
- shellInit: ShellInit,
- private val viewHostFactory: ReusableWindowDecorViewHost.Factory =
- ReusableWindowDecorViewHost.DefaultFactory,
- maxPoolSize: Int,
- private val preWarmSize: Int,
-) : WindowDecorViewHostSupplier<ReusableWindowDecorViewHost> {
-
- private val pool: Pools.Pool<ReusableWindowDecorViewHost> = Pools.SynchronizedPool(maxPoolSize)
- private var nextDecorViewHostId = 0
-
- init {
- require(preWarmSize <= maxPoolSize) { "Pre-warm size should not exceed pool size" }
- shellInit.addInitCallback(this::onShellInit, this)
- }
-
- private fun onShellInit() {
- if (preWarmSize <= 0) {
- return
- }
- preWarmViewHosts(preWarmSize)
- }
-
- private fun preWarmViewHosts(preWarmSize: Int) {
- mainScope.launch {
- // Applying isn't needed, as the surface was never actually shown.
- val t = SurfaceControl.Transaction()
- repeat(preWarmSize) {
- val warmedViewHost = create(context, context.display).apply {
- warmUp()
- }
- // Put the warmed view host in the pool by releasing it.
- release(warmedViewHost, t)
- }
- }
- }
-
- override fun acquire(context: Context, display: Display): ReusableWindowDecorViewHost {
- val reusedDecorViewHost = pool.acquire()
- if (reusedDecorViewHost != null) {
- return reusedDecorViewHost
- }
- Trace.beginSection("WindowDecorViewHostPool#acquire-new")
- val newDecorViewHost = create(context, display)
- Trace.endSection()
- return newDecorViewHost
- }
-
- override fun release(viewHost: ReusableWindowDecorViewHost, t: SurfaceControl.Transaction) {
- val cached = pool.release(viewHost)
- if (!cached) {
- viewHost.release(t)
- }
- }
-
- private fun create(context: Context, display: Display): ReusableWindowDecorViewHost {
- return viewHostFactory.create(
- context,
- mainScope,
- display,
- nextDecorViewHostId++
- )
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/ReusableWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/ReusableWindowDecorViewHost.kt
deleted file mode 100644
index 64536d1a7897..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/ReusableWindowDecorViewHost.kt
+++ /dev/null
@@ -1,161 +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.wm.shell.windowdecor.viewhost
-
-import android.content.Context
-import android.content.res.Configuration
-import android.graphics.PixelFormat
-import android.os.Trace
-import android.view.Display
-import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
-import android.view.View
-import android.view.WindowManager
-import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-import android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
-import android.view.WindowManager.LayoutParams.TYPE_APPLICATION
-import android.widget.FrameLayout
-import com.android.internal.annotations.VisibleForTesting
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-
-/**
- * An implementation of [WindowDecorViewHost] that supports:
- * 1) Replacing the root [View], meaning [WindowDecorViewHost.updateView] maybe be
- * called with different [View] instances. This is useful when reusing [WindowDecorViewHost]s
- * instances for vastly different view hierarchies, such as Desktop Windowing's App Handles and
- * App Headers.
- * 2) Pre-warming of the underlying [SurfaceControlViewHost]s. Useful because their creation and
- * first root view assignment are expensive, which is undesirable in latency-sensitive code
- * paths like during a shell transition.
- */
-class ReusableWindowDecorViewHost(
- private val context: Context,
- @ShellMainThread private val mainScope: CoroutineScope,
- display: Display,
- val id: Int,
- @VisibleForTesting val viewHostAdapter: SurfaceControlViewHostAdapter =
- SurfaceControlViewHostAdapter(context, display)
-) : WindowDecorViewHost, Warmable {
-
- @VisibleForTesting
- val rootView = FrameLayout(context)
-
- private var currentUpdateJob: Job? = null
-
- override val surfaceControl: SurfaceControl
- get() = viewHostAdapter.rootSurface
-
- override fun warmUp() {
- if (viewHostAdapter.isInitialized()) {
- // Already warmed up.
- return
- }
- Trace.beginSection("$TAG#warmUp")
- viewHostAdapter.prepareViewHost(context.resources.configuration)
- viewHostAdapter.updateView(
- rootView,
- WindowManager.LayoutParams(
- 0 /* width*/,
- 0 /* height */,
- TYPE_APPLICATION,
- FLAG_NOT_FOCUSABLE or FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSPARENT
- ).apply {
- setTitle("View root of $TAG#$id")
- setTrustedOverlay()
- }
- )
- Trace.endSection()
- }
-
- override fun updateView(
- view: View,
- attrs: WindowManager.LayoutParams,
- configuration: Configuration,
- onDrawTransaction: SurfaceControl.Transaction?
- ) {
- clearCurrentUpdateJob()
- updateViewHost(view, attrs, configuration, onDrawTransaction)
- }
-
- override fun updateViewAsync(
- view: View,
- attrs: WindowManager.LayoutParams,
- configuration: Configuration
- ) {
- clearCurrentUpdateJob()
- currentUpdateJob = mainScope.launch {
- updateViewHost(view, attrs, configuration, onDrawTransaction = null)
- }
- }
-
- override fun release(t: SurfaceControl.Transaction) {
- clearCurrentUpdateJob()
- viewHostAdapter.release(t)
- }
-
- private fun updateViewHost(
- view: View,
- attrs: WindowManager.LayoutParams,
- configuration: Configuration,
- onDrawTransaction: SurfaceControl.Transaction?
- ) {
- viewHostAdapter.prepareViewHost(configuration)
- onDrawTransaction?.let {
- viewHostAdapter.applyTransactionOnDraw(it)
- }
- rootView.removeAllViews()
- rootView.addView(view)
- viewHostAdapter.updateView(rootView, attrs)
- }
-
- private fun clearCurrentUpdateJob() {
- currentUpdateJob?.cancel()
- currentUpdateJob = null
- }
-
- interface Factory {
- fun create(
- context: Context,
- @ShellMainThread mainScope: CoroutineScope,
- display: Display,
- id: Int
- ): ReusableWindowDecorViewHost
- }
-
- object DefaultFactory : Factory {
- override fun create(
- context: Context,
- @ShellMainThread mainScope: CoroutineScope,
- display: Display,
- id: Int
- ): ReusableWindowDecorViewHost {
- return ReusableWindowDecorViewHost(
- context,
- mainScope,
- display,
- id
- )
- }
- }
-
- companion object {
- private const val TAG = "ReusableWindowDecorViewHost"
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/SurfaceControlViewHostAdapter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/SurfaceControlViewHostAdapter.kt
deleted file mode 100644
index a54c9ba67cf8..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/SurfaceControlViewHostAdapter.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.windowdecor.viewhost
-
-import android.content.Context
-import android.content.res.Configuration
-import android.view.AttachedSurfaceControl
-import android.view.Display
-import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
-import android.view.View
-import android.view.WindowManager
-import android.view.WindowlessWindowManager
-import androidx.tracing.Trace
-import com.android.internal.annotations.VisibleForTesting
-typealias SurfaceControlViewHostFactory =
- (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost
-
-/**
- * Adapter for a [SurfaceControlViewHost] and its backing [SurfaceControl].
- *
- * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
- * any attempts to do will throw, which means that once a [View] is added using [updateView], only
- * its properties and binding may be changed, its children views may be added, removed or changed
- * and its [WindowManager.LayoutParams] may be changed.
- */
-class SurfaceControlViewHostAdapter(
- private val context: Context,
- private val display: Display,
- private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
- SurfaceControlViewHost(c, d, wwm, s)
- }
-) {
- val rootSurface: SurfaceControl = SurfaceControl.Builder()
- .setName("SurfaceControlViewHostAdapter surface")
- .setContainerLayer()
- .setCallsite("SurfaceControlViewHostAdapter#init")
- .build()
-
- private var wwm: WindowlessWindowManager? = null
- @VisibleForTesting
- var viewHost: SurfaceControlViewHost? = null
-
- /** Initialize the [SurfaceControlViewHost] if needed. */
- fun prepareViewHost(configuration: Configuration) {
- if (wwm == null) {
- wwm = WindowlessWindowManager(configuration, rootSurface, null)
- }
- requireWindowlessWindowManager().setConfiguration(configuration)
- if (viewHost == null) {
- viewHost = surfaceControlViewHostFactory.invoke(
- context,
- display,
- requireWindowlessWindowManager(),
- "SurfaceControlViewHostAdapter#prepareViewHost"
- )
- }
- }
-
- /**
- * Request to apply the transaction atomically with the next draw of the view hierarchy.
- * See [AttachedSurfaceControl.applyTransactionOnDraw].
- */
- fun applyTransactionOnDraw(t: SurfaceControl.Transaction) {
- requireViewHost().rootSurfaceControl.applyTransactionOnDraw(t)
- }
-
- /** Update the view hierarchy of the view host. */
- fun updateView(view: View, attrs: WindowManager.LayoutParams) {
- if (requireViewHost().view == null) {
- Trace.beginSection("SurfaceControlViewHostAdapter#updateView-setView")
- requireViewHost().setView(view, attrs)
- Trace.endSection()
- } else {
- check(requireViewHost().view == view) { "Changing view is not allowed" }
- Trace.beginSection("SurfaceControlViewHostAdapter#updateView-relayout")
- requireViewHost().relayout(attrs)
- Trace.endSection()
- }
- }
-
- /** Release the view host and remove the backing surface. */
- fun release(t: SurfaceControl.Transaction) {
- viewHost?.release()
- t.remove(rootSurface)
- }
-
- /** Whether the view host has had a view hierarchy set. */
- fun isInitialized(): Boolean = viewHost?.view != null
-
- private fun requireWindowlessWindowManager(): WindowlessWindowManager {
- return wwm ?: error("Expected non-null windowless window manager")
- }
-
- private fun requireViewHost(): SurfaceControlViewHost {
- return viewHost ?: error("Expected non-null view host")
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/Warmable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/Warmable.kt
deleted file mode 100644
index 0df9bfa2ee78..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/Warmable.kt
+++ /dev/null
@@ -1,23 +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.wm.shell.windowdecor.viewhost
-
-/**
- * An interface for an object that can be warmed up before it's needed.
- */
-interface Warmable {
- fun warmUp()
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index b53ea3837178..227060d15640 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -137,6 +137,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
private Transitions mTransitions;
@Mock
private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ @Mock
+ private Handler mHandler;
private BackAnimationController mController;
private TestableContentResolver mContentResolver;
@@ -161,13 +163,14 @@ public class BackAnimationControllerTest extends ShellTestCase {
mTestableLooper = TestableLooper.get(this);
mShellInit = spy(new ShellInit(mShellExecutor));
mDefaultCrossActivityBackAnimation = new DefaultCrossActivityBackAnimation(mContext,
- mAnimationBackground, mRootTaskDisplayAreaOrganizer);
- mCrossTaskBackAnimation = new CrossTaskBackAnimation(mContext, mAnimationBackground);
+ mAnimationBackground, mRootTaskDisplayAreaOrganizer, mHandler);
+ mCrossTaskBackAnimation = new CrossTaskBackAnimation(mContext, mAnimationBackground,
+ mHandler);
mShellBackAnimationRegistry =
new ShellBackAnimationRegistry(mDefaultCrossActivityBackAnimation,
mCrossTaskBackAnimation, /* dialogCloseAnimation= */ null,
new CustomCrossActivityBackAnimation(mContext, mAnimationBackground,
- mRootTaskDisplayAreaOrganizer),
+ mRootTaskDisplayAreaOrganizer, mHandler),
/* defaultBackToHomeAnimation= */ null);
mController =
new BackAnimationController(
@@ -181,7 +184,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
mAnimationBackground,
mShellBackAnimationRegistry,
mShellCommandHandler,
- mTransitions);
+ mTransitions,
+ mHandler);
mShellInit.init();
mShellExecutor.flushAll();
mTouchableRegion = new Rect(0, 0, 100, 100);
@@ -344,7 +348,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
mAnimationBackground,
mShellBackAnimationRegistry,
mShellCommandHandler,
- mTransitions);
+ mTransitions,
+ mHandler);
shellInit.init();
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
@@ -898,7 +903,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
new BackAnimationRunner(
mAnimatorCallback,
mBackAnimationRunner,
- mContext));
+ mContext,
+ mHandler));
}
private void unregisterAnimation(int type) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
index 080ad901c656..5b5ef6f48789 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -22,6 +22,7 @@ import android.app.WindowConfiguration
import android.graphics.Color
import android.graphics.Point
import android.graphics.Rect
+import android.os.Handler
import android.os.RemoteException
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -66,6 +67,7 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() {
@Mock private lateinit var transitionAnimation: TransitionAnimation
@Mock private lateinit var appCompatTaskInfo: AppCompatTaskInfo
@Mock private lateinit var transaction: Transaction
+ @Mock private lateinit var handler: Handler
private lateinit var customCrossActivityBackAnimation: CustomCrossActivityBackAnimation
private lateinit var customAnimationLoader: CustomAnimationLoader
@@ -80,7 +82,8 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() {
backAnimationBackground,
rootTaskDisplayAreaOrganizer,
transaction,
- customAnimationLoader
+ customAnimationLoader,
+ handler,
)
whenever(transitionAnimation.loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(OPEN_RES_ID)))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
index f5847cc27071..cf69704a0470 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.view.InputDevice;
@@ -55,6 +56,7 @@ public class DividerViewTest extends ShellTestCase {
private @Mock DisplayController mDisplayController;
private @Mock DisplayImeController mDisplayImeController;
private @Mock ShellTaskOrganizer mTaskOrganizer;
+ private @Mock Handler mHandler;
private SplitLayout mSplitLayout;
private DividerView mDividerView;
@@ -65,7 +67,7 @@ public class DividerViewTest extends ShellTestCase {
Configuration configuration = getConfiguration();
mSplitLayout = new SplitLayout("TestSplitLayout", mContext, configuration,
mSplitLayoutHandler, mCallbacks, mDisplayController, mDisplayImeController,
- mTaskOrganizer, SplitLayout.PARALLAX_NONE);
+ mTaskOrganizer, SplitLayout.PARALLAX_NONE, mHandler);
SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager",
mContext,
configuration, mCallbacks);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 82b3a7de521b..177e47a342f6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Handler;
import android.window.WindowContainerTransaction;
import androidx.test.annotation.UiThreadTest;
@@ -65,6 +66,7 @@ public class SplitLayoutTests extends ShellTestCase {
@Mock DisplayImeController mDisplayImeController;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Mock WindowContainerTransaction mWct;
+ @Mock Handler mHandler;
@Captor ArgumentCaptor<Runnable> mRunnableCaptor;
private SplitLayout mSplitLayout;
@@ -80,7 +82,8 @@ public class SplitLayoutTests extends ShellTestCase {
mDisplayController,
mDisplayImeController,
mTaskOrganizer,
- SplitLayout.PARALLAX_NONE));
+ SplitLayout.PARALLAX_NONE,
+ mHandler));
}
@Test
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 ccbcabc9b1f5..8f20841e76b3 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
@@ -40,6 +40,7 @@ import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.os.Binder
+import android.os.Handler
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
@@ -181,6 +182,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockSurface: SurfaceControl
@Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
+ @Mock private lateinit var mockHandler: Handler
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -221,7 +223,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
shellTaskOrganizer,
MAX_TASK_LIMIT,
mockInteractionJankMonitor,
- mContext)
+ mContext,
+ mockHandler)
whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
@@ -274,7 +277,9 @@ class DesktopTasksControllerTest : ShellTestCase() {
shellExecutor,
Optional.of(desktopTasksLimiter),
recentTasksController,
- mockInteractionJankMonitor)
+ mockInteractionJankMonitor,
+ mockHandler,
+ )
}
@After
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 2d0e428c45cb..61d03cac035c 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
@@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode
import android.app.ActivityManager.RunningTaskInfo
import android.os.Binder
+import android.os.Handler
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
@@ -70,6 +71,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
@Mock lateinit var transitions: Transitions
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+ @Mock lateinit var handler: Handler
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var desktopTasksLimiter: DesktopTasksLimiter
@@ -85,7 +87,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
desktopTasksLimiter =
DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT,
- interactionJankMonitor, mContext)
+ interactionJankMonitor, mContext, handler)
}
@After
@@ -97,7 +99,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun createDesktopTasksLimiter_withZeroLimit_shouldThrow() {
assertFailsWith<IllegalArgumentException> {
DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, 0,
- interactionJankMonitor, mContext)
+ interactionJankMonitor, mContext, handler)
}
}
@@ -105,7 +107,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun createDesktopTasksLimiter_withNegativeLimit_shouldThrow() {
assertFailsWith<IllegalArgumentException> {
DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, -5,
- interactionJankMonitor, mContext)
+ interactionJankMonitor, mContext, handler)
}
}
@@ -334,7 +336,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun getTaskToMinimizeIfNeeded_tasksAboveLimit_otherLimit_returnsBackTask() {
desktopTasksLimiter =
DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT2,
- interactionJankMonitor, mContext)
+ interactionJankMonitor, mContext, handler)
val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }
val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded(
@@ -375,6 +377,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
verify(interactionJankMonitor).begin(
any(),
eq(mContext),
+ eq(handler),
eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
@@ -403,7 +406,9 @@ class DesktopTasksLimiterTest : ShellTestCase() {
verify(interactionJankMonitor).begin(
any(),
eq(mContext),
- eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+ eq(handler),
+ eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW),
+ )
desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
transition,
@@ -432,6 +437,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
verify(interactionJankMonitor).begin(
any(),
eq(mContext),
+ eq(handler),
eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
desktopTasksLimiter.getTransitionObserver().onTransitionMerged(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
index e0463b41ad20..fefa933c5208 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
@@ -34,6 +34,7 @@ import android.app.WindowConfiguration;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
+import android.os.Handler;
import android.os.IBinder;
import android.util.DisplayMetrics;
import android.view.SurfaceControl;
@@ -81,6 +82,8 @@ public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase {
Transitions.TransitionFinishCallback mTransitionFinishCallback;
@Mock
ShellExecutor mExecutor;
+ @Mock
+ Handler mHandler;
private Point mPoint;
private ExitDesktopTaskTransitionHandler mExitDesktopTaskTransitionHandler;
@@ -97,7 +100,7 @@ public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase {
.thenReturn(getContext().getResources().getDisplayMetrics());
mExitDesktopTaskTransitionHandler = new ExitDesktopTaskTransitionHandler(mTransitions,
- mContext, mInteractionJankMonitor);
+ mContext, mInteractionJankMonitor, mHandler);
mPoint = new Point(0, 0);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 9c7f7237871a..9146906b6385 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -37,6 +37,7 @@ import static org.mockito.Mockito.when;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
@@ -100,6 +101,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
OneHandedSettingsUtil mMockSettingsUitl;
@Mock
InteractionJankMonitor mJankMonitor;
+ @Mock
+ Handler mMockHandler;
List<DisplayAreaAppearedInfo> mDisplayAreaAppearedInfoList = new ArrayList<>();
@@ -142,7 +145,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mMockAnimationController,
mTutorialHandler,
mJankMonitor,
- mMockShellMainExecutor));
+ mMockShellMainExecutor,
+ mMockHandler));
for (int i = 0; i < DISPLAYAREA_INFO_COUNT; i++) {
mDisplayAreaAppearedInfoList.add(getDummyDisplayAreaInfo());
@@ -429,7 +433,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mMockAnimationController,
mTutorialHandler,
mJankMonitor,
- mMockShellMainExecutor));
+ mMockShellMainExecutor,
+ mMockHandler));
assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index f3944d5ac352..96003515a485 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -40,6 +40,7 @@ import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Handler;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -115,6 +116,7 @@ public class PipControllerTest extends ShellTestCase {
@Mock private PipParamsChangedForwarder mMockPipParamsChangedForwarder;
@Mock private DisplayInsetsController mMockDisplayInsetsController;
@Mock private TabletopModeController mMockTabletopModeController;
+ @Mock private Handler mMockHandler;
@Mock private DisplayLayout mMockDisplayLayout1;
@Mock private DisplayLayout mMockDisplayLayout2;
@@ -138,7 +140,7 @@ public class PipControllerTest extends ShellTestCase {
mMockPipTransitionController, mMockWindowManagerShellWrapper,
mMockTaskStackListener, mMockPipParamsChangedForwarder,
mMockDisplayInsetsController, mMockTabletopModeController,
- mMockOneHandedController, mMockExecutor);
+ mMockOneHandedController, mMockExecutor, mMockHandler);
mShellInit.init();
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -230,7 +232,7 @@ public class PipControllerTest extends ShellTestCase {
mMockPipTransitionController, mMockWindowManagerShellWrapper,
mMockTaskStackListener, mMockPipParamsChangedForwarder,
mMockDisplayInsetsController, mMockTabletopModeController,
- mMockOneHandedController, mMockExecutor));
+ mMockOneHandedController, mMockExecutor, mMockHandler));
}
@Test
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 a18fbf0891ef..85bc7cc287e6 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
@@ -247,7 +247,18 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor)
- whenever(mockTaskPositionerFactory.create(any(), any(), any(), any(), any(), any(), any()))
+ whenever(
+ mockTaskPositionerFactory.create(
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ )
.thenReturn(mockTaskPositioner)
doReturn(mockToast).`when` { Toast.makeText(any(), anyInt(), anyInt()) }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 7784af6b1111..ab41d9c80177 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.content.res.Resources
import android.graphics.Point
import android.graphics.Rect
+import android.os.Handler
import android.os.IBinder
import android.testing.AndroidTestingRunner
import android.view.Display
@@ -107,6 +108,8 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
private lateinit var mockResources: Resources
@Mock
private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+ @Mock
+ private lateinit var mockHandler: Handler
private lateinit var taskPositioner: VeiledResizeTaskPositioner
@@ -155,7 +158,8 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
mockDragStartListener,
mockTransactionFactory,
mockTransitions,
- mockInteractionJankMonitor
+ mockInteractionJankMonitor,
+ mockHandler,
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt
index 1b0b7d95e657..1b2ce9e4df36 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor.viewhost
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
import android.view.View
import android.view.WindowManager
import androidx.test.filters.SmallTest
@@ -27,6 +28,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -57,8 +59,54 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
onDrawTransaction = null
)
- assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
- assertThat(windowDecorViewHost.view()).isEqualTo(view)
+ assertThat(windowDecorViewHost.viewHost).isNotNull()
+ assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
+ }
+
+ @Test
+ fun updateView_alreadyLaidOut_relayouts() = runTest {
+ val windowDecorViewHost = createDefaultViewHost()
+ val view = View(context)
+ windowDecorViewHost.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null
+ )
+
+ val otherParams = WindowManager.LayoutParams(200, 200)
+ windowDecorViewHost.updateView(
+ view = view,
+ attrs = otherParams,
+ configuration = context.resources.configuration,
+ onDrawTransaction = null
+ )
+
+ assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
+ assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
+ .isEqualTo(otherParams.width)
+ }
+
+ @Test
+ fun updateView_replacingView_throws() = runTest {
+ val windowDecorViewHost = createDefaultViewHost()
+ val view = View(context)
+ windowDecorViewHost.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null
+ )
+
+ val otherView = View(context)
+ assertThrows(Exception::class.java) {
+ windowDecorViewHost.updateView(
+ view = otherView,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null
+ )
+ }
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -77,7 +125,7 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
)
// No view host yet, since the coroutine hasn't run.
- assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isFalse()
+ assertThat(windowDecorViewHost.viewHost).isNull()
windowDecorViewHost.updateView(
view = syncView,
@@ -89,13 +137,14 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
// Would run coroutine if it hadn't been cancelled.
advanceUntilIdle()
- assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
- assertThat(windowDecorViewHost.view()).isNotNull()
+ assertThat(windowDecorViewHost.viewHost).isNotNull()
+ assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
// View host view/attrs should match the ones from the sync call, plus, since the
// sync/async were made with different views, if the job hadn't been cancelled there
// would've been an exception thrown as replacing views isn't allowed.
- assertThat(windowDecorViewHost.view()).isEqualTo(syncView)
- assertThat(windowDecorViewHost.view()!!.layoutParams.width).isEqualTo(syncAttrs.width)
+ assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(syncView)
+ assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
+ .isEqualTo(syncAttrs.width)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -111,11 +160,11 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
configuration = context.resources.configuration,
)
- assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isFalse()
+ assertThat(windowDecorViewHost.viewHost).isNull()
advanceUntilIdle()
- assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(windowDecorViewHost.viewHost).isNotNull()
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -138,8 +187,9 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
advanceUntilIdle()
- assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
- assertThat(windowDecorViewHost.view()).isEqualTo(otherView)
+ assertThat(windowDecorViewHost.viewHost).isNotNull()
+ assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
+ assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(otherView)
}
@Test
@@ -157,15 +207,16 @@ class DefaultWindowDecorViewHostTest : ShellTestCase() {
val t = mock(SurfaceControl.Transaction::class.java)
windowDecorViewHost.release(t)
- verify(windowDecorViewHost.viewHostAdapter).release(t)
+ verify(windowDecorViewHost.viewHost!!).release()
+ verify(t).remove(windowDecorViewHost.surfaceControl)
}
private fun CoroutineScope.createDefaultViewHost() = DefaultWindowDecorViewHost(
context = context,
mainScope = this,
display = context.display,
- viewHostAdapter = spy(SurfaceControlViewHostAdapter(context, context.display)),
+ surfaceControlViewHostFactory = { c, d, wwm, s ->
+ spy(SurfaceControlViewHost(c, d, wwm, s))
+ }
)
-
- private fun DefaultWindowDecorViewHost.view(): View? = viewHostAdapter.viewHost?.view
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/PooledWindowDecorViewHostSupplierTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/PooledWindowDecorViewHostSupplierTest.kt
deleted file mode 100644
index a7e4213ad01d..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/PooledWindowDecorViewHostSupplierTest.kt
+++ /dev/null
@@ -1,181 +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.wm.shell.windowdecor.viewhost
-
-import android.testing.AndroidTestingRunner
-import android.view.SurfaceControl
-import androidx.test.filters.SmallTest
-import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.TestShellExecutor
-import com.android.wm.shell.sysui.ShellInit
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.kotlin.any
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
-import org.mockito.kotlin.verify
-import org.mockito.kotlin.whenever
-
-/**
- * Tests for [PooledWindowDecorViewHostSupplier].
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:PooledWindowDecorViewHostSupplierTest
- */
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class PooledWindowDecorViewHostSupplierTest : ShellTestCase() {
-
- private val testExecutor = TestShellExecutor()
- private val testShellInit = ShellInit(testExecutor)
- @Mock
- private lateinit var mockViewHostFactory: ReusableWindowDecorViewHost.Factory
-
- private lateinit var supplier: PooledWindowDecorViewHostSupplier
-
- @Test
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- }
-
- @Test
- fun onInit_warmsAndPoolsViewHosts() = runTest {
- supplier = createSupplier(maxPoolSize = 5, preWarmSize = 2)
- val mockViewHost1 = mock<ReusableWindowDecorViewHost>()
- val mockViewHost2 = mock<ReusableWindowDecorViewHost>()
- whenever(mockViewHostFactory
- .create(context, this, context.display, id = 0))
- .thenReturn(mockViewHost1)
- whenever(mockViewHostFactory
- .create(context, this, context.display, id = 1))
- .thenReturn(mockViewHost2)
-
- testExecutor.flushAll()
- advanceUntilIdle()
-
- // Both were warmed up.
- verify(mockViewHost1).warmUp()
- verify(mockViewHost2).warmUp()
- // Both were released, so re-acquiring them provides the same instance.
- assertThat(mockViewHost2)
- .isEqualTo(supplier.acquire(context, context.display))
- assertThat(mockViewHost1)
- .isEqualTo(supplier.acquire(context, context.display))
- }
-
- @Test(expected = Throwable::class)
- fun onInit_warmUpSizeExceedsPoolSize_throws() = runTest {
- createSupplier(maxPoolSize = 3, preWarmSize = 4)
- }
-
- @Test
- fun acquire_poolHasInstances_reuses() = runTest {
- supplier = createSupplier(maxPoolSize = 5, preWarmSize = 0)
-
- // Prepare the pool with one instance.
- val mockViewHost = mock<ReusableWindowDecorViewHost>()
- supplier.release(mockViewHost, SurfaceControl.Transaction())
-
- assertThat(mockViewHost)
- .isEqualTo(supplier.acquire(context, context.display))
- verify(mockViewHostFactory, never()).create(any(), any(), any(), any())
- }
-
- @Test
- fun acquire_pooledHasZeroInstances_creates() = runTest {
- supplier = createSupplier(maxPoolSize = 5, preWarmSize = 0)
-
- supplier.acquire(context, context.display)
-
- verify(mockViewHostFactory).create(context, this, context.display, id = 0)
- }
-
- @Test
- fun release_poolBelowLimit_caches() = runTest {
- supplier = createSupplier(maxPoolSize = 5, preWarmSize = 0)
-
- val mockViewHost = mock<ReusableWindowDecorViewHost>()
- val mockT = mock<SurfaceControl.Transaction>()
- supplier.release(mockViewHost, mockT)
-
- assertThat(mockViewHost)
- .isEqualTo(supplier.acquire(context, context.display))
- }
-
- @Test
- fun release_poolBelowLimit_doesNotReleaseViewHost() = runTest {
- supplier = createSupplier(maxPoolSize = 5, preWarmSize = 0)
-
- val mockViewHost = mock<ReusableWindowDecorViewHost>()
- val mockT = mock<SurfaceControl.Transaction>()
- supplier.release(mockViewHost, mockT)
-
- verify(mockViewHost, never()).release(mockT)
- }
-
- @Test
- fun release_poolAtLimit_doesNotCache() = runTest {
- supplier = createSupplier(maxPoolSize = 1, preWarmSize = 0)
- val mockT = mock<SurfaceControl.Transaction>()
- val mockViewHost = mock<ReusableWindowDecorViewHost>()
- supplier.release(mockViewHost, mockT) // Maxes pool.
-
- val mockViewHost2 = mock<ReusableWindowDecorViewHost>()
- supplier.release(mockViewHost2, mockT) // Beyond limit.
-
- assertThat(mockViewHost)
- .isEqualTo(supplier.acquire(context, context.display))
- // Second one wasn't cached, so the acquired one should've been a new instance.
- assertThat(mockViewHost2)
- .isNotEqualTo(supplier.acquire(context, context.display))
- }
-
- @Test
- fun release_poolAtLimit_releasesViewHost() = runTest {
- supplier = createSupplier(maxPoolSize = 1, preWarmSize = 0)
- val mockT = mock<SurfaceControl.Transaction>()
- val mockViewHost = mock<ReusableWindowDecorViewHost>()
- supplier.release(mockViewHost, mockT) // Maxes pool.
-
- val mockViewHost2 = mock<ReusableWindowDecorViewHost>()
- supplier.release(mockViewHost2, mockT) // Beyond limit.
-
- // Second one doesn't fit, so it needs to be released.
- verify(mockViewHost2).release(mockT)
- }
-
- private fun CoroutineScope.createSupplier(
- maxPoolSize: Int,
- preWarmSize: Int
- ) = PooledWindowDecorViewHostSupplier(
- context,
- this,
- testShellInit,
- mockViewHostFactory,
- maxPoolSize,
- preWarmSize
- ).also {
- testShellInit.init()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/ReusableWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/ReusableWindowDecorViewHostTest.kt
deleted file mode 100644
index de2444e34ca9..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/ReusableWindowDecorViewHostTest.kt
+++ /dev/null
@@ -1,182 +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.wm.shell.windowdecor.viewhost
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.SurfaceControl
-import android.view.View
-import android.view.WindowManager
-import androidx.test.filters.SmallTest
-import com.android.wm.shell.ShellTestCase
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
-import org.mockito.kotlin.spy
-import org.mockito.kotlin.verify
-
-/**
- * Tests for [ReusableWindowDecorViewHost].
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:ReusableWindowDecorViewHostTest
- */
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
-class ReusableWindowDecorViewHostTest : ShellTestCase() {
-
- @Test
- fun warmUp_addsRootView() = runTest {
- val reusableVH = createReusableViewHost().apply {
- warmUp()
- }
-
- assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
- assertThat(reusableVH.view()).isEqualTo(reusableVH.rootView)
- }
-
- @Test
- fun update_differentView_replacesView() = runTest {
- val view = View(context)
- val lp = WindowManager.LayoutParams()
- val reusableVH = createReusableViewHost()
- reusableVH.updateView(view, lp, context.resources.configuration, null)
-
- assertThat(reusableVH.rootView.childCount).isEqualTo(1)
- assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(view)
-
- val newView = View(context)
- val newLp = WindowManager.LayoutParams()
- reusableVH.updateView(newView, newLp, context.resources.configuration, null)
-
- assertThat(reusableVH.rootView.childCount).isEqualTo(1)
- assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(newView)
- }
-
- @OptIn(ExperimentalCoroutinesApi::class)
- @Test
- fun updateView_clearsPendingAsyncJob() = runTest {
- val reusableVH = createReusableViewHost()
- val asyncView = View(context)
- val syncView = View(context)
- val asyncAttrs = WindowManager.LayoutParams(100, 100)
- val syncAttrs = WindowManager.LayoutParams(200, 200)
-
- reusableVH.updateViewAsync(
- view = asyncView,
- attrs = asyncAttrs,
- configuration = context.resources.configuration,
- )
-
- // No view host yet, since the coroutine hasn't run.
- assertThat(reusableVH.viewHostAdapter.isInitialized()).isFalse()
-
- reusableVH.updateView(
- view = syncView,
- attrs = syncAttrs,
- configuration = context.resources.configuration,
- onDrawTransaction = null
- )
-
- // Would run coroutine if it hadn't been cancelled.
- advanceUntilIdle()
-
- assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
- // View host view/attrs should match the ones from the sync call, plus, since the
- // sync/async were made with different views, if the job hadn't been cancelled there
- // would've been an exception thrown as replacing views isn't allowed.
- assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(syncView)
- assertThat(reusableVH.view()!!.layoutParams.width).isEqualTo(syncAttrs.width)
- }
-
- @OptIn(ExperimentalCoroutinesApi::class)
- @Test
- fun updateViewAsync() = runTest {
- val reusableVH = createReusableViewHost()
- val view = View(context)
- val attrs = WindowManager.LayoutParams(100, 100)
-
- reusableVH.updateViewAsync(
- view = view,
- attrs = attrs,
- configuration = context.resources.configuration,
- )
-
- assertThat(reusableVH.viewHostAdapter.isInitialized()).isFalse()
-
- advanceUntilIdle()
-
- assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
- }
-
- @OptIn(ExperimentalCoroutinesApi::class)
- @Test
- fun updateViewAsync_clearsPendingAsyncJob() = runTest {
- val reusableVH = createReusableViewHost()
-
- val view = View(context)
- reusableVH.updateViewAsync(
- view = view,
- attrs = WindowManager.LayoutParams(100, 100),
- configuration = context.resources.configuration,
- )
- val otherView = View(context)
- reusableVH.updateViewAsync(
- view = otherView,
- attrs = WindowManager.LayoutParams(100, 100),
- configuration = context.resources.configuration,
- )
-
- advanceUntilIdle()
-
- assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
- assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(otherView)
- }
-
- @Test
- fun release() = runTest {
- val reusableVH = createReusableViewHost()
-
- val view = View(context)
- reusableVH.updateView(
- view = view,
- attrs = WindowManager.LayoutParams(100, 100),
- configuration = context.resources.configuration,
- onDrawTransaction = null
- )
-
- val t = mock(SurfaceControl.Transaction::class.java)
- reusableVH.release(t)
-
- verify(reusableVH.viewHostAdapter).release(t)
- }
-
- private fun CoroutineScope.createReusableViewHost() = ReusableWindowDecorViewHost(
- context = context,
- mainScope = this,
- display = context.display,
- id = 1,
- viewHostAdapter = spy(SurfaceControlViewHostAdapter(context, context.display)),
- )
-
- private fun ReusableWindowDecorViewHost.view(): View? = viewHostAdapter.viewHost?.view
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/SurfaceControlViewHostAdapterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/SurfaceControlViewHostAdapterTest.kt
deleted file mode 100644
index d6c80a7fffc1..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/SurfaceControlViewHostAdapterTest.kt
+++ /dev/null
@@ -1,144 +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.wm.shell.windowdecor.viewhost
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
-import android.view.View
-import android.view.WindowManager
-import androidx.test.filters.SmallTest
-import com.android.wm.shell.ShellTestCase
-import com.google.common.truth.Truth.assertThat
-import org.junit.Assert.assertThrows
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
-import org.mockito.kotlin.spy
-import org.mockito.kotlin.verify
-
-/**
- * Tests for [SurfaceControlViewHostAdapter].
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:SurfaceControlViewHostAdapterTest
- */
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
-class SurfaceControlViewHostAdapterTest : ShellTestCase() {
-
- private lateinit var adapter: SurfaceControlViewHostAdapter
-
- @Before
- fun setUp() {
- adapter = SurfaceControlViewHostAdapter(
- context,
- context.display,
- surfaceControlViewHostFactory = { c, d, wwm, s ->
- spy(SurfaceControlViewHost(c, d, wwm, s))
- }
- )
- }
-
- @Test
- fun prepareViewHost() {
- adapter.prepareViewHost(context.resources.configuration)
-
- assertThat(adapter.viewHost).isNotNull()
- }
-
- @Test
- fun prepareViewHost_alreadyCreated_skips() {
- adapter.prepareViewHost(context.resources.configuration)
-
- val viewHost = adapter.viewHost!!
-
- adapter.prepareViewHost(context.resources.configuration)
-
- assertThat(adapter.viewHost).isEqualTo(viewHost)
- }
-
- @Test
- fun updateView_layoutInViewHost() {
- val view = View(context)
- adapter.prepareViewHost(context.resources.configuration)
-
- adapter.updateView(
- view = view,
- attrs = WindowManager.LayoutParams(100, 100)
- )
-
- assertThat(adapter.isInitialized()).isTrue()
- assertThat(adapter.view()).isEqualTo(view)
- }
-
- @Test
- fun updateView_alreadyLaidOut_relayouts() {
- val view = View(context)
- adapter.prepareViewHost(context.resources.configuration)
- adapter.updateView(
- view = view,
- attrs = WindowManager.LayoutParams(100, 100)
- )
-
- val otherParams = WindowManager.LayoutParams(200, 200)
- adapter.updateView(
- view = view,
- attrs = otherParams
- )
-
- assertThat(adapter.view()).isEqualTo(view)
- assertThat(adapter.view()!!.layoutParams.width).isEqualTo(otherParams.width)
- }
-
- @Test
- fun updateView_replacingView_throws() {
- val view = View(context)
- adapter.prepareViewHost(context.resources.configuration)
- adapter.updateView(
- view = view,
- attrs = WindowManager.LayoutParams(100, 100)
- )
-
- val otherView = View(context)
- assertThrows(Exception::class.java) {
- adapter.updateView(
- view = otherView,
- attrs = WindowManager.LayoutParams(100, 100)
- )
- }
- }
-
- @Test
- fun release() {
- adapter.prepareViewHost(context.resources.configuration)
- adapter.updateView(
- view = View(context),
- attrs = WindowManager.LayoutParams(100, 100)
- )
-
- val mockT = mock(SurfaceControl.Transaction::class.java)
- adapter.release(mockT)
-
- verify(adapter.viewHost!!).release()
- verify(mockT).remove(adapter.rootSurface)
- }
-
- private fun SurfaceControlViewHostAdapter.view(): View? = viewHost?.view
-}
diff --git a/libs/appfunctions/Android.bp b/libs/appfunctions/Android.bp
new file mode 100644
index 000000000000..09e2f423c3ba
--- /dev/null
+++ b/libs/appfunctions/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_sdk_library {
+ name: "com.google.android.appfunctions.sidecar",
+ owner: "google",
+ srcs: ["java/**/*.java"],
+ api_packages: ["com.google.android.appfunctions.sidecar"],
+ dex_preopt: {
+ enabled: false,
+ },
+ system_ext_specific: true,
+ no_dist: true,
+ unsafe_ignore_missing_latest_api: true,
+}
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
new file mode 100644
index 000000000000..504e3290b0ae
--- /dev/null
+++ b/libs/appfunctions/api/current.txt
@@ -0,0 +1,49 @@
+// Signature format: 2.0
+package com.google.android.appfunctions.sidecar {
+
+ public final class AppFunctionManager {
+ ctor public AppFunctionManager(android.content.Context);
+ method public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+ }
+
+ public abstract class AppFunctionService extends android.app.Service {
+ ctor public AppFunctionService();
+ method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
+ method @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+ field @NonNull public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE";
+ field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
+ }
+
+ public final class ExecuteAppFunctionRequest {
+ method @NonNull public android.os.Bundle getExtras();
+ method @NonNull public String getFunctionIdentifier();
+ method @NonNull public android.app.appsearch.GenericDocument getParameters();
+ method @NonNull public String getTargetPackageName();
+ }
+
+ public static final class ExecuteAppFunctionRequest.Builder {
+ ctor public ExecuteAppFunctionRequest.Builder(@NonNull String, @NonNull String);
+ method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest build();
+ method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder setParameters(@NonNull android.app.appsearch.GenericDocument);
+ }
+
+ public final class ExecuteAppFunctionResponse {
+ method @Nullable public String getErrorMessage();
+ method @NonNull public android.os.Bundle getExtras();
+ method public int getResultCode();
+ method @NonNull public android.app.appsearch.GenericDocument getResultDocument();
+ method public boolean isSuccess();
+ method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
+ method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
+ field public static final String PROPERTY_RETURN_VALUE = "returnValue";
+ field public static final int RESULT_APP_UNKNOWN_ERROR = 2; // 0x2
+ field public static final int RESULT_DENIED = 1; // 0x1
+ field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
+ field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_TIMED_OUT = 5; // 0x5
+ }
+
+}
+
diff --git a/libs/appfunctions/api/removed.txt b/libs/appfunctions/api/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/libs/appfunctions/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/libs/appfunctions/api/system-current.txt b/libs/appfunctions/api/system-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/libs/appfunctions/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/libs/appfunctions/api/system-removed.txt b/libs/appfunctions/api/system-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/libs/appfunctions/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/libs/appfunctions/api/test-current.txt b/libs/appfunctions/api/test-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/libs/appfunctions/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/libs/appfunctions/api/test-removed.txt b/libs/appfunctions/api/test-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/libs/appfunctions/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
new file mode 100644
index 000000000000..b1dd4676a35e
--- /dev/null
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
@@ -0,0 +1,82 @@
+/*
+ * 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.google.android.appfunctions.sidecar;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.content.Context;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+
+/**
+ * Provides app functions related functionalities.
+ *
+ * <p>App function is a specific piece of functionality that an app offers to the system. These
+ * functionalities can be integrated into various system features.
+ *
+ * <p>This class wraps {@link android.app.appfunctions.AppFunctionManager} functionalities and
+ * exposes it here as a sidecar library (avoiding direct dependency on the platform API).
+ */
+// TODO(b/357551503): Implement get and set enabled app function APIs.
+// TODO(b/367329899): Add sidecar library to Android B builds.
+public final class AppFunctionManager {
+ private final android.app.appfunctions.AppFunctionManager mManager;
+ private final Context mContext;
+
+ /**
+ * Creates an instance.
+ *
+ * @param context A {@link Context}.
+ * @throws java.lang.IllegalStateException if the underlying {@link
+ * android.app.appfunctions.AppFunctionManager} is not found.
+ */
+ public AppFunctionManager(Context context) {
+ mContext = Objects.requireNonNull(context);
+ mManager = context.getSystemService(android.app.appfunctions.AppFunctionManager.class);
+ if (mManager == null) {
+ throw new IllegalStateException(
+ "Underlying AppFunctionManager system service not found.");
+ }
+ }
+
+ /**
+ * Executes the app function.
+ *
+ * <p>Proxies request and response to the underlying {@link
+ * android.app.appfunctions.AppFunctionManager#executeAppFunction}, converting the request and
+ * response in the appropriate type required by the function.
+ */
+ public void executeAppFunction(
+ @NonNull ExecuteAppFunctionRequest sidecarRequest,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+ Objects.requireNonNull(sidecarRequest);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ android.app.appfunctions.ExecuteAppFunctionRequest platformRequest =
+ SidecarConverter.getPlatformExecuteAppFunctionRequest(sidecarRequest);
+ mManager.executeAppFunction(
+ platformRequest, executor, (platformResponse) -> {
+ callback.accept(SidecarConverter.getSidecarExecuteAppFunctionResponse(
+ platformResponse));
+ });
+ }
+}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
new file mode 100644
index 000000000000..65959dfdf561
--- /dev/null
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
@@ -0,0 +1,114 @@
+/*
+ * 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.google.android.appfunctions.sidecar;
+
+import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class to provide app functions to the system.
+ *
+ * <p>Include the following in the manifest:
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".YourService"
+ * android:permission="android.permission.BIND_APP_FUNCTION_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.app.appfunctions.AppFunctionService" />
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ *
+ * <p>This class wraps {@link android.app.appfunctions.AppFunctionService} functionalities and
+ * exposes it here as a sidecar library (avoiding direct dependency on the platform API).
+ *
+ * @see AppFunctionManager
+ */
+public abstract class AppFunctionService extends Service {
+ /**
+ * The permission to only allow system access to the functions through {@link
+ * AppFunctionManagerService}.
+ */
+ @NonNull
+ public static final String BIND_APP_FUNCTION_SERVICE =
+ "android.permission.BIND_APP_FUNCTION_SERVICE";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the {@link BIND_APP_FUNCTION_SERVICE} permission so that other
+ * applications can not abuse it.
+ */
+ @NonNull
+ public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
+
+ private final Binder mBinder =
+ android.app.appfunctions.AppFunctionService.createBinder(
+ /* context= */ this,
+ /* onExecuteFunction= */ (platformRequest, callback) -> {
+ AppFunctionService.this.onExecuteFunction(
+ SidecarConverter.getSidecarExecuteAppFunctionRequest(
+ platformRequest),
+ (sidecarResponse) -> {
+ callback.accept(
+ SidecarConverter.getPlatformExecuteAppFunctionResponse(
+ sidecarResponse));
+ });
+ }
+ );
+
+ @NonNull
+ @Override
+ public final IBinder onBind(@Nullable Intent intent) {
+ return mBinder;
+ }
+
+ /**
+ * Called by the system to execute a specific app function.
+ *
+ * <p>This method is triggered when the system requests your AppFunctionService to handle a
+ * particular function you have registered and made available.
+ *
+ * <p>To ensure proper routing of function requests, assign a unique identifier to each
+ * function. This identifier doesn't need to be globally unique, but it must be unique within
+ * your app. For example, a function to order food could be identified as "orderFood". In most
+ * cases this identifier should come from the ID automatically generated by the AppFunctions
+ * SDK. You can determine the specific function to invoke by calling {@link
+ * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
+ *
+ * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
+ * thread and dispatch the result with the given callback. You should always report back the
+ * result using the callback, no matter if the execution was successful or not.
+ *
+ * @param request The function execution request.
+ * @param callback A callback to report back the result.
+ */
+ @MainThread
+ public abstract void onExecuteFunction(
+ @NonNull ExecuteAppFunctionRequest request,
+ @NonNull Consumer<ExecuteAppFunctionResponse> callback);
+}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
new file mode 100644
index 000000000000..fa6d2ff12313
--- /dev/null
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.google.android.appfunctions.sidecar;
+
+import android.annotation.NonNull;
+import android.app.appsearch.GenericDocument;
+import android.os.Bundle;
+
+import java.util.Objects;
+
+/**
+ * A request to execute an app function.
+ *
+ * <p>This class copies {@link android.app.appfunctions.ExecuteAppFunctionRequest} without parcel
+ * functionality and exposes it here as a sidecar library (avoiding direct dependency on the
+ * platform API).
+ */
+public final class ExecuteAppFunctionRequest {
+ /** Returns the package name of the app that hosts the function. */
+ @NonNull private final String mTargetPackageName;
+
+ /**
+ * Returns the unique string identifier of the app function to be executed. TODO(b/357551503):
+ * Document how callers can get the available function identifiers.
+ */
+ @NonNull private final String mFunctionIdentifier;
+
+ /** Returns additional metadata relevant to this function execution request. */
+ @NonNull private final Bundle mExtras;
+
+ /**
+ * Returns the parameters required to invoke this function. Within this [GenericDocument], the
+ * property names are the names of the function parameters and the property values are the
+ * values of those parameters.
+ *
+ * <p>The document may have missing parameters. Developers are advised to implement defensive
+ * handling measures.
+ *
+ * <p>TODO(b/357551503): Document how function parameters can be obtained for function execution
+ */
+ @NonNull private final GenericDocument mParameters;
+
+ private ExecuteAppFunctionRequest(
+ @NonNull String targetPackageName,
+ @NonNull String functionIdentifier,
+ @NonNull Bundle extras,
+ @NonNull GenericDocument parameters) {
+ mTargetPackageName = Objects.requireNonNull(targetPackageName);
+ mFunctionIdentifier = Objects.requireNonNull(functionIdentifier);
+ mExtras = Objects.requireNonNull(extras);
+ mParameters = Objects.requireNonNull(parameters);
+ }
+
+ /** Returns the package name of the app that hosts the function. */
+ @NonNull
+ public String getTargetPackageName() {
+ return mTargetPackageName;
+ }
+
+ /** Returns the unique string identifier of the app function to be executed. */
+ @NonNull
+ public String getFunctionIdentifier() {
+ return mFunctionIdentifier;
+ }
+
+ /**
+ * Returns the function parameters. The key is the parameter name, and the value is the
+ * parameter value.
+ *
+ * <p>The bundle may have missing parameters. Developers are advised to implement defensive
+ * handling measures.
+ */
+ @NonNull
+ public GenericDocument getParameters() {
+ return mParameters;
+ }
+
+ /** Returns the additional data relevant to this function execution. */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /** Builder for {@link ExecuteAppFunctionRequest}. */
+ public static final class Builder {
+ @NonNull private final String mTargetPackageName;
+ @NonNull private final String mFunctionIdentifier;
+ @NonNull private Bundle mExtras = Bundle.EMPTY;
+
+ @NonNull
+ private GenericDocument mParameters = new GenericDocument.Builder<>("", "", "").build();
+
+ public Builder(@NonNull String targetPackageName, @NonNull String functionIdentifier) {
+ mTargetPackageName = Objects.requireNonNull(targetPackageName);
+ mFunctionIdentifier = Objects.requireNonNull(functionIdentifier);
+ }
+
+ /** Sets the additional data relevant to this function execution. */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = Objects.requireNonNull(extras);
+ return this;
+ }
+
+ /** Sets the function parameters. */
+ @NonNull
+ public Builder setParameters(@NonNull GenericDocument parameters) {
+ Objects.requireNonNull(parameters);
+ mParameters = parameters;
+ return this;
+ }
+
+ /** Builds the {@link ExecuteAppFunctionRequest}. */
+ @NonNull
+ public ExecuteAppFunctionRequest build() {
+ return new ExecuteAppFunctionRequest(
+ mTargetPackageName,
+ mFunctionIdentifier,
+ mExtras,
+ mParameters);
+ }
+ }
+}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
new file mode 100644
index 000000000000..60c25fae58d1
--- /dev/null
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
@@ -0,0 +1,240 @@
+/*
+ * 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.google.android.appfunctions.sidecar;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.appsearch.GenericDocument;
+import android.os.Bundle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The response to an app function execution.
+ *
+ * <p>This class copies {@link android.app.appfunctions.ExecuteAppFunctionResponse} without parcel
+ * functionality and exposes it here as a sidecar library (avoiding direct dependency on the
+ * platform API).
+ */
+public final class ExecuteAppFunctionResponse {
+ /**
+ * The name of the property that stores the function return value within the {@code
+ * resultDocument}.
+ *
+ * <p>See {@link GenericDocument#getProperty(String)} for more information.
+ *
+ * <p>If the function returns {@code void} or throws an error, the {@code resultDocument} will
+ * be empty {@link GenericDocument}.
+ *
+ * <p>If the {@code resultDocument} is empty, {@link GenericDocument#getProperty(String)} will
+ * return {@code null}.
+ *
+ * <p>See {@link #getResultDocument} for more information on extracting the return value.
+ */
+ public static final String PROPERTY_RETURN_VALUE = "returnValue";
+
+ /** The call was successful. */
+ public static final int RESULT_OK = 0;
+
+ /** The caller does not have the permission to execute an app function. */
+ public static final int RESULT_DENIED = 1;
+
+ /** An unknown error occurred while processing the call in the AppFunctionService. */
+ public static final int RESULT_APP_UNKNOWN_ERROR = 2;
+
+ /**
+ * An internal error occurred within AppFunctionManagerService.
+ *
+ * <p>This error may be considered similar to {@link IllegalStateException}
+ */
+ public static final int RESULT_INTERNAL_ERROR = 3;
+
+ /**
+ * The caller supplied invalid arguments to the call.
+ *
+ * <p>This error may be considered similar to {@link IllegalArgumentException}.
+ */
+ public static final int RESULT_INVALID_ARGUMENT = 4;
+
+ /** The operation was timed out. */
+ public static final int RESULT_TIMED_OUT = 5;
+
+ /** The result code of the app function execution. */
+ @ResultCode private final int mResultCode;
+
+ /**
+ * The error message associated with the result, if any. This is {@code null} if the result code
+ * is {@link #RESULT_OK}.
+ */
+ @Nullable private final String mErrorMessage;
+
+ /**
+ * Returns the return value of the executed function.
+ *
+ * <p>The return value is stored in a {@link GenericDocument} with the key {@link
+ * #PROPERTY_RETURN_VALUE}.
+ *
+ * <p>See {@link #getResultDocument} for more information on extracting the return value.
+ */
+ @NonNull private final GenericDocument mResultDocument;
+
+ /** Returns the additional metadata data relevant to this function execution response. */
+ @NonNull private final Bundle mExtras;
+
+ private ExecuteAppFunctionResponse(
+ @NonNull GenericDocument resultDocument,
+ @NonNull Bundle extras,
+ @ResultCode int resultCode,
+ @Nullable String errorMessage) {
+ mResultDocument = Objects.requireNonNull(resultDocument);
+ mExtras = Objects.requireNonNull(extras);
+ mResultCode = resultCode;
+ mErrorMessage = errorMessage;
+ }
+
+ /**
+ * Returns result codes from throwable.
+ *
+ * @hide
+ */
+ static @ResultCode int getResultCode(@NonNull Throwable t) {
+ if (t instanceof IllegalArgumentException) {
+ return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT;
+ }
+ return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR;
+ }
+
+ /**
+ * Returns a successful response.
+ *
+ * @param resultDocument The return value of the executed function.
+ * @param extras The additional metadata data relevant to this function execution response.
+ */
+ @NonNull
+ public static ExecuteAppFunctionResponse newSuccess(
+ @NonNull GenericDocument resultDocument, @Nullable Bundle extras) {
+ Objects.requireNonNull(resultDocument);
+ Bundle actualExtras = getActualExtras(extras);
+
+ return new ExecuteAppFunctionResponse(
+ resultDocument, actualExtras, RESULT_OK, /* errorMessage= */ null);
+ }
+
+ /**
+ * Returns a failure response.
+ *
+ * @param resultCode The result code of the app function execution.
+ * @param extras The additional metadata data relevant to this function execution response.
+ * @param errorMessage The error message associated with the result, if any.
+ */
+ @NonNull
+ public static ExecuteAppFunctionResponse newFailure(
+ @ResultCode int resultCode, @Nullable String errorMessage, @Nullable Bundle extras) {
+ if (resultCode == RESULT_OK) {
+ throw new IllegalArgumentException("resultCode must not be RESULT_OK");
+ }
+ Bundle actualExtras = getActualExtras(extras);
+ GenericDocument emptyDocument = new GenericDocument.Builder<>("", "", "").build();
+ return new ExecuteAppFunctionResponse(
+ emptyDocument, actualExtras, resultCode, errorMessage);
+ }
+
+ private static Bundle getActualExtras(@Nullable Bundle extras) {
+ if (extras == null) {
+ return Bundle.EMPTY;
+ }
+ return extras;
+ }
+
+ /**
+ * Returns a generic document containing the return value of the executed function.
+ *
+ * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
+ *
+ * <p>An empty document is returned if {@link #isSuccess} is {@code false} or if the executed
+ * function does not produce a return value.
+ *
+ * <p>Sample code for extracting the return value:
+ *
+ * <pre>
+ * GenericDocument resultDocument = response.getResultDocument();
+ * Object returnValue = resultDocument.getProperty(PROPERTY_RETURN_VALUE);
+ * if (returnValue != null) {
+ * // Cast returnValue to expected type, or use {@link GenericDocument#getPropertyString},
+ * // {@link GenericDocument#getPropertyLong} etc.
+ * // Do something with the returnValue
+ * }
+ * </pre>
+ */
+ @NonNull
+ public GenericDocument getResultDocument() {
+ return mResultDocument;
+ }
+
+ /** Returns the extras of the app function execution. */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getResultCode} equals {@link
+ * ExecuteAppFunctionResponse#RESULT_OK}.
+ */
+ public boolean isSuccess() {
+ return getResultCode() == RESULT_OK;
+ }
+
+ /**
+ * Returns one of the {@code RESULT} constants defined in {@link ExecuteAppFunctionResponse}.
+ */
+ @ResultCode
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ /**
+ * Returns the error message associated with this result.
+ *
+ * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}.
+ */
+ @Nullable
+ public String getErrorMessage() {
+ return mErrorMessage;
+ }
+
+ /**
+ * Result codes.
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = {"RESULT_"},
+ value = {
+ RESULT_OK,
+ RESULT_DENIED,
+ RESULT_APP_UNKNOWN_ERROR,
+ RESULT_INTERNAL_ERROR,
+ RESULT_INVALID_ARGUMENT,
+ RESULT_TIMED_OUT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ResultCode {}
+}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java
new file mode 100644
index 000000000000..b1b05f79f33f
--- /dev/null
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java
@@ -0,0 +1,104 @@
+/*
+ * 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.google.android.appfunctions.sidecar;
+
+import android.annotation.NonNull;
+
+/**
+ * Utility class containing methods to convert Sidecar objects of AppFunctions API into the
+ * underlying platform classes.
+ *
+ * @hide
+ */
+public final class SidecarConverter {
+ private SidecarConverter() {}
+
+ /**
+ * Converts sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest}
+ * into platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest}
+ *
+ * @hide
+ */
+ @NonNull
+ public static android.app.appfunctions.ExecuteAppFunctionRequest
+ getPlatformExecuteAppFunctionRequest(@NonNull ExecuteAppFunctionRequest request) {
+ return new
+ android.app.appfunctions.ExecuteAppFunctionRequest.Builder(
+ request.getTargetPackageName(),
+ request.getFunctionIdentifier())
+ .setExtras(request.getExtras())
+ .setParameters(request.getParameters())
+ .build();
+ }
+
+ /**
+ * Converts sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse}
+ * into platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse}
+ *
+ * @hide
+ */
+ @NonNull
+ public static android.app.appfunctions.ExecuteAppFunctionResponse
+ getPlatformExecuteAppFunctionResponse(@NonNull ExecuteAppFunctionResponse response) {
+ if (response.isSuccess()) {
+ return android.app.appfunctions.ExecuteAppFunctionResponse.newSuccess(
+ response.getResultDocument(), response.getExtras());
+ } else {
+ return android.app.appfunctions.ExecuteAppFunctionResponse.newFailure(
+ response.getResultCode(),
+ response.getErrorMessage(),
+ response.getExtras());
+ }
+ }
+
+ /**
+ * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest}
+ * into sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest}
+ *
+ * @hide
+ */
+ @NonNull
+ public static ExecuteAppFunctionRequest getSidecarExecuteAppFunctionRequest(
+ @NonNull android.app.appfunctions.ExecuteAppFunctionRequest request) {
+ return new ExecuteAppFunctionRequest.Builder(
+ request.getTargetPackageName(),
+ request.getFunctionIdentifier())
+ .setExtras(request.getExtras())
+ .setParameters(request.getParameters())
+ .build();
+ }
+
+ /**
+ * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse}
+ * into sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse}
+ *
+ * @hide
+ */
+ @NonNull
+ public static ExecuteAppFunctionResponse getSidecarExecuteAppFunctionResponse(
+ @NonNull android.app.appfunctions.ExecuteAppFunctionResponse response) {
+ if (response.isSuccess()) {
+ return ExecuteAppFunctionResponse.newSuccess(
+ response.getResultDocument(), response.getExtras());
+ } else {
+ return ExecuteAppFunctionResponse.newFailure(
+ response.getResultCode(),
+ response.getErrorMessage(),
+ response.getExtras());
+ }
+ }
+}
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 185436160349..84bd45dfc012 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -264,6 +264,7 @@ Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBy
, mPixelStorageType(PixelStorageType::Heap) {
mPixelStorage.heap.address = address;
mPixelStorage.heap.size = size;
+ traceBitmapCreate();
}
Bitmap::Bitmap(SkPixelRef& pixelRef, const SkImageInfo& info)
@@ -272,6 +273,7 @@ Bitmap::Bitmap(SkPixelRef& pixelRef, const SkImageInfo& info)
, mPixelStorageType(PixelStorageType::WrappedPixelRef) {
pixelRef.ref();
mPixelStorage.wrapped.pixelRef = &pixelRef;
+ traceBitmapCreate();
}
Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes)
@@ -281,6 +283,7 @@ Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info
mPixelStorage.ashmem.address = address;
mPixelStorage.ashmem.fd = fd;
mPixelStorage.ashmem.size = mappedSize;
+ traceBitmapCreate();
}
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
@@ -297,10 +300,12 @@ Bitmap::Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes
setImmutable(); // HW bitmaps are always immutable
mImage = SkImages::DeferredFromAHardwareBuffer(buffer, mInfo.alphaType(),
mInfo.refColorSpace());
+ traceBitmapCreate();
}
#endif
Bitmap::~Bitmap() {
+ traceBitmapDelete();
switch (mPixelStorageType) {
case PixelStorageType::WrappedPixelRef:
mPixelStorage.wrapped.pixelRef->unref();
@@ -572,4 +577,28 @@ void Bitmap::setGainmap(sp<uirenderer::Gainmap>&& gainmap) {
mGainmap = std::move(gainmap);
}
+std::mutex Bitmap::mLock{};
+size_t Bitmap::mTotalBitmapBytes = 0;
+size_t Bitmap::mTotalBitmapCount = 0;
+
+void Bitmap::traceBitmapCreate() {
+ if (ATRACE_ENABLED()) {
+ std::lock_guard lock{mLock};
+ mTotalBitmapBytes += getAllocationByteCount();
+ mTotalBitmapCount++;
+ ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
+ ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
+ }
+}
+
+void Bitmap::traceBitmapDelete() {
+ if (ATRACE_ENABLED()) {
+ std::lock_guard lock{mLock};
+ mTotalBitmapBytes -= getAllocationByteCount();
+ mTotalBitmapCount--;
+ ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
+ ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
+ }
+}
+
} // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index dd344e2f5517..3d55d859ed5f 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -25,6 +25,7 @@
#include <cutils/compiler.h>
#include <utils/StrongPointer.h>
+#include <mutex>
#include <optional>
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
@@ -227,6 +228,13 @@ private:
} mPixelStorage;
sk_sp<SkImage> mImage; // Cache is used only for HW Bitmaps with Skia pipeline.
+
+ // for tracing total number and memory usage of bitmaps
+ static std::mutex mLock;
+ static size_t mTotalBitmapBytes;
+ static size_t mTotalBitmapCount;
+ void traceBitmapCreate();
+ void traceBitmapDelete();
};
} // namespace android
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index d7bf20130b71..e13e136550ca 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -73,6 +73,7 @@ static void simplifyPaint(int color, Paint* paint) {
}
paint->setStrokeJoin(SkPaint::kRound_Join);
paint->setLooper(nullptr);
+ paint->setBlendMode(SkBlendMode::kSrcOver);
}
class DrawTextFunctor {
diff --git a/libs/hwui/platform/host/thread/ThreadBase.h b/libs/hwui/platform/host/thread/ThreadBase.h
index d709430cc9b6..b4e7bd34971a 100644
--- a/libs/hwui/platform/host/thread/ThreadBase.h
+++ b/libs/hwui/platform/host/thread/ThreadBase.h
@@ -48,7 +48,7 @@ protected:
nsecs_t nextWakeup = mQueue.nextWakeup(lock);
std::chrono::nanoseconds duration = std::chrono::nanoseconds::max();
if (nextWakeup < std::numeric_limits<nsecs_t>::max()) {
- int timeout = nextWakeup - WorkQueue::clock::now();
+ nsecs_t timeout = nextWakeup - WorkQueue::clock::now();
if (timeout < 0) timeout = 0;
duration = std::chrono::nanoseconds(timeout);
}
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 1afef75bc741..d993b8715260 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -64,25 +64,6 @@ MouseCursorController::~MouseCursorController() {
mLocked.pointerSprite.clear();
}
-std::optional<FloatRect> MouseCursorController::getBounds() const {
- std::scoped_lock lock(mLock);
-
- return getBoundsLocked();
-}
-
-std::optional<FloatRect> MouseCursorController::getBoundsLocked() const REQUIRES(mLock) {
- if (!mLocked.viewport.isValid()) {
- return {};
- }
-
- return FloatRect{
- static_cast<float>(mLocked.viewport.logicalLeft),
- static_cast<float>(mLocked.viewport.logicalTop),
- static_cast<float>(mLocked.viewport.logicalRight - 1),
- static_cast<float>(mLocked.viewport.logicalBottom - 1),
- };
-}
-
void MouseCursorController::move(float deltaX, float deltaY) {
#if DEBUG_MOUSE_CURSOR_UPDATES
ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
@@ -105,11 +86,20 @@ void MouseCursorController::setPosition(float x, float y) {
}
void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
- const auto bounds = getBoundsLocked();
- if (!bounds) return;
+ const auto& v = mLocked.viewport;
+ if (!v.isValid()) return;
- mLocked.pointerX = std::max(bounds->left, std::min(bounds->right, x));
- mLocked.pointerY = std::max(bounds->top, std::min(bounds->bottom, y));
+ // The valid bounds for a mouse cursor. Since the right and bottom edges are considered outside
+ // the display, clip the bounds by one pixel instead of letting the cursor get arbitrarily
+ // close to the outside edge.
+ const FloatRect bounds{
+ static_cast<float>(mLocked.viewport.logicalLeft),
+ static_cast<float>(mLocked.viewport.logicalTop),
+ static_cast<float>(mLocked.viewport.logicalRight - 1),
+ static_cast<float>(mLocked.viewport.logicalBottom - 1),
+ };
+ mLocked.pointerX = std::max(bounds.left, std::min(bounds.right, x));
+ mLocked.pointerY = std::max(bounds.top, std::min(bounds.bottom, y));
updatePointerLocked();
}
@@ -216,9 +206,11 @@ void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
// Reset cursor position to center if size or display changed.
if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
oldDisplayHeight != newDisplayHeight) {
- if (const auto bounds = getBoundsLocked(); bounds) {
- mLocked.pointerX = (bounds->left + bounds->right) * 0.5f;
- mLocked.pointerY = (bounds->top + bounds->bottom) * 0.5f;
+ if (viewport.isValid()) {
+ // Use integer coordinates as the starting point for the cursor location.
+ // We usually expect display sizes to be even numbers, so the flooring is precautionary.
+ mLocked.pointerX = std::floor((viewport.logicalLeft + viewport.logicalRight) / 2);
+ mLocked.pointerY = std::floor((viewport.logicalTop + viewport.logicalBottom) / 2);
// Reload icon resources for density may be changed.
loadResourcesLocked(getAdditionalMouseResources);
} else {
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index 860034141a0b..12b31a8c531a 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -43,7 +43,6 @@ public:
MouseCursorController(PointerControllerContext& context);
~MouseCursorController();
- std::optional<FloatRect> getBounds() const;
void move(float deltaX, float deltaY);
void setPosition(float x, float y);
FloatPoint getPosition() const;
@@ -104,7 +103,6 @@ private:
} mLocked GUARDED_BY(mLock);
- std::optional<FloatRect> getBoundsLocked() const;
void setPositionLocked(float x, float y);
void updatePointerLocked();
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 5ae967bc369a..78d7d3a7051b 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -138,10 +138,6 @@ std::mutex& PointerController::getLock() const {
return mDisplayInfoListener->mLock;
}
-std::optional<FloatRect> PointerController::getBounds() const {
- return mCursorController.getBounds();
-}
-
void PointerController::move(float deltaX, float deltaY) {
const ui::LogicalDisplayId displayId = mCursorController.getDisplayId();
vec2 transformed;
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 4d1e1d733cc1..ee8d1211341f 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -51,7 +51,6 @@ public:
~PointerController() override;
- std::optional<FloatRect> getBounds() const override;
void move(float deltaX, float deltaY) override;
void setPosition(float x, float y) override;
FloatPoint getPosition() const override;
@@ -166,9 +165,6 @@ public:
~TouchPointerController() override;
- std::optional<FloatRect> getBounds() const override {
- LOG_ALWAYS_FATAL("Should not be called");
- }
void move(float, float) override {
LOG_ALWAYS_FATAL("Should not be called");
}
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index dcf5c5b46478..cddc337d95b8 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -111,6 +111,17 @@ flag {
}
flag {
+ name: "enable_ni_supl_message_injection_by_carrier_config_bugfix"
+ namespace: "location"
+ description: "Flag for enabling NI SUPL message injection by carrier config"
+ bug: "242105192"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_ni_supl_message_injection_by_carrier_config"
namespace: "location"
description: "Flag for enabling NI SUPL message injection by carrier config"
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e2e7a46a0ad0..cdb517b3fd3e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6318,7 +6318,14 @@ public class AudioManager {
/**
* @hide
* Get the audio devices that would be used for the routing of the given audio attributes.
- * @param attributes the {@link AudioAttributes} for which the routing is being queried
+ * @param attributes the {@link AudioAttributes} for which the routing is being queried.
+ * For queries about output devices (playback use cases), a valid usage must be specified in
+ * the audio attributes via AudioAttributes.Builder.setUsage(). The capture preset MUST NOT
+ * be changed from default.
+ * For queries about input devices (capture use case), a valid capture preset MUST be
+ * specified in the audio attributes via AudioAttributes.Builder.setCapturePreset(). If a
+ * capture preset is present, then this has precedence over any usage or content type also
+ * present in the audio attrirutes.
* @return an empty list if there was an issue with the request, a list of audio devices
* otherwise (typically one device, except for duplicated paths).
*/
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 3cc0ad27bd9a..31f89960836b 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -90,24 +90,24 @@ public final class MediaProjection {
mDisplayManager = displayManager;
}
- /**
- * Register a listener to receive notifications about when the {@link MediaProjection} or captured
- * content changes state.
- *
- * <p>The callback must be registered before invoking {@link #createVirtualDisplay(String, int,
- * int, int, int, Surface, VirtualDisplay.Callback, Handler)} to ensure that any notifications on
- * the callback are not missed. The client must implement {@link Callback#onStop()} and clean up
- * any resources it is holding, e.g. the {@link VirtualDisplay} and {@link Surface}. This should
- * also update any application UI indicating the MediaProjection status as MediaProjection has
- * stopped.
- *
- * @param callback The callback to call.
- * @param handler The handler on which the callback should be invoked, or null if the callback
- * should be invoked on the calling thread's looper.
- * @throws NullPointerException If the given callback is null.
- * @see #unregisterCallback
- */
- public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) {
+ /**
+ * Register a listener to receive notifications about when the {@link MediaProjection} or
+ * captured content changes state.
+ *
+ * <p>The callback must be registered before invoking {@link #createVirtualDisplay(String, int,
+ * int, int, int, Surface, VirtualDisplay.Callback, Handler)} to ensure that any notifications
+ * on the callback are not missed. The client must implement {@link Callback#onStop()} to
+ * properly handle MediaProjection clean up any resources it is holding, e.g. the {@link
+ * VirtualDisplay} and {@link Surface}. This should also update any application UI indicating
+ * the MediaProjection status as MediaProjection has stopped.
+ *
+ * @param callback The callback to call.
+ * @param handler The handler on which the callback should be invoked, or null if the callback
+ * should be invoked on the calling thread's looper.
+ * @throws NullPointerException If the given callback is null.
+ * @see #unregisterCallback
+ */
+ public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) {
try {
final Callback c = Objects.requireNonNull(callback);
if (handler == null) {
@@ -313,7 +313,7 @@ public final class MediaProjection {
*/
public abstract static class Callback {
/**
- * Called when the MediaProjection session is no longer valid.
+ * Called when the MediaProjection session has been stopped and is no longer valid.
*
* <p>Once a MediaProjection has been stopped, it's up to the application to release any
* resources it may be holding (e.g. releasing the {@link VirtualDisplay} and {@link
@@ -321,9 +321,9 @@ public final class MediaProjection {
* it should be updated to indicate that MediaProjection is no longer active.
*
* <p>MediaProjection stopping can be a result of the system stopping the ongoing
- * MediaProjection due to various reasons, such as another MediaProjection session starting.
- * MediaProjection may also stop due to the user explicitly stopping ongoing MediaProjection
- * via any available system-level UI.
+ * MediaProjection due to various reasons, such as another MediaProjection session starting,
+ * a user stopping the session via UI affordances in system-level UI, or the screen being
+ * locked.
*
* <p>After this callback any call to {@link MediaProjection#createVirtualDisplay} will
* fail, even if no such {@link VirtualDisplay} was ever created for this MediaProjection
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 03fd2c67fe46..dc55e41fb74c 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -60,13 +60,14 @@ import java.util.Map;
* <li>Register a {@link MediaProjection.Callback} by calling {@link
* MediaProjection#registerCallback(MediaProjection.Callback, Handler)}. This is required to
* receive notifications about when the {@link MediaProjection} or captured content changes
- * state. When receiving an `onStop()` callback, the client must clean up any resources it is
- * holding, e.g. the {@link VirtualDisplay} and {@link Surface}. The MediaProjection may
- * further no longer create any new {@link VirtualDisplay}s via {@link
- * MediaProjection#createVirtualDisplay(String, int, int, int, int, Surface,
- * VirtualDisplay.Callback, Handler)}. Note that the `onStop()` callback can be a result of
- * the system stopping MediaProjection due to various reasons or the user stopping the
- * MediaProjection via UI affordances in system-level UI.
+ * state. When receiving an `onStop()` callback the {@link MediaProjection} session has been
+ * finished and the client must clean up any resources it is holding, e.g. the {@link
+ * VirtualDisplay} and {@link Surface}. The MediaProjection may further no longer create any
+ * new {@link VirtualDisplay}s via {@link MediaProjection#createVirtualDisplay(String, int,
+ * int, int, int, Surface, VirtualDisplay.Callback, Handler)}. Note that the `onStop()`
+ * callback can be a result of the system stopping MediaProjection due to various reasons.
+ * This includes the user stopping the MediaProjection via UI affordances in system-level UI,
+ * the screen being locked, or another {@link MediaProjection} session starting.
* <li>Start the screen capture session for media projection by calling {@link
* MediaProjection#createVirtualDisplay(String, int, int, int, int, Surface,
* android.hardware.display.VirtualDisplay.Callback, Handler)}.
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index bc8a7afd94e9..9603c0a9f7c7 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -60,8 +60,13 @@ package android.nfc {
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback);
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void setControllerAlwaysOn(int);
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback);
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int DISABLE = 0; // 0x0
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_DEFAULT = 1; // 0x1
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_EE = 3; // 0x3
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_TRANSPARENT = 2; // 0x2
field public static final int HCE_ACTIVATE = 1; // 0x1
field public static final int HCE_DATA_TRANSFERRED = 2; // 0x2
field public static final int HCE_DEACTIVATE = 3; // 0x3
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index e2ec95215d1a..246efc7ca557 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -73,7 +73,7 @@ interface INfcAdapter
boolean setNfcSecure(boolean enable);
NfcAntennaInfo getNfcAntennaInfo();
- boolean setControllerAlwaysOn(boolean value);
+ void setControllerAlwaysOn(int mode);
boolean isControllerAlwaysOn();
boolean isControllerAlwaysOnSupported();
void registerControllerAlwaysOnListener(in INfcControllerAlwaysOnListener listener);
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index f47879385070..de85f1eeabe3 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -559,6 +559,18 @@ public final class NfcAdapter {
@Retention(RetentionPolicy.SOURCE)
public @interface TagIntentAppPreferenceResult {}
+ /**
+ * Mode Type for {@link NfcOemExtension#setControllerAlwaysOn(int)}.
+ * @hide
+ */
+ public static final int CONTROLLER_ALWAYS_ON_MODE_DEFAULT = 1;
+
+ /**
+ * Mode Type for {@link NfcOemExtension#setControllerAlwaysOn(int)}.
+ * @hide
+ */
+ public static final int CONTROLLER_ALWAYS_ON_DISABLE = 0;
+
// Guarded by sLock
static boolean sIsInitialized = false;
static boolean sHasNfcFeature;
@@ -2330,7 +2342,8 @@ public final class NfcAdapter {
* FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
* FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
* are unavailable
- * @return void
+ * @return true if feature is supported by the device and operation has bee initiated,
+ * false if the feature is not supported by the device.
* @hide
*/
@SystemApi
@@ -2339,8 +2352,13 @@ public final class NfcAdapter {
if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
- return callServiceReturn(() -> sService.setControllerAlwaysOn(value), false);
-
+ int mode = value ? CONTROLLER_ALWAYS_ON_MODE_DEFAULT : CONTROLLER_ALWAYS_ON_DISABLE;
+ try {
+ callService(() -> sService.setControllerAlwaysOn(mode));
+ } catch (UnsupportedOperationException e) {
+ return false;
+ }
+ return true;
}
/**
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index d51b704a7e7f..45038d41e237 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -70,6 +70,58 @@ public final class NfcOemExtension {
private boolean mRfDiscoveryStarted = false;
/**
+ * Mode Type for {@link #setControllerAlwaysOn(int)}.
+ * Enables the controller in default mode when NFC is disabled (existing API behavior).
+ * works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int ENABLE_DEFAULT = NfcAdapter.CONTROLLER_ALWAYS_ON_MODE_DEFAULT;
+
+ /**
+ * Mode Type for {@link #setControllerAlwaysOn(int)}.
+ * Enables the controller in transparent mode when NFC is disabled.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int ENABLE_TRANSPARENT = 2;
+
+ /**
+ * Mode Type for {@link #setControllerAlwaysOn(int)}.
+ * Enables the controller and initializes and enables the EE subsystem when NFC is disabled.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int ENABLE_EE = 3;
+
+ /**
+ * Mode Type for {@link #setControllerAlwaysOn(int)}.
+ * Disable the Controller Always On Mode.
+ * works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int DISABLE = NfcAdapter.CONTROLLER_ALWAYS_ON_DISABLE;
+
+ /**
+ * Possible controller modes for {@link #setControllerAlwaysOn(int)}.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "" }, value = {
+ ENABLE_DEFAULT,
+ ENABLE_TRANSPARENT,
+ ENABLE_EE,
+ DISABLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ControllerMode{}
+
+ /**
* Event that Host Card Emulation is activated.
*/
public static final int HCE_ACTIVATE = 1;
@@ -389,6 +441,32 @@ public final class NfcOemExtension {
NfcAdapter.sService.fetchActiveNfceeList(), new ArrayList<String>());
}
+ /**
+ * Sets NFC controller always on feature.
+ * <p>This API is for the NFCC internal state management. It allows to discriminate
+ * the controller function from the NFC function by keeping the NFC controller on without
+ * any NFC RF enabled if necessary.
+ * <p>This call is asynchronous, register listener {@link NfcAdapter.ControllerAlwaysOnListener}
+ * by {@link NfcAdapter#registerControllerAlwaysOnListener} to find out when the operation is
+ * complete.
+ * @param mode one of {@link ControllerMode} modes
+ * @throws UnsupportedOperationException if
+ * <li> if FEATURE_NFC, FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable </li>
+ * <li> if the feature is unavailable @see NfcAdapter#isNfcControllerAlwaysOnSupported() </li>
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+ public void setControllerAlwaysOn(@ControllerMode int mode) {
+ if (!NfcAdapter.sHasNfcFeature && !NfcAdapter.sHasCeFeature) {
+ throw new UnsupportedOperationException();
+ }
+ NfcAdapter.callService(() -> NfcAdapter.sService.setControllerAlwaysOn(mode));
+ }
+
private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
@Override
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index bd84b58aa0f4..a30c0c3c6d4c 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
default_applicable_licenses: [
"frameworks_base_packages_PackageInstaller_license",
],
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 717ec02d9dd6..91882fde1537 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -65,6 +65,17 @@
"name": "CtsIntentSignatureTestCases"
},
{
+ "name": "CtsPackageInstallerCUJDeviceAdminTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJInstallationTestCases",
"options":[
{
@@ -76,6 +87,17 @@
]
},
{
+ "name": "CtsPackageInstallerCUJMultiUsersTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJUninstallationTestCases",
"options":[
{
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 0b094a2f70e2..34e33c0df8f5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1639,7 +1639,7 @@
<string name="cached_apps_freezer_reboot_dialog_text">Your device must be rebooted for this change to apply. Reboot now or cancel.</string>
<!-- Name of the 3.5mm and usb audio device. [CHAR LIMIT=50] -->
- <string name="media_transfer_wired_usb_device_name">Wired headphone</string>
+ <string name="media_transfer_wired_headphone_name">Wired headphone</string>
<!-- Name of the 3.5mm headphone, used in desktop devices. [CHAR LIMIT=50] -->
<string name="media_transfer_headphone_name">Headphone</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
index 148e164e4806..c9f9d1be9c15 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -151,7 +151,19 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile
Log.d(TAG, "The BluetoothLeBroadcastAssistant is null");
return;
}
- mService.addSource(sink, metadata, isGroupOp);
+ try {
+ mService.addSource(sink, metadata, isGroupOp);
+ } catch (IllegalStateException e) {
+ // BT will check callback registration before add source.
+ // If it throw callback exception when bt is disabled, then the failure is intended,
+ // just catch it here.
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null && adapter.isEnabled()) {
+ throw e;
+ } else {
+ Log.d(TAG, "Catch addSource failure when bt is disabled: " + e);
+ }
+ }
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index 7eae5b2a1f5f..3d8ff86c9377 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -56,11 +56,13 @@ import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
@OptIn(ExperimentalCoroutinesApi::class)
@@ -101,8 +103,7 @@ class DeviceSettingServiceConnection(
} else if (allStatus.all { it is ServiceConnectionStatus.Connected }) {
allStatus
.filterIsInstance<
- ServiceConnectionStatus.Connected<
- IDeviceSettingsProviderService>
+ ServiceConnectionStatus.Connected<IDeviceSettingsProviderService>
>()
.all { it.service.serviceStatus?.enabled == true }
} else {
@@ -232,7 +233,7 @@ class DeviceSettingServiceConnection(
IDeviceSettingsProviderService.Stub::asInterface,
)
.stateIn(
- coroutineScope,
+ coroutineScope.plus(backgroundCoroutineContext),
SharingStarted.WhileSubscribed(),
ServiceConnectionStatus.Connecting,
)
@@ -263,21 +264,30 @@ class DeviceSettingServiceConnection(
transform: ((IBinder) -> T),
): Flow<ServiceConnectionStatus<T>> {
return callbackFlow {
- val serviceConnection =
- object : ServiceConnection {
- override fun onServiceConnected(name: ComponentName, service: IBinder) {
- launch { send(ServiceConnectionStatus.Connected(transform(service))) }
- }
+ val serviceConnection =
+ object : ServiceConnection {
+ override fun onServiceConnected(name: ComponentName, service: IBinder) {
+ launch { send(ServiceConnectionStatus.Connected(transform(service))) }
+ }
- override fun onServiceDisconnected(name: ComponentName?) {
- launch { send(ServiceConnectionStatus.Connecting) }
+ override fun onServiceDisconnected(name: ComponentName?) {
+ launch { send(ServiceConnectionStatus.Connecting) }
+ }
}
+ if (
+ !context.bindService(
+ intent,
+ Context.BIND_AUTO_CREATE,
+ { launch { it.run() } },
+ serviceConnection,
+ )
+ ) {
+ Log.w(TAG, "Fail to bind service $intent")
+ launch { send(ServiceConnectionStatus.Failed) }
}
- if (!context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) {
- launch { send(ServiceConnectionStatus.Failed) }
+ awaitClose { context.unbindService(serviceConnection) }
}
- awaitClose { context.unbindService(serviceConnection) }
- }
+ .flowOn(backgroundCoroutineContext)
}
private suspend fun tryGetEndpointFromMetadata(cachedDevice: CachedBluetoothDevice): EndPoint? =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 116de567a7da..0b8fb22cef3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -90,7 +90,7 @@ public class PhoneMediaDevice extends MediaDevice {
name =
inputRoutingEnabledAndIsDesktop()
? context.getString(R.string.media_transfer_headphone_name)
- : context.getString(R.string.media_transfer_wired_usb_device_name);
+ : context.getString(R.string.media_transfer_wired_headphone_name);
break;
case TYPE_USB_DEVICE:
case TYPE_USB_HEADSET:
@@ -98,7 +98,7 @@ public class PhoneMediaDevice extends MediaDevice {
name =
inputRoutingEnabledAndIsDesktop()
? context.getString(R.string.media_transfer_usb_speaker_name)
- : context.getString(R.string.media_transfer_wired_usb_device_name);
+ : context.getString(R.string.media_transfer_wired_headphone_name);
break;
case TYPE_DOCK:
name = context.getString(R.string.media_transfer_dock_speaker_device_name);
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
index 251cd3615897..9a9a96012984 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
@@ -87,7 +87,7 @@ public class ConversationIconFactory extends BaseIconFactory {
* Returns the conversation info drawable
*/
public Drawable getBaseIconDrawable(ShortcutInfo shortcutInfo) {
- return mLauncherApps.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
+ return mLauncherApps.getShortcutIconDrawable(shortcutInfo, mFullResIconDpi);
}
/**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index 81b56343ceed..0cb6bc1b1261 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -102,9 +102,9 @@ class DeviceSettingRepositoryTest {
`when`(settingProviderService2.queryLocalInterface(anyString()))
.thenReturn(settingProviderService2)
- `when`(context.bindService(any(), any(), anyInt())).then { input ->
+ `when`(context.bindService(any(), anyInt(), any(), any())).then { input ->
val intent = input.getArgument<Intent?>(0)
- val connection = input.getArgument<ServiceConnection>(1)
+ val connection = input.getArgument<ServiceConnection>(3)
when (intent?.action) {
CONFIG_SERVICE_INTENT_ACTION ->
@@ -210,7 +210,7 @@ class DeviceSettingRepositoryTest {
fun getDeviceSettingsConfig_bindingServiceFail_returnNull() {
testScope.runTest {
`when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
- doReturn(false).`when`(context).bindService(any(), any(), anyInt())
+ doReturn(false).`when`(context).bindService(any(), anyInt(), any(), any())
val config = underTest.getDeviceSettingsConfig(cachedDevice)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
index 23cfc01b07b8..da5f428ce23b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
@@ -106,12 +106,12 @@ public class PhoneMediaDeviceTest {
when(mInfo.getName()).thenReturn(deviceName);
assertThat(mPhoneMediaDevice.getName())
- .isEqualTo(mContext.getString(R.string.media_transfer_wired_usb_device_name));
+ .isEqualTo(mContext.getString(R.string.media_transfer_wired_headphone_name));
when(mInfo.getType()).thenReturn(TYPE_USB_DEVICE);
assertThat(mPhoneMediaDevice.getName())
- .isEqualTo(mContext.getString(R.string.media_transfer_wired_usb_device_name));
+ .isEqualTo(mContext.getString(R.string.media_transfer_wired_headphone_name));
when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ba59ce81d362..0ae4da53045e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -545,6 +545,10 @@ public class SettingsProvider extends ContentProvider {
reportDeviceConfigAccess(prefix);
return result;
}
+ case Settings.CALL_METHOD_LIST_NAMESPACES_CONFIG -> {
+ Bundle result = packageNamespacesForCallResult(getAllConfigFlagNamespaces());
+ return result;
+ }
case Settings.CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG -> {
RemoteCallback callback = args.getParcelable(
Settings.CALL_METHOD_MONITOR_CALLBACK_KEY);
@@ -1337,6 +1341,23 @@ public class SettingsProvider extends ContentProvider {
}
@NonNull
+ private HashSet<String> getAllConfigFlagNamespaces() {
+ Set<String> flagNames = getAllConfigFlags(null).keySet();
+ HashSet<String> namespaces = new HashSet();
+ for (String name : flagNames) {
+ int slashIndex = name.indexOf("/");
+ boolean validSlashIndex = slashIndex != -1
+ && slashIndex != 0
+ && slashIndex != name.length();
+ if (validSlashIndex) {
+ String namespace = name.substring(0, slashIndex);
+ namespaces.add(namespace);
+ }
+ }
+ return namespaces;
+ }
+
+ @NonNull
private HashMap<String, String> getAllConfigFlags(@Nullable String prefix) {
if (DEBUG) {
Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
@@ -2561,6 +2582,12 @@ public class SettingsProvider extends ContentProvider {
return result;
}
+ private Bundle packageNamespacesForCallResult(@NonNull HashSet<String> namespaces) {
+ Bundle result = new Bundle();
+ result.putSerializable(Settings.NameValueTable.VALUE, namespaces);
+ return result;
+ }
+
private void setMonitorCallback(RemoteCallback callback) {
if (callback == null) {
return;
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index f3a28ca3f974..c49ffb49a1da 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -538,6 +538,13 @@ flag {
}
flag {
+ name: "status_bar_connected_displays"
+ namespace: "systemui"
+ description: "Shows the status bar on connected displays"
+ bug: "362720336"
+}
+
+flag {
name: "status_bar_switch_to_spn_from_data_spn"
namespace: "systemui"
description: "Fix usage of the SPN broadcast extras"
@@ -548,6 +555,13 @@ flag {
}
flag {
+ name: "status_bar_simple_fragment"
+ namespace: "systemui"
+ description: "Feature flag for refactoring the collapsed status bar fragment"
+ bug: "364360986"
+}
+
+flag {
name: "new_volume_panel"
namespace: "systemui"
description: "Switches to the new volume panel (without Slices)."
@@ -1049,6 +1063,13 @@ flag {
}
flag {
+ name: "communal_widget_resizing"
+ namespace: "systemui"
+ description: "Allow resizing of widgets on glanceable hub"
+ bug: "368053818"
+}
+
+flag {
name: "app_clips_backlinks"
namespace: "systemui"
description: "Enables Backlinks improvement feature in App Clips"
@@ -1401,3 +1422,13 @@ flag {
description: "Allow non-touchscreen devices to bypass falsing"
bug: "319809270"
}
+
+flag {
+ name: "media_projection_dialog_behind_lockscreen"
+ namespace: "systemui"
+ description: "Ensure MediaProjection Dialog appears behind the lockscreen"
+ bug: "351409536"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt b/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
index c01396a96b6e..4674d6e5f25a 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
@@ -16,16 +16,15 @@
package com.android.compose.windowsizeclass
-import android.view.WindowManager
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.toComposeRect
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
+import androidx.window.layout.WindowMetricsCalculator
val LocalWindowSizeClass =
staticCompositionLocalOf<WindowSizeClass> {
@@ -42,10 +41,7 @@ fun calculateWindowSizeClass(): WindowSizeClass {
LocalConfiguration.current
val density = LocalDensity.current
val context = LocalContext.current
- val metrics =
- remember(context) {
- context.getSystemService(WindowManager::class.java)!!.currentWindowMetrics
- }
+ val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
val size = with(density) { metrics.bounds.toComposeRect().size.toDpSize() }
return WindowSizeClass.calculateFromSize(size)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 163b35596c1b..8321238b28b1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -16,7 +16,6 @@
package com.android.systemui.bouncer.ui.composable
-import android.view.HapticFeedbackConstants
import androidx.annotation.VisibleForTesting
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
@@ -133,10 +132,7 @@ fun PatternBouncer(
// Perform haptic feedback, but only if the current dot is not null, so we don't perform it
// when the UI first shows up or when the user lifts their pointer/finger.
if (currentDot != null) {
- view.performHapticFeedback(
- HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
- )
+ viewModel.performDotFeedback(view)
}
if (!isAnimationEnabled) {
@@ -206,10 +202,7 @@ fun PatternBouncer(
// Show the failure animation if the user entered the wrong input.
LaunchedEffect(animateFailure) {
if (animateFailure) {
- showFailureAnimation(
- dots = dots,
- scalingAnimatables = dotScalingAnimatables,
- )
+ showFailureAnimation(dots = dots, scalingAnimatables = dotScalingAnimatables)
viewModel.onFailureAnimationShown()
}
}
@@ -358,15 +351,10 @@ fun PatternBouncer(
(1 - checkNotNull(dotAppearMoveUpAnimatables[dot]).value) * initialOffset
drawCircle(
center =
- pixelOffset(
- dot,
- spacing,
- horizontalOffset,
- verticalOffset + appearOffset,
- ),
+ pixelOffset(dot, spacing, horizontalOffset, verticalOffset + appearOffset),
color =
dotColor.copy(alpha = checkNotNull(dotAppearFadeInAnimatables[dot]).value),
- radius = dotRadius * checkNotNull(dotScalingAnimatables[dot]).value
+ radius = dotRadius * checkNotNull(dotScalingAnimatables[dot]).value,
)
}
}
@@ -387,7 +375,7 @@ private suspend fun showEntryAnimation(
delayMillis = 33 * dot.y,
durationMillis = 450,
easing = Easings.LegacyDecelerate,
- )
+ ),
)
}
}
@@ -400,7 +388,7 @@ private suspend fun showEntryAnimation(
delayMillis = 0,
durationMillis = 450 + (33 * dot.y),
easing = Easings.StandardDecelerate,
- )
+ ),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 489e24e8b328..0830c9b359a4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -16,8 +16,8 @@
package com.android.systemui.bouncer.ui.composable
-import android.view.HapticFeedbackConstants
import android.view.MotionEvent
+import android.view.View
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
@@ -72,11 +72,7 @@ import kotlinx.coroutines.launch
/** Renders the PIN button pad. */
@Composable
-fun PinPad(
- viewModel: PinBouncerViewModel,
- verticalSpacing: Dp,
- modifier: Modifier = Modifier,
-) {
+fun PinPad(viewModel: PinBouncerViewModel, verticalSpacing: Dp, modifier: Modifier = Modifier) {
DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
@@ -104,7 +100,7 @@ fun PinPad(
columns = columns,
verticalSpacing = verticalSpacing,
horizontalSpacing = calculateHorizontalSpacingBetweenColumns(gridWidth = 300.dp),
- modifier = modifier.focusRequester(focusRequester).sysuiResTag("pin_pad_grid")
+ modifier = modifier.focusRequester(focusRequester).sysuiResTag("pin_pad_grid"),
) {
repeat(9) { index ->
DigitButton(
@@ -126,10 +122,11 @@ fun PinPad(
),
isInputEnabled = isInputEnabled,
onClicked = viewModel::onBackspaceButtonClicked,
+ onPointerDown = viewModel::onBackspaceButtonPressed,
onLongPressed = viewModel::onBackspaceButtonLongPressed,
appearance = backspaceButtonAppearance,
scaling = buttonScaleAnimatables[9]::value,
- elementId = "delete_button"
+ elementId = "delete_button",
)
DigitButton(
@@ -138,7 +135,7 @@ fun PinPad(
onClicked = viewModel::onPinButtonClicked,
scaling = buttonScaleAnimatables[10]::value,
isAnimationEnabled = isDigitButtonAnimationEnabled,
- onPointerDown = viewModel::onDigitButtonDown
+ onPointerDown = viewModel::onDigitButtonDown,
)
ActionButton(
@@ -152,7 +149,7 @@ fun PinPad(
onClicked = viewModel::onAuthenticateButtonClicked,
appearance = confirmButtonAppearance,
scaling = buttonScaleAnimatables[11]::value,
- elementId = "key_enter"
+ elementId = "key_enter",
)
}
}
@@ -162,7 +159,7 @@ private fun DigitButton(
digit: Int,
isInputEnabled: Boolean,
onClicked: (Int) -> Unit,
- onPointerDown: () -> Unit,
+ onPointerDown: (View?) -> Unit,
scaling: () -> Float,
isAnimationEnabled: Boolean,
) {
@@ -178,7 +175,7 @@ private fun DigitButton(
val scale = if (isAnimationEnabled) scaling() else 1f
scaleX = scale
scaleY = scale
- }
+ },
) { contentColor ->
// TODO(b/281878426): once "color: () -> Color" (added to BasicText in aosp/2568972) makes
// it into Text, use that here, to animate more efficiently.
@@ -197,6 +194,7 @@ private fun ActionButton(
onClicked: () -> Unit,
elementId: String,
onLongPressed: (() -> Unit)? = null,
+ onPointerDown: ((View?) -> Unit)? = null,
appearance: ActionButtonAppearance,
scaling: () -> Float,
) {
@@ -222,18 +220,16 @@ private fun ActionButton(
foregroundColor = foregroundColor,
isAnimationEnabled = true,
elementId = elementId,
+ onPointerDown = onPointerDown,
modifier =
Modifier.graphicsLayer {
alpha = hiddenAlpha
val scale = scaling()
scaleX = scale
scaleY = scale
- }
+ },
) { contentColor ->
- Icon(
- icon = icon,
- tint = contentColor(),
- )
+ Icon(icon = icon, tint = contentColor())
}
}
@@ -247,22 +243,13 @@ private fun PinPadButton(
modifier: Modifier = Modifier,
elementId: String? = null,
onLongPressed: (() -> Unit)? = null,
- onPointerDown: (() -> Unit)? = null,
+ onPointerDown: ((View?) -> Unit)? = null,
content: @Composable (contentColor: () -> Color) -> Unit,
) {
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
val indication = LocalIndication.current.takeUnless { isPressed }
-
val view = LocalView.current
- LaunchedEffect(isPressed) {
- if (isPressed) {
- view.performHapticFeedback(
- HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
- )
- }
- }
// Pin button animation specification is asymmetric: fast animation to the pressed state, and a
// slow animation upon release. Note that isPressed is guaranteed to be true for at least the
@@ -277,7 +264,7 @@ private fun PinPadButton(
animateDpAsState(
if (isAnimationEnabled && isPressed) 24.dp else pinButtonMaxSize / 2,
label = "PinButton round corners",
- animationSpec = tween(animDurationMillis, easing = animEasing)
+ animationSpec = tween(animDurationMillis, easing = animEasing),
)
val colorAnimationSpec: AnimationSpec<Color> = tween(animDurationMillis, easing = animEasing)
val containerColor: Color by
@@ -287,7 +274,7 @@ private fun PinPadButton(
else -> backgroundColor
},
label = "Pin button container color",
- animationSpec = colorAnimationSpec
+ animationSpec = colorAnimationSpec,
)
val contentColor =
animateColorAsState(
@@ -296,7 +283,7 @@ private fun PinPadButton(
else -> foregroundColor
},
label = "Pin button container color",
- animationSpec = colorAnimationSpec
+ animationSpec = colorAnimationSpec,
)
Box(
@@ -319,11 +306,11 @@ private fun PinPadButton(
interactionSource = interactionSource,
indication = indication,
onClick = onClicked,
- onLongClick = onLongPressed
+ onLongClick = onLongPressed,
)
.pointerInteropFilter { motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_DOWN) {
- onPointerDown?.let { it() }
+ onPointerDown?.let { it(view) }
}
false
}
@@ -353,10 +340,7 @@ private suspend fun showFailureAnimation(
animatable.animateTo(
targetValue = 1f,
animationSpec =
- tween(
- durationMillis = pinButtonErrorRevertMs,
- easing = Easings.Legacy,
- ),
+ tween(durationMillis = pinButtonErrorRevertMs, easing = Easings.Legacy),
)
}
}
@@ -364,9 +348,7 @@ private suspend fun showFailureAnimation(
}
/** Returns the amount of horizontal spacing between columns, in dips. */
-private fun calculateHorizontalSpacingBetweenColumns(
- gridWidth: Dp,
-): Dp {
+private fun calculateHorizontalSpacingBetweenColumns(gridWidth: Dp): Dp {
return (gridWidth - (pinButtonMaxSize * columns)) / (columns - 1)
}
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 5f7b1adca6a0..f4d9e820ad8f 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
@@ -35,7 +35,6 @@ import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.res.R
import com.android.systemui.util.animation.MeasurementInput
-import kotlinx.coroutines.ExperimentalCoroutinesApi
object MediaCarousel {
object Elements {
@@ -47,7 +46,6 @@ object MediaCarousel {
}
}
-@ExperimentalCoroutinesApi
@Composable
fun SceneScope.MediaCarousel(
isVisible: Boolean,
@@ -79,7 +77,7 @@ fun SceneScope.MediaCarousel(
offsetProvider?.invoke() ?: IntOffset.Zero
)
}
- }
+ },
)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
@@ -89,7 +87,7 @@ fun SceneScope.MediaCarousel(
MeasurementInput(placeable.width, placeable.height)
carouselController.setSceneContainerSize(
placeable.width,
- placeable.height
+ placeable.height,
)
layout(placeable.width, placeable.height) {
@@ -106,7 +104,7 @@ fun SceneScope.MediaCarousel(
}
},
update = { it.setView(carouselController.mediaFrame) },
- onRelease = { it.removeAllViews() }
+ onRelease = { it.removeAllViews() },
)
}
}
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 0c1c16522567..d91958adaa1b 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
@@ -392,7 +392,7 @@ private fun SceneScope.QuickSettingsScene(
{ mediaOffset.roundToPx() },
)
}
- Box(modifier = Modifier.padding(horizontal = shadeHorizontalPadding)) {
+ Column(modifier = Modifier.padding(horizontal = shadeHorizontalPadding)) {
if (mediaInRow) {
Layout(content = content, measurePolicy = landscapeQsMediaMeasurePolicy)
} else {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index deef65218c4b..9552564cf1a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -16,18 +16,26 @@
package com.android.systemui.bouncer.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.keyguard.AuthInteractionProperties
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.haptics.msdl.bouncerHapticPlayer
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.testKosmos
+import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -39,11 +47,15 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
+ private val msdlPlayer = kosmos.fakeMSDLPlayer
+ private val bouncerHapticPlayer = kosmos.bouncerHapticPlayer
+ private val authInteractionProperties = AuthInteractionProperties()
private val underTest =
kosmos.pinBouncerViewModelFactory.create(
isInputEnabled = MutableStateFlow(true),
onIntentionalUserInput = {},
authenticationMethod = AuthenticationMethodModel.Pin,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
@Before
@@ -77,4 +89,42 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateButtonClicked()
assertThat(animateFailure).isFalse()
}
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun onAuthenticationResult_playUnlockTokenIfSuccessful() =
+ testScope.runTest {
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+ // Correct PIN:
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
+ underTest.onPinButtonClicked(digit)
+ }
+ underTest.onAuthenticateButtonClicked()
+ runCurrent()
+
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun onAuthenticationResult_playFailureTokenIfFailure() =
+ testScope.runTest {
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+ // Wrong PIN:
+ FakeAuthenticationRepository.DEFAULT_PIN.drop(2).forEach { digit ->
+ underTest.onPinButtonClicked(digit)
+ }
+ underTest.onAuthenticateButtonClicked()
+ runCurrent()
+
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 7c773a902367..c163c6fc0a30 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -16,9 +16,11 @@
package com.android.systemui.bouncer.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.authenticationRepository
@@ -27,12 +29,16 @@ import com.android.systemui.authentication.domain.interactor.authenticationInter
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate as Point
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.haptics.msdl.FakeMSDLPlayer
+import com.android.systemui.haptics.msdl.bouncerHapticPlayer
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
+import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -55,10 +61,13 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val bouncerViewModel by lazy { kosmos.bouncerSceneContentViewModel }
+ private val msdlPlayer: FakeMSDLPlayer = kosmos.fakeMSDLPlayer
+ private val bouncerHapticHelper = kosmos.bouncerHapticPlayer
private val underTest =
kosmos.patternBouncerViewModelFactory.create(
isInputEnabled = MutableStateFlow(true).asStateFlow(),
onIntentionalUserInput = {},
+ bouncerHapticPlayer = bouncerHapticHelper,
)
private val containerSize = 90 // px
@@ -115,10 +124,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
.that(selectedDots)
.isEqualTo(
CORRECT_PATTERN.subList(0, index + 1).map {
- PatternDotViewModel(
- x = it.x,
- y = it.y,
- )
+ PatternDotViewModel(x = it.x, y = it.y)
}
)
assertWithMessage("Wrong current dot for index $index")
@@ -174,7 +180,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
listOf(
PatternDotViewModel(0, 0),
PatternDotViewModel(1, 0),
- PatternDotViewModel(2, 0)
+ PatternDotViewModel(2, 0),
)
)
}
@@ -200,7 +206,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
listOf(
PatternDotViewModel(1, 0),
PatternDotViewModel(1, 1),
- PatternDotViewModel(1, 2)
+ PatternDotViewModel(1, 2),
)
)
}
@@ -228,7 +234,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
listOf(
PatternDotViewModel(2, 0),
PatternDotViewModel(1, 1),
- PatternDotViewModel(0, 2)
+ PatternDotViewModel(0, 2),
)
)
}
@@ -300,10 +306,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
val attempts = FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT + 1
repeat(attempts) { attempt ->
underTest.onDragStart()
- CORRECT_PATTERN.subList(
- 0,
- kosmos.authenticationRepository.minPatternLength - 1,
- )
+ CORRECT_PATTERN.subList(0, kosmos.authenticationRepository.minPatternLength - 1)
.forEach { coordinate ->
underTest.onDrag(
xPx = 30f * coordinate.x + 15,
@@ -341,6 +344,16 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(authResult).isTrue()
}
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun performDotFeedback_deliversDragToken() =
+ testScope.runTest {
+ underTest.performDotFeedback(null)
+
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.DRAG_INDICATOR)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
+
private fun dragOverCoordinates(vararg coordinatesDragged: Point) {
underTest.onDragStart()
coordinatesDragged.forEach(::dragToCoordinate)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 2ee4aee2abab..af5f2acb444d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -27,6 +27,7 @@ import androidx.compose.ui.input.key.KeyEventType
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -35,12 +36,15 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.bouncer.data.repository.fakeSimBouncerRepository
import com.android.systemui.classifier.fakeFalsingCollector
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.haptics.msdl.bouncerHapticPlayer
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
+import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
import kotlin.random.Random
import kotlin.random.nextInt
@@ -64,11 +68,14 @@ class PinBouncerViewModelTest : SysuiTestCase() {
private val testScope = kosmos.testScope
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
+ private val msdlPlayer = kosmos.fakeMSDLPlayer
+ private val bouncerHapticPlayer = kosmos.bouncerHapticPlayer
private val underTest by lazy {
kosmos.pinBouncerViewModelFactory.create(
isInputEnabled = MutableStateFlow(true),
onIntentionalUserInput = {},
authenticationMethod = AuthenticationMethodModel.Pin,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
}
@@ -97,6 +104,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
isInputEnabled = MutableStateFlow(true),
onIntentionalUserInput = {},
authenticationMethod = AuthenticationMethodModel.Sim,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
assertThat(underTest.isSimAreaVisible).isTrue()
@@ -122,6 +130,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
isInputEnabled = MutableStateFlow(true),
onIntentionalUserInput = {},
authenticationMethod = AuthenticationMethodModel.Pin,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
@@ -487,11 +496,39 @@ class PinBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
lockDeviceAndOpenPinBouncer()
- underTest.onDigitButtonDown()
+ underTest.onDigitButtonDown(null)
assertTrue(kosmos.fakeFalsingCollector.wasLastGestureAvoided())
}
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun onDigiButtonDown_deliversKeyStandardToken() =
+ testScope.runTest {
+ underTest.onDigitButtonDown(null)
+
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.KEYPRESS_STANDARD)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun onBackspaceButtonPressed_deliversKeyDeleteToken() {
+ underTest.onBackspaceButtonPressed(null)
+
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.KEYPRESS_DELETE)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun onBackspaceButtonLongPressed_deliversLongPressToken() {
+ underTest.onBackspaceButtonLongPressed()
+
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.LONG_PRESS)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
+
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.currentScene)
val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index 956c12916c98..2028d28804bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -25,6 +25,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;
@@ -33,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -201,6 +204,7 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
public void testTrackpadGesture() {
assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true);
@@ -208,6 +212,14 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void testTrackpadGesture_touchScreenSource_false() {
+ assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+ when(mFalsingDataProvider.isTouchScreenSource()).thenReturn(false);
+ assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+ }
+
+ @Test
public void testAddAndRemoveFalsingBeliefListener() {
verify(mHistoryTracker, never()).addBeliefListener(any());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index df4b0480f5c7..5a4799cecae5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -312,6 +312,7 @@ public class FalsingDataProviderTest extends ClassifierTest {
}
@Test
+ @DisableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
public void test_IsFromTrackpad() {
MotionEvent motionEventOrigin = appendTrackpadDownEvent(0, 0);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 70529cc762e0..ee65fbd810ae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -21,6 +21,8 @@ import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
+import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
import com.android.systemui.SysuiTestCase
@@ -28,6 +30,7 @@ import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.coroutines.collectLastValue
@@ -35,7 +38,6 @@ import com.android.systemui.dock.dockManager
import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -93,6 +95,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
bgScope = applicationCoroutineScope,
mainDispatcher = testDispatcher,
centralSurfacesOpt = centralSurfacesOptional,
+ uiEventLogger = uiEventLoggerFake,
)
.apply { start() }
@@ -119,7 +122,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.GONE,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -140,7 +143,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.GONE,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -161,7 +164,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.GONE,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Blank)
@@ -181,7 +184,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.ALTERNATE_BOUNCER,
to = KeyguardState.GONE,
- testScope = this
+ testScope = this,
)
// Scene change will be handled in EditWidgetsActivity not here
assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -200,7 +203,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GONE,
to = KeyguardState.LOCKSCREEN,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
}
@@ -220,7 +223,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.OCCLUDED,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
@@ -240,7 +243,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.OCCLUDED,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
}
@@ -258,7 +261,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.LOCKSCREEN,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
@@ -276,7 +279,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.OFF,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -298,7 +301,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.OFF,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY / 2)
@@ -307,7 +310,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.OFF,
to = KeyguardState.GLANCEABLE_HUB,
- testScope = this
+ testScope = this,
)
advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY)
@@ -327,7 +330,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.LOCKSCREEN,
- testScope = this
+ testScope = this,
)
updateDocked(true)
@@ -349,7 +352,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.LOCKSCREEN,
- testScope = this
+ testScope = this,
)
updateDocked(true)
@@ -361,7 +364,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.DREAMING,
- testScope = this
+ testScope = this,
)
advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY)
assertThat(scene).isEqualTo(CommunalScenes.Blank)
@@ -511,6 +514,9 @@ class CommunalSceneStartableTest : SysuiTestCase() {
advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
assertThat(scene).isEqualTo(CommunalScenes.Blank)
+ assertThat(uiEventLoggerFake.logs.first().eventId)
+ .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT.id)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
}
}
@@ -526,7 +532,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.DOZING,
to = KeyguardState.GLANCEABLE_HUB,
- testScope = this
+ testScope = this,
)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index c18deb134075..fac931273ac7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -14,22 +14,6 @@
* 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.keyguard.domain.interactor
import android.os.PowerManager
@@ -47,7 +31,6 @@ import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -129,7 +112,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.DOZING,
- testScope
+ testScope,
)
kosmos.fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockMode.NONE)
reset(transitionRepository)
@@ -145,10 +128,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT
// Under default conditions, we should transition to LOCKSCREEN when waking up.
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.DOZING,
- to = KeyguardState.LOCKSCREEN,
- )
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.LOCKSCREEN)
}
@Test
@@ -166,10 +146,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT
// If dreaming is possible and communal is available, then we should transition to
// GLANCEABLE_HUB when waking up due to power button press.
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.DOZING,
- to = KeyguardState.GLANCEABLE_HUB,
- )
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GLANCEABLE_HUB)
}
@Test
@@ -186,8 +163,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT
// If dreaming is possible and communal is available, then we should transition to
// GLANCEABLE_HUB when waking up due to power button press.
- verify(kosmos.fakeCommunalSceneRepository)
- .changeScene(CommunalScenes.Communal, CommunalTransitionKeys.Immediately)
+ verify(kosmos.fakeCommunalSceneRepository).snapToScene(CommunalScenes.Communal)
}
@Test
@@ -204,10 +180,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT
// If dreaming is NOT possible but communal is available, then we should transition to
// LOCKSCREEN when waking up due to power button press.
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.DOZING,
- to = KeyguardState.LOCKSCREEN,
- )
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.LOCKSCREEN)
}
@Test
@@ -224,10 +197,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT
// If dreaming is possible but communal is NOT available, then we should transition to
// LOCKSCREEN when waking up due to power button press.
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.DOZING,
- to = KeyguardState.LOCKSCREEN,
- )
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.LOCKSCREEN)
}
@Test
@@ -245,10 +215,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT
// Under default conditions, we should transition to LOCKSCREEN when waking up.
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.DOZING,
- to = KeyguardState.GLANCEABLE_HUB,
- )
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GLANCEABLE_HUB)
}
@Test
@@ -261,10 +228,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT
// Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED.
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.DOZING,
- to = KeyguardState.OCCLUDED,
- )
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.OCCLUDED)
}
@Test
@@ -282,10 +246,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT
// Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED.
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.DOZING,
- to = KeyguardState.OCCLUDED,
- )
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.OCCLUDED)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
index 9558e5d23a25..01220285e10c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
@@ -24,26 +24,25 @@ import android.media.session.MediaSession
import android.media.session.PlaybackState
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.util.mediaInstanceId
import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -52,30 +51,31 @@ class MediaControlViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val mediaDataFilter = kosmos.mediaDataFilter
private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
private val packageManager = kosmos.packageManager
private val drawable = context.getDrawable(R.drawable.ic_media_play)
- private val instanceId: InstanceId = kosmos.mediaInstanceId
-
+ private val instanceId = kosmos.mediaInstanceId
private val underTest: MediaControlViewModel = kosmos.mediaControlViewModel
+ @Before
+ fun setUp() {
+ whenever(packageManager.getApplicationIcon(Mockito.anyString())).thenReturn(drawable)
+ whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
+ .thenReturn(drawable)
+ whenever(packageManager.getApplicationInfo(eq(PACKAGE_NAME), ArgumentMatchers.anyInt()))
+ .thenReturn(ApplicationInfo())
+ whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE_NAME)
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+ context.setMockPackageManager(packageManager)
+ }
+
@Test
fun addMediaControl_mediaControlViewModelIsLoaded() =
testScope.runTest {
- whenever(packageManager.getApplicationIcon(Mockito.anyString())).thenReturn(drawable)
- whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
- .thenReturn(drawable)
- whenever(packageManager.getApplicationInfo(eq(PACKAGE_NAME), ArgumentMatchers.anyInt()))
- .thenReturn(ApplicationInfo())
- whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE_NAME)
- whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
- whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
val playerModel by collectLastValue(underTest.player)
-
- context.setMockPackageManager(packageManager)
-
- val mediaData = initMediaData()
+ val mediaData = initMediaData(ARTIST, TITLE)
mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
@@ -88,7 +88,51 @@ class MediaControlViewModelTest : SysuiTestCase() {
assertThat(playerModel?.playTurbulenceNoise).isFalse()
}
- private fun initMediaData(): MediaData {
+ @Test
+ fun emitDuplicateMediaControls_mediaControlIsNotBound() =
+ testScope.runTest {
+ val playerModel by collectLastValue(underTest.player)
+ val mediaData = initMediaData(ARTIST, TITLE)
+
+ mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
+
+ assertThat(playerModel).isNotNull()
+ assertThat(playerModel?.titleName).isEqualTo(TITLE)
+ assertThat(playerModel?.artistName).isEqualTo(ARTIST)
+ assertThat(underTest.isNewPlayer(playerModel!!)).isTrue()
+
+ mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
+
+ assertThat(playerModel).isNotNull()
+ assertThat(playerModel?.titleName).isEqualTo(TITLE)
+ assertThat(playerModel?.artistName).isEqualTo(ARTIST)
+ assertThat(underTest.isNewPlayer(playerModel!!)).isFalse()
+ }
+
+ @Test
+ fun emitDifferentMediaControls_mediaControlIsBound() =
+ testScope.runTest {
+ val playerModel by collectLastValue(underTest.player)
+ var mediaData = initMediaData(ARTIST, TITLE)
+
+ mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
+
+ assertThat(playerModel).isNotNull()
+ assertThat(playerModel?.titleName).isEqualTo(TITLE)
+ assertThat(playerModel?.artistName).isEqualTo(ARTIST)
+ assertThat(underTest.isNewPlayer(playerModel!!)).isTrue()
+
+ mediaData = initMediaData(ARTIST_2, TITLE_2)
+
+ mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
+
+ assertThat(playerModel).isNotNull()
+ assertThat(playerModel?.titleName).isEqualTo(TITLE_2)
+ assertThat(playerModel?.artistName).isEqualTo(ARTIST_2)
+ assertThat(underTest.isNewPlayer(playerModel!!)).isTrue()
+ }
+
+ private fun initMediaData(artist: String, title: String): MediaData {
val device = MediaDeviceData(true, null, DEVICE_NAME, null, showBroadcastButton = true)
// Create media session
@@ -111,12 +155,12 @@ class MediaControlViewModelTest : SysuiTestCase() {
return MediaData(
userId = USER_ID,
- artist = ARTIST,
- song = TITLE,
+ artist = artist,
+ song = title,
packageName = PACKAGE,
token = session.sessionToken,
device = device,
- instanceId = instanceId
+ instanceId = instanceId,
)
}
@@ -127,6 +171,8 @@ class MediaControlViewModelTest : SysuiTestCase() {
private const val PACKAGE = "PKG"
private const val ARTIST = "ARTIST"
private const val TITLE = "TITLE"
+ private const val ARTIST_2 = "ARTIST_2"
+ private const val TITLE_2 = "TITLE_2"
private const val DEVICE_NAME = "DEVICE_NAME"
private const val SESSION_KEY = "SESSION_KEY"
private const val SESSION_ARTIST = "SESSION_ARTIST"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index a770ee199ba6..a770ee199ba6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
index 85bc634c28b1..85bc634c28b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index eae6cdbe4d2c..eae6cdbe4d2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
index f3cea3e8bb96..f3cea3e8bb96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java
index e58c8f281fc1..e58c8f281fc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 2905a7329d21..2905a7329d21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java
index 3621ab975daf..3621ab975daf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java
index 403a883e1760..403a883e1760 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/buttons/NavigationBarContextTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/NavigationBarContextTest.java
index 79c4a96e84bb..79c4a96e84bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/buttons/NavigationBarContextTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/NavigationBarContextTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/buttons/NearestTouchFrameTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/NearestTouchFrameTest.java
index 7941a68fa91e..7941a68fa91e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/buttons/NearestTouchFrameTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/NearestTouchFrameTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt
index 450aadd70171..450aadd70171 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
index 9ef6b9c13315..9ef6b9c13315 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
index c9a5d0620c65..c9a5d0620c65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
index 0c88da750885..0c88da750885 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
index 8f4078b88fc0..8f4078b88fc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/NotificationHelperTest.java
index 3673a25b68b2..3673a25b68b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/NotificationHelperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java
index dae34524c203..dae34524c203 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/PeopleProviderTestable.java
index 3e6d6746a997..3e6d6746a997 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/PeopleProviderTestable.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/SharedPreferencesHelperTest.java
index ae7fba939944..ae7fba939944 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/SharedPreferencesHelperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index e701dc63325f..e701dc63325f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java
index b3ded15f582f..b3ded15f582f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
index 0d6652cca0e8..0d6652cca0e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
index 9ac04cf98375..9ac04cf98375 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java
index 5250d56edc0b..5250d56edc0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
index 3ae7a1672821..3ae7a1672821 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt
index 466a09be4ff3..466a09be4ff3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
index db8612ad4182..db8612ad4182 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSContainerImplTest.kt
index 52ad931185e2..52ad931185e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSContainerImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
index cc3e27c71564..cc3e27c71564 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseSceneContainerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseSceneContainerTest.kt
index 07ec38e6ae6c..07ec38e6ae6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseSceneContainerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseSceneContainerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 225adab04ff0..225adab04ff0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 02c5b5ad214c..02c5b5ad214c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
index 56e25fcd580c..56e25fcd580c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
index ecdabbf9853e..ecdabbf9853e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 369bb228494c..369bb228494c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelTest.kt
index 3d6ba94556b7..3d6ba94556b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index a0ccec11fec3..a0ccec11fec3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/TileStateToProtoTest.kt
index be388a17ab5d..be388a17ab5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/TileStateToProtoTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/TouchAnimatorTest.java
index 29e2a8a8824f..29e2a8a8824f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/TouchAnimatorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
index 0c2b59fed078..0c2b59fed078 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
index 9f2b1ea9e37e..9f2b1ea9e37e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileAdapterTest.java
index cbcd8104ce35..cbcd8104ce35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileAdapterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 09a6c2c7f1f7..09a6c2c7f1f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/CustomTileTest.kt
index bd03acb6e7ec..bd03acb6e7ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/CustomTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileColorPickerTest.java
index eb013c5975b9..eb013c5975b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileColorPickerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
index 03483c98856e..03483c98856e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
index c5a2370adcda..c5a2370adcda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index f90e1e931099..f90e1e931099 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
index 090a85b0d3f9..090a85b0d3f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index 12c566ca9e2d..12c566ca9e2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 2580ac2c8da7..2580ac2c8da7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
index e112bb05ab2e..e112bb05ab2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
index 7a99aefc98fe..7a99aefc98fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index d6be31450fc0..d6be31450fc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
index 093cdf22a64b..093cdf22a64b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 9f12b189d76a..9f12b189d76a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
index 028beb599644..028beb599644 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index 1343527e631b..1343527e631b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index 73ae4ee5aa0d..73ae4ee5aa0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
index f90463e7f589..f90463e7f589 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
index c854920cbf1f..c854920cbf1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index 0cf96047fcc0..0cf96047fcc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
index 0a1455fe12cc..0a1455fe12cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
index dbdf3a499f8b..dbdf3a499f8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index 442a94887157..442a94887157 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NfcTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
index f1c589512895..f1c589512895 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
index d6fa124f3f91..d6fa124f3f91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
index f8f82f2c2ed8..f8f82f2c2ed8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 1aff45bf581d..1aff45bf581d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index 41930636cfa3..41930636cfa3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 0d12483bad0a..0d12483bad0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
index 8324a7303cff..8324a7303cff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
index c764c548ed92..c764c548ed92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
index c918ed82604c..c918ed82604c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index b88861756889..b88861756889 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java
index 57484c21c767..57484c21c767 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
index e7bde681fe6f..e7bde681fe6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index ad6c64b32ddc..ad6c64b32ddc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt
index d8618fa71aab..d8618fa71aab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandlerTest.kt
index 57cfe1b9e902..57cfe1b9e902 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
index 4ab3c7b203c4..4ab3c7b203c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index 8d84c3e7392e..8d84c3e7392e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt
index ba7a65dd8e65..ba7a65dd8e65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt
index b53652067755..b53652067755 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 40fb7691c0c2..614d51e7ac99 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -39,6 +39,7 @@ import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
@@ -53,6 +54,7 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -466,6 +468,32 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
assertThat(lp.preferredMinDisplayRefreshRate).isEqualTo(0);
}
+ @Test
+ @EnableSceneContainer
+ public void configChanged_boundsUpdate() {
+ when(mNotificationShadeWindowView.getWidth()).thenReturn(1600);
+ when(mNotificationShadeWindowView.getHeight()).thenReturn(800);
+ when(mNotificationShadeWindowView.getVisibility()).thenReturn(View.INVISIBLE);
+ Configuration newConfig = new Configuration();
+ // swap width and height in new bounds to simulate auto-rotate
+ newConfig.windowConfiguration.setBounds(new Rect(0, 0, 800, 1600));
+ mNotificationShadeWindowController.onConfigChanged(newConfig);
+ verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), any());
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void configChanged_boundsDontUpdate() {
+ when(mNotificationShadeWindowView.getWidth()).thenReturn(1600);
+ when(mNotificationShadeWindowView.getHeight()).thenReturn(800);
+ when(mNotificationShadeWindowView.getVisibility()).thenReturn(View.INVISIBLE);
+ Configuration newConfig = new Configuration();
+ // same bounds as view's current bounds
+ newConfig.windowConfiguration.setBounds(new Rect(0, 0, 1600, 800));
+ mNotificationShadeWindowController.onConfigChanged(newConfig);
+ verify(mWindowManager, never()).updateViewLayout(any(), any());
+ }
+
private void setKeyguardShowing() {
mNotificationShadeWindowController.setKeyguardShowing(true);
mNotificationShadeWindowController.setKeyguardGoingAway(false);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 21a45ecb5922..9dcbe1b591e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -616,6 +616,71 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
}
@Test
+ fun angleDecreaseAfterCancelAnimation_emitsStartClosingEvent() {
+ setFoldState(folded = true)
+ sendHingeAngleEvent(0)
+ foldUpdates.clear()
+
+ setFoldState(folded = false)
+ rotationListener.value.onRotationChanged(1)
+ sendHingeAngleEvent(180)
+ screenOnStatusProvider.notifyScreenTurningOn()
+ screenOnStatusProvider.notifyScreenTurnedOn()
+
+ // Start folding
+ (180 downTo 60).forEach {
+ sendHingeAngleEvent(it)
+ }
+ // Stopped folding and simulate timeout in this posture
+ simulateTimeout()
+
+ assertThat(foldUpdates)
+ .containsExactly(
+ FOLD_UPDATE_START_OPENING, // unfolded
+ FOLD_UPDATE_FINISH_HALF_OPEN, // force-finished the animation because of rotation
+ FOLD_UPDATE_START_CLOSING, // start closing the device
+ FOLD_UPDATE_FINISH_HALF_OPEN, // finished closing and timed-out in this state
+ )
+
+ }
+
+ @Test
+ fun angleIncreaseDecreaseAfterHalfUnfold_emitsStartClosingEvent() {
+ setFoldState(folded = true)
+ sendHingeAngleEvent(0)
+ foldUpdates.clear()
+
+ setFoldState(folded = false)
+ sendHingeAngleEvent(90)
+ screenOnStatusProvider.notifyScreenTurningOn()
+ screenOnStatusProvider.notifyScreenTurnedOn()
+
+ // Stopped folding and simulate timeout in this posture
+ simulateTimeout()
+
+ // Unfold further
+ (90 until 180).forEach {
+ sendHingeAngleEvent(it)
+ }
+ // Start folding
+ (180 downTo 90).forEach {
+ sendHingeAngleEvent(it)
+ }
+
+ // Stopped folding and simulate timeout in this posture
+ simulateTimeout()
+
+ assertThat(foldUpdates)
+ .containsExactly(
+ FOLD_UPDATE_START_OPENING, // unfolded
+ FOLD_UPDATE_FINISH_HALF_OPEN, // force-finished the animation because of rotation
+ FOLD_UPDATE_START_CLOSING, // start closing the device
+ FOLD_UPDATE_FINISH_HALF_OPEN, // finished closing and timed-out in this state
+ )
+
+ }
+
+ @Test
fun onUnfold_onSmallScreen_emitsStartOpening() {
// the new display state might arrive later, so it shouldn't be used to decide to send the
// start opening event, but only for the closing.
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt
index 19e7537007bf..b8c30fe9d4a8 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt
@@ -76,11 +76,7 @@ class BouncerHapticPlayer @Inject constructor(private val msdlPlayer: dagger.Laz
/** Deliver MSDL feedback when the delete key of the pin bouncer is pressed */
fun playDeleteKeyPressFeedback() = msdlPlayer.get().playToken(MSDLToken.KEYPRESS_DELETE)
- /**
- * Deliver MSDL feedback when the delete key of the pin bouncer is long-pressed
- *
- * @return whether MSDL feedback is allowed to play.
- */
+ /** Deliver MSDL feedback when the delete key of the pin bouncer is long-pressed. */
fun playDeleteKeyLongPressedFeedback() = msdlPlayer.get().playToken(MSDLToken.LONG_PRESS)
/** Deliver MSDL feedback when a numpad key is pressed on the pin bouncer */
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index c67b35424cc9..873d1b3cc03d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -21,6 +21,7 @@ import com.android.app.tracing.coroutines.flow.collectLatest
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer
import com.android.systemui.lifecycle.ExclusiveActivatable
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.channels.Channel
@@ -42,6 +43,7 @@ sealed class AuthMethodBouncerViewModel(
/** Name to use for performance tracing purposes. */
val traceName: String,
+ protected val bouncerHapticPlayer: BouncerHapticPlayer? = null,
) : ExclusiveActivatable() {
private val _animateFailure = MutableStateFlow(false)
@@ -80,6 +82,8 @@ sealed class AuthMethodBouncerViewModel(
return@collectLatest
}
+ performAuthenticationHapticFeedback(authenticationResult)
+
_animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
clearInput()
}
@@ -112,20 +116,23 @@ sealed class AuthMethodBouncerViewModel(
/** Returns the input entered so far. */
protected abstract fun getInput(): List<Any>
+ /** Perform authentication result haptics */
+ private fun performAuthenticationHapticFeedback(result: AuthenticationResult) {
+ if (result == AuthenticationResult.SKIPPED) return
+
+ bouncerHapticPlayer?.playAuthenticationFeedback(
+ authenticationSucceeded = result == AuthenticationResult.SUCCEEDED
+ )
+ }
+
/**
* Attempts to authenticate the user using the current input value.
*
* @see BouncerInteractor.authenticate
*/
- protected fun tryAuthenticate(
- input: List<Any> = getInput(),
- useAutoConfirm: Boolean = false,
- ) {
+ protected fun tryAuthenticate(input: List<Any> = getInput(), useAutoConfirm: Boolean = false) {
authenticationRequests.trySend(AuthenticationRequest(input, useAutoConfirm))
}
- private data class AuthenticationRequest(
- val input: List<Any>,
- val useAutoConfirm: Boolean,
- )
+ private data class AuthenticationRequest(val input: List<Any>, val useAutoConfirm: Boolean)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
index 0aada06a7eb7..0bcb58dee934 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
@@ -30,6 +30,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.qualifiers.Application
@@ -61,6 +62,7 @@ constructor(
private val pinViewModelFactory: PinBouncerViewModel.Factory,
private val patternViewModelFactory: PatternBouncerViewModel.Factory,
private val passwordViewModelFactory: PasswordBouncerViewModel.Factory,
+ private val bouncerHapticPlayer: BouncerHapticPlayer,
) : ExclusiveActivatable() {
private val _selectedUserImage = MutableStateFlow<Bitmap?>(null)
val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow()
@@ -162,10 +164,7 @@ constructor(
}
launch {
- combine(
- userSwitcher.users,
- userSwitcher.menu,
- ) { users, actions ->
+ combine(userSwitcher.users, userSwitcher.menu) { users, actions ->
users.map { user ->
UserSwitcherDropdownItemViewModel(
icon = Icon.Loaded(user.image, contentDescription = null),
@@ -178,7 +177,7 @@ constructor(
icon =
Icon.Resource(
action.iconResourceId,
- contentDescription = null
+ contentDescription = null,
),
text = Text.Resource(action.textResourceId),
onClick = action.onClicked,
@@ -226,7 +225,7 @@ constructor(
}
private fun getChildViewModel(
- authenticationMethod: AuthenticationMethodModel,
+ authenticationMethod: AuthenticationMethodModel
): AuthMethodBouncerViewModel? {
// If the current child view-model matches the authentication method, reuse it instead of
// creating a new instance.
@@ -241,12 +240,14 @@ constructor(
authenticationMethod = authenticationMethod,
onIntentionalUserInput = ::onIntentionalUserInput,
isInputEnabled = isInputEnabled,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
is AuthenticationMethodModel.Sim ->
pinViewModelFactory.create(
authenticationMethod = authenticationMethod,
onIntentionalUserInput = ::onIntentionalUserInput,
isInputEnabled = isInputEnabled,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
is AuthenticationMethodModel.Password ->
passwordViewModelFactory.create(
@@ -257,6 +258,7 @@ constructor(
patternViewModelFactory.create(
onIntentionalUserInput = ::onIntentionalUserInput,
isInputEnabled = isInputEnabled,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
else -> null
}
@@ -317,10 +319,7 @@ constructor(
return when {
// The wipe dialog takes priority over the lockout dialog.
wipeText != null ->
- DialogViewModel(
- text = wipeText,
- onDismiss = { wipeDialogMessage.value = null },
- )
+ DialogViewModel(text = wipeText, onDismiss = { wipeDialogMessage.value = null })
lockoutText != null ->
DialogViewModel(
text = lockoutText,
@@ -338,7 +337,7 @@ constructor(
fun onKeyEvent(keyEvent: KeyEvent): Boolean {
return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent(
keyEvent.type,
- keyEvent.nativeKeyEvent.keyCode
+ keyEvent.nativeKeyEvent.keyCode,
) ?: false
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 0a866b43429f..158f102ccdb3 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -18,9 +18,11 @@ package com.android.systemui.bouncer.ui.viewmodel
import android.content.Context
import android.util.TypedValue
+import android.view.View
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer
import com.android.systemui.res.R
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -35,7 +37,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the pattern bouncer UI. */
@@ -44,6 +45,7 @@ class PatternBouncerViewModel
constructor(
private val applicationContext: Context,
interactor: BouncerInteractor,
+ @Assisted bouncerHapticPlayer: BouncerHapticPlayer,
@Assisted isInputEnabled: StateFlow<Boolean>,
@Assisted private val onIntentionalUserInput: () -> Unit,
) :
@@ -51,6 +53,7 @@ constructor(
interactor = interactor,
isInputEnabled = isInputEnabled,
traceName = "PatternBouncerViewModel",
+ bouncerHapticPlayer = bouncerHapticPlayer,
) {
/** The number of columns in the dot grid. */
@@ -190,14 +193,7 @@ constructor(
private fun defaultDots(): List<PatternDotViewModel> {
return buildList {
(0 until columnCount).forEach { x ->
- (0 until rowCount).forEach { y ->
- add(
- PatternDotViewModel(
- x = x,
- y = y,
- )
- )
- }
+ (0 until rowCount).forEach { y -> add(PatternDotViewModel(x = x, y = y)) }
}
}
}
@@ -207,14 +203,17 @@ constructor(
applicationContext.resources.getValue(
com.android.internal.R.dimen.lock_pattern_dot_hit_factor,
outValue,
- true
+ true,
)
max(min(outValue.float, 1f), MIN_DOT_HIT_FACTOR)
}
+ fun performDotFeedback(view: View?) = bouncerHapticPlayer?.playPatternDotFeedback(view)
+
@AssistedFactory
interface Factory {
fun create(
+ bouncerHapticPlayer: BouncerHapticPlayer,
isInputEnabled: StateFlow<Boolean>,
onIntentionalUserInput: () -> Unit,
): PatternBouncerViewModel
@@ -231,7 +230,7 @@ constructor(
*/
private fun PatternDotViewModel.isOnLineSegment(
first: PatternDotViewModel,
- second: PatternDotViewModel
+ second: PatternDotViewModel,
): Boolean {
val anotherPoint = this
// No need to consider any points outside the bounds of two end points
@@ -253,14 +252,8 @@ private fun Int.isBetween(a: Int, b: Int): Boolean {
return (this in a..b) || (this in b..a)
}
-data class PatternDotViewModel(
- val x: Int,
- val y: Int,
-) {
+data class PatternDotViewModel(val x: Int, val y: Int) {
fun toCoordinate(): AuthenticationPatternCoordinate {
- return AuthenticationPatternCoordinate(
- x = x,
- y = y,
- )
+ return AuthenticationPatternCoordinate(x = x, y = y)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index da29c6230cd8..0cb4260e4d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -19,12 +19,14 @@
package com.android.systemui.bouncer.ui.viewmodel
import android.content.Context
+import android.view.HapticFeedbackConstants
import android.view.KeyEvent.KEYCODE_0
import android.view.KeyEvent.KEYCODE_9
import android.view.KeyEvent.KEYCODE_DEL
import android.view.KeyEvent.KEYCODE_NUMPAD_0
import android.view.KeyEvent.KEYCODE_NUMPAD_9
import android.view.KeyEvent.isConfirmKey
+import android.view.View
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import com.android.keyguard.PinShapeAdapter
@@ -32,6 +34,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
+import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer
import com.android.systemui.res.R
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -56,6 +59,7 @@ constructor(
applicationContext: Context,
interactor: BouncerInteractor,
private val simBouncerInteractor: SimBouncerInteractor,
+ @Assisted bouncerHapticPlayer: BouncerHapticPlayer,
@Assisted isInputEnabled: StateFlow<Boolean>,
@Assisted private val onIntentionalUserInput: () -> Unit,
@Assisted override val authenticationMethod: AuthenticationMethodModel,
@@ -64,6 +68,7 @@ constructor(
interactor = interactor,
isInputEnabled = isInputEnabled,
traceName = "PinBouncerViewModel",
+ bouncerHapticPlayer = bouncerHapticPlayer,
) {
/**
* Whether the sim-related UI in the pin view is showing.
@@ -126,10 +131,9 @@ constructor(
.collect { _hintedPinLength.value = it }
}
launch {
- combine(
- mutablePinInput,
- interactor.isAutoConfirmEnabled,
- ) { mutablePinEntries, isAutoConfirmEnabled ->
+ combine(mutablePinInput, interactor.isAutoConfirmEnabled) {
+ mutablePinEntries,
+ isAutoConfirmEnabled ->
computeBackspaceButtonAppearance(
pinInput = mutablePinEntries,
isAutoConfirmEnabled = isAutoConfirmEnabled,
@@ -183,8 +187,22 @@ constructor(
mutablePinInput.value = mutablePinInput.value.deleteLast()
}
+ fun onBackspaceButtonPressed(view: View?) {
+ if (bouncerHapticPlayer?.isEnabled == true) {
+ bouncerHapticPlayer.playDeleteKeyPressFeedback()
+ } else {
+ view?.performHapticFeedback(
+ HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
+ )
+ }
+ }
+
/** Notifies that the user long-pressed the backspace button. */
fun onBackspaceButtonLongPressed() {
+ if (bouncerHapticPlayer?.isEnabled == true) {
+ bouncerHapticPlayer.playDeleteKeyLongPressedFeedback()
+ }
clearInput()
}
@@ -266,13 +284,24 @@ constructor(
}
}
- /** Notifies that the user has pressed down on a digit button. */
- fun onDigitButtonDown() {
+ /**
+ * Notifies that the user has pressed down on a digit button. This function also performs haptic
+ * feedback on the view.
+ */
+ fun onDigitButtonDown(view: View?) {
if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) {
// Current PIN bouncer informs FalsingInteractor#avoidGesture() upon every Pin button
// touch.
super.onDown()
}
+ if (bouncerHapticPlayer?.isEnabled == true) {
+ bouncerHapticPlayer.playNumpadKeyFeedback()
+ } else {
+ view?.performHapticFeedback(
+ HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
+ )
+ }
}
@AssistedFactory
@@ -281,6 +310,7 @@ constructor(
isInputEnabled: StateFlow<Boolean>,
onIntentionalUserInput: () -> Unit,
authenticationMethod: AuthenticationMethodModel,
+ bouncerHapticPlayer: BouncerHapticPlayer,
): PinBouncerViewModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index 769976ef5058..ae4b679dd4b8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -282,6 +282,8 @@ public class FalsingDataProvider {
return !mRecentKeyEvents.isEmpty();
}
+ // Deprecated in favor of {@code isTouchScreenSource}, b/329221787
+ @Deprecated
public boolean isFromTrackpad() {
if (Flags.nonTouchscreenDevicesBypassFalsing()) {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 6e01393015ed..08a7c395e57f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -19,11 +19,13 @@ package com.android.systemui.communal
import android.provider.Settings
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.shared.model.EditModeState
@@ -84,6 +86,7 @@ constructor(
@Application private val applicationScope: CoroutineScope,
@Background private val bgScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
+ private val uiEventLogger: UiEventLogger,
) : CoreStartable {
private var screenTimeout: Int = DEFAULT_SCREEN_TIMEOUT
@@ -146,7 +149,7 @@ constructor(
screenTimeout =
systemSettings.getInt(
Settings.System.SCREEN_OFF_TIMEOUT,
- DEFAULT_SCREEN_TIMEOUT
+ DEFAULT_SCREEN_TIMEOUT,
)
}
.launchIn(bgScope)
@@ -160,7 +163,7 @@ constructor(
combine(
communalSceneInteractor.currentScene,
// Emit a value on start so the combine starts.
- communalInteractor.userActivity.emitOnStart()
+ communalInteractor.userActivity.emitOnStart(),
) { scene, _ ->
// Only timeout if we're on the hub is open.
scene == CommunalScenes.Communal
@@ -184,6 +187,7 @@ constructor(
CommunalScenes.Blank,
"dream started after timeout",
)
+ uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT)
}
}
}
@@ -212,6 +216,7 @@ constructor(
newScene = CommunalScenes.Blank,
loggingReason = "hub timeout",
)
+ uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT)
}
timeoutJob = null
}
@@ -219,7 +224,7 @@ constructor(
}
private suspend fun determineSceneAfterTransition(
- lastStartedTransition: TransitionStep,
+ lastStartedTransition: TransitionStep
): Pair<SceneKey, TransitionKey>? {
val to = lastStartedTransition.to
val from = lastStartedTransition.from
@@ -251,9 +256,8 @@ constructor(
Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
}
from == KeyguardState.DOZING && to == KeyguardState.GLANCEABLE_HUB -> {
- // Make sure the communal hub is showing (immediately, not fading in) when
- // transitioning from dozing to hub.
- Pair(CommunalScenes.Communal, CommunalTransitionKeys.Immediately)
+ // Make sure the communal hub is showing when transitioning from dozing to hub.
+ Pair(CommunalScenes.Communal, CommunalTransitionKeys.SimpleFade)
}
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
index 11fb2332dc5f..78156dbc8964 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
@@ -30,6 +30,4 @@ object CommunalTransitionKeys {
val ToEditMode = TransitionKey("ToEditMode")
/** Transition to the glanceable hub after exiting edit mode */
val FromEditMode = TransitionKey("FromEditMode")
- /** Immediately transitions without any delay */
- val Immediately = TransitionKey("Immediately")
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index 8966209d825e..e09e1987698d 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -45,7 +45,6 @@ import javax.inject.Inject
* @property[vibratorHelper] The [VibratorHelper] to deliver haptic effects.
* @property[effectDuration] The duration of the effect in ms.
*/
-// TODO(b/332902869): In addition from being injectable, we can consider making it a singleton
class QSLongPressEffect
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 80a0cee4f319..b0820a747e17 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -24,7 +24,6 @@ import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -95,10 +94,7 @@ constructor(
scope.launch {
powerInteractor.isAwake
.filterRelevantKeyguardStateAnd { isAwake -> isAwake }
- .sample(
- keyguardInteractor.biometricUnlockState,
- ::Pair,
- )
+ .sample(keyguardInteractor.biometricUnlockState, ::Pair)
.collect {
(
_,
@@ -203,21 +199,21 @@ constructor(
if (!SceneContainerFlag.isEnabled) {
startTransitionTo(
KeyguardState.GONE,
- ownerReason = "waking from dozing"
+ ownerReason = "waking from dozing",
)
}
} else if (primaryBouncerShowing) {
if (!SceneContainerFlag.isEnabled) {
startTransitionTo(
KeyguardState.PRIMARY_BOUNCER,
- ownerReason = "waking from dozing"
+ ownerReason = "waking from dozing",
)
}
} else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
if (!SceneContainerFlag.isEnabled) {
startTransitionTo(
KeyguardState.GLANCEABLE_HUB,
- ownerReason = "waking from dozing"
+ ownerReason = "waking from dozing",
)
}
} else if (isCommunalAvailable && dreamManager.canStartDreaming(true)) {
@@ -227,7 +223,7 @@ constructor(
} else {
startTransitionTo(
KeyguardState.LOCKSCREEN,
- ownerReason = "waking from dozing"
+ ownerReason = "waking from dozing",
)
}
}
@@ -237,11 +233,9 @@ constructor(
private suspend fun transitionToGlanceableHub() {
if (communalSceneKtfRefactor()) {
- communalSceneInteractor.changeScene(
+ communalSceneInteractor.snapToScene(
newScene = CommunalScenes.Communal,
loggingReason = "from dozing to hub",
- // Immediately show the hub when transitioning from dozing to hub.
- transitionKey = CommunalTransitionKeys.Immediately,
)
} else {
startTransitionTo(KeyguardState.GLANCEABLE_HUB)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 199caa168e31..7759298cb32a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -162,10 +162,9 @@ constructor(
.filterRelevantKeyguardStateAnd { isAsleep -> isAsleep }
.collect {
if (communalSceneKtfRefactor()) {
- communalSceneInteractor.changeScene(
+ communalSceneInteractor.snapToScene(
newScene = CommunalScenes.Blank,
loggingReason = "hub to dozing",
- transitionKey = CommunalTransitionKeys.Immediately,
keyguardState = KeyguardState.DOZING,
)
} else {
@@ -210,12 +209,12 @@ constructor(
// ends, to avoid transitioning to OCCLUDED erroneously when exiting
// the dream.
.debounce(100.milliseconds),
- ::Pair
+ ::Pair,
)
.sampleFilter(
// When launching activities from widgets on the hub, we have a
// custom occlusion animation.
- communalSceneInteractor.isLaunchingWidget,
+ communalSceneInteractor.isLaunchingWidget
) { launchingWidget ->
!launchingWidget
}
@@ -253,7 +252,7 @@ constructor(
noneOf(
// When launching activities from widgets on the hub, we wait to change
// scenes until the activity launch is complete.
- communalSceneInteractor.isLaunchingWidget,
+ communalSceneInteractor.isLaunchingWidget
),
)
.filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
@@ -270,7 +269,7 @@ constructor(
newScene = CommunalScenes.Blank,
loggingReason = "hub to gone",
transitionKey = CommunalTransitionKeys.SimpleFade,
- keyguardState = KeyguardState.GONE
+ keyguardState = KeyguardState.GONE,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index deb0b2d8f848..b5f6b418e322 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -261,7 +261,10 @@ object KeyguardRootViewBinder {
->
if (biometricMessage?.message != null) {
chipbarCoordinator!!.displayView(
- createChipbarInfo(biometricMessage.message, R.drawable.ic_lock)
+ createChipbarInfo(
+ biometricMessage.message,
+ R.drawable.ic_lock,
+ )
)
} else {
chipbarCoordinator!!.removeView(ID, "occludingAppMsgNull")
@@ -324,16 +327,12 @@ object KeyguardRootViewBinder {
.getDimensionPixelSize(R.dimen.shelf_appear_translation)
.stateIn(this)
viewModel.isNotifIconContainerVisible.collect { isVisible ->
- if (isVisible.value) {
- blueprintViewModel.refreshBlueprint()
- } else {
- childViews[aodNotificationIconContainerId]
- ?.setAodNotifIconContainerIsVisible(
- isVisible,
- iconsAppearTranslationPx.value,
- screenOffAnimationController,
- )
- }
+ childViews[aodNotificationIconContainerId]
+ ?.setAodNotifIconContainerIsVisible(
+ isVisible,
+ iconsAppearTranslationPx.value,
+ screenOffAnimationController,
+ )
}
}
@@ -383,7 +382,7 @@ object KeyguardRootViewBinder {
if (msdlFeedback()) {
msdlPlayer?.playToken(
MSDLToken.UNLOCK,
- authInteractionProperties,
+ authInteractionProperties
)
} else {
vibratorHelper.performHapticFeedback(
@@ -399,7 +398,7 @@ object KeyguardRootViewBinder {
if (msdlFeedback()) {
msdlPlayer?.playToken(
MSDLToken.FAILURE,
- authInteractionProperties,
+ authInteractionProperties
)
} else {
vibratorHelper.performHapticFeedback(
@@ -426,7 +425,7 @@ object KeyguardRootViewBinder {
blueprintViewModel,
clockViewModel,
childViews,
- burnInParams,
+ burnInParams
)
)
@@ -465,7 +464,11 @@ object KeyguardRootViewBinder {
*/
private fun createChipbarInfo(message: String, @DrawableRes icon: Int): ChipbarInfo {
return ChipbarInfo(
- startIcon = TintedIcon(Icon.Resource(icon, null), ChipbarInfo.DEFAULT_ICON_TINT),
+ startIcon =
+ TintedIcon(
+ Icon.Resource(icon, null),
+ ChipbarInfo.DEFAULT_ICON_TINT,
+ ),
text = Text.Loaded(message),
endItem = null,
vibrationEffect = null,
@@ -496,7 +499,7 @@ object KeyguardRootViewBinder {
oldLeft: Int,
oldTop: Int,
oldRight: Int,
- oldBottom: Int,
+ oldBottom: Int
) {
// After layout, ensure the notifications are positioned correctly
childViews[nsslPlaceholderId]?.let { notificationListPlaceholder ->
@@ -512,7 +515,7 @@ object KeyguardRootViewBinder {
viewModel.onNotificationContainerBoundsChanged(
notificationListPlaceholder.top.toFloat(),
notificationListPlaceholder.bottom.toFloat(),
- animate = shouldAnimate,
+ animate = shouldAnimate
)
}
@@ -528,7 +531,7 @@ object KeyguardRootViewBinder {
Int.MAX_VALUE
} else {
view.getTop()
- },
+ }
)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
index 4cf3c4e7f6d0..c6efcfad8da7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
@@ -25,18 +25,20 @@ import androidx.constraintlayout.widget.ConstraintLayout
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
-import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-data class TransitionData(val config: Config, val start: Long = System.currentTimeMillis())
+data class TransitionData(
+ val config: Config,
+ val start: Long = System.currentTimeMillis(),
+)
class KeyguardBlueprintViewModel
@Inject
constructor(
@Main private val handler: Handler,
- private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
+ keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
) {
val blueprint = keyguardBlueprintInteractor.blueprint
val blueprintId = keyguardBlueprintInteractor.blueprintId
@@ -74,9 +76,6 @@ constructor(
}
}
- fun refreshBlueprint(type: Type = Type.NoTransition) =
- keyguardBlueprintInteractor.refreshBlueprint(type)
-
fun updateTransitions(data: TransitionData?, mutate: MutableSet<Transition>.() -> Unit) {
runningTransitions.mutate()
@@ -96,7 +95,7 @@ constructor(
Log.w(
TAG,
"runTransition: skipping ${transition::class.simpleName}: " +
- "currentPriority=$currentPriority; config=$config",
+ "currentPriority=$currentPriority; config=$config"
)
}
apply()
@@ -107,7 +106,7 @@ constructor(
Log.i(
TAG,
"runTransition: running ${transition::class.simpleName}: " +
- "currentPriority=$currentPriority; config=$config",
+ "currentPriority=$currentPriority; config=$config"
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 130868dc3c1c..e34ff7b1028c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -33,6 +33,7 @@ import com.android.systemui.bluetooth.BroadcastDialogController
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
import com.android.systemui.media.controls.domain.pipeline.getNotificationActions
+import com.android.systemui.media.controls.shared.MediaLogger
import com.android.systemui.media.controls.shared.model.MediaControlModel
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.util.MediaSmartspaceLogger
@@ -59,6 +60,7 @@ constructor(
private val lockscreenUserManager: NotificationLockscreenUserManager,
private val mediaOutputDialogManager: MediaOutputDialogManager,
private val broadcastDialogController: BroadcastDialogController,
+ private val mediaLogger: MediaLogger,
) {
val mediaControl: Flow<MediaControlModel?> =
@@ -73,7 +75,7 @@ constructor(
instanceId: InstanceId,
delayMs: Long,
eventId: Int,
- location: Int
+ location: Int,
): Boolean {
logSmartspaceUserEvent(eventId, location)
val dismissed =
@@ -81,7 +83,7 @@ constructor(
if (!dismissed) {
Log.w(
TAG,
- "Manager failed to dismiss media of instanceId=$instanceId, Token uid=${token?.uid}"
+ "Manager failed to dismiss media of instanceId=$instanceId, Token uid=${token?.uid}",
)
}
return dismissed
@@ -120,13 +122,13 @@ constructor(
expandable: Expandable,
clickIntent: PendingIntent,
eventId: Int,
- location: Int
+ location: Int,
) {
logSmartspaceUserEvent(eventId, location)
if (!launchOverLockscreen(clickIntent)) {
activityStarter.postStartActivityDismissingKeyguard(
clickIntent,
- expandable.activityTransitionController(Cuj.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER)
+ expandable.activityTransitionController(Cuj.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER),
)
}
}
@@ -146,7 +148,7 @@ constructor(
keyguardStateController.isShowing &&
activityIntentHelper.wouldPendingShowOverLockscreen(
pendingIntent,
- lockscreenUserManager.currentUserId
+ lockscreenUserManager.currentUserId,
)
if (showOverLockscreen) {
try {
@@ -166,7 +168,7 @@ constructor(
fun startMediaOutputDialog(
expandable: Expandable,
packageName: String,
- token: MediaSession.Token? = null
+ token: MediaSession.Token? = null,
) {
mediaOutputDialogManager.createAndShowWithController(
packageName,
@@ -180,7 +182,7 @@ constructor(
broadcastDialogController.createBroadcastDialogWithController(
broadcastApp,
packageName,
- expandable.dialogTransitionController()
+ expandable.dialogTransitionController(),
)
}
@@ -188,10 +190,14 @@ constructor(
repository.logSmartspaceCardUserEvent(
eventId,
MediaSmartspaceLogger.getSurface(location),
- instanceId = instanceId
+ instanceId = instanceId,
)
}
+ fun logMediaControlIsBound(artistName: CharSequence, songName: CharSequence) {
+ mediaLogger.logMediaControlIsBound(instanceId, artistName, songName)
+ }
+
private fun Expandable.dialogController(): DialogTransitionAnimator.Controller? {
return dialogTransitionController(
cuj =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
index 7d20e170d8bc..88c47ba4d243 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
@@ -36,7 +36,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
bool1 = active
str2 = reason
},
- { "add media $str1, active: $bool1, reason: $str2" }
+ { "add media $str1, active: $bool1, reason: $str2" },
)
}
@@ -48,7 +48,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
str1 = instanceId.toString()
str2 = reason
},
- { "removing media $str1, reason: $str2" }
+ { "removing media $str1, reason: $str2" },
)
}
@@ -61,7 +61,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
bool1 = isActive
str2 = reason
},
- { "add recommendation $str1, active $bool1, reason: $str2" }
+ { "add recommendation $str1, active $bool1, reason: $str2" },
)
}
@@ -74,7 +74,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
bool1 = immediately
str2 = reason
},
- { "removing recommendation $str1, immediate=$bool1, reason: $str2" }
+ { "removing recommendation $str1, immediate=$bool1, reason: $str2" },
)
}
@@ -83,7 +83,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
TAG,
LogLevel.DEBUG,
{ str1 = instanceId.toString() },
- { "adding media card $str1 to carousel" }
+ { "adding media card $str1 to carousel" },
)
}
@@ -92,7 +92,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
TAG,
LogLevel.DEBUG,
{ str1 = instanceId.toString() },
- { "removing media card $str1 from carousel" }
+ { "removing media card $str1 from carousel" },
)
}
@@ -101,7 +101,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
TAG,
LogLevel.DEBUG,
{ str1 = key },
- { "adding recommendation card $str1 to carousel" }
+ { "adding recommendation card $str1 to carousel" },
)
}
@@ -110,7 +110,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
TAG,
LogLevel.DEBUG,
{ str1 = key },
- { "removing recommendation card $str1 from carousel" }
+ { "removing recommendation card $str1 from carousel" },
)
}
@@ -119,7 +119,24 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
TAG,
LogLevel.DEBUG,
{ str1 = key },
- { "duplicate media notification $str1 posted" }
+ { "duplicate media notification $str1 posted" },
+ )
+ }
+
+ fun logMediaControlIsBound(
+ instanceId: InstanceId,
+ artistName: CharSequence,
+ title: CharSequence,
+ ) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = instanceId.toString()
+ str2 = artistName.toString()
+ str3 = title.toString()
+ },
+ { "binding media control, instance id= $str1, artist= $str2, title= $str3" },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 6373feda9c9b..4055818dcb0b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -80,16 +80,19 @@ object MediaControlViewBinder {
mediaCard.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- viewModel.player.collectLatest { playerViewModel ->
- playerViewModel?.let {
- bindMediaCard(
- viewHolder,
- viewController,
- it,
- falsingManager,
- backgroundDispatcher,
- mainDispatcher,
- )
+ viewModel.player.collectLatest { player ->
+ player?.let {
+ if (viewModel.isNewPlayer(it)) {
+ bindMediaCard(
+ viewHolder,
+ viewController,
+ it,
+ falsingManager,
+ backgroundDispatcher,
+ mainDispatcher,
+ )
+ viewModel.onMediaControlIsBound(it.artistName, it.titleName)
+ }
}
}
}
@@ -143,7 +146,7 @@ object MediaControlViewBinder {
viewHolder,
viewModel.outputSwitcher,
viewController,
- falsingManager
+ falsingManager,
)
bindGutsViewModel(viewHolder, viewModel, viewController, falsingManager)
bindActionButtons(viewHolder, viewModel, viewController, falsingManager)
@@ -157,7 +160,7 @@ object MediaControlViewBinder {
viewController,
backgroundDispatcher,
mainDispatcher,
- isSongUpdated
+ isSongUpdated,
)
if (viewModel.playTurbulenceNoise) {
@@ -259,12 +262,12 @@ object MediaControlViewBinder {
if (buttonView.id == R.id.actionPrev) {
viewController.setUpPrevButtonInfo(
buttonModel.isEnabled,
- buttonModel.notVisibleValue
+ buttonModel.notVisibleValue,
)
} else if (buttonView.id == R.id.actionNext) {
viewController.setUpNextButtonInfo(
buttonModel.isEnabled,
- buttonModel.notVisibleValue
+ buttonModel.notVisibleValue,
)
}
val animHandler = (buttonView.tag ?: AnimationBindHandler()) as AnimationBindHandler
@@ -295,7 +298,7 @@ object MediaControlViewBinder {
viewController.collapsedLayout,
visible,
buttonModel.notVisibleValue,
- buttonModel.showInCollapsed
+ buttonModel.showInCollapsed,
)
}
}
@@ -350,7 +353,7 @@ object MediaControlViewBinder {
createTouchRippleAnimation(
button,
viewController.colorSchemeTransition,
- multiRippleView
+ multiRippleView,
)
)
@@ -382,12 +385,12 @@ object MediaControlViewBinder {
setVisibleAndAlpha(
expandedSet,
R.id.media_explicit_indicator,
- viewModel.isExplicitVisible
+ viewModel.isExplicitVisible,
)
setVisibleAndAlpha(
collapsedSet,
R.id.media_explicit_indicator,
- viewModel.isExplicitVisible
+ viewModel.isExplicitVisible,
)
// refreshState is required here to resize the text views (and prevent ellipsis)
@@ -398,7 +401,7 @@ object MediaControlViewBinder {
// something is incorrectly bound, but needs to be run if other elements were
// updated while the enter animation was running
viewController.refreshState()
- }
+ },
)
}
@@ -427,7 +430,7 @@ object MediaControlViewBinder {
viewModel.backgroundCover!!,
viewModel.colorScheme,
width,
- height
+ height,
)
} else {
ColorDrawable(Color.TRANSPARENT)
@@ -493,7 +496,7 @@ object MediaControlViewBinder {
transitionDrawable: TransitionDrawable,
layer: Int,
targetWidth: Int,
- targetHeight: Int
+ targetHeight: Int,
) {
val drawable = transitionDrawable.getDrawable(layer) ?: return
val width = drawable.intrinsicWidth
@@ -509,7 +512,7 @@ object MediaControlViewBinder {
artworkIcon: android.graphics.drawable.Icon,
mutableColorScheme: ColorScheme,
width: Int,
- height: Int
+ height: Int,
): LayerDrawable {
val albumArt = MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height)
return MediaArtworkHelper.setUpGradientColorOnDrawable(
@@ -517,7 +520,7 @@ object MediaControlViewBinder {
context.getDrawable(R.drawable.qs_media_scrim)?.mutate() as GradientDrawable,
mutableColorScheme,
MEDIA_PLAYER_SCRIM_START_ALPHA,
- MEDIA_PLAYER_SCRIM_END_ALPHA
+ MEDIA_PLAYER_SCRIM_END_ALPHA,
)
}
@@ -544,7 +547,7 @@ object MediaControlViewBinder {
private fun createTouchRippleAnimation(
button: ImageButton,
colorSchemeTransition: ColorSchemeTransition,
- multiRippleView: MultiRippleView
+ multiRippleView: MultiRippleView,
): RippleAnimation {
val maxSize = (multiRippleView.width * 2).toFloat()
return RippleAnimation(
@@ -562,7 +565,7 @@ object MediaControlViewBinder {
baseRingFadeParams = null,
sparkleRingFadeParams = null,
centerFillFadeParams = null,
- shouldDistort = false
+ shouldDistort = false,
)
)
}
@@ -596,7 +599,7 @@ object MediaControlViewBinder {
set: ConstraintSet,
resId: Int,
visible: Boolean,
- notVisibleValue: Int
+ notVisibleValue: Int,
) {
set.setVisibility(resId, if (visible) ConstraintSet.VISIBLE else notVisibleValue)
set.setAlpha(resId, if (visible) 1.0f else 0.0f)
@@ -618,7 +621,7 @@ object MediaControlViewBinder {
collapsedSet: ConstraintSet,
visible: Boolean,
notVisibleValue: Int,
- showInCollapsed: Boolean
+ showInCollapsed: Boolean,
) {
if (notVisibleValue == ConstraintSet.INVISIBLE) {
// Since time views should appear instead of buttons.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 8505a784d302..49a8758ed51e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -123,7 +123,7 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
* Class that is responsible for keeping the view carousel up to date. This also handles changes in
* state and applies them to the media carousel like the expansion.
*/
-@ExperimentalCoroutinesApi
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class MediaCarouselController
@Inject
@@ -252,13 +252,13 @@ constructor(
fun calculateAlpha(
squishinessFraction: Float,
startPosition: Float,
- endPosition: Float
+ endPosition: Float,
): Float {
val transformFraction =
MathUtils.constrain(
(squishinessFraction - startPosition) / (endPosition - startPosition),
0F,
- 1F
+ 1F,
)
return TRANSFORM_BEZIER.getInterpolation(transformFraction)
}
@@ -354,7 +354,7 @@ constructor(
this::closeGuts,
falsingManager,
this::logSmartspaceImpression,
- logger
+ logger,
)
carouselLocale = context.resources.configuration.locales.get(0)
isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
@@ -387,7 +387,7 @@ constructor(
object : MediaHostStatesManager.Callback {
override fun onHostStateChanged(
@MediaLocation location: Int,
- mediaHostState: MediaHostState
+ mediaHostState: MediaHostState,
) {
updateUserVisibility()
if (location == desiredLocation) {
@@ -412,7 +412,7 @@ constructor(
bgExecutor.execute {
globalSettings.registerContentObserverSync(
Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
- animationScaleObserver
+ animationScaleObserver,
)
}
}
@@ -452,7 +452,7 @@ constructor(
data: MediaData,
immediately: Boolean,
receivedSmartspaceCardLatency: Int,
- isSsReactivated: Boolean
+ isSsReactivated: Boolean,
) {
debugLogger.logMediaLoaded(key, data.active)
if (addOrUpdatePlayer(key, oldKey, data, isSsReactivated)) {
@@ -468,7 +468,7 @@ constructor(
SSPACE_CARD_REPORTED__LOCKSCREEN,
SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
- rank = MediaPlayerData.getMediaPlayerIndex(key)
+ rank = MediaPlayerData.getMediaPlayerIndex(key),
)
}
if (
@@ -500,7 +500,7 @@ constructor(
SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
rank = index,
- receivedLatencyMillis = receivedSmartspaceCardLatency
+ receivedLatencyMillis = receivedSmartspaceCardLatency,
)
}
}
@@ -533,7 +533,7 @@ constructor(
override fun onSmartspaceMediaDataLoaded(
key: String,
data: SmartspaceMediaData,
- shouldPrioritize: Boolean
+ shouldPrioritize: Boolean,
) {
debugLogger.logRecommendationLoaded(key, data.isActive)
// Log the case where the hidden media carousel with the existed inactive resume
@@ -568,7 +568,7 @@ constructor(
receivedLatencyMillis =
(systemClock.currentTimeMillis() -
data.headphoneConnectionTimeMillis)
- .toInt()
+ .toInt(),
)
}
}
@@ -589,7 +589,7 @@ constructor(
receivedLatencyMillis =
(systemClock.currentTimeMillis() -
data.headphoneConnectionTimeMillis)
- .toInt()
+ .toInt(),
)
}
if (
@@ -644,10 +644,7 @@ constructor(
mediaCarouselScrollHandler.onSettingsButtonUpdated(settings)
settingsButton.setOnClickListener {
logger.logCarouselSettings()
- activityStarter.startActivity(
- settingsIntent,
- /* dismissShade= */ true,
- )
+ activityStarter.startActivity(settingsIntent, /* dismissShade= */ true)
}
}
@@ -739,7 +736,7 @@ constructor(
val lp =
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
+ ViewGroup.LayoutParams.WRAP_CONTENT,
)
when (commonViewModel) {
is MediaCommonViewModel.MediaControl -> {
@@ -785,7 +782,7 @@ constructor(
) {
mediaCarouselScrollHandler.scrollToPlayer(
mediaCarouselScrollHandler.visibleMediaIndex,
- destIndex = 0
+ destIndex = 0,
)
}
mediaCarouselScrollHandler.onPlayersChanged()
@@ -799,7 +796,7 @@ constructor(
mediaCarouselScrollHandler.onPlayersChanged()
onAddOrUpdateVisibleToUserCard(
position,
- commonViewModel is MediaCommonViewModel.MediaControl
+ commonViewModel is MediaCommonViewModel.MediaControl,
)
}
@@ -856,7 +853,7 @@ constructor(
mediaCarouselScrollHandler.qsExpanded,
mediaCarouselScrollHandler.visibleMediaIndex,
currentEndLocation,
- isMediaCardUpdate
+ isMediaCardUpdate,
)
}
}
@@ -893,7 +890,7 @@ constructor(
secureSettings.getBoolForUser(
Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
true,
- UserHandle.USER_CURRENT
+ UserHandle.USER_CURRENT,
)
}
}
@@ -926,7 +923,7 @@ constructor(
private fun reorderAllPlayers(
previousVisiblePlayerKey: MediaPlayerData.MediaSortKey?,
- key: String? = null
+ key: String? = null,
) {
mediaContent.removeAllViews()
for (mediaPlayer in MediaPlayerData.players()) {
@@ -960,7 +957,7 @@ constructor(
TAG,
"Size of players list and number of views in carousel are out of sync. " +
"Players size is ${MediaPlayerData.players().size}. " +
- "View count is ${mediaContent.childCount}."
+ "View count is ${mediaContent.childCount}.",
)
}
}
@@ -970,7 +967,7 @@ constructor(
key: String,
oldKey: String?,
data: MediaData,
- isSsReactivated: Boolean
+ isSsReactivated: Boolean,
): Boolean =
traceSection("MediaCarouselController#addOrUpdatePlayer") {
MediaPlayerData.moveIfExists(oldKey, key)
@@ -992,7 +989,7 @@ constructor(
val lp =
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
+ ViewGroup.LayoutParams.WRAP_CONTENT,
)
newPlayer.mediaViewHolder?.player?.setLayoutParams(lp)
newPlayer.bindPlayer(data, key)
@@ -1005,7 +1002,7 @@ constructor(
newPlayer,
systemClock,
isSsReactivated,
- debugLogger
+ debugLogger,
)
updateViewControllerToState(newPlayer.mediaViewController, noAnimation = true)
// Media data added from a recommendation card should starts playing.
@@ -1025,7 +1022,7 @@ constructor(
existingPlayer,
systemClock,
isSsReactivated,
- debugLogger
+ debugLogger,
)
val packageName = MediaPlayerData.smartspaceMediaData?.packageName ?: String()
// In case of recommendations hits.
@@ -1051,7 +1048,7 @@ constructor(
private fun addSmartspaceMediaRecommendations(
key: String,
data: SmartspaceMediaData,
- shouldPrioritize: Boolean
+ shouldPrioritize: Boolean,
) =
traceSection("MediaCarouselController#addSmartspaceMediaRecommendations") {
if (DEBUG) Log.d(TAG, "Updating smartspace target in carousel")
@@ -1090,7 +1087,7 @@ constructor(
val lp =
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
+ ViewGroup.LayoutParams.WRAP_CONTENT,
)
newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp)
newRecs.bindRecommendation(data)
@@ -1117,7 +1114,7 @@ constructor(
TAG,
"Size of players list and number of views in carousel are out of sync. " +
"Players size is ${MediaPlayerData.players().size}. " +
- "View count is ${mediaContent.childCount}."
+ "View count is ${mediaContent.childCount}.",
)
}
}
@@ -1173,7 +1170,7 @@ constructor(
addSmartspaceMediaRecommendations(
it.targetId,
it,
- MediaPlayerData.shouldPrioritizeSs
+ MediaPlayerData.shouldPrioritizeSs,
)
}
} else {
@@ -1185,7 +1182,7 @@ constructor(
key = key,
oldKey = null,
data = data,
- isSsReactivated = isSsReactivated
+ isSsReactivated = isSsReactivated,
)
}
if (recreateMedia) {
@@ -1248,7 +1245,7 @@ constructor(
@MediaLocation startLocation: Int,
@MediaLocation endLocation: Int,
progress: Float,
- immediately: Boolean
+ immediately: Boolean,
) {
if (
startLocation != currentStartLocation ||
@@ -1286,7 +1283,7 @@ constructor(
squishFraction,
(pageIndicator.translationY + pageIndicator.height) /
mediaCarousel.measuredHeight,
- 1F
+ 1F,
)
var alpha = 1.0f
if (!endIsVisible || !startIsVisible) {
@@ -1354,7 +1351,7 @@ constructor(
currentCarouselHeight = height
mediaCarouselScrollHandler.setCarouselBounds(
currentCarouselWidth,
- currentCarouselHeight
+ currentCarouselHeight,
)
updatePageIndicatorLocation()
updatePageIndicatorAlpha()
@@ -1386,13 +1383,13 @@ constructor(
private fun updateViewControllerToState(
viewController: MediaViewController,
- noAnimation: Boolean
+ noAnimation: Boolean,
) {
viewController.setCurrentState(
startLocation = currentStartLocation,
endLocation = currentEndLocation,
transitionProgress = currentTransitionProgress,
- applyImmediately = noAnimation
+ applyImmediately = noAnimation,
)
}
@@ -1411,7 +1408,7 @@ constructor(
desiredHostState: MediaHostState?,
animate: Boolean,
duration: Long = 200,
- startDelay: Long = 0
+ startDelay: Long = 0,
) =
traceSection("MediaCarouselController#onDesiredLocationChanged") {
desiredHostState?.let {
@@ -1435,7 +1432,7 @@ constructor(
if (animate) {
mediaPlayer.mediaViewController.animatePendingStateChange(
duration = duration,
- delay = startDelay
+ delay = startDelay,
)
}
if (shouldCloseGuts && mediaPlayer.mediaViewController.isGutsVisible) {
@@ -1506,7 +1503,7 @@ constructor(
mediaCarouselViewModel.onCardVisibleToUser(
qsExpanded,
mediaCarouselScrollHandler.visibleMediaIndex,
- currentEndLocation
+ currentEndLocation,
)
return
}
@@ -1524,7 +1521,7 @@ constructor(
800, // SMARTSPACE_CARD_SEEN
it.mSmartspaceId,
it.mUid,
- intArrayOf(it.surfaceForSmartspaceLogging)
+ intArrayOf(it.surfaceForSmartspaceLogging),
)
it.mIsImpressed = true
}
@@ -1559,7 +1556,7 @@ constructor(
interactedSubcardCardinality: Int = 0,
rank: Int = mediaCarouselScrollHandler.visibleMediaIndex,
receivedLatencyMillis: Int = 0,
- isSwipeToDismiss: Boolean = false
+ isSwipeToDismiss: Boolean = false,
) {
if (MediaPlayerData.players().size <= rank) {
return
@@ -1600,7 +1597,7 @@ constructor(
interactedSubcardCardinality,
receivedLatencyMillis,
null, // Media cards cannot have subcards.
- null // Media cards don't have dimensions today.
+ null, // Media cards don't have dimensions today.
)
if (DEBUG) {
@@ -1613,7 +1610,7 @@ constructor(
"uid: $uid " +
"interactedSubcardRank: $interactedSubcardRank " +
"interactedSubcardCardinality: $interactedSubcardCardinality " +
- "received_latency_millis: $receivedLatencyMillis"
+ "received_latency_millis: $receivedLatencyMillis",
)
}
}
@@ -1633,7 +1630,7 @@ constructor(
it.mUid,
intArrayOf(it.surfaceForSmartspaceLogging),
rank = index,
- isSwipeToDismiss = true
+ isSwipeToDismiss = true,
)
// Reset card impressed state when swipe to dismissed
it.mIsImpressed = false
@@ -1692,7 +1689,7 @@ internal object MediaPlayerData {
active = true,
resumeAction = null,
instanceId = InstanceId.fakeInstanceId(-1),
- appUid = -1
+ appUid = -1,
)
// Whether should prioritize Smartspace card.
@@ -1741,7 +1738,7 @@ internal object MediaPlayerData {
player: MediaControlPanel,
clock: SystemClock,
isSsReactivated: Boolean,
- debugLogger: MediaCarouselControllerLogger? = null
+ debugLogger: MediaCarouselControllerLogger? = null,
) {
val removedPlayer = removeMediaPlayer(key)
if (removedPlayer != null && removedPlayer != player) {
@@ -1754,7 +1751,7 @@ internal object MediaPlayerData {
data,
key,
clock.currentTimeMillis(),
- isSsReactivated = isSsReactivated
+ isSsReactivated = isSsReactivated,
)
mediaData.put(key, sortKey)
mediaPlayers.put(sortKey, player)
@@ -1768,7 +1765,7 @@ internal object MediaPlayerData {
shouldPrioritize: Boolean,
clock: SystemClock,
debugLogger: MediaCarouselControllerLogger? = null,
- update: Boolean = false
+ update: Boolean = false,
) {
shouldPrioritizeSs = shouldPrioritize
val removedPlayer = removeMediaPlayer(key)
@@ -1782,7 +1779,7 @@ internal object MediaPlayerData {
EMPTY.copy(active = data.isActive, isPlaying = false),
key,
clock.currentTimeMillis(),
- isSsReactivated = true
+ isSsReactivated = true,
)
mediaData.put(key, sortKey)
mediaPlayers.put(sortKey, player)
@@ -1793,7 +1790,7 @@ internal object MediaPlayerData {
fun moveIfExists(
oldKey: String?,
newKey: String,
- debugLogger: MediaCarouselControllerLogger? = null
+ debugLogger: MediaCarouselControllerLogger? = null,
) {
if (oldKey == null || oldKey == newKey) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt
index 82099e61009f..3f22d549698c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt
@@ -32,4 +32,16 @@ data class MediaActionViewModel(
val buttonId: Int? = null,
val isEnabled: Boolean,
val onClicked: (Int) -> Unit,
-)
+) {
+ fun contentEquals(other: MediaActionViewModel?): Boolean {
+ return other?.let {
+ contentDescription == other.contentDescription &&
+ isVisibleWhenScrubbing == other.isVisibleWhenScrubbing &&
+ notVisibleValue == other.notVisibleValue &&
+ showInCollapsed == other.showInCollapsed &&
+ rebindId == other.rebindId &&
+ buttonId == other.buttonId &&
+ isEnabled == other.isEnabled
+ } ?: false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 64820e0d0ced..104d155ab74e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -69,18 +69,30 @@ class MediaControlViewModel(
mediaControl?.let { toViewModel(it) }
}
}
- .distinctUntilChanged()
+ .distinctUntilChanged { old, new ->
+ (new == null && old == null) || new?.contentEquals(old) ?: false
+ }
.flowOn(backgroundDispatcher)
private var isPlaying = false
private var isAnyButtonClicked = false
private var location = -1
+ private var playerViewModel: MediaPlayerViewModel? = null
+
+ fun isNewPlayer(viewModel: MediaPlayerViewModel): Boolean {
+ val contentEquals = playerViewModel?.contentEquals(viewModel) ?: false
+ return (!contentEquals).also { playerViewModel = viewModel }
+ }
+
+ fun onMediaControlIsBound(artistName: CharSequence, titleName: CharSequence) {
+ interactor.logMediaControlIsBound(artistName, titleName)
+ }
private fun onDismissMediaData(
token: Token?,
uid: Int,
packageName: String,
- instanceId: InstanceId
+ instanceId: InstanceId,
) {
logger.logLongPressDismiss(uid, packageName, instanceId)
interactor.removeMediaControl(
@@ -88,7 +100,7 @@ class MediaControlViewModel(
instanceId,
MEDIA_PLAYER_ANIMATION_DELAY,
SMARTSPACE_CARD_DISMISS_EVENT,
- location
+ location,
)
}
@@ -99,7 +111,7 @@ class MediaControlViewModel(
applicationContext,
backgroundDispatcher,
model.artwork,
- TAG
+ TAG,
)
val scheme =
wallpaperColors?.let { ColorScheme(it, true, Style.CONTENT) }
@@ -107,7 +119,7 @@ class MediaControlViewModel(
applicationContext,
model.packageName,
TAG,
- Style.CONTENT
+ Style.CONTENT,
)
?: return null
@@ -131,7 +143,7 @@ class MediaControlViewModel(
R.string.controls_media_playing_item_description,
model.songName,
model.artistName,
- model.appName
+ model.appName,
)
}
},
@@ -157,7 +169,7 @@ class MediaControlViewModel(
expandable,
clickIntent,
SMARTSPACE_CARD_CLICK_EVENT,
- location
+ location,
)
}
},
@@ -177,7 +189,7 @@ class MediaControlViewModel(
}
}
},
- onLocationChanged = { location = it }
+ onLocationChanged = { location = it },
)
}
@@ -191,7 +203,7 @@ class MediaControlViewModel(
device?.name?.let {
TextUtils.equals(
it,
- applicationContext.getString(R.string.broadcasting_description_is_broadcasting)
+ applicationContext.getString(R.string.broadcasting_description_is_broadcasting),
)
} ?: false
val useDisabledAlpha =
@@ -236,19 +248,19 @@ class MediaControlViewModel(
logger.logOpenBroadcastDialog(
model.uid,
model.packageName,
- model.instanceId
+ model.instanceId,
)
interactor.startBroadcastDialog(
expandable,
device?.name.toString(),
- model.packageName
+ model.packageName,
)
} else {
logger.logOpenOutputSwitcher(model.uid, model.packageName, model.instanceId)
interactor.startMediaOutputDialog(
expandable,
model.packageName,
- model.token
+ model.token,
)
}
} else {
@@ -257,10 +269,10 @@ class MediaControlViewModel(
?: interactor.startMediaOutputDialog(
expandable,
model.packageName,
- model.token
+ model.token,
)
}
- }
+ },
)
}
@@ -270,7 +282,7 @@ class MediaControlViewModel(
if (model.isDismissible) {
applicationContext.getString(
R.string.controls_media_close_session,
- model.appName
+ model.appName,
)
} else {
applicationContext.getString(R.string.controls_media_active_session)
@@ -304,7 +316,7 @@ class MediaControlViewModel(
model,
mediaButton.getActionById(buttonId),
buttonId,
- isScrubbingTimeEnabled
+ isScrubbingTimeEnabled,
)
}
}
@@ -319,7 +331,7 @@ class MediaControlViewModel(
model: MediaControlModel,
mediaAction: MediaAction?,
buttonId: Int,
- canShowScrubbingTimeViews: Boolean
+ canShowScrubbingTimeViews: Boolean,
): MediaActionViewModel {
val showInCollapsed = SEMANTIC_ACTIONS_COMPACT.contains(buttonId)
val hideWhenScrubbing = SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.contains(buttonId)
@@ -353,7 +365,7 @@ class MediaControlViewModel(
private fun toNotifActionViewModel(
model: MediaControlModel,
mediaAction: MediaAction,
- index: Int
+ index: Int,
): MediaActionViewModel {
return MediaActionViewModel(
icon = mediaAction.icon,
@@ -375,7 +387,7 @@ class MediaControlViewModel(
uid: Int,
packageName: String,
instanceId: InstanceId,
- action: Runnable
+ action: Runnable,
) {
logger.logTapAction(id, uid, packageName, instanceId)
interactor.logSmartspaceUserEvent(SMARTSPACE_CARD_CLICK_EVENT, location)
@@ -424,7 +436,7 @@ class MediaControlViewModel(
R.id.actionPrev,
R.id.actionNext,
R.id.action0,
- R.id.action1
+ R.id.action1,
)
const val TURBULENCE_NOISE_PLAY_MS_DURATION = 7500L
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaOutputSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaOutputSwitcherViewModel.kt
index 9df9bccdf522..2a47a5af790a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaOutputSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaOutputSwitcherViewModel.kt
@@ -29,4 +29,15 @@ data class MediaOutputSwitcherViewModel(
val alpha: Float,
val isVisible: Boolean,
val onClicked: (Expandable) -> Unit,
-)
+) {
+ fun contentEquals(other: MediaOutputSwitcherViewModel?): Boolean {
+ return (other?.let {
+ isTapEnabled == other.isTapEnabled &&
+ deviceString == other.deviceString &&
+ isCurrentBroadcastApp == other.isCurrentBroadcastApp &&
+ isIntentValid == other.isIntentValid &&
+ alpha == other.alpha &&
+ isVisible == other.isVisible
+ } ?: false)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
index 96e7fc79c8eb..f4b0d6e2c990 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -43,4 +43,30 @@ data class MediaPlayerViewModel(
val onSeek: () -> Unit,
val onBindSeekbar: (SeekBarViewModel) -> Unit,
val onLocationChanged: (Int) -> Unit,
-)
+) {
+ fun contentEquals(other: MediaPlayerViewModel?): Boolean {
+ return other?.let {
+ other.backgroundCover == backgroundCover &&
+ appIcon == other.appIcon &&
+ useGrayColorFilter == other.useGrayColorFilter &&
+ artistName == other.artistName &&
+ titleName == other.titleName &&
+ isExplicitVisible == other.isExplicitVisible &&
+ shouldAddGradient == other.shouldAddGradient &&
+ canShowTime == other.canShowTime &&
+ playTurbulenceNoise == other.playTurbulenceNoise &&
+ useSemanticActions == other.useSemanticActions &&
+ areActionsEqual(other.actionButtons) &&
+ outputSwitcher.contentEquals(other.outputSwitcher)
+ } ?: false
+ }
+
+ private fun areActionsEqual(other: List<MediaActionViewModel>): Boolean {
+ actionButtons.forEachIndexed { index, mediaActionViewModel ->
+ if (!mediaActionViewModel.contentEquals(other[index])) {
+ return false
+ }
+ }
+ return true
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 4251b81226b3..8351597f35de 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -34,6 +34,7 @@ import android.annotation.RequiresPermission;
import android.app.Activity;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AlertDialog;
+import android.app.KeyguardManager;
import android.app.StatusBarManager;
import android.app.compat.CompatChanges;
import android.content.Context;
@@ -83,6 +84,7 @@ public class MediaProjectionPermissionActivity extends Activity {
private final StatusBarManager mStatusBarManager;
private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
private final ScreenCaptureDisabledDialogDelegate mScreenCaptureDisabledDialogDelegate;
+ private final KeyguardManager mKeyguardManager;
private String mPackageName;
private int mUid;
@@ -101,11 +103,13 @@ public class MediaProjectionPermissionActivity extends Activity {
FeatureFlags featureFlags,
Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
StatusBarManager statusBarManager,
+ KeyguardManager keyguardManager,
MediaProjectionMetricsLogger mediaProjectionMetricsLogger,
ScreenCaptureDisabledDialogDelegate screenCaptureDisabledDialogDelegate) {
mFeatureFlags = featureFlags;
mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
mStatusBarManager = statusBarManager;
+ mKeyguardManager = keyguardManager;
mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
mScreenCaptureDisabledDialogDelegate = screenCaptureDisabledDialogDelegate;
}
@@ -208,7 +212,14 @@ public class MediaProjectionPermissionActivity extends Activity {
}
setUpDialog(mDialog);
- mDialog.show();
+
+ boolean shouldDismissKeyguard =
+ com.android.systemui.Flags.mediaProjectionDialogBehindLockscreen();
+ if (shouldDismissKeyguard && mKeyguardManager.isDeviceLocked()) {
+ requestDeviceUnlock();
+ } else {
+ mDialog.show();
+ }
if (savedInstanceState == null) {
mMediaProjectionMetricsLogger.notifyPermissionRequestDisplayed(mUid);
@@ -332,6 +343,16 @@ public class MediaProjectionPermissionActivity extends Activity {
return false;
}
+ private void requestDeviceUnlock() {
+ mKeyguardManager.requestDismissKeyguard(this,
+ new KeyguardManager.KeyguardDismissCallback() {
+ @Override
+ public void onDismissSucceeded() {
+ mDialog.show();
+ }
+ });
+ }
+
private void grantMediaProjectionPermission(
int screenShareMode, boolean hasCastingCapabilities) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index aa6c08eecd76..45aad825a70f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -140,6 +140,7 @@ fun Tile(tile: TileViewModel, iconOnly: Boolean, modifier: Modifier) {
}
},
onLongClick = { tile.onLongClick(expandable) },
+ accessibilityUiState = uiState.accessibilityUiState,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 3f3ad13f9b12..4f47536f6b32 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
import android.os.Build;
@@ -987,6 +988,19 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
@Override
public void onConfigChanged(Configuration newConfig) {
+ // If the shade window is not visible, the bounds will not update until it becomes visible.
+ // Touches that should invoke shade expansion but are not within those incorrect bounds
+ // (because the shape of the shade window remains portrait after flipping to landscape) will
+ // be dropped, causing the shade expansion to fail silently. Since the shade doesn't open,
+ // it doesn't become visible, and the bounds will never update. Therefore, we must detect
+ // the incorrect bounds here and force the update so that touches are routed correctly.
+ if (SceneContainerFlag.isEnabled() && mWindowRootView.getVisibility() == View.INVISIBLE) {
+ Rect bounds = newConfig.windowConfiguration.getBounds();
+ if (mWindowRootView.getWidth() != bounds.width()) {
+ mLogger.logConfigChangeWidthAdjust(mWindowRootView.getWidth(), bounds.width());
+ updateRootViewBounds(bounds);
+ }
+ }
final boolean newScreenRotationAllowed = mKeyguardStateController
.isKeyguardScreenRotationAllowed();
@@ -996,6 +1010,16 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
+ private void updateRootViewBounds(Rect bounds) {
+ int originalMlpWidth = mLp.width;
+ int originalMlpHeight = mLp.height;
+ mLp.width = bounds.width();
+ mLp.height = bounds.height();
+ mWindowManager.updateViewLayout(mWindowRootView, mLp);
+ mLp.width = originalMlpWidth;
+ mLp.height = originalMlpHeight;
+ }
+
/**
* When keyguard will be dismissed but didn't start animation yet.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
index e7a397b0fa09..1693e62c89fb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
@@ -31,18 +31,13 @@ class ShadeWindowLogger @Inject constructor(@ShadeWindowLog private val buffer:
ConstantStringsLogger by ConstantStringsLoggerImpl(buffer, TAG) {
fun logNewState(state: Any) {
- buffer.log(
- TAG,
- DEBUG,
- { str1 = state.toString() },
- { "Applying new state: $str1" }
- )
+ buffer.log(TAG, DEBUG, { str1 = state.toString() }, { "Applying new state: $str1" })
}
private inline fun log(
logLevel: LogLevel,
initializer: LogMessage.() -> Unit,
- noinline printer: LogMessage.() -> String
+ noinline printer: LogMessage.() -> String,
) {
buffer.log(TAG, logLevel, initializer, printer)
}
@@ -52,7 +47,8 @@ class ShadeWindowLogger @Inject constructor(@ShadeWindowLog private val buffer:
TAG,
DEBUG,
{ bool1 = visible },
- { "Updating visibility, should be visible : $bool1" })
+ { "Updating visibility, should be visible : $bool1" },
+ )
}
fun logIsExpanded(
@@ -65,7 +61,7 @@ class ShadeWindowLogger @Inject constructor(@ShadeWindowLog private val buffer:
headsUpNotificationShowing: Boolean,
scrimsVisibilityNotTransparent: Boolean,
backgroundBlurRadius: Boolean,
- launchingActivityFromNotification: Boolean
+ launchingActivityFromNotification: Boolean,
) {
buffer.log(
TAG,
@@ -82,11 +78,13 @@ class ShadeWindowLogger @Inject constructor(@ShadeWindowLog private val buffer:
long2 = if (backgroundBlurRadius) 1 else 0
double1 = if (launchingActivityFromNotification) 1.0 else 0.0
},
- { "Setting isExpanded to $str1: forceWindowCollapsed $bool1, " +
+ {
+ "Setting isExpanded to $str1: forceWindowCollapsed $bool1, " +
"isKeyguardShowingAndNotOccluded $bool2, panelVisible $bool3, " +
"keyguardFadingAway $bool4, bouncerShowing $int1," +
"headsUpNotificationShowing $int2, scrimsVisibilityNotTransparent $long1," +
- "backgroundBlurRadius $long2, launchingActivityFromNotification $double1"}
+ "backgroundBlurRadius $long2, launchingActivityFromNotification $double1"
+ },
)
}
@@ -95,7 +93,7 @@ class ShadeWindowLogger @Inject constructor(@ShadeWindowLog private val buffer:
TAG,
DEBUG,
{ bool1 = visible },
- { "Updating shade, should be visible and focusable: $bool1" }
+ { "Updating shade, should be visible and focusable: $bool1" },
)
}
@@ -104,7 +102,19 @@ class ShadeWindowLogger @Inject constructor(@ShadeWindowLog private val buffer:
TAG,
DEBUG,
{ bool1 = focusable },
- { "Updating shade, should be focusable : $bool1" }
+ { "Updating shade, should be focusable : $bool1" },
+ )
+ }
+
+ fun logConfigChangeWidthAdjust(originalWidth: Int, newWidth: Int) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = originalWidth
+ int2 = newWidth
+ },
+ { "Config changed. SceneWindowRootView width updating from $int1 to $int2." },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 4056e7b89c2c..975b92e632b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -16,11 +16,12 @@
package com.android.systemui.statusbar.core
import android.app.Fragment
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.fragments.FragmentHostManager
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
+import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.PhoneStatusBarView
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
@@ -33,50 +34,16 @@ import javax.inject.Provider
* Responsible for creating the status bar window and initializing the root components of that
* window (see [CollapsedStatusBarFragment])
*/
-@SysUISingleton
-class StatusBarInitializer @Inject constructor(
- private val windowController: StatusBarWindowController,
- private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
- private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
-) {
+interface StatusBarInitializer {
- var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
+ var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener?
/**
* Creates the status bar window and root views, and initializes the component.
*
* TODO(b/277764509): Initialize the status bar via [CoreStartable#start].
*/
- fun initializeStatusBar() {
- windowController.fragmentHostManager.addTagListener(
- CollapsedStatusBarFragment.TAG,
- object : FragmentHostManager.FragmentListener {
- override fun onFragmentViewCreated(tag: String, fragment: Fragment) {
- val statusBarFragmentComponent = (fragment as CollapsedStatusBarFragment)
- .statusBarFragmentComponent ?: throw IllegalStateException()
- statusBarViewUpdatedListener?.onStatusBarViewUpdated(
- statusBarFragmentComponent.phoneStatusBarView,
- statusBarFragmentComponent.phoneStatusBarViewController,
- statusBarFragmentComponent.phoneStatusBarTransitions
- )
- creationListeners.forEach { listener ->
- listener.onStatusBarViewInitialized(statusBarFragmentComponent)
- }
- }
-
- override fun onFragmentViewDestroyed(tag: String?, fragment: Fragment?) {
- // nop
- }
- }
- ).fragmentManager
- .beginTransaction()
- .replace(
- R.id.status_bar_container,
- collapsedStatusBarFragmentProvider.get(),
- CollapsedStatusBarFragment.TAG
- )
- .commit()
- }
+ fun initializeStatusBar()
interface OnStatusBarViewInitializedListener {
@@ -84,16 +51,61 @@ class StatusBarInitializer @Inject constructor(
* The status bar view has been initialized.
*
* @param component Dagger component that is created when the status bar view is created.
- * Can be used to retrieve dependencies from that scope, including the status bar root view.
+ * Can be used to retrieve dependencies from that scope, including the status bar root
+ * view.
*/
fun onStatusBarViewInitialized(component: StatusBarFragmentComponent)
}
interface OnStatusBarViewUpdatedListener {
fun onStatusBarViewUpdated(
- statusBarView: PhoneStatusBarView,
statusBarViewController: PhoneStatusBarViewController,
- statusBarTransitions: PhoneStatusBarTransitions
+ statusBarTransitions: PhoneStatusBarTransitions,
)
}
}
+
+@SysUISingleton
+class StatusBarInitializerImpl
+@Inject
+constructor(
+ private val windowController: StatusBarWindowController,
+ private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
+ private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
+) : StatusBarInitializer {
+
+ override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
+
+ override fun initializeStatusBar() {
+ windowController.fragmentHostManager
+ .addTagListener(
+ CollapsedStatusBarFragment.TAG,
+ object : FragmentHostManager.FragmentListener {
+ override fun onFragmentViewCreated(tag: String, fragment: Fragment) {
+ val statusBarFragmentComponent =
+ (fragment as CollapsedStatusBarFragment).statusBarFragmentComponent
+ ?: throw IllegalStateException()
+ statusBarViewUpdatedListener?.onStatusBarViewUpdated(
+ statusBarFragmentComponent.phoneStatusBarViewController,
+ statusBarFragmentComponent.phoneStatusBarTransitions,
+ )
+ creationListeners.forEach { listener ->
+ listener.onStatusBarViewInitialized(statusBarFragmentComponent)
+ }
+ }
+
+ override fun onFragmentViewDestroyed(tag: String?, fragment: Fragment?) {
+ // nop
+ }
+ },
+ )
+ .fragmentManager
+ .beginTransaction()
+ .replace(
+ R.id.status_bar_container,
+ collapsedStatusBarFragmentProvider.get(),
+ CollapsedStatusBarFragment.TAG,
+ )
+ .commit()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarSimpleFragment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarSimpleFragment.kt
new file mode 100644
index 000000000000..214151383dc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarSimpleFragment.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.core
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading and using the status bar simple fragment flag state */
+object StatusBarSimpleFragment {
+ /** Aconfig flag for removing the fragment */
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.statusBarSimpleFragment()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is not enabled to ensure that the refactor author catches issues in testing.
+ * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+ */
+ @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)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 406a66449f82..526c64c15696 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -20,12 +20,16 @@ import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.statusbar.core.StatusBarInitializer
+import com.android.systemui.statusbar.core.StatusBarInitializerImpl
import com.android.systemui.statusbar.data.StatusBarDataLayerModule
import com.android.systemui.statusbar.phone.LightBarController
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -57,6 +61,13 @@ abstract class StatusBarModule {
@ClassKey(StatusBarSignalPolicy::class)
abstract fun bindStatusBarSignalPolicy(impl: StatusBarSignalPolicy): CoreStartable
+ @Binds abstract fun statusBarInitializer(impl: StatusBarInitializerImpl): StatusBarInitializer
+
+ @Binds
+ abstract fun statusBarWindowController(
+ impl: StatusBarWindowControllerImpl
+ ): StatusBarWindowController
+
companion object {
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 50e92498b114..59533b343a57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -372,7 +372,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final Point mCurrentDisplaySize = new Point();
- protected PhoneStatusBarView mStatusBarView;
private PhoneStatusBarViewController mPhoneStatusBarViewController;
private PhoneStatusBarTransitions mStatusBarTransitions;
private final AuthRippleController mAuthRippleController;
@@ -1191,8 +1190,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// Set up CollapsedStatusBarFragment and PhoneStatusBarView
mStatusBarInitializer.setStatusBarViewUpdatedListener(
- (statusBarView, statusBarViewController, statusBarTransitions) -> {
- mStatusBarView = statusBarView;
+ (statusBarViewController, statusBarTransitions) -> {
mPhoneStatusBarViewController = statusBarViewController;
mStatusBarTransitions = statusBarTransitions;
getNotificationShadeWindowViewController()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index c0e36b2ab42a..f026b99af49c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -27,7 +27,6 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarLocation;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
@@ -114,14 +113,6 @@ public interface StatusBarFragmentModule {
/** */
@Provides
@StatusBarFragmentScope
- static StatusBarUserSwitcherContainer provideStatusBarUserSwitcherContainer(
- @RootView PhoneStatusBarView view) {
- return view.findViewById(R.id.user_switcher_container);
- }
-
- /** */
- @Provides
- @StatusBarFragmentScope
static PhoneStatusBarViewController providePhoneStatusBarViewController(
PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
@RootView PhoneStatusBarView phoneStatusBarView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
new file mode 100644
index 000000000000..421e5c45bbfe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.window
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.fragments.FragmentHostManager
+import java.util.Optional
+
+/** Encapsulates all logic for the status bar window state management. */
+interface StatusBarWindowController {
+ val statusBarHeight: Int
+
+ /** Rereads the status bar height and reapplies the current state if the height is different. */
+ fun refreshStatusBarHeight()
+
+ /** Adds the status bar view to the window manager. */
+ fun attach()
+
+ /** Adds the given view to the status bar window view. */
+ fun addViewToWindow(view: View, layoutParams: ViewGroup.LayoutParams)
+
+ /** Returns the status bar window's background view. */
+ val backgroundView: View
+
+ /** Returns a fragment host manager for the status bar window view. */
+ val fragmentHostManager: FragmentHostManager
+
+ /**
+ * Provides an updated animation controller if we're animating a view in the status bar.
+ *
+ * This is needed because we have to make sure that the status bar window matches the full
+ * screen during the animation and that we are expanding the view below the other status bar
+ * text.
+ *
+ * @param rootView the root view of the animation
+ * @param animationController the default animation controller to use
+ * @return If the animation is on a view in the status bar, returns an Optional containing an
+ * updated animation controller that handles status-bar-related animation details. Returns an
+ * empty optional if the animation is *not* on a view in the status bar.
+ */
+ fun wrapAnimationControllerIfInStatusBar(
+ rootView: View,
+ animationController: ActivityTransitionAnimator.Controller,
+ ): Optional<ActivityTransitionAnimator.Controller>
+
+ /** Set force status bar visible. */
+ fun setForceStatusBarVisible(forceStatusBarVisible: Boolean)
+
+ /**
+ * Sets whether an ongoing process requires the status bar to be forced visible.
+ *
+ * This method is separate from {@link this#setForceStatusBarVisible} because the ongoing
+ * process **takes priority**. For example, if {@link this#setForceStatusBarVisible} is set to
+ * false but this method is set to true, then the status bar **will** be visible.
+ *
+ * TODO(b/195839150): We should likely merge this method and {@link
+ * this#setForceStatusBarVisible} together and use some sort of ranking system instead.
+ */
+ fun setOngoingProcessRequiresStatusBarVisible(visible: Boolean)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
index c30a6b7d0f17..1a0327cdd809 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
@@ -46,6 +46,8 @@ import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
+import androidx.annotation.NonNull;
+
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.animation.ActivityTransitionAnimator;
@@ -67,7 +69,7 @@ import javax.inject.Inject;
* Encapsulates all logic for the status bar window state management.
*/
@SysUISingleton
-public class StatusBarWindowController {
+public class StatusBarWindowControllerImpl implements StatusBarWindowController {
private static final String TAG = "StatusBarWindowController";
private static final boolean DEBUG = false;
@@ -89,7 +91,7 @@ public class StatusBarWindowController {
private final Binder mInsetsSourceOwner = new Binder();
@Inject
- public StatusBarWindowController(
+ public StatusBarWindowControllerImpl(
Context context,
@StatusBarWindowModule.InternalWindowView StatusBarWindowView statusBarWindowView,
ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
@@ -117,14 +119,12 @@ public class StatusBarWindowController {
/* attachedViewProvider=*/ () -> mStatusBarWindowView)));
}
+ @Override
public int getStatusBarHeight() {
return mBarHeight;
}
- /**
- * Rereads the status bar height and reapplys the current state if the height
- * is different.
- */
+ @Override
public void refreshStatusBarHeight() {
Trace.beginSection("StatusBarWindowController#refreshStatusBarHeight");
try {
@@ -141,9 +141,7 @@ public class StatusBarWindowController {
}
}
- /**
- * Adds the status bar view to the window manager.
- */
+ @Override
public void attach() {
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
@@ -161,54 +159,47 @@ public class StatusBarWindowController {
apply(mCurrentState);
}
- /** Adds the given view to the status bar window view. */
- public void addViewToWindow(View view, ViewGroup.LayoutParams layoutParams) {
+ @Override
+ public void addViewToWindow(@NonNull View view, @NonNull ViewGroup.LayoutParams layoutParams) {
mStatusBarWindowView.addView(view, layoutParams);
}
- /** Returns the status bar window's background view. */
+ @NonNull
+ @Override
public View getBackgroundView() {
return mStatusBarWindowView.findViewById(R.id.status_bar_container);
}
- /** Returns a fragment host manager for the status bar window view. */
+ @NonNull
+ @Override
public FragmentHostManager getFragmentHostManager() {
return mFragmentService.getFragmentHostManager(mStatusBarWindowView);
}
- /**
- * Provides an updated animation controller if we're animating a view in the status bar.
- *
- * This is needed because we have to make sure that the status bar window matches the full
- * screen during the animation and that we are expanding the view below the other status bar
- * text.
- *
- * @param rootView the root view of the animation
- * @param animationController the default animation controller to use
- * @return If the animation is on a view in the status bar, returns an Optional containing an
- * updated animation controller that handles status-bar-related animation details. Returns an
- * empty optional if the animation is *not* on a view in the status bar.
- */
+ @NonNull
+ @Override
public Optional<ActivityTransitionAnimator.Controller> wrapAnimationControllerIfInStatusBar(
- View rootView, ActivityTransitionAnimator.Controller animationController) {
+ @NonNull View rootView,
+ @NonNull ActivityTransitionAnimator.Controller animationController) {
if (rootView != mStatusBarWindowView) {
return Optional.empty();
}
animationController.setTransitionContainer(mLaunchAnimationContainer);
- return Optional.of(new DelegateTransitionAnimatorController(animationController) {
- @Override
- public void onTransitionAnimationStart(boolean isExpandingFullyAbove) {
- getDelegate().onTransitionAnimationStart(isExpandingFullyAbove);
- setLaunchAnimationRunning(true);
- }
-
- @Override
- public void onTransitionAnimationEnd(boolean isExpandingFullyAbove) {
- getDelegate().onTransitionAnimationEnd(isExpandingFullyAbove);
- setLaunchAnimationRunning(false);
- }
- });
+ return Optional.of(
+ new DelegateTransitionAnimatorController(animationController) {
+ @Override
+ public void onTransitionAnimationStart(boolean isExpandingFullyAbove) {
+ getDelegate().onTransitionAnimationStart(isExpandingFullyAbove);
+ setLaunchAnimationRunning(true);
+ }
+
+ @Override
+ public void onTransitionAnimationEnd(boolean isExpandingFullyAbove) {
+ getDelegate().onTransitionAnimationEnd(isExpandingFullyAbove);
+ setLaunchAnimationRunning(false);
+ }
+ });
}
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
@@ -275,22 +266,13 @@ public class StatusBarWindowController {
}
}
- /** Set force status bar visible. */
+ @Override
public void setForceStatusBarVisible(boolean forceStatusBarVisible) {
mCurrentState.mForceStatusBarVisible = forceStatusBarVisible;
apply(mCurrentState);
}
- /**
- * Sets whether an ongoing process requires the status bar to be forced visible.
- *
- * This method is separate from {@link this#setForceStatusBarVisible} because the ongoing
- * process **takes priority**. For example, if {@link this#setForceStatusBarVisible} is set to
- * false but this method is set to true, then the status bar **will** be visible.
- *
- * TODO(b/195839150): We should likely merge this method and
- * {@link this#setForceStatusBarVisible} together and use some sort of ranking system instead.
- */
+ @Override
public void setOngoingProcessRequiresStatusBarVisible(boolean visible) {
mCurrentState.mOngoingProcessRequiresStatusBarVisible = visible;
apply(mCurrentState);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index 103449b6b0f7..ee8ce17cecd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.UiModeManager;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.platform.test.annotations.EnableFlags;
@@ -78,6 +79,12 @@ public class MenuViewTest extends SysuiTestCase {
mNightMode = mUiModeManager.getNightMode();
mUiModeManager.setNightMode(MODE_NIGHT_YES);
+ // Programmatically update the resource's configuration to night mode to reduce flakiness
+ Configuration nightConfig = new Configuration(mContext.getResources().getConfiguration());
+ nightConfig.uiMode = Configuration.UI_MODE_NIGHT_YES;
+ mContext.getResources().updateConfiguration(nightConfig,
+ mContext.getResources().getDisplayMetrics(), null);
+
mSpyContext = spy(mContext);
doNothing().when(mSpyContext).startActivity(any());
@@ -101,6 +108,8 @@ public class MenuViewTest extends SysuiTestCase {
@Test
public void insetsOnDarkTheme_menuOnLeft_matchInsets() {
+ // In dark theme, the inset is not 0 to avoid weird spacing issue between the menu and
+ // the edge of the screen.
mMenuView.onConfigurationChanged(/* newConfig= */ null);
final InstantInsetLayerDrawable insetLayerDrawable =
(InstantInsetLayerDrawable) mMenuView.getBackground();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
index 4b61a0d02f1e..088bb02512b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
@@ -25,6 +25,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.ui.viewmodel.patternBouncerViewModelFactory
+import com.android.systemui.haptics.msdl.bouncerHapticPlayer
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.motion.createSysUiComposeMotionTestRule
import com.android.systemui.testKosmos
@@ -55,6 +56,7 @@ class PatternBouncerTest : SysuiTestCase() {
kosmos.patternBouncerViewModelFactory.create(
isInputEnabled = MutableStateFlow(true).asStateFlow(),
onIntentionalUserInput = {},
+ bouncerHapticPlayer = kosmos.bouncerHapticPlayer,
)
@Before
@@ -75,11 +77,11 @@ class PatternBouncerTest : SysuiTestCase() {
content = { play -> if (play) PatternBouncerUnderTest() },
ComposeRecordingSpec.until(
recordBefore = false,
- checkDone = { motionTestValueOfNode(MotionTestKeys.entryCompleted) }
+ checkDone = { motionTestValueOfNode(MotionTestKeys.entryCompleted) },
) {
feature(MotionTestKeys.dotAppearFadeIn, floatArray)
feature(MotionTestKeys.dotAppearMoveUp, floatArray)
- }
+ },
)
assertThat(motion).timeSeriesMatchesGolden()
@@ -100,7 +102,7 @@ class PatternBouncerTest : SysuiTestCase() {
viewModel.onDragEnd()
// Failure animation starts when animateFailure flips to true...
viewModel.animateFailure.takeWhile { !it }.collect {}
- }
+ },
) {
// ... and ends when the composable flips it back to false.
viewModel.animateFailure.takeWhile { it }.collect {}
@@ -111,7 +113,7 @@ class PatternBouncerTest : SysuiTestCase() {
content = { PatternBouncerUnderTest() },
ComposeRecordingSpec(failureAnimationMotionControl) {
feature(MotionTestKeys.dotScaling, floatArray)
- }
+ },
)
assertThat(motion).timeSeriesMatchesGolden()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index af043093b6f7..c710c56fd516 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -168,7 +168,7 @@ import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.core.StatusBarInitializerImpl;
import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -504,7 +504,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mock(FragmentService.class),
mLightBarController,
mAutoHideController,
- new StatusBarInitializer(
+ new StatusBarInitializerImpl(
mStatusBarWindowController,
mCollapsedStatusBarFragmentProvider,
emptySet()),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index 649e4e8a6f7e..1b1d8c5d0f63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -25,6 +25,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
+import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer
+import com.android.systemui.haptics.msdl.bouncerHapticPlayer
import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -34,9 +36,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.StateFlow
val Kosmos.bouncerUserActionsViewModel by Fixture {
- BouncerUserActionsViewModel(
- bouncerInteractor = bouncerInteractor,
- )
+ BouncerUserActionsViewModel(bouncerInteractor = bouncerInteractor)
}
val Kosmos.bouncerUserActionsViewModelFactory by Fixture {
@@ -59,6 +59,7 @@ val Kosmos.bouncerSceneContentViewModel by Fixture {
pinViewModelFactory = pinBouncerViewModelFactory,
patternViewModelFactory = patternBouncerViewModelFactory,
passwordViewModelFactory = passwordBouncerViewModelFactory,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
}
@@ -76,6 +77,7 @@ val Kosmos.pinBouncerViewModelFactory by Fixture {
isInputEnabled: StateFlow<Boolean>,
onIntentionalUserInput: () -> Unit,
authenticationMethod: AuthenticationMethodModel,
+ bouncerHapticPlayer: BouncerHapticPlayer,
): PinBouncerViewModel {
return PinBouncerViewModel(
applicationContext = applicationContext,
@@ -84,6 +86,7 @@ val Kosmos.pinBouncerViewModelFactory by Fixture {
isInputEnabled = isInputEnabled,
onIntentionalUserInput = onIntentionalUserInput,
authenticationMethod = authenticationMethod,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
}
}
@@ -92,6 +95,7 @@ val Kosmos.pinBouncerViewModelFactory by Fixture {
val Kosmos.patternBouncerViewModelFactory by Fixture {
object : PatternBouncerViewModel.Factory {
override fun create(
+ bouncerHapticPlayer: BouncerHapticPlayer,
isInputEnabled: StateFlow<Boolean>,
onIntentionalUserInput: () -> Unit,
): PatternBouncerViewModel {
@@ -100,6 +104,7 @@ val Kosmos.patternBouncerViewModelFactory by Fixture {
interactor = bouncerInteractor,
isInputEnabled = isInputEnabled,
onIntentionalUserInput = onIntentionalUserInput,
+ bouncerHapticPlayer = bouncerHapticPlayer,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
index 6e650a3c5391..77afa7989e83 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.bluetooth.mockBroadcastDialogController
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
+import com.android.systemui.media.controls.shared.mediaLogger
import com.android.systemui.media.controls.util.mediaInstanceId
import com.android.systemui.media.mediaOutputDialogManager
import com.android.systemui.plugins.activityStarter
@@ -39,5 +40,6 @@ val Kosmos.mediaControlInteractor by
lockscreenUserManager = notificationLockscreenUserManager,
mediaOutputDialogManager = mediaOutputDialogManager,
broadcastDialogController = mockBroadcastDialogController,
+ mediaLogger = mediaLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
index e490b7502894..9ea660fd3704 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
@@ -23,6 +23,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaControlInteractor
import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
+import com.android.systemui.media.controls.shared.mediaLogger
import com.android.systemui.media.mediaOutputDialogManager
import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.notificationLockscreenUserManager
@@ -42,6 +43,7 @@ val Kosmos.mediaControlInteractorFactory by
lockscreenUserManager = notificationLockscreenUserManager,
mediaOutputDialogManager = mediaOutputDialogManager,
broadcastDialogController = mockBroadcastDialogController,
+ mediaLogger = mediaLogger,
)
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index a10097427ae5..57b58d8741da 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -128,7 +128,11 @@ constructor(
val currentDirection =
if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
- if (isTransitionInProgress && currentDirection != lastFoldUpdate) {
+ val changedDirectionWhileInTransition =
+ isTransitionInProgress && currentDirection != lastFoldUpdate
+ val unfoldedPastThresholdSinceLastTransition =
+ angle - lastHingeAngleBeforeTransition > HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES
+ if (changedDirectionWhileInTransition || unfoldedPastThresholdSinceLastTransition) {
lastHingeAngleBeforeTransition = lastHingeAngle
}
@@ -153,7 +157,7 @@ constructor(
isOnLargeScreen // Avoids sending closing event when on small screen.
// Start event is sent regardless due to hall sensor.
) {
- notifyFoldUpdate(transitionUpdate, lastHingeAngle)
+ notifyFoldUpdate(transitionUpdate, angle)
}
if (isTransitionInProgress) {
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 4cb2ce1bdfd7..5d251bdafd44 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -195,23 +195,25 @@ public final class RavenwoodAwareTestRunner extends Runner implements Filterable
try {
performGlobalInitialization();
- mTestClass = new TestClass(testClass);
-
- Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
-
- onRunnerInitializing();
-
/*
* If the class has @DisabledOnRavenwood, then we'll delegate to
* ClassSkippingTestRunner, which simply skips it.
+ *
+ * We need to do it before instantiating TestClass for b/367694651.
*/
if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood(
- mTestClass.getJavaClass())) {
- mRealRunner = new ClassSkippingTestRunner(mTestClass);
+ testClass)) {
+ mRealRunner = new ClassSkippingTestRunner(testClass);
mDescription = mRealRunner.getDescription();
return;
}
+ mTestClass = new TestClass(testClass);
+
+ Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
+
+ onRunnerInitializing();
+
// Find the real runner.
final Class<? extends Runner> realRunnerClass;
final InnerRunner innerRunnerAnnotation = mTestClass.getAnnotation(InnerRunner.class);
@@ -444,14 +446,11 @@ public final class RavenwoodAwareTestRunner extends Runner implements Filterable
* filter.
*/
private static class ClassSkippingTestRunner extends Runner implements Filterable {
- private final TestClass mTestClass;
private final Description mDescription;
private boolean mFilteredOut;
- ClassSkippingTestRunner(TestClass testClass) {
- mTestClass = testClass;
- mDescription = Description.createTestDescription(
- testClass.getJavaClass(), testClass.getJavaClass().getSimpleName());
+ ClassSkippingTestRunner(Class<?> testClass) {
+ mDescription = Description.createTestDescription(testClass, testClass.getSimpleName());
mFilteredOut = false;
}
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
index 6d8fb983504b..09ed12d49cea 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
@@ -17,12 +17,14 @@ package com.android.ravenwoodtest.runnercallbacktests;
import static org.junit.Assume.assumeTrue;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.NoRavenizer;
import android.platform.test.ravenwood.RavenwoodAwareTestRunner;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.ClassRule;
@@ -353,4 +355,34 @@ public class RavenwoodRunnerCallbackTest extends RavenwoodRunnerTestBase {
public void test2() {
}
}
+
+ /**
+ * The test class is unloadable, but has a @DisabledOnRavenwood.
+ */
+ @RunWith(AndroidJUnit4.class)
+ @DisabledOnRavenwood
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: ClassUnloadbleTest(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleTest)
+ testIgnored: ClassUnloadbleTest(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleTest)
+ testSuiteFinished: ClassUnloadbleTest(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleTest)
+ testSuiteFinished: classes
+ testRunFinished: 0,0,0,1
+ """)
+ // CHECKSTYLE:ON
+ public static class ClassUnloadbleTest {
+ static {
+ Assert.fail("Class unloadable!");
+ }
+
+ @Test
+ public void test1() {
+ }
+
+ @Test
+ public void test2() {
+ }
+ }
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
index 8c6f50e5c1bd..e29b6e403f2a 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
@@ -145,13 +145,25 @@ public class MetadataSyncAdapter {
ArrayMap<String, ArraySet<String>> removedFunctionsDiffMap =
getRemovedFunctionsDiffMap(staticPackageToFunctionMap, runtimePackageToFunctionMap);
- Set<AppSearchSchema> appRuntimeMetadataSchemas =
- getAllRuntimeMetadataSchemas(staticPackageToFunctionMap.keySet());
- appRuntimeMetadataSchemas.add(
- AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema());
+ if (!staticPackageToFunctionMap.keySet().equals(runtimePackageToFunctionMap.keySet())) {
+ // Drop removed packages from removedFunctionsDiffMap, as setSchema() deletes them
+ ArraySet<String> removedPackages =
+ getRemovedPackages(
+ staticPackageToFunctionMap.keySet(), removedFunctionsDiffMap.keySet());
+ for (String packageName : removedPackages) {
+ removedFunctionsDiffMap.remove(packageName);
+ }
+ Set<AppSearchSchema> appRuntimeMetadataSchemas =
+ getAllRuntimeMetadataSchemas(staticPackageToFunctionMap.keySet());
+ appRuntimeMetadataSchemas.add(
+ AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema());
+ SetSchemaRequest addSetSchemaRequest =
+ buildSetSchemaRequestForRuntimeMetadataSchemas(
+ mPackageManager, appRuntimeMetadataSchemas);
+ Objects.requireNonNull(
+ runtimeMetadataSearchSession.setSchema(addSetSchemaRequest).get());
+ }
- // Operation order matters here. i.e. remove -> setSchema -> add. Otherwise we would
- // encounter an error trying to delete a document with no existing schema.
if (!removedFunctionsDiffMap.isEmpty()) {
RemoveByDocumentIdRequest removeByDocumentIdRequest =
buildRemoveRuntimeMetadataRequest(removedFunctionsDiffMap);
@@ -164,12 +176,6 @@ public class MetadataSyncAdapter {
}
if (!addedFunctionsDiffMap.isEmpty()) {
- // TODO(b/357551503): only set schema on package diff
- SetSchemaRequest addSetSchemaRequest =
- buildSetSchemaRequestForRuntimeMetadataSchemas(
- mPackageManager, appRuntimeMetadataSchemas);
- Objects.requireNonNull(
- runtimeMetadataSearchSession.setSchema(addSetSchemaRequest).get());
PutDocumentsRequest putDocumentsRequest =
buildPutRuntimeMetadataRequest(addedFunctionsDiffMap);
AppSearchBatchResult<String, Void> putDocumentBatchResult =
@@ -276,6 +282,30 @@ public class MetadataSyncAdapter {
}
/**
+ * This method returns a set of packages that are in the removed function packages but not in
+ * the all existing static packages.
+ *
+ * @param allExistingStaticPackages A set of all existing static metadata packages.
+ * @param removedFunctionPackages A set of all removed function packages.
+ * @return A set of packages that are in the removed function packages but not in the all
+ * existing static packages.
+ */
+ @NonNull
+ private static ArraySet<String> getRemovedPackages(
+ @NonNull Set<String> allExistingStaticPackages,
+ @NonNull Set<String> removedFunctionPackages) {
+ ArraySet<String> removedPackages = new ArraySet<>();
+
+ for (String packageName : removedFunctionPackages) {
+ if (!allExistingStaticPackages.contains(packageName)) {
+ removedPackages.add(packageName);
+ }
+ }
+
+ return removedPackages;
+ }
+
+ /**
* This method returns a map of package names to a set of function ids that are in the static
* metadata but not in the runtime metadata.
*
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b109472a2a1e..2fa0e0d0d946 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -720,6 +720,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
void handleInlineSuggestionRequest(InlineSuggestionsRequest inlineSuggestionsRequest,
ViewState viewState) {
+ if (sVerbose) {
+ Slog.v(TAG, "handleInlineSuggestionRequest(): inline suggestion request received");
+ }
synchronized (mLock) {
if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) {
return;
@@ -734,15 +737,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
void maybeRequestFillLocked() {
if (mPendingFillRequest == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request "
+ + "due to empty pending fill request");
+ }
return;
}
mFieldClassificationIdSnapshot = sIdCounterForPcc.get();
if (mWaitForInlineRequest) {
if (mPendingInlineSuggestionsRequest == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request "
+ + "due to waiting for inline request and pending inline request is "
+ + "currently empty");
+ }
return;
}
-
+ if (sVerbose) {
+ Slog.v(TAG, "maybeRequestFillLocked(): adding inline request to pending "
+ + "fill request");
+ }
mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
mPendingFillRequest.getFillContexts(),
mPendingFillRequest.getHints(),
@@ -750,8 +765,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPendingFillRequest.getFlags(),
mPendingInlineSuggestionsRequest,
mPendingFillRequest.getDelayedFillIntentSender());
+ } else {
+ if (sVerbose) {
+ Slog.v(TAG, "maybeRequestFillLocked(): not adding inline request to pending "
+ + "fill request");
+ }
}
+
mLastFillRequest = mPendingFillRequest;
+ if (sVerbose) {
+ Slog.v(TAG, "maybeRequestFillLocked(): sending fill request");
+ }
if (shouldRequestSecondaryProvider(mPendingFillRequest.getFlags())
&& mSecondaryProviderHandler != null) {
Slog.v(TAG, "Requesting fill response to secondary provider.");
diff --git a/services/companion/java/com/android/server/companion/virtual/OWNERS b/services/companion/java/com/android/server/companion/virtual/OWNERS
index 4fe0592f9075..4b732ac8e5a9 100644
--- a/services/companion/java/com/android/server/companion/virtual/OWNERS
+++ b/services/companion/java/com/android/server/companion/virtual/OWNERS
@@ -2,7 +2,9 @@
set noparent
-marvinramin@google.com
vladokom@google.com
+marvinramin@google.com
+caen@google.com
+biswarupp@google.com
ogunwale@google.com
michaelwr@google.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 6657c1c1ba89..59dea099c2a1 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -160,6 +160,7 @@ public final class BatteryService extends SystemService {
private int mLastChargeCounter;
private int mLastBatteryCycleCount;
private int mLastChargingState;
+ private int mLastBatteryCapacityLevel;
/**
* The last seen charging policy. This requires the
* {@link android.Manifest.permission#BATTERY_STATS} permission and should therefore not be
@@ -609,7 +610,8 @@ public final class BatteryService extends SystemService {
|| mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
|| mInvalidCharger != mLastInvalidCharger
|| mHealthInfo.batteryCycleCount != mLastBatteryCycleCount
- || mHealthInfo.chargingState != mLastChargingState)) {
+ || mHealthInfo.chargingState != mLastChargingState
+ || mHealthInfo.batteryCapacityLevel != mLastBatteryCapacityLevel)) {
if (mPlugType != mLastPlugType) {
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -829,6 +831,7 @@ public final class BatteryService extends SystemService {
mLastInvalidCharger = mInvalidCharger;
mLastBatteryCycleCount = mHealthInfo.batteryCycleCount;
mLastChargingState = mHealthInfo.chargingState;
+ mLastBatteryCapacityLevel = mHealthInfo.batteryCapacityLevel;
}
}
@@ -862,6 +865,7 @@ public final class BatteryService extends SystemService {
intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
intent.putExtra(BatteryManager.EXTRA_CYCLE_COUNT, mHealthInfo.batteryCycleCount);
intent.putExtra(BatteryManager.EXTRA_CHARGING_STATUS, mHealthInfo.chargingState);
+ intent.putExtra(BatteryManager.EXTRA_CAPACITY_LEVEL, mHealthInfo.batteryCapacityLevel);
if (DEBUG) {
Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
+ ", info:" + mHealthInfo.toString());
@@ -964,6 +968,7 @@ public final class BatteryService extends SystemService {
event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
event.putInt(BatteryManager.EXTRA_CYCLE_COUNT, mHealthInfo.batteryCycleCount);
event.putInt(BatteryManager.EXTRA_CHARGING_STATUS, mHealthInfo.chargingState);
+ event.putInt(BatteryManager.EXTRA_CAPACITY_LEVEL, mHealthInfo.batteryCapacityLevel);
boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
mBatteryLevelsEventQueue.add(event);
@@ -1401,6 +1406,7 @@ public final class BatteryService extends SystemService {
pw.println(" technology: " + mHealthInfo.batteryTechnology);
pw.println(" Charging state: " + mHealthInfo.chargingState);
pw.println(" Charging policy: " + mHealthInfo.chargingPolicy);
+ pw.println(" Capacity level: " + mHealthInfo.batteryCapacityLevel);
} else {
Shell shell = new Shell();
shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 68d0ad265a46..a459ea944008 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -173,6 +173,15 @@
"include-filter": "com.android.server.wm.BackgroundActivityStart*"
}
]
+ },
+ {
+ "name": "CtsOsTestCases",
+ "file_patterns": ["StorageManagerService\\.java"],
+ "options": [
+ {
+ "include-filter": "android.os.storage.cts.StorageStatsManagerTest"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 1c13ad5b3ceb..f32031dec43c 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -32,11 +32,11 @@ import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
import static android.os.UserHandle.USER_SYSTEM;
import static android.os.UserHandle.getCallingUserId;
-import static android.os.UserManager.isVisibleBackgroundUsersEnabled;
import static android.provider.Settings.Secure.CONTRAST_LEVEL;
import static android.util.TimeUtils.isTimeBetween;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
+import static com.android.server.pm.UserManagerService.enforceCurrentUserIfVisibleBackgroundEnabled;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -100,7 +100,6 @@ import com.android.internal.app.DisableCarModeActivity;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.DumpUtils;
-import com.android.server.pm.UserManagerService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
@@ -850,7 +849,7 @@ final class UiModeManagerService extends SystemService {
}
final int user = UserHandle.getCallingUserId();
- enforceValidCallingUser(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(user);
final long ident = Binder.clearCallingIdentity();
try {
@@ -914,7 +913,7 @@ final class UiModeManagerService extends SystemService {
@AttentionModeThemeOverlayType int attentionModeThemeOverlayType) {
setAttentionModeThemeOverlay_enforcePermission();
- enforceValidCallingUser(UserHandle.getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(UserHandle.getCallingUserId());
synchronized (mLock) {
if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) {
@@ -1005,7 +1004,7 @@ final class UiModeManagerService extends SystemService {
return false;
}
final int user = Binder.getCallingUserHandle().getIdentifier();
- enforceValidCallingUser(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(user);
if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS)
@@ -1064,7 +1063,7 @@ final class UiModeManagerService extends SystemService {
return;
}
final int user = UserHandle.getCallingUserId();
- enforceValidCallingUser(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(user);
final long ident = Binder.clearCallingIdentity();
try {
@@ -1094,7 +1093,7 @@ final class UiModeManagerService extends SystemService {
return;
}
final int user = UserHandle.getCallingUserId();
- enforceValidCallingUser(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(user);
final long ident = Binder.clearCallingIdentity();
try {
@@ -1116,7 +1115,7 @@ final class UiModeManagerService extends SystemService {
assertLegit(callingPackage);
assertSingleProjectionType(projectionType);
enforceProjectionTypePermissions(projectionType);
- enforceValidCallingUser(getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
synchronized (mLock) {
if (mProjectionHolders == null) {
@@ -1162,7 +1161,7 @@ final class UiModeManagerService extends SystemService {
assertLegit(callingPackage);
assertSingleProjectionType(projectionType);
enforceProjectionTypePermissions(projectionType);
- enforceValidCallingUser(getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
return releaseProjectionUnchecked(projectionType, callingPackage);
}
@@ -1204,7 +1203,7 @@ final class UiModeManagerService extends SystemService {
return;
}
- enforceValidCallingUser(getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
synchronized (mLock) {
if (mProjectionListeners == null) {
@@ -1253,32 +1252,6 @@ final class UiModeManagerService extends SystemService {
}
};
- // This method validates whether calling user is valid in visible background users
- // feature. Valid user is the current user or the system or in the same profile group as
- // the current user.
- private void enforceValidCallingUser(int userId) {
- if (!isVisibleBackgroundUsersEnabled()) {
- return;
- }
- if (LOG) {
- Slog.d(TAG, "enforceValidCallingUser: userId=" + userId
- + " isSystemUser=" + (userId == USER_SYSTEM) + " current user=" + mCurrentUser
- + " callingPid=" + Binder.getCallingPid()
- + " callingUid=" + mInjector.getCallingUid());
- }
- long ident = Binder.clearCallingIdentity();
- try {
- if (userId != USER_SYSTEM && userId != mCurrentUser
- && !UserManagerService.getInstance().isSameProfileGroup(userId, mCurrentUser)) {
- throw new SecurityException(
- "Calling user is not valid for level-1 compatibility in MUMD. "
- + "callingUserId=" + userId + " currentUserId=" + mCurrentUser);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
private void enforceProjectionTypePermissions(@UiModeManager.ProjectionType int p) {
if ((p & PROJECTION_TYPE_AUTOMOTIVE) != 0) {
getContext().enforceCallingPermission(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 54a741060bbe..871c32086a7f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -714,12 +714,14 @@ public class ActivityManagerService extends IActivityManager.Stub
/**
* Map userId to its companion app uids.
*/
+ @GuardedBy("mCompanionAppUidsMap")
private final Map<Integer, Set<Integer>> mCompanionAppUidsMap = new ArrayMap<>();
/**
* The profile owner UIDs.
*/
- private ArraySet<Integer> mProfileOwnerUids = null;
+ @GuardedBy("mProfileOwnerUids")
+ private final ArraySet<Integer> mProfileOwnerUids = new ArraySet<>();
final UserController mUserController;
@VisibleForTesting
@@ -17535,32 +17537,35 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void setProfileOwnerUid(ArraySet<Integer> profileOwnerUids) {
- synchronized (ActivityManagerService.this) {
- mProfileOwnerUids = profileOwnerUids;
+ synchronized (mProfileOwnerUids) {
+ mProfileOwnerUids.clear();
+ mProfileOwnerUids.addAll(profileOwnerUids);
}
}
@Override
public boolean isProfileOwner(int uid) {
- synchronized (ActivityManagerService.this) {
- return mProfileOwnerUids != null && mProfileOwnerUids.indexOf(uid) >= 0;
+ synchronized (mProfileOwnerUids) {
+ return mProfileOwnerUids.indexOf(uid) >= 0;
}
}
@Override
public void setCompanionAppUids(int userId, Set<Integer> companionAppUids) {
- synchronized (ActivityManagerService.this) {
+ synchronized (mCompanionAppUidsMap) {
mCompanionAppUidsMap.put(userId, companionAppUids);
}
}
@Override
public boolean isAssociatedCompanionApp(int userId, int uid) {
- final Set<Integer> allUids = mCompanionAppUidsMap.get(userId);
- if (allUids == null) {
- return false;
+ synchronized (mCompanionAppUidsMap) {
+ final Set<Integer> allUids = mCompanionAppUidsMap.get(userId);
+ if (allUids == null) {
+ return false;
+ }
+ return allUids.contains(uid);
}
- return allUids.contains(uid);
}
@Override
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f0cc09f7c232..22ec7904f972 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -73,6 +73,7 @@ import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
import static android.media.audio.Flags.roForegroundAudioControl;
import static android.os.Process.THREAD_GROUP_BACKGROUND;
import static android.os.Process.THREAD_GROUP_DEFAULT;
+import static android.os.Process.THREAD_GROUP_FOREGROUND_WINDOW;
import static android.os.Process.THREAD_GROUP_RESTRICTED;
import static android.os.Process.THREAD_GROUP_TOP_APP;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
@@ -116,6 +117,7 @@ import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ;
import static com.android.server.am.ProcessList.PREVIOUS_APP_ADJ;
import static com.android.server.am.ProcessList.SCHED_GROUP_BACKGROUND;
import static com.android.server.am.ProcessList.SCHED_GROUP_DEFAULT;
+import static com.android.server.am.ProcessList.SCHED_GROUP_FOREGROUND_WINDOW;
import static com.android.server.am.ProcessList.SCHED_GROUP_RESTRICTED;
import static com.android.server.am.ProcessList.SCHED_GROUP_TOP_APP;
import static com.android.server.am.ProcessList.SCHED_GROUP_TOP_APP_BOUND;
@@ -1731,6 +1733,11 @@ public class OomAdjuster {
// The recently used non-top visible freeform app.
schedGroup = SCHED_GROUP_TOP_APP;
mAdjType = "perceptible-freeform-activity";
+ } else if ((flags
+ & WindowProcessController.ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE) != 0) {
+ // Currently the only case is from freeform apps which are not close to top.
+ schedGroup = SCHED_GROUP_FOREGROUND_WINDOW;
+ mAdjType = "vis-multi-window-activity";
}
foregroundActivities = true;
mHasVisibleActivities = true;
@@ -3438,6 +3445,9 @@ public class OomAdjuster {
case SCHED_GROUP_RESTRICTED:
processGroup = THREAD_GROUP_RESTRICTED;
break;
+ case SCHED_GROUP_FOREGROUND_WINDOW:
+ processGroup = THREAD_GROUP_FOREGROUND_WINDOW;
+ break;
default:
processGroup = THREAD_GROUP_DEFAULT;
break;
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 21842db590b0..fb1c2e9a1f9d 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -331,7 +331,7 @@ public class OomAdjusterModernImpl extends OomAdjuster {
void forEachNewNode(int slot, @NonNull Consumer<OomAdjusterArgs> callback) {
ProcessRecordNode node = mLastNode[slot].mNext;
final ProcessRecordNode tail = mProcessRecordNodes[slot].TAIL;
- while (node != tail) {
+ while (node != null && node != tail) {
mTmpOomAdjusterArgs.mApp = node.mApp;
if (node.mApp == null) {
// TODO(b/336178916) - Temporary logging for root causing b/336178916.
@@ -365,7 +365,9 @@ public class OomAdjusterModernImpl extends OomAdjuster {
}
// Save the next before calling callback, since that may change the node.mNext.
final ProcessRecordNode next = node.mNext;
- callback.accept(mTmpOomAdjusterArgs);
+ if (mTmpOomAdjusterArgs.mApp != null) {
+ callback.accept(mTmpOomAdjusterArgs);
+ }
// There are couple of cases:
// a) The current node is moved to another slot
// - for this case, we'd need to keep using the "next" node.
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cb918a045ec6..00250b4ef463 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -303,6 +303,9 @@ public final class ProcessList {
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
// Disambiguate between actual top app and processes bound to the top app
static final int SCHED_GROUP_TOP_APP_BOUND = 4;
+ // Activity manager's version of Process.THREAD_GROUP_FOREGROUND_WINDOW
+ // The priority is like between default and top-app.
+ static final int SCHED_GROUP_FOREGROUND_WINDOW = 5;
// The minimum number of cached apps we want to be able to keep around,
// without empty apps being able to push them out of memory.
diff --git a/services/core/java/com/android/server/cpu/CpuMonitorService.java b/services/core/java/com/android/server/cpu/CpuMonitorService.java
index 88ff7e4103f9..2cadbc58d0f2 100644
--- a/services/core/java/com/android/server/cpu/CpuMonitorService.java
+++ b/services/core/java/com/android/server/cpu/CpuMonitorService.java
@@ -216,7 +216,7 @@ public final class CpuMonitorService extends SystemService {
@Override
public void onBootPhase(int phase) {
- if (phase != PHASE_BOOT_COMPLETED) {
+ if (phase != PHASE_BOOT_COMPLETED || mHandler == null) {
return;
}
Slogf.i(TAG, "Stopping periodic cpuset reading on boot complete");
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index ea240c75452d..9d04682f2374 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -196,12 +196,7 @@ final class UpdatableFontDir {
File signatureFile = new File(dir, FONT_SIGNATURE_FILE);
if (!signatureFile.exists()) {
Slog.i(TAG, "The signature file is missing.");
- if (com.android.text.flags.Flags.fixFontUpdateFailure()) {
- return;
- } else {
- FileUtils.deleteContentsAndDir(dir);
- continue;
- }
+ return;
}
byte[] signature;
try {
@@ -226,39 +221,33 @@ final class UpdatableFontDir {
FontFileInfo fontFileInfo = validateFontFile(fontFile, signature);
if (fontConfig == null) {
- if (com.android.text.flags.Flags.fixFontUpdateFailure()) {
- // Use preinstalled font config for checking revision number.
- fontConfig = mConfigSupplier.apply(Collections.emptyMap());
- } else {
- fontConfig = getSystemFontConfig();
- }
+ // Use preinstalled font config for checking revision number.
+ fontConfig = mConfigSupplier.apply(Collections.emptyMap());
}
addFileToMapIfSameOrNewer(fontFileInfo, fontConfig, true /* deleteOldFile */);
}
- if (com.android.text.flags.Flags.fixFontUpdateFailure()) {
- // Treat as error if post script name of font family was not installed.
- for (int i = 0; i < config.fontFamilies.size(); ++i) {
- FontUpdateRequest.Family family = config.fontFamilies.get(i);
- for (int j = 0; j < family.getFonts().size(); ++j) {
- FontUpdateRequest.Font font = family.getFonts().get(j);
- if (mFontFileInfoMap.containsKey(font.getPostScriptName())) {
- continue;
- }
-
- if (fontConfig == null) {
- fontConfig = mConfigSupplier.apply(Collections.emptyMap());
- }
-
- if (getFontByPostScriptName(font.getPostScriptName(), fontConfig) != null) {
- continue;
- }
-
- Slog.e(TAG, "Unknown font that has PostScript name "
- + font.getPostScriptName() + " is requested in FontFamily "
- + family.getName());
- return;
+ // Treat as error if post script name of font family was not installed.
+ for (int i = 0; i < config.fontFamilies.size(); ++i) {
+ FontUpdateRequest.Family family = config.fontFamilies.get(i);
+ for (int j = 0; j < family.getFonts().size(); ++j) {
+ FontUpdateRequest.Font font = family.getFonts().get(j);
+ if (mFontFileInfoMap.containsKey(font.getPostScriptName())) {
+ continue;
}
+
+ if (fontConfig == null) {
+ fontConfig = mConfigSupplier.apply(Collections.emptyMap());
+ }
+
+ if (getFontByPostScriptName(font.getPostScriptName(), fontConfig) != null) {
+ continue;
+ }
+
+ Slog.e(TAG, "Unknown font that has PostScript name "
+ + font.getPostScriptName() + " is requested in FontFamily "
+ + family.getName());
+ return;
}
}
@@ -273,9 +262,7 @@ final class UpdatableFontDir {
mFontFileInfoMap.clear();
mLastModifiedMillis = 0;
FileUtils.deleteContents(mFilesDir);
- if (com.android.text.flags.Flags.fixFontUpdateFailure()) {
- mConfigFile.delete();
- }
+ mConfigFile.delete();
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index ac75ef7b4656..8e41d18f0953 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1688,7 +1688,11 @@ public class HdmiControlService extends SystemService {
private void sendCecCommandWithRetries(HdmiCecMessage command,
@Nullable SendMessageCallback callback) {
assertRunOnServiceThread();
- HdmiCecLocalDevice localDevice = getAllCecLocalDevices().get(0);
+ List<HdmiCecLocalDevice> devices = getAllCecLocalDevices();
+ if (devices.isEmpty()) {
+ return;
+ }
+ HdmiCecLocalDevice localDevice = devices.get(0);
if (localDevice != null) {
sendCecCommandWithoutRetries(command, new SendMessageCallback() {
@Override
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index edc35e553244..65adaba62a4d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -456,6 +456,14 @@ public class InputManagerService extends IInputManager.Stub
void registerLocalService(InputManagerInternal localService) {
LocalServices.addService(InputManagerInternal.class, localService);
}
+
+ KeyboardBacklightControllerInterface getKeyboardBacklightController(
+ NativeInputManagerService nativeService, PersistentDataStore dataStore) {
+ return InputFeatureFlagProvider.isKeyboardBacklightControlEnabled()
+ ? new KeyboardBacklightController(mContext, nativeService, dataStore,
+ mLooper, mUEventManager)
+ : new KeyboardBacklightControllerInterface() {};
+ }
}
public InputManagerService(Context context) {
@@ -479,10 +487,7 @@ public class InputManagerService extends IInputManager.Stub
injector.getLooper(), this) : null;
mBatteryController = new BatteryController(mContext, mNative, injector.getLooper(),
injector.getUEventManager());
- mKeyboardBacklightController = InputFeatureFlagProvider.isKeyboardBacklightControlEnabled()
- ? new KeyboardBacklightController(mContext, mNative, mDataStore,
- injector.getLooper(), injector.getUEventManager())
- : new KeyboardBacklightControllerInterface() {};
+ mKeyboardBacklightController = injector.getKeyboardBacklightController(mNative, mDataStore);
mStickyModifierStateController = new StickyModifierStateController();
mKeyGestureController = new KeyGestureController(mContext, injector.getLooper());
mKeyboardLedController = new KeyboardLedController(mContext, injector.getLooper(),
@@ -606,6 +611,8 @@ public class InputManagerService extends IInputManager.Stub
mKeyRemapper.systemRunning();
mPointerIconCache.systemRunning();
mKeyboardGlyphManager.systemRunning();
+
+ initKeyGestures();
}
private void reloadDeviceAliases() {
@@ -2504,6 +2511,59 @@ public class InputManagerService extends IInputManager.Stub
null, null, null) == PERMISSION_GRANTED;
}
+ private void initKeyGestures() {
+ if (!useKeyGestureEventHandler()) {
+ return;
+ }
+ InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
+ im.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() {
+ @Override
+ public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
+ @Nullable IBinder focussedToken) {
+ int deviceId = event.getDeviceId();
+ boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ && !event.isCancelled();
+ switch (event.getKeyGestureType()) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+ if (complete) {
+ mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+ if (complete) {
+ mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+ // TODO(b/367748270): Add functionality to turn keyboard backlight on/off.
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+ if (complete) {
+ mNative.toggleCapsLock(deviceId);
+ }
+ return true;
+ default:
+ return false;
+
+ }
+ }
+
+ @Override
+ public boolean isKeyGestureSupported(int gestureType) {
+ switch (gestureType) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+ return true;
+ default:
+ return false;
+
+ }
+ }
+ });
+ }
+
// Native callback.
@SuppressWarnings("unused")
private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
@@ -3207,6 +3267,8 @@ public class InputManagerService extends IInputManager.Stub
mKeyboardBacklightController.onInteractiveChanged(interactive);
}
+ // TODO(b/358569822): Remove this method from InputManagerInternal after key gesture
+ // handler refactoring complete
@Override
public void toggleCapsLock(int deviceId) {
mNative.toggleCapsLock(deviceId);
@@ -3294,11 +3356,15 @@ public class InputManagerService extends IInputManager.Stub
mKeyboardBacklightController.notifyUserActivity();
}
+ // TODO(b/358569822): Remove this method from InputManagerInternal after key gesture
+ // handler refactoring complete
@Override
public void incrementKeyboardBacklight(int deviceId) {
mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
}
+ // TODO(b/358569822): Remove this method from InputManagerInternal after key gesture
+ // handler refactoring complete
@Override
public void decrementKeyboardBacklight(int deviceId) {
mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
index 5ff8568f81b2..0e940d281b09 100644
--- a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
@@ -180,6 +180,10 @@ public class TouchpadDebugView extends LinearLayout {
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (event.getClassification() == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE) {
+ return false;
+ }
+
float deltaX;
float deltaY;
switch (event.getAction()) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 4b2c12abe5bb..63bd9ab815b2 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -389,7 +389,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
// Reload gnss config for no SIM case
mGnssConfiguration.reloadGpsProperties();
}
- if (Flags.enableNiSuplMessageInjectionByCarrierConfig()) {
+ if (Flags.enableNiSuplMessageInjectionByCarrierConfigBugfix()) {
updateNiSuplMessageListenerRegistration(
mGnssConfiguration.isNiSuplMessageInjectionEnabled());
}
@@ -538,7 +538,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
mContext.registerReceiver(mIntentReceiver, intentFilter, null, mHandler);
- if (!Flags.enableNiSuplMessageInjectionByCarrierConfig()) {
+ if (!Flags.enableNiSuplMessageInjectionByCarrierConfigBugfix()) {
updateNiSuplMessageListenerRegistration(
mGnssConfiguration.isNiSuplMessageInjectionEnabled());
}
@@ -1672,7 +1672,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
if (dumpAll) {
mNetworkTimeHelper.dump(pw);
pw.println("mSupportsPsds=" + mSupportsPsds);
- if (Flags.enableNiSuplMessageInjectionByCarrierConfig()) {
+ if (Flags.enableNiSuplMessageInjectionByCarrierConfigBugfix()) {
pw.println("mNiSuplMessageListenerRegistered="
+ mNiSuplMessageListenerRegistered);
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 158d444bcff2..1e25f1cf1d5e 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -140,7 +140,7 @@ class LockSettingsStorage {
try {
db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?",
new String[] {key, Integer.toString(userId)});
- db.insert(TABLE, null, cv);
+ db.insertOrThrow(TABLE, null, cv);
db.setTransactionSuccessful();
mCache.putKeyValue(key, value, userId);
} finally {
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 621c090d37b8..48d24f2e14dd 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -179,7 +179,8 @@ public final class MediaProjectionManagerService extends SystemService
/**
* In order to record the keyguard, the MediaProjection package must be either:
* - a holder of RECORD_SENSITIVE_CONTENT permission, or
- * - be one of the bugreport whitelisted packages
+ * - be one of the bugreport allowlisted packages, or
+ * - hold the OP_PROJECT_MEDIA AppOp.
*/
private boolean canCaptureKeyguard() {
if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
@@ -194,6 +195,14 @@ public final class MediaProjectionManagerService extends SystemService
== PackageManager.PERMISSION_GRANTED) {
return true;
}
+ boolean operationActive = mAppOps.isOperationActive(AppOpsManager.OP_PROJECT_MEDIA,
+ mProjectionGrant.uid,
+ mProjectionGrant.packageName);
+ if (operationActive) {
+ // Some tools use media projection by granting the OP_PROJECT_MEDIA app
+ // op via a shell command. Those tools can be granted keyguard capture
+ return true;
+ }
return SystemConfig.getInstance().getBugreportWhitelistedPackages()
.contains(mProjectionGrant.packageName);
}
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index fbe77720b9fc..f54c1f7654dc 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -218,10 +218,16 @@ public final class VibratorHelper {
* @param uri {@code Uri} an uri including query parameter "vibraiton_uri"
*/
public @Nullable VibrationEffect createVibrationEffectFromSoundUri(Uri uri) {
- if (uri == null) {
+ if (uri == null || uri.isOpaque()) {
return null;
}
- return Utils.parseVibrationEffect(mVibrator, Utils.getVibrationUri(uri));
+
+ try {
+ return Utils.parseVibrationEffect(mVibrator, Utils.getVibrationUri(uri));
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to get vibration effect: ", e);
+ }
+ return null;
}
/** Returns if a given vibration can be played by the vibrator that does notification buzz. */
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index c75622cb9bbc..21a6df203015 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -208,6 +208,22 @@
"name": "CtsUpdateOwnershipEnforcementTestCases"
},
{
+ "name": "CtsPackageInstallerCUJDeviceAdminTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJInstallationTestCases",
"file_patterns": [
"core/java/.*Install.*",
@@ -224,6 +240,22 @@
]
},
{
+ "name": "CtsPackageInstallerCUJMultiUsersTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJUninstallationTestCases",
"file_patterns": [
"core/java/.*Install.*",
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ef37464704ae..02c02b04c5de 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3849,10 +3849,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
return true;
default:
return false;
@@ -4010,25 +4006,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
sendSwitchKeyboardLayout(displayId, focusedToken, direction);
}
return true;
- // TODO (b/358569822): Move these input specific gesture handling to IMS since we
- // are calling into InputManager through internal API anyways
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
- if (complete) {
- mInputManagerInternal.incrementKeyboardBacklight(deviceId);
- }
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
- if (complete) {
- mInputManagerInternal.decrementKeyboardBacklight(deviceId);
- }
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
- if (complete) {
- mInputManagerInternal.toggleCapsLock(deviceId);
- }
- return true;
}
return false;
}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 68026ea9094a..e3d71e4998be 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -24,8 +24,11 @@ import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -78,6 +81,8 @@ import java.util.function.Consumer;
public final class RollbackPackageHealthObserver implements PackageHealthObserver {
private static final String TAG = "RollbackPackageHealthObserver";
private static final String NAME = "rollback-observer";
+ private static final String ACTION_NAME = RollbackPackageHealthObserver.class.getName();
+
private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
| ApplicationInfo.FLAG_SYSTEM;
@@ -596,12 +601,40 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
}
};
- final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver(result -> {
- mHandler.post(() -> onResult.accept(result));
- });
+ if (Flags.refactorCrashrecovery()) {
+ // Define a BroadcastReceiver to handle the result
+ BroadcastReceiver rollbackReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent result) {
+ mHandler.post(() -> onResult.accept(result));
+ }
+ };
+
+ // Register the BroadcastReceiver
+ mContext.registerReceiver(rollbackReceiver,
+ new IntentFilter(ACTION_NAME),
+ Context.RECEIVER_NOT_EXPORTED);
+
+ Intent intentReceiver = new Intent(ACTION_NAME);
+ intentReceiver.putExtra("rollbackId", rollback.getRollbackId());
+ intentReceiver.setPackage(mContext.getPackageName());
- rollbackManager.commitRollback(rollback.getRollbackId(),
- Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender());
+ PendingIntent rollbackPendingIntent = PendingIntent.getBroadcast(mContext,
+ rollback.getRollbackId(),
+ intentReceiver,
+ PendingIntent.FLAG_MUTABLE);
+
+ rollbackManager.commitRollback(rollback.getRollbackId(),
+ Collections.singletonList(failedPackage),
+ rollbackPendingIntent.getIntentSender());
+ } else {
+ final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver(result -> {
+ mHandler.post(() -> onResult.accept(result));
+ });
+
+ rollbackManager.commitRollback(rollback.getRollbackId(),
+ Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender());
+ }
}
/**
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 96a25dac21e3..1e82b8999834 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -322,9 +322,16 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
if (SubscriptionManager.isValidSubscriptionId(subId)) {
// Get only configs as needed to save memory.
- final PersistableBundle carrierConfig =
- CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
- VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+ PersistableBundle carrierConfig = new PersistableBundle();
+ try {
+ carrierConfig =
+ mCarrierConfigManager.getConfigForSubId(
+ subId, VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+
+ } catch (RuntimeException exception) {
+ Slog.w(TAG, "CarrierConfigLoader is not available.");
+ }
+
if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
mReadySubIdsBySlotId.put(slotId, subId);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java
index a698429ff09e..15f86e9c08ff 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperData.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java
@@ -78,7 +78,7 @@ class WallpaperData {
/**
* The component name of the currently set live wallpaper.
*/
- ComponentName wallpaperComponent;
+ private ComponentName mWallpaperComponent;
// TODO(b/347235611) Remove this field
/**
@@ -195,7 +195,7 @@ class WallpaperData {
*/
WallpaperData(WallpaperData source) {
this.userId = source.userId;
- this.wallpaperComponent = source.wallpaperComponent;
+ this.mWallpaperComponent = source.mWallpaperComponent;
this.mWhich = source.mWhich;
this.wallpaperId = source.wallpaperId;
this.cropHint.set(source.cropHint);
@@ -230,6 +230,14 @@ class WallpaperData {
return result;
}
+ ComponentName getComponent() {
+ return mWallpaperComponent;
+ }
+
+ void setComponent(ComponentName componentName) {
+ this.mWallpaperComponent = componentName;
+ }
+
@Override
public String toString() {
StringBuilder out = new StringBuilder(defaultString(this));
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
index b15facb2945c..e3e83b3e1fd7 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
@@ -189,13 +189,13 @@ public class WallpaperDataParser {
String comp = parser.getAttributeValue(null, "component");
if (removeNextWallpaperComponent()) {
- wallpaperToParse.wallpaperComponent = comp != null
+ wallpaperToParse.setComponent(comp != null
? ComponentName.unflattenFromString(comp)
- : null;
- if (wallpaperToParse.wallpaperComponent == null
- || "android".equals(wallpaperToParse.wallpaperComponent
+ : null);
+ if (wallpaperToParse.getComponent() == null
+ || "android".equals(wallpaperToParse.getComponent()
.getPackageName())) {
- wallpaperToParse.wallpaperComponent = mImageWallpaper;
+ wallpaperToParse.setComponent(mImageWallpaper);
}
} else {
wallpaperToParse.nextWallpaperComponent = comp != null
@@ -219,7 +219,7 @@ public class WallpaperDataParser {
Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
Slog.v(TAG, "mName:" + wallpaper.name);
if (removeNextWallpaperComponent()) {
- Slog.v(TAG, "mWallpaperComponent:" + wallpaper.wallpaperComponent);
+ Slog.v(TAG, "mWallpaperComponent:" + wallpaper.getComponent());
} else {
Slog.v(TAG, "mNextWallpaperComponent:"
+ wallpaper.nextWallpaperComponent);
@@ -340,7 +340,7 @@ public class WallpaperDataParser {
getAttributeInt(parser, "totalCropTop", 0),
getAttributeInt(parser, "totalCropRight", 0),
getAttributeInt(parser, "totalCropBottom", 0));
- ComponentName componentName = removeNextWallpaperComponent() ? wallpaper.wallpaperComponent
+ ComponentName componentName = removeNextWallpaperComponent() ? wallpaper.getComponent()
: wallpaper.nextWallpaperComponent;
if (multiCrop() && mImageWallpaper.equals(componentName)) {
wallpaper.mCropHints = new SparseArray<>();
@@ -480,7 +480,7 @@ public class WallpaperDataParser {
out.startTag(null, tag);
out.attributeInt(null, "id", wallpaper.wallpaperId);
- if (multiCrop() && mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
+ if (multiCrop() && mImageWallpaper.equals(wallpaper.getComponent())) {
if (wallpaper.mCropHints == null) {
Slog.e(TAG, "cropHints should not be null when saved");
wallpaper.mCropHints = new SparseArray<>();
@@ -580,10 +580,10 @@ public class WallpaperDataParser {
}
out.attribute(null, "name", wallpaper.name);
- if (wallpaper.wallpaperComponent != null
- && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
+ if (wallpaper.getComponent() != null
+ && !wallpaper.getComponent().equals(mImageWallpaper)) {
out.attribute(null, "component",
- wallpaper.wallpaperComponent.flattenToShortString());
+ wallpaper.getComponent().flattenToShortString());
}
if (wallpaper.allowBackup) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6cc37ddda9bc..4754ffb5cf6e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -276,7 +276,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
final boolean isMigration = moved && lockWallpaperChanged;
final boolean isRestore = moved && !isMigration;
final boolean isAppliedToLock = (wallpaper.mWhich & FLAG_LOCK) != 0;
- final boolean needsUpdate = wallpaper.wallpaperComponent == null
+ final boolean needsUpdate = wallpaper.getComponent() == null
|| event != CLOSE_WRITE // includes the MOVED_TO case
|| wallpaper.imageWallpaperPending;
@@ -527,7 +527,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
* @return true unless the wallpaper changed during the color computation
*/
private boolean extractColors(WallpaperData wallpaper) {
- if (offloadColorExtraction()) return !mImageWallpaper.equals(wallpaper.wallpaperComponent);
+ if (offloadColorExtraction()) return !mImageWallpaper.equals(wallpaper.getComponent());
String cropFile = null;
boolean defaultImageWallpaper = false;
int wallpaperId;
@@ -550,8 +550,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
synchronized (mLock) {
// Not having a wallpaperComponent means it's a lock screen wallpaper.
- final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
- || wallpaper.wallpaperComponent == null;
+ final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.getComponent())
+ || wallpaper.getComponent() == null;
if (imageWallpaper && wallpaper.getCropFile().exists()) {
cropFile = wallpaper.getCropFile().getAbsolutePath();
} else if (imageWallpaper && !wallpaper.cropExists() && !wallpaper.sourceExists()) {
@@ -824,13 +824,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
- t.traceBegin("WPMS.connectLocked-" + wallpaper.wallpaperComponent);
+ t.traceBegin("WPMS.connectLocked-" + wallpaper.getComponent());
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
null /* options */);
mWindowManagerInternal.setWallpaperShowWhenLocked(
mToken, (wallpaper.mWhich & FLAG_LOCK) != 0);
- if (multiCrop() && mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
+ if (multiCrop() && mImageWallpaper.equals(wallpaper.getComponent())) {
mWindowManagerInternal.setWallpaperCropHints(mToken,
mWallpaperCropper.getRelativeCropHints(wallpaper));
} else {
@@ -906,7 +906,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
if (!mWallpaper.wallpaperUpdating && mWallpaper.userId == mCurrentUserId) {
- Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
+ Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.getComponent()
+ ", reverting to built-in wallpaper!");
clearWallpaperLocked(mWallpaper.mWhich, mWallpaper.userId, false, null);
}
@@ -1035,9 +1035,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
Slog.w(TAG, "Wallpaper service gone: " + name);
- if (!Objects.equals(name, mWallpaper.wallpaperComponent)) {
+ if (!Objects.equals(name, mWallpaper.getComponent())) {
Slog.e(TAG, "Does not match expected wallpaper component "
- + mWallpaper.wallpaperComponent);
+ + mWallpaper.getComponent());
}
mService = null;
forEachDisplayConnector(connector -> connector.mEngine = null);
@@ -1065,7 +1065,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
if (DEBUG_LIVE) {
Slog.i(TAG,
- "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
+ "Started wallpaper reconnect timeout for " + mWallpaper.getComponent());
}
}
@@ -1081,7 +1081,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
- final ComponentName wpService = mWallpaper.wallpaperComponent;
+ final ComponentName wpService = mWallpaper.getComponent();
// The broadcast of package update could be delayed after service disconnected. Try
// to re-bind the service for 10 seconds.
mWallpaper.mBindSource = BindSource.CONNECTION_TRY_TO_REBIND;
@@ -1110,7 +1110,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// The wallpaper disappeared. If this isn't a system-default one, track
// crashes and fall back to default if it continues to misbehave.
if (this == mWallpaper.connection) {
- final ComponentName wpService = mWallpaper.wallpaperComponent;
+ final ComponentName wpService = mWallpaper.getComponent();
if (!mWallpaper.wallpaperUpdating
&& mWallpaper.userId == mCurrentUserId
&& !Objects.equals(mDefaultWallpaperComponent, wpService)
@@ -1188,7 +1188,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
synchronized (mLock) {
// Do not broadcast changes on ImageWallpaper since it's handled
// internally by this class.
- boolean isImageWallpaper = mImageWallpaper.equals(mWallpaper.wallpaperComponent);
+ boolean isImageWallpaper = mImageWallpaper.equals(mWallpaper.getComponent());
if (isImageWallpaper && (!offloadColorExtraction() || primaryColors == null)) {
return;
}
@@ -1303,7 +1303,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (mNewWallpaper.mWhich == FLAG_SYSTEM) {
// New wp is system only, so old system+lock is now lock only
final boolean originalIsStatic = mImageWallpaper.equals(
- mOriginalSystem.wallpaperComponent);
+ mOriginalSystem.getComponent());
if (originalIsStatic) {
// Static wp: image file rename has already been tried via
// migrateStaticSystemToLockWallpaperLocked() and added to the lock wp map
@@ -1314,8 +1314,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (DEBUG) {
Slog.v(TAG, "static system+lock to system success");
}
- lockWp.wallpaperComponent =
- mOriginalSystem.wallpaperComponent;
+ lockWp.setComponent(mOriginalSystem.getComponent());
lockWp.connection = mOriginalSystem.connection;
lockWp.connection.mWallpaper = lockWp;
mOriginalSystem.mWhich = FLAG_LOCK;
@@ -1376,7 +1375,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
for (WallpaperData wallpaper: getWallpapers()) {
- final ComponentName wpService = wallpaper.wallpaperComponent;
+ final ComponentName wpService = wallpaper.getComponent();
if (wpService != null && wpService.getPackageName().equals(packageName)) {
if (DEBUG_LIVE) {
Slog.i(TAG, "Wallpaper " + wpService + " update has finished");
@@ -1402,8 +1401,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
for (WallpaperData wallpaper: getWallpapers()) {
- if (wallpaper.wallpaperComponent != null
- && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
+ if (wallpaper.getComponent() != null
+ && wallpaper.getComponent().getPackageName().equals(packageName)) {
doPackagesChangedLocked(true, wallpaper);
}
}
@@ -1417,10 +1416,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
for (WallpaperData wallpaper: getWallpapers()) {
- if (wallpaper.wallpaperComponent != null
- && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
+ if (wallpaper.getComponent() != null
+ && wallpaper.getComponent().getPackageName().equals(packageName)) {
if (DEBUG_LIVE) {
- Slog.i(TAG, "Wallpaper service " + wallpaper.wallpaperComponent
+ Slog.i(TAG, "Wallpaper service " + wallpaper.getComponent()
+ " is updating");
}
wallpaper.wallpaperUpdating = true;
@@ -1462,15 +1461,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
boolean changed = false;
- if (wallpaper.wallpaperComponent != null) {
- int change = isPackageDisappearing(wallpaper.wallpaperComponent
+ if (wallpaper.getComponent() != null) {
+ int change = isPackageDisappearing(wallpaper.getComponent()
.getPackageName());
if (change == PACKAGE_PERMANENT_CHANGE
|| change == PACKAGE_TEMPORARY_CHANGE) {
changed = true;
if (doit) {
Slog.w(TAG, "Wallpaper uninstalled, removing: "
- + wallpaper.wallpaperComponent);
+ + wallpaper.getComponent());
clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, false, null);
}
}
@@ -1485,15 +1484,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
}
- if (wallpaper.wallpaperComponent != null
- && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
+ if (wallpaper.getComponent() != null
+ && isPackageModified(wallpaper.getComponent().getPackageName())) {
try {
- mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent,
+ mContext.getPackageManager().getServiceInfo(wallpaper.getComponent(),
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
} catch (NameNotFoundException e) {
Slog.w(TAG, "Wallpaper component gone, removing: "
- + wallpaper.wallpaperComponent);
+ + wallpaper.getComponent());
clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, false, null);
}
}
@@ -1636,8 +1635,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// sure we have something to render
boolean isImageComponent;
if (removeNextWallpaperComponent()) {
- isImageComponent = wallpaper.wallpaperComponent == null
- || mImageWallpaper.equals(wallpaper.wallpaperComponent);
+ isImageComponent = wallpaper.getComponent() == null
+ || mImageWallpaper.equals(wallpaper.getComponent());
} else {
isImageComponent = mImageWallpaper.equals(wallpaper.nextWallpaperComponent);
}
@@ -1892,10 +1891,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
final ComponentName cname;
if (removeNextWallpaperComponent()) {
- cname = wallpaper.wallpaperComponent;
+ cname = wallpaper.getComponent();
} else {
- cname = (wallpaper.wallpaperComponent != null)
- ? wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
+ cname = (wallpaper.getComponent() != null)
+ ? wallpaper.getComponent() : wallpaper.nextWallpaperComponent;
}
if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
// We failed to bind the desired wallpaper, but that might
@@ -1927,7 +1926,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// We might end up persisting the current wallpaper data
// while locked, so pretend like the component was actually
// bound into place
- wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
+ wallpaper.setComponent(wallpaper.nextWallpaperComponent);
}
final WallpaperData fallback = new WallpaperData(wallpaper.userId, wallpaper.mWhich);
@@ -2004,7 +2003,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// lock only case: set the system wallpaper component to both screens
if (which == FLAG_LOCK) {
- component = wallpaper.wallpaperComponent;
+ component = wallpaper.getComponent();
finalWhich = FLAG_LOCK | FLAG_SYSTEM;
} else {
component = null;
@@ -2310,7 +2309,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
checkPermission(READ_WALLPAPER_INTERNAL);
WallpaperData wallpaper = (which == FLAG_LOCK) ? mLockWallpaperMap.get(userId)
: mWallpaperMap.get(userId);
- if (wallpaper == null || !mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
+ if (wallpaper == null || !mImageWallpaper.equals(wallpaper.getComponent())) {
return null;
}
SparseArray<Rect> relativeSuggestedCrops =
@@ -2760,7 +2759,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperData wallpaperData = (which == FLAG_LOCK ? mLockWallpaperMap : mWallpaperMap)
.get(mCurrentUserId);
if (wallpaperData == null) return false;
- return mImageWallpaper.equals(wallpaperData.wallpaperComponent);
+ return mImageWallpaper.equals(wallpaperData.getComponent());
}
}
@@ -2996,7 +2995,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
final WallpaperData originalSystemWallpaper = mWallpaperMap.get(userId);
final boolean systemIsStatic =
originalSystemWallpaper != null && mImageWallpaper.equals(
- originalSystemWallpaper.wallpaperComponent);
+ originalSystemWallpaper.getComponent());
final boolean systemIsBoth = mLockWallpaperMap.get(userId) == null;
/* If we're setting system but not lock, and lock is currently sharing the system
@@ -3190,7 +3189,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
}
final boolean systemIsStatic = mImageWallpaper.equals(
- originalSystemWallpaper.wallpaperComponent);
+ originalSystemWallpaper.getComponent());
final boolean systemIsBoth = mLockWallpaperMap.get(userId) == null;
if (which == FLAG_SYSTEM && systemIsBoth && systemIsStatic) {
@@ -3212,7 +3211,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
liveSync = new WallpaperDestinationChangeHandler(
newWallpaper);
boolean same = changingToSame(name, newWallpaper.connection,
- newWallpaper.wallpaperComponent);
+ newWallpaper.getComponent());
/*
* If we have a shared system+lock wallpaper, and we reapply the same wallpaper
@@ -3243,7 +3242,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
boolean lockBitmapCleared = false;
- if (!mImageWallpaper.equals(newWallpaper.wallpaperComponent)) {
+ if (!mImageWallpaper.equals(newWallpaper.getComponent())) {
clearWallpaperBitmaps(newWallpaper);
lockBitmapCleared = newWallpaper.mWhich == FLAG_LOCK;
}
@@ -3324,7 +3323,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
// Has the component changed?
if (!force && changingToSame(componentName, wallpaper.connection,
- wallpaper.wallpaperComponent)) {
+ wallpaper.getComponent())) {
try {
if (DEBUG_LIVE) {
Slog.v(TAG, "Changing to the same component, ignoring");
@@ -3461,7 +3460,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return false;
}
maybeDetachLastWallpapers(wallpaper);
- wallpaper.wallpaperComponent = componentName;
+ wallpaper.setComponent(componentName);
wallpaper.connection = newConn;
newConn.mReply = reply;
updateCurrentWallpapers(wallpaper);
@@ -3586,7 +3585,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
- wallpaper.wallpaperComponent = null;
+ wallpaper.setComponent(null);
detachWallpaperLocked(wallpaper);
}
@@ -3831,7 +3830,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper.wallpaperId = makeWallpaperIdLocked(); // always bump id at restore
wallpaper.allowBackup = true; // by definition if it was restored
ComponentName componentName =
- removeNextWallpaperComponent() ? wallpaper.wallpaperComponent
+ removeNextWallpaperComponent() ? wallpaper.getComponent()
: wallpaper.nextWallpaperComponent;
if (componentName != null && !componentName.equals(mImageWallpaper)) {
wallpaper.mBindSource = BindSource.RESTORE_SETTINGS_LIVE_SUCCESS;
@@ -3907,7 +3906,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (multiCrop()) pw.print(" mCropHints="); pw.println(wallpaper.mCropHints);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
- pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
+ pw.print(" mWallpaperComponent="); pw.println(wallpaper.getComponent());
pw.print(" mWallpaperDimAmount="); pw.println(wallpaper.mWallpaperDimAmount);
pw.print(" isColorExtracted="); pw.println(wallpaper.mIsColorExtractedFromDim);
pw.println(" mUidToDimAmount:");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3d5b2732e948..6009b4a70e3e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6218,7 +6218,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public void onProcessRemoved(String name, int uid) {
synchronized (mGlobalLockWithoutBoost) {
final WindowProcessController proc = mProcessNames.remove(name, uid);
- if (proc != null && !mStartingProcessActivities.isEmpty()) {
+ if (proc != null && !proc.mHasEverAttached
+ && !mStartingProcessActivities.isEmpty()) {
// Use a copy in case finishIfPossible changes the list indirectly.
final ArrayList<ActivityRecord> activities =
new ArrayList<>(mStartingProcessActivities);
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index cc6904f9b3af..156d8a065b67 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -103,7 +103,7 @@ public final class DesktopModeBoundsCalculator {
final TaskDisplayArea displayArea = task.getDisplayArea();
final Rect screenBounds = displayArea.getBounds();
final Size idealSize = calculateIdealSize(screenBounds, DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
- if (!DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(activity.mWmService.mContext)) {
+ if (!DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()) {
return centerInScreen(idealSize, screenBounds);
}
if (activity.mAppCompatController.getAppCompatAspectRatioOverrides()
diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java
index 61fbb96882ec..da7631723185 100644
--- a/services/core/java/com/android/server/wm/DesktopModeHelper.java
+++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java
@@ -35,8 +35,8 @@ public final class DesktopModeHelper {
"persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
/** Whether desktop mode is enabled. */
- static boolean isDesktopModeEnabled(@NonNull Context context) {
- return DesktopModeFlags.DESKTOP_WINDOWING_MODE.isEnabled(context);
+ static boolean isDesktopModeEnabled() {
+ return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isEnabled();
}
/**
@@ -60,7 +60,7 @@ public final class DesktopModeHelper {
* Return {@code true} if desktop mode can be entered on the current device.
*/
static boolean canEnterDesktopMode(@NonNull Context context) {
- return isDesktopModeEnabled(context)
+ return isDesktopModeEnabled()
&& (!shouldEnforceDeviceRestrictions() || isDesktopModeSupported(context));
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 8f5612c61e1c..84072e26761a 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1841,6 +1841,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
boolean attachApplication(WindowProcessController app) throws RemoteException {
+ app.mHasEverAttached = true;
final ArrayList<ActivityRecord> activities = mService.mStartingProcessActivities;
RemoteException remoteException = null;
boolean hasActivityStarted = false;
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 638e92f112c7..42ea5a88a09b 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -28,6 +28,7 @@ import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -57,6 +58,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import java.io.PrintWriter;
@@ -1761,10 +1763,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
* @return last reparented root task, or {@code null} if the root tasks had to be destroyed.
*/
Task remove() {
+ final TaskDisplayArea toDisplayArea = getReparentToTaskDisplayArea(getFocusedRootTask());
mPreferredTopFocusableRootTask = null;
// TODO(b/153090332): Allow setting content removal mode per task display area
final boolean destroyContentOnRemoval = mDisplayContent.shouldDestroyContentOnRemove();
- final TaskDisplayArea toDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
Task lastReparentedRootTask = null;
// Root tasks could be reparented from the removed display area to other display area. After
@@ -1830,6 +1832,41 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
return lastReparentedRootTask;
}
+ /**
+ * Returns the {@link TaskDisplayArea} to which root tasks should be reparented.
+ *
+ * <p>In the automotive multi-user multi-display environment where background users have
+ * UI access on their assigned displays (a.k.a. visible background users), it's not allowed to
+ * launch an activity on an unassigned display. If an activity is attempted to launch on an
+ * unassigned display, it throws an exception.
+ * <p>This method determines the appropriate {@link TaskDisplayArea} for reparenting root tasks
+ * when a display is removed, in order to avoid the exception. If the root task is null,
+ * the visible background user is not supported or the user associated with the root task is
+ * not a visible background user, it returns the default {@link TaskDisplayArea} of the default
+ * display. Otherwise, it returns the default {@link TaskDisplayArea} of the main display
+ * assigned to the user.
+ *
+ * @param rootTask The root task whose {@link TaskDisplayArea} needs to be determined.
+ * @return The {@link TaskDisplayArea} where the root tasks should be reparented to.
+ */
+ private TaskDisplayArea getReparentToTaskDisplayArea(Task rootTask) {
+ final TaskDisplayArea defaultTaskDisplayArea =
+ mRootWindowContainer.getDefaultTaskDisplayArea();
+ if (rootTask == null) {
+ return defaultTaskDisplayArea;
+ }
+ UserManagerInternal userManagerInternal = mAtmService.mWindowManager.mUmInternal;
+ if (!userManagerInternal.isVisibleBackgroundFullUser(rootTask.mUserId)) {
+ return defaultTaskDisplayArea;
+ }
+ int toDisplayId = userManagerInternal.getMainDisplayAssignedToUser(rootTask.mUserId);
+ if (toDisplayId == INVALID_DISPLAY) {
+ return defaultTaskDisplayArea;
+ }
+ DisplayContent dc = mRootWindowContainer.getDisplayContent(toDisplayId);
+ return dc != null ? dc.getDefaultTaskDisplayArea() : defaultTaskDisplayArea;
+ }
+
/** Whether this task display area can request orientation. */
boolean canSpecifyOrientation(@ScreenOrientation int orientation) {
// Only allow to specify orientation if this TDA is the last focused one on this logical
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 9d4652957487..7c3f0f22608e 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -35,6 +35,7 @@ import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
+import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -115,7 +116,6 @@ import com.android.server.wm.SurfaceAnimator.Animatable;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.AlwaysTruePredicate;
-import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -457,7 +457,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
source.setFrame(provider.getArbitraryRectangle())
.updateSideHint(getBounds())
.setBoundingRects(provider.getBoundingRects());
- if (Flags.enableCaptionCompatInsetForceConsumption()) {
+ if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()) {
source.setFlags(provider.getFlags());
}
mLocalInsetsSources.put(id, source);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 976be4aa3bd4..32fe303b9e90 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -204,6 +204,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// Set to true when process was launched with a wrapper attached
private volatile boolean mUsingWrapper;
+ /** Whether this process has ever completed ActivityThread#handleBindApplication. */
+ boolean mHasEverAttached;
+
/** Non-null if this process may have a window. */
@Nullable
Session mWindowSession;
@@ -326,6 +329,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
public static final int ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK = 1 << 22;
public static final int ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN = 1 << 23;
public static final int ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM = 1 << 24;
+ public static final int ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE = 1 << 25;
public static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff;
/**
@@ -1293,8 +1297,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
if (hasResumedFreeform
&& com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode()
// Exclude task layer 1 because it is already the top most.
- && minTaskLayer > 1 && minTaskLayer <= 1 + MAX_NUM_PERCEPTIBLE_FREEFORM) {
- stateFlags |= ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM;
+ && minTaskLayer > 1) {
+ if (minTaskLayer <= 1 + MAX_NUM_PERCEPTIBLE_FREEFORM) {
+ stateFlags |= ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM;
+ } else {
+ stateFlags |= ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE;
+ }
}
stateFlags |= minTaskLayer & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
if (visible) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 8c4448e7915f..155e73c53819 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1808,9 +1808,30 @@ void NativeInputManager::loadAdditionalMouseResources(
ATRACE_CALL();
JNIEnv* env = jniEnv();
- for (int32_t iconId = static_cast<int32_t>(PointerIconStyle::TYPE_CONTEXT_MENU);
- iconId <= static_cast<int32_t>(PointerIconStyle::TYPE_HANDWRITING); ++iconId) {
- const PointerIconStyle pointerIconStyle = static_cast<PointerIconStyle>(iconId);
+ constexpr static std::array ADDITIONAL_STYLES{PointerIconStyle::TYPE_CONTEXT_MENU,
+ PointerIconStyle::TYPE_HAND,
+ PointerIconStyle::TYPE_HELP,
+ PointerIconStyle::TYPE_WAIT,
+ PointerIconStyle::TYPE_CELL,
+ PointerIconStyle::TYPE_CROSSHAIR,
+ PointerIconStyle::TYPE_TEXT,
+ PointerIconStyle::TYPE_VERTICAL_TEXT,
+ PointerIconStyle::TYPE_ALIAS,
+ PointerIconStyle::TYPE_COPY,
+ PointerIconStyle::TYPE_NO_DROP,
+ PointerIconStyle::TYPE_ALL_SCROLL,
+ PointerIconStyle::TYPE_HORIZONTAL_DOUBLE_ARROW,
+ PointerIconStyle::TYPE_VERTICAL_DOUBLE_ARROW,
+ PointerIconStyle::TYPE_TOP_RIGHT_DOUBLE_ARROW,
+ PointerIconStyle::TYPE_TOP_LEFT_DOUBLE_ARROW,
+ PointerIconStyle::TYPE_ZOOM_IN,
+ PointerIconStyle::TYPE_ZOOM_OUT,
+ PointerIconStyle::TYPE_GRAB,
+ PointerIconStyle::TYPE_GRABBING,
+ PointerIconStyle::TYPE_HANDWRITING,
+ PointerIconStyle::TYPE_SPOT_HOVER};
+
+ for (const auto pointerIconStyle : ADDITIONAL_STYLES) {
PointerIcon pointerIcon = loadPointerIcon(env, displayId, pointerIconStyle);
(*outResources)[pointerIconStyle] = toSpriteIcon(pointerIcon);
if (!pointerIcon.bitmapFrames.empty()) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ab459df1cdf6..9fdf088c3d1d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -107,7 +107,7 @@ import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.ProtoLog;
-import com.android.internal.protolog.ProtoLogConfigurationService;
+import com.android.internal.protolog.ProtoLogConfigurationServiceImpl;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.EmergencyAffordanceManager;
@@ -1097,7 +1097,7 @@ public final class SystemServer implements Dumpable {
if (android.tracing.Flags.clientSideProtoLogging()) {
t.traceBegin("StartProtoLogConfigurationService");
ServiceManager.addService(
- Context.PROTOLOG_CONFIGURATION_SERVICE, new ProtoLogConfigurationService());
+ Context.PROTOLOG_CONFIGURATION_SERVICE, new ProtoLogConfigurationServiceImpl());
t.traceEnd();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
index 8b653378664e..32135f1cb7fa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
@@ -22,9 +22,10 @@ import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PRO
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
@@ -36,6 +37,7 @@ import android.content.pm.PackageManagerInternal;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Process;
+import android.os.RemoteException;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -137,9 +139,17 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
mSensitiveContentProtectionManagerService.mNotificationListener =
spy(mSensitiveContentProtectionManagerService.mNotificationListener);
- doCallRealMethod()
- .when(mSensitiveContentProtectionManagerService.mNotificationListener)
- .onListenerConnected();
+
+ // Unexpected NLS interactions when registered cause test flakes. For purposes of this test,
+ // the test will control any NLS calls.
+ try {
+ doNothing().when(mSensitiveContentProtectionManagerService.mNotificationListener)
+ .registerAsSystemService(any(), any(), anyInt());
+ doNothing().when(mSensitiveContentProtectionManagerService.mNotificationListener)
+ .unregisterAsSystemService();
+ } catch (RemoteException e) {
+ // Intra-process call, should never happen.
+ }
// Setup RankingMap and two possilbe rankings
when(mSensitiveRanking.hasSensitiveContent()).thenReturn(true);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 5ec53023dc67..f6ad07d03673 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -62,6 +62,7 @@ import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ;
import static com.android.server.am.ProcessList.PREVIOUS_APP_ADJ;
import static com.android.server.am.ProcessList.SCHED_GROUP_BACKGROUND;
import static com.android.server.am.ProcessList.SCHED_GROUP_DEFAULT;
+import static com.android.server.am.ProcessList.SCHED_GROUP_FOREGROUND_WINDOW;
import static com.android.server.am.ProcessList.SCHED_GROUP_RESTRICTED;
import static com.android.server.am.ProcessList.SCHED_GROUP_TOP_APP;
import static com.android.server.am.ProcessList.SCHED_GROUP_TOP_APP_BOUND;
@@ -534,6 +535,14 @@ public class MockingOomAdjusterTests {
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
assertEquals("perceptible-freeform-activity", app.mState.getAdjType());
+
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ | WindowProcessController.ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE)
+ .when(wpc).getActivityStateFlags();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ,
+ SCHED_GROUP_FOREGROUND_WINDOW);
+ assertEquals("vis-multi-window-activity", app.mState.getAdjType());
}
@SuppressWarnings("GuardedBy")
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 15ae4634b573..0b762df86df9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -290,7 +290,7 @@ public class WallpaperManagerServiceTests {
final WallpaperData fallbackData = mService.mFallbackWallpaper;
assertEquals("Fallback wallpaper component should be ImageWallpaper.",
- sImageWallpaperComponentName, fallbackData.wallpaperComponent);
+ sImageWallpaperComponentName, fallbackData.getComponent());
verifyLastWallpaperData(USER_SYSTEM, sDefaultWallpaperComponent);
verifyDisplayData();
@@ -580,7 +580,7 @@ public class WallpaperManagerServiceTests {
final WallpaperData lastData = mService.mLastWallpaper;
assertNotNull("Last wallpaper must not be null", lastData);
assertEquals("Last wallpaper component must be equals.", expectedComponent,
- lastData.wallpaperComponent);
+ lastData.getComponent());
assertEquals("The user id in last wallpaper should be the last switched user",
lastUserId, lastData.userId);
assertNotNull("Must exist user data connection on last wallpaper data",
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 44aa868716eb..a55aa2364aa5 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -30,7 +30,6 @@ import android.graphics.fonts.SystemFonts;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.system.Os;
@@ -41,8 +40,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.text.flags.Flags;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -1106,7 +1103,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void signatureMissingCase_fontFamilyInstalled_fontFamilyInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1126,7 +1122,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void signatureMissingCase_fontFamilyInstalled_fontInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1);
@@ -1146,7 +1141,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void signatureMissingCase_fontFileInstalled_fontFamilyInstallLater() {
// Install font file, foo.ttf and bar.ttf
installTestFontFile(2 /* numFonts */, 1 /* version */);
@@ -1166,7 +1160,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void signatureMissingCase_fontFileInstalled_fontFileInstallLater() {
// Install font file, foo.ttf and bar.ttf
installTestFontFile(2 /* numFonts */, 1 /* version */);
@@ -1186,7 +1179,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void signatureAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1206,7 +1198,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void signatureAllMissingCase_fontFamilyInstalled_fontInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1226,7 +1217,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void signatureAllMissingCase_fontFileInstalled_fontFamilyInstallLater() {
// Install font file, foo.ttf
installTestFontFile(1 /* numFonts */, 1 /* version */);
@@ -1246,7 +1236,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void signatureAllMissingCase_fontFileInstalled_fontFileInstallLater() {
// Install font file, foo.ttf
installTestFontFile(1 /* numFonts */, 1 /* version */);
@@ -1266,7 +1255,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontMissingCase_fontFamilyInstalled_fontFamilyInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1286,7 +1274,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontMissingCase_fontFamilyInstalled_fontInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1);
@@ -1306,7 +1293,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontMissingCase_fontFileInstalled_fontFamilyInstallLater() {
// Install font file, foo.ttf and bar.ttf
installTestFontFile(2 /* numFonts */, 1 /* version */);
@@ -1326,7 +1312,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontMissingCase_fontFileInstalled_fontFileInstallLater() {
// Install font file, foo.ttf and bar.ttf
installTestFontFile(2 /* numFonts */, 1 /* version */);
@@ -1346,7 +1331,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1366,7 +1350,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontAllMissingCase_fontFamilyInstalled_fontInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1386,7 +1369,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontAllMissingCase_fontFileInstalled_fontFamilyInstallLater() {
// Install font file, foo.ttf
installTestFontFile(1 /* numFonts */, 1 /* version */);
@@ -1406,7 +1388,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontAllMissingCase_fontFileInstalled_fontFileInstallLater() {
// Install font file, foo.ttf
installTestFontFile(1 /* numFonts */, 1 /* version */);
@@ -1426,7 +1407,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontDirAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1446,7 +1426,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontDirAllMissingCase_fontFamilyInstalled_fontInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1466,7 +1445,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontDirAllMissingCase_fontFileInstalled_fontFamilyInstallLater() {
// Install font file, foo.ttf
installTestFontFile(1 /* numFonts */, 1 /* version */);
@@ -1486,7 +1464,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void fontDirAllMissingCase_fontFileInstalled_fontFileInstallLater() {
// Install font file, foo.ttf
installTestFontFile(1 /* numFonts */, 1 /* version */);
@@ -1506,7 +1483,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void dirContentAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1527,7 +1503,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void dirContentAllMissingCase_fontFamilyInstalled_fontInstallLater() {
// Install font families, foo.ttf, bar.ttf.
installTestFontFamilies(1 /* version */);
@@ -1548,7 +1523,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void dirContentAllMissingCase_fontFileInstalled_fontFamilyInstallLater() {
// Install font file, foo.ttf
installTestFontFile(1 /* numFonts */, 1 /* version */);
@@ -1569,7 +1543,6 @@ public final class UpdatableFontDirTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FIX_FONT_UPDATE_FAILURE)
public void dirContentAllMissingCase_fontFileInstalled_fontFileInstallLater() {
// Install font file, foo.ttf
installTestFontFile(1 /* numFonts */, 1 /* version */);
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 689b241f0faa..abc9ce3fdc36 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -50,11 +50,12 @@ import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
+import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions.LaunchCookie;
+import android.app.AppOpsManager;
import android.app.KeyguardManager;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
@@ -72,6 +73,7 @@ import android.os.test.TestLooper;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.testing.TestableContext;
import android.view.ContentRecordingSession;
import android.view.ContentRecordingSession.RecordContent;
@@ -99,13 +101,14 @@ import java.util.concurrent.TimeUnit;
/**
* Tests for the {@link MediaProjectionManagerService} class.
- *
+ * <p>
* Build/Install/Run:
* atest FrameworksServicesTests:MediaProjectionManagerServiceTest
*/
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
+@SuppressLint({"UseCheckPermission", "VisibleForTests", "MissingPermission"})
public class MediaProjectionManagerServiceTest {
private static final int UID = 10;
private static final String PACKAGE_NAME = "test.package";
@@ -151,7 +154,10 @@ public class MediaProjectionManagerServiceTest {
}
};
- private Context mContext;
+ @Rule
+ public final TestableContext mContext = spy(
+ new TestableContext(InstrumentationRegistry.getInstrumentation().getContext()));
+
private MediaProjectionManagerService mService;
private OffsettableClock mClock;
private ContentRecordingSession mWaitingDisplaySession =
@@ -169,6 +175,8 @@ public class MediaProjectionManagerServiceTest {
@Mock
private KeyguardManager mKeyguardManager;
@Mock
+ AppOpsManager mAppOpsManager;
+ @Mock
private IMediaProjectionWatcherCallback mWatcherCallback;
@Mock
private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
@@ -185,10 +193,9 @@ public class MediaProjectionManagerServiceTest {
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
- mContext = spy(new ContextWrapper(
- InstrumentationRegistry.getInstrumentation().getTargetContext()));
- doReturn(mPackageManager).when(mContext).getPackageManager();
- doReturn(mKeyguardManager).when(mContext).getSystemService(eq(Context.KEYGUARD_SERVICE));
+ mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager);
+ mContext.addMockSystemService(KeyguardManager.class, mKeyguardManager);
+ mContext.setMockPackageManager(mPackageManager);
mClock = new OffsettableClock.Stopped();
mWaitingDisplaySession.setWaitingForConsent(true);
@@ -291,6 +298,27 @@ public class MediaProjectionManagerServiceTest {
assertThat(mService.getActiveProjectionInfo()).isNotNull();
}
+ @SuppressLint("MissingPermission")
+ @EnableFlags(android.companion.virtualdevice.flags
+ .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+ @Test
+ public void testCreateProjection_keyguardLocked_AppOpMediaProjection()
+ throws NameNotFoundException {
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ doReturn(true).when(mAppOpsManager).isOperationActive(eq(AppOpsManager.OP_PROJECT_MEDIA),
+ eq(projection.uid), eq(projection.packageName));
+ doReturn(true).when(mKeyguardManager).isKeyguardLocked();
+
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, projection.packageName);
+
+ projection.start(mIMediaProjectionCallback);
+ projection.notifyVirtualDisplayCreated(10);
+
+ // The projection was started because it was allowed to capture the keyguard.
+ assertThat(mService.getActiveProjectionInfo()).isNotNull();
+ }
+
@Test
public void testCreateProjection_attemptReuse_noPriorProjectionGrant()
throws NameNotFoundException {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
index 4d2396c78d16..65b4ac116b34 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
@@ -104,6 +104,12 @@ public class VibratorHelperTest extends UiServiceTestCase {
}
@Test
+ public void createVibrationEffectFromSoundUri_opaqueUri() {
+ Uri uri = Uri.parse("a:b#c");
+ assertNull(mVibratorHelper.createVibrationEffectFromSoundUri(uri));
+ }
+
+ @Test
public void createVibrationEffectFromSoundUri_uriWithoutRequiredQueryParameter() {
Uri uri = Settings.System.DEFAULT_NOTIFICATION_URI;
assertNull(mVibratorHelper.createVibrationEffectFromSoundUri(uri));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 957b5e04fef6..ae0c6e551246 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -331,6 +331,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
final WindowProcessController proc = mSystemServicesTestRule.addProcess(
activity.packageName, activity.processName,
6789 /* pid */, activity.info.applicationInfo.uid);
+ assertFalse(proc.mHasEverAttached);
try {
mRootWindowContainer.attachApplication(proc);
verify(mSupervisor).realStartActivityLocked(eq(topActivity), eq(proc),
@@ -338,6 +339,15 @@ public class RootWindowContainerTests extends WindowTestsBase {
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
+
+ // Verify that onProcessRemoved won't clear the launching activities if an attached process
+ // is died. Because in real case, it should be handled from WindowProcessController's
+ // and ActivityRecord's handleAppDied to decide whether to remove the activities.
+ assertTrue(proc.mHasEverAttached);
+ assertTrue(mAtm.mStartingProcessActivities.isEmpty());
+ mAtm.mStartingProcessActivities.add(activity);
+ mAtm.mInternal.onProcessRemoved(proc.mName, proc.mUid);
+ assertFalse(mAtm.mStartingProcessActivities.isEmpty());
}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 41223db750c0..2ef057350033 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5038,7 +5038,7 @@ public class CarrierConfigManager {
* {@code true} - Enable NI SUPL message injection.
*/
@FlaggedApi(android.location.flags.Flags
- .FLAG_ENABLE_NI_SUPL_MESSAGE_INJECTION_BY_CARRIER_CONFIG)
+ .FLAG_ENABLE_NI_SUPL_MESSAGE_INJECTION_BY_CARRIER_CONFIG_BUGFIX)
public static final String KEY_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL =
KEY_PREFIX + "enable_ni_supl_message_injection_bool";
@@ -5059,7 +5059,7 @@ public class CarrierConfigManager {
defaults.putInt(KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
SUPL_EMERGENCY_MODE_TYPE_CP_ONLY);
defaults.putStringArray(KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY, null);
- if (android.location.flags.Flags.enableNiSuplMessageInjectionByCarrierConfig()) {
+ if (android.location.flags.Flags.enableNiSuplMessageInjectionByCarrierConfigBugfix()) {
defaults.putBoolean(KEY_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL, false);
}
return defaults;
diff --git a/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
index 66a20ae28f39..50e3a0e4a79d 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
@@ -34,4 +34,12 @@ oneway interface ISatelliteModemStateCallback {
* @param isEmergency True means satellite enabled for emergency mode, false otherwise.
*/
void onEmergencyModeChanged(in boolean isEmergency);
+
+ /**
+ * Indicates that the satellite registration failed with following failure code
+ *
+ * @param causeCode the primary failure cause code of the procedure.
+ * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+ */
+ void onRegistrationFailure(in int causeCode);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 90dae3be058c..4eefaaca71f4 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -19,6 +19,7 @@ package android.telephony.satellite;
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
+import android.annotation.Hide;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1579,6 +1580,13 @@ public final class SatelliteManager {
executor.execute(() -> Binder.withCleanCallingIdentity(() ->
callback.onEmergencyModeChanged(isEmergency)));
}
+
+ @Hide
+ @Override
+ public void onRegistrationFailure(int causeCode) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onRegistrationFailure(causeCode)));
+ }
};
sSatelliteModemStateCallbackMap.put(callback, internalCallback);
return telephony.registerForSatelliteModemStateChanged(internalCallback);
diff --git a/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
index 423a7859dd6b..13af4694389b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
@@ -45,4 +45,13 @@ public interface SatelliteModemStateCallback {
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
default void onEmergencyModeChanged(boolean isEmergency) {};
+
+ /**
+ * Indicates that the satellite registration failed with following failure code
+ *
+ * @param causeCode the primary failure cause code of the procedure.
+ * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+ * @hide
+ */
+ default void onRegistrationFailure(int causeCode) {};
}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index c2e71f8269da..2a82d5f9bd7c 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -21,6 +21,8 @@ import android.Manifest
import android.content.Context
import android.content.ContextWrapper
import android.content.PermissionChecker
+import android.content.pm.PackageManager
+import android.content.pm.PackageManagerInternal
import android.hardware.display.DisplayManager
import android.hardware.display.DisplayViewport
import android.hardware.display.VirtualDisplay
@@ -29,10 +31,13 @@ import android.hardware.input.InputManagerGlobal
import android.os.InputEventInjectionSync
import android.os.SystemClock
import android.os.test.TestLooper
+import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import android.view.View.OnKeyListener
import android.view.InputDevice
+import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
@@ -94,6 +99,10 @@ class InputManagerServiceTests {
ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java)
.mockStatic(PermissionChecker::class.java).build()!!
+ @JvmField
+ @Rule
+ val setFlagsRule = SetFlagsRule()
+
@get:Rule
val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
@@ -107,8 +116,14 @@ class InputManagerServiceTests {
private lateinit var windowManagerInternal: WindowManagerInternal
@Mock
+ private lateinit var packageManagerInternal: PackageManagerInternal
+
+ @Mock
private lateinit var uEventManager: UEventManager
+ @Mock
+ private lateinit var kbdController: InputManagerService.KeyboardBacklightControllerInterface
+
private lateinit var service: InputManagerService
private lateinit var localService: InputManagerInternal
private lateinit var context: Context
@@ -135,15 +150,29 @@ class InputManagerServiceTests {
override fun registerLocalService(service: InputManagerInternal?) {
localService = service!!
}
+
+ override fun getKeyboardBacklightController(
+ nativeService: NativeInputManagerService?,
+ dataStore: PersistentDataStore?
+ ): InputManagerService.KeyboardBacklightControllerInterface {
+ return kbdController
+ }
})
inputManagerGlobalSession = InputManagerGlobal.createTestSession(service)
val inputManager = InputManager(context)
whenever(context.getSystemService(InputManager::class.java)).thenReturn(inputManager)
whenever(context.getSystemService(Context.INPUT_SERVICE)).thenReturn(inputManager)
+ whenever(context.checkCallingOrSelfPermission(Manifest.permission.MANAGE_KEY_GESTURES))
+ .thenReturn(
+ PackageManager.PERMISSION_GRANTED
+ )
ExtendedMockito.doReturn(windowManagerInternal).`when` {
LocalServices.getService(eq(WindowManagerInternal::class.java))
}
+ ExtendedMockito.doReturn(packageManagerInternal).`when` {
+ LocalServices.getService(eq(PackageManagerInternal::class.java))
+ }
assertTrue("Local service must be registered", this::localService.isInitialized)
service.setWindowManagerCallbacks(wmCallbacks)
@@ -442,6 +471,37 @@ class InputManagerServiceTests {
verify(mockOnKeyListener, never()).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, upEvent)
}
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun handleKeyGestures_keyboardBacklight() {
+ service.systemRunning()
+
+ val backlightDownEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN)
+ service.interceptKeyBeforeDispatching(null, backlightDownEvent, /* policyFlags = */0)
+ verify(kbdController).decrementKeyboardBacklight(anyInt())
+
+ val backlightUpEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP)
+ service.interceptKeyBeforeDispatching(null, backlightUpEvent, /* policyFlags = */0)
+ verify(kbdController).incrementKeyboardBacklight(anyInt())
+ }
+
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun handleKeyGestures_toggleCapsLock() {
+ service.systemRunning()
+
+ val metaDownEvent = createKeyEvent(KeyEvent.KEYCODE_META_LEFT)
+ service.interceptKeyBeforeDispatching(null, metaDownEvent, /* policyFlags = */0)
+ val altDownEvent =
+ createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_DOWN)
+ service.interceptKeyBeforeDispatching(null, altDownEvent, /* policyFlags = */0)
+ val altUpEvent =
+ createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_UP)
+ service.interceptKeyBeforeDispatching(null, altUpEvent, /* policyFlags = */0)
+
+ verify(native).toggleCapsLock(anyInt())
+ }
+
fun overrideSendActionKeyEventsToFocusedWindow(
hasPermission: Boolean,
hasPrivateFlag: Boolean
@@ -476,6 +536,25 @@ class InputManagerServiceTests {
)
whenever(windowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info)
}
+
+ private fun createKeyEvent(
+ keycode: Int,
+ modifierState: Int = 0,
+ action: Int = KeyEvent.ACTION_DOWN
+ ): KeyEvent {
+ return KeyEvent(
+ /* downTime = */0,
+ /* eventTime = */0,
+ action,
+ keycode,
+ /* repeat = */0,
+ modifierState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD,
+ /* scancode = */0,
+ /* flags = */0,
+ InputDevice.SOURCE_KEYBOARD
+ )
+ }
}
private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
diff --git a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
index 681b7f28451b..b3a998ebca0d 100644
--- a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
+++ b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
@@ -16,6 +16,7 @@
package com.android.server.input.debug;
+import static android.view.InputDevice.SOURCE_MOUSE;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static org.junit.Assert.assertEquals;
@@ -29,7 +30,9 @@ import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
+import android.hardware.input.InputManager;
import android.testing.TestableContext;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -60,13 +63,15 @@ import org.mockito.MockitoAnnotations;
*/
@RunWith(AndroidJUnit4.class)
public class TouchpadDebugViewTest {
- private static final int TOUCHPAD_DEVICE_ID = 6;
+ private static final int TOUCHPAD_DEVICE_ID = 60;
private TouchpadDebugView mTouchpadDebugView;
private WindowManager.LayoutParams mWindowLayoutParams;
@Mock
WindowManager mWindowManager;
+ @Mock
+ InputManager mInputManager;
Rect mWindowBounds;
WindowMetrics mWindowMetrics;
@@ -79,12 +84,21 @@ public class TouchpadDebugViewTest {
mTestableContext = new TestableContext(context);
mTestableContext.addMockSystemService(WindowManager.class, mWindowManager);
+ mTestableContext.addMockSystemService(InputManager.class, mInputManager);
mWindowBounds = new Rect(0, 0, 2560, 1600);
mWindowMetrics = new WindowMetrics(mWindowBounds, new WindowInsets(mWindowBounds), 1.0f);
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
+ InputDevice inputDevice = new InputDevice.Builder()
+ .setId(TOUCHPAD_DEVICE_ID)
+ .setSources(InputDevice.SOURCE_TOUCHPAD | SOURCE_MOUSE)
+ .setName("Test Device " + TOUCHPAD_DEVICE_ID)
+ .build();
+
+ when(mInputManager.getInputDevice(TOUCHPAD_DEVICE_ID)).thenReturn(inputDevice);
+
mTouchpadDebugView = new TouchpadDebugView(mTestableContext, TOUCHPAD_DEVICE_ID,
new TouchpadHardwareProperties.Builder(0f, 0f, 500f,
500f, 45f, 47f, -4f, 5f, (short) 10, true,
@@ -341,4 +355,43 @@ public class TouchpadDebugViewTest {
mTouchpadDebugView.updateGestureInfo(gestureType, TOUCHPAD_DEVICE_ID);
assertEquals(child.getText().toString(), TouchpadDebugView.getGestureText(gestureType));
}
-}
+
+ @Test
+ public void testTwoFingerDrag() {
+ float offsetX = ViewConfiguration.get(mTestableContext).getScaledTouchSlop() + 10;
+ float offsetY = ViewConfiguration.get(mTestableContext).getScaledTouchSlop() + 10;
+
+ // Simulate ACTION_DOWN event (gesture starts).
+ MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_MOUSE)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(40f)
+ .y(40f)
+ )
+ .classification(MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE)
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+ // Simulate ACTION_MOVE event (dragging with two fingers, processed as one pointer).
+ MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_MOUSE)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(40f + offsetX)
+ .y(40f + offsetY)
+ )
+ .classification(MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE)
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+ // Simulate ACTION_UP event (gesture ends).
+ MotionEvent actionUp = new MotionEventBuilder(MotionEvent.ACTION_UP, SOURCE_MOUSE)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(40f + offsetX)
+ .y(40f + offsetY)
+ )
+ .classification(MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE)
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionUp);
+
+ // Verify that no updateViewLayout is called (as expected for a two-finger drag gesture).
+ verify(mWindowManager, times(0)).updateViewLayout(any(), any());
+ }
+} \ No newline at end of file
diff --git a/tests/Tracing/Android.bp b/tests/Tracing/Android.bp
index 5a7f12f56655..90998e67ae31 100644
--- a/tests/Tracing/Android.bp
+++ b/tests/Tracing/Android.bp
@@ -15,7 +15,7 @@ android_test {
},
// Include some source files directly to be able to access package members
srcs: ["src/**/*.java"],
- libs: ["android.test.runner"],
+ libs: ["android.test.runner.stubs.system"],
static_libs: [
"junit",
"androidx.test.rules",
diff --git a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index e841d9ea0880..c882b4e569a1 100644
--- a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -42,7 +42,7 @@ import android.util.proto.ProtoInputStream;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.internal.protolog.ProtoLogConfigurationService.ViewerConfigFileTracer;
+import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.ViewerConfigFileTracer;
import com.android.internal.protolog.common.IProtoLogGroup;
import com.android.internal.protolog.common.LogDataType;
import com.android.internal.protolog.common.LogLevel;
@@ -166,7 +166,8 @@ public class PerfettoProtoLogImplTest {
return new ProtoInputStream(sViewerConfigBuilder.build().toByteArray());
});
};
- sProtoLogConfigurationService = new ProtoLogConfigurationService(dataSourceBuilder, tracer);
+ sProtoLogConfigurationService =
+ new ProtoLogConfigurationServiceImpl(dataSourceBuilder, tracer);
if (android.tracing.Flags.clientSideProtoLogging()) {
sProtoLog = new PerfettoProtoLogImpl(
diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java
index e1bdd777dc5f..a3d03a8278ed 100644
--- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java
+++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java
@@ -150,11 +150,11 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void canRegisterClientWithGroupsOnly() throws RemoteException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, true));
service.registerClient(mMockClient, args);
@@ -165,11 +165,11 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void willDumpViewerConfigOnlyOnceOnTraceStop()
throws RemoteException, InvalidProtocolBufferException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, true))
.setViewerConfigFile(mViewerConfigFile.getAbsolutePath());
service.registerClient(mMockClient, args);
@@ -200,13 +200,13 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void willDumpViewerConfigOnLastClientDisconnected()
throws RemoteException, FileNotFoundException {
- final ProtoLogConfigurationService.ViewerConfigFileTracer tracer =
- Mockito.mock(ProtoLogConfigurationService.ViewerConfigFileTracer.class);
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService(tracer);
+ final ProtoLogConfigurationServiceImpl.ViewerConfigFileTracer tracer =
+ Mockito.mock(ProtoLogConfigurationServiceImpl.ViewerConfigFileTracer.class);
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl(tracer);
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, true))
.setViewerConfigFile(mViewerConfigFile.getAbsolutePath());
service.registerClient(mMockClient, args);
@@ -225,10 +225,10 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void sendEnableLoggingToLogcatToClient() throws RemoteException {
- final var service = new ProtoLogConfigurationService();
+ final var service = new ProtoLogConfigurationServiceImpl();
- final var args = new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final var args = new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, false));
service.registerClient(mMockClient, args);
@@ -242,11 +242,11 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void sendDisableLoggingToLogcatToClient() throws RemoteException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, true));
service.registerClient(mMockClient, args);
@@ -260,11 +260,11 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void doNotSendLoggingToLogcatToClientWithoutRegisteredGroup() throws RemoteException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, false));
service.registerClient(mMockClient, args);
@@ -277,15 +277,15 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void handlesToggleToLogcatBeforeClientIsRegistered() throws RemoteException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
Truth.assertThat(service.getGroups()).asList().doesNotContain(TEST_GROUP);
service.enableProtoLogToLogcat(TEST_GROUP);
Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, false));
service.registerClient(mMockClient, args);
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
index 290e7be9f6c4..94674348df08 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
@@ -22,6 +22,7 @@ import com.android.tools.lint.detector.api.CURRENT_API
import com.google.android.lint.aidl.EnforcePermissionDetector
import com.google.android.lint.aidl.PermissionAnnotationDetector
import com.google.android.lint.aidl.SimpleManualPermissionEnforcementDetector
+import com.google.android.lint.aidl.SimpleRequiresNoPermissionDetector
import com.google.auto.service.AutoService
@AutoService(IssueRegistry::class)
@@ -34,6 +35,7 @@ class AndroidGlobalIssueRegistry : IssueRegistry() {
EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION,
PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
+ SimpleRequiresNoPermissionDetector.ISSUE_SIMPLE_REQUIRES_NO_PERMISSION,
)
override val api: Int
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleRequiresNoPermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleRequiresNoPermissionDetector.kt
new file mode 100644
index 000000000000..1a13c0280ec6
--- /dev/null
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleRequiresNoPermissionDetector.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.aidl
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UastCallKind
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UMethod
+import org.jetbrains.uast.visitor.AbstractUastVisitor
+
+/**
+ * Ensures all AIDL implementations hosted by system_server which don't call other methods are
+ * annotated with @RequiresNoPermission. AIDL Interfaces part of `exemptAidlInterfaces` are skipped
+ * during this search to ensure the detector targets only new AIDL Interfaces.
+ */
+class SimpleRequiresNoPermissionDetector : AidlImplementationDetector() {
+ override fun visitAidlMethod(
+ context: JavaContext,
+ node: UMethod,
+ interfaceName: String,
+ body: UBlockExpression
+ ) {
+ if (!isSystemServicePath(context)) return
+ if (context.evaluator.isAbstract(node)) return
+
+ val fullyQualifiedInterfaceName =
+ getContainingAidlInterfaceQualified(context, node) ?: return
+ if (exemptAidlInterfaces.contains(fullyQualifiedInterfaceName)) return
+
+ if (node.hasAnnotation(ANNOTATION_REQUIRES_NO_PERMISSION)) return
+
+ if (!isCallingMethod(node)) {
+ context.report(
+ ISSUE_SIMPLE_REQUIRES_NO_PERMISSION,
+ node,
+ context.getLocation(node),
+ """
+ Method ${node.name} doesn't perform any permission checks, meaning it should \
+ be annotated with @RequiresNoPermission.
+ """.trimMargin()
+ )
+ }
+ }
+
+ private fun isCallingMethod(node: UMethod): Boolean {
+ val uCallExpressionVisitor = UCallExpressionVisitor()
+ node.accept(uCallExpressionVisitor)
+
+ return uCallExpressionVisitor.isCallingMethod
+ }
+
+ /**
+ * Visits the body of a `UMethod` and determines if it encounters a `UCallExpression` which is
+ * a `UastCallKind.METHOD_CALL`. `isCallingMethod` will hold the result of the search procedure.
+ */
+ private class UCallExpressionVisitor : AbstractUastVisitor() {
+ var isCallingMethod = false
+
+ override fun visitElement(node: UElement): Boolean {
+ // Stop the search early when a method call has been found.
+ return isCallingMethod
+ }
+
+ override fun visitCallExpression(node: UCallExpression): Boolean {
+ if (node.kind != UastCallKind.METHOD_CALL) return false
+
+ isCallingMethod = true
+ return true
+ }
+ }
+
+ companion object {
+
+ private val EXPLANATION = """
+ Method implementations of AIDL Interfaces hosted by the `system_server` which do not
+ call any other methods should be annotated with @RequiresNoPermission. That is because
+ not calling any other methods implies that the method does not perform any permission
+ checking.
+
+ Please migrate to an @RequiresNoPermission annotation.
+ """.trimIndent()
+
+ @JvmField
+ val ISSUE_SIMPLE_REQUIRES_NO_PERMISSION = Issue.create(
+ id = "SimpleRequiresNoPermission",
+ briefDescription = "System Service APIs not calling other methods should use @RNP",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ SimpleRequiresNoPermissionDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ ),
+ )
+ }
+}
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/PermissionAnnotationDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/PermissionAnnotationDetectorTest.kt
index 92d0829911bf..824be9309dbc 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/PermissionAnnotationDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/PermissionAnnotationDetectorTest.kt
@@ -17,7 +17,6 @@
package com.google.android.lint.aidl
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
-import com.android.tools.lint.checks.infrastructure.TestFile
import com.android.tools.lint.checks.infrastructure.TestLintTask
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
@@ -64,7 +63,7 @@ class PermissionAnnotationDetectorTest : LintDetectorTest() {
"""
package com.android.server;
public class Bar extends IBar.Stub {
- public void testMethod() { }
+ public void testMethod(int parameter1, int parameter2) { }
}
"""
)
@@ -75,8 +74,8 @@ class PermissionAnnotationDetectorTest : LintDetectorTest() {
.expect(
"""
src/frameworks/base/services/java/com/android/server/Bar.java:3: Error: The method testMethod is not permission-annotated. [MissingPermissionAnnotation]
- public void testMethod() { }
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ public void testMethod(int parameter1, int parameter2) { }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
)
@@ -90,7 +89,7 @@ class PermissionAnnotationDetectorTest : LintDetectorTest() {
"""
package com.android.server;
public class Bar extends IBar.Stub {
- public void testMethod() { }
+ public void testMethod(int parameter1, int parameter2) { }
}
"""
)
@@ -132,7 +131,7 @@ class PermissionAnnotationDetectorTest : LintDetectorTest() {
"""
package com.android.server;
public abstract class Bar extends IBar.Stub {
- public abstract void testMethod();
+ public abstract void testMethod(int parameter1, int parameter2);
}
"""
)
@@ -177,50 +176,6 @@ class PermissionAnnotationDetectorTest : LintDetectorTest() {
.expectClean()
}
- /* Stubs */
-
- // A service with permission annotation on the method.
- private val interfaceIFoo: TestFile = java(
- """
- public interface IFoo extends android.os.IInterface {
- public static abstract class Stub extends android.os.Binder implements IFoo {
- }
- @Override
- @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
- public void testMethod();
- @Override
- @android.annotation.RequiresNoPermission
- public void testMethodNoPermission();
- @Override
- @android.annotation.PermissionManuallyEnforced
- public void testMethodManual();
- }
- """
- ).indented()
-
- // A service with no permission annotation.
- private val interfaceIBar: TestFile = java(
- """
- public interface IBar extends android.os.IInterface {
- public static abstract class Stub extends android.os.Binder implements IBar {
- }
- public void testMethod();
- }
- """
- ).indented()
-
- // A service whose AIDL Interface is exempted.
- private val interfaceIExempted: TestFile = java(
- """
- package android.accessibilityservice;
- public interface IBrailleDisplayConnection extends android.os.IInterface {
- public static abstract class Stub extends android.os.Binder implements IBrailleDisplayConnection {
- }
- public void testMethod();
- }
- """
- ).indented()
-
private val stubs = arrayOf(interfaceIFoo, interfaceIBar, interfaceIExempted)
private fun createVisitedPath(filename: String) =
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleRequiresNoPermissionDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleRequiresNoPermissionDetectorTest.kt
new file mode 100644
index 000000000000..a33b48c7eaa0
--- /dev/null
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleRequiresNoPermissionDetectorTest.kt
@@ -0,0 +1,244 @@
+/*
+ * 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.google.android.lint.aidl
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+class SimpleRequiresNoPermissionDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = SimpleRequiresNoPermissionDetector()
+ override fun getIssues(): List<Issue> = listOf(
+ SimpleRequiresNoPermissionDetector
+ .ISSUE_SIMPLE_REQUIRES_NO_PERMISSION
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk()
+
+ fun testRequiresNoPermissionUsedCorrectly_shouldNotWarn() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("Foo.java"),
+ """
+ package com.android.server;
+ public class Foo extends IFoo.Stub {
+ private int memberInt;
+
+ @Override
+ @android.annotation.RequiresNoPermission
+ public void testMethodNoPermission(int parameter1, int parameter2) {
+ if (parameter1 < parameter2) {
+ memberInt = parameter1;
+ } else {
+ memberInt = parameter2;
+ }
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testMissingRequiresNoPermission_shouldWarn() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("Bar.java"),
+ """
+ package com.android.server;
+ public class Bar extends IBar.Stub {
+ private int memberInt;
+
+ @Override
+ public void testMethod(int parameter1, int parameter2) {
+ if (parameter1 < parameter2) {
+ memberInt = parameter1;
+ } else {
+ memberInt = parameter2;
+ }
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/frameworks/base/services/java/com/android/server/Bar.java:5: Error: Method testMethod doesn't perform any permission checks, meaning it should be annotated with @RequiresNoPermission. [SimpleRequiresNoPermission]
+ @Override
+ ^
+ 1 errors, 0 warnings
+ """
+ )
+ }
+
+ fun testMethodOnlyPerformsConstructorCall_shouldWarn() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("Bar.java"),
+ """
+ package com.android.server;
+ public class Bar extends IBar.Stub {
+ private IntPair memberIntPair;
+
+ @Override
+ public void testMethod(int parameter1, int parameter2) {
+ memberIntPair = new IntPair(parameter1, parameter2);
+ }
+
+ private static class IntPair {
+ public int first;
+ public int second;
+
+ public IntPair(int first, int second) {
+ this.first = first;
+ this.second = second;
+ }
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/frameworks/base/services/java/com/android/server/Bar.java:5: Error: Method testMethod doesn't perform any permission checks, meaning it should be annotated with @RequiresNoPermission. [SimpleRequiresNoPermission]
+ @Override
+ ^
+ 1 errors, 0 warnings
+ """
+ )
+ }
+
+ fun testMissingRequiresNoPermissionInIgnoredDirectory_shouldNotWarn() {
+ lint()
+ .files(
+ java(
+ ignoredPath,
+ """
+ package com.android.server;
+ public class Bar extends IBar.Stub {
+ @Override
+ public void testMethod(int parameter1, int parameter2) {}
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testMissingRequiresNoPermissionAbstractMethod_shouldNotWarn() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("Bar.java"),
+ """
+ package com.android.server;
+ public abstract class Bar extends IBar.Stub {
+ private int memberInt;
+
+ @Override
+ public abstract void testMethodNoPermission(int parameter1, int parameter2);
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ // If this test fails, consider the following steps:
+ // 1. Pick the first entry (interface) from `exemptAidlInterfaces`.
+ // 2. Change `interfaceIExempted` to use that interface.
+ // 3. Change this test's class to extend the interface's Stub.
+ fun testMissingRequiresNoPermissionAidlInterfaceExempted_shouldNotWarn() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("Bar.java"),
+ """
+ package com.android.server;
+ public class Bar extends android.accessibilityservice.IBrailleDisplayConnection.Stub {
+ public void testMethod(int parameter1, int parameter2) {}
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testMethodMakesAnotherMethodCall_shouldNotWarn() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("Bar.java"),
+ """
+ package com.android.server;
+ public class Bar extends IBar.Stub {
+ private int memberInt;
+
+ @Override
+ public void testMethod(int parameter1, int parameter2) {
+ if (!hasPermission()) return;
+
+ if (parameter1 < parameter2) {
+ memberInt = parameter1;
+ } else {
+ memberInt = parameter2;
+ }
+ }
+
+ private bool hasPermission() {
+ // Perform a permission check.
+ return true;
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ private val stubs = arrayOf(interfaceIFoo, interfaceIBar, interfaceIExempted)
+
+ private fun createVisitedPath(filename: String) =
+ "src/frameworks/base/services/java/com/android/server/$filename"
+
+ private val ignoredPath = "src/test/pkg/TestClass.java"
+}
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
index 2ec8fddbb4e9..18a8f186b624 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
@@ -85,4 +85,46 @@ val manifestStub: TestFile = java(
}
}
""".trimIndent()
-) \ No newline at end of file
+)
+
+// A service with permission annotation on the method.
+val interfaceIFoo: TestFile = java(
+ """
+ public interface IFoo extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IFoo {
+ }
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod();
+ @Override
+ @android.annotation.RequiresNoPermission
+ public void testMethodNoPermission(int parameter1, int parameter2);
+ @Override
+ @android.annotation.PermissionManuallyEnforced
+ public void testMethodManual();
+ }
+ """
+).indented()
+
+// A service with no permission annotation.
+val interfaceIBar: TestFile = java(
+ """
+ public interface IBar extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IBar {
+ }
+ public void testMethod(int parameter1, int parameter2);
+ }
+ """
+).indented()
+
+// A service whose AIDL Interface is exempted.
+val interfaceIExempted: TestFile = java(
+ """
+ package android.accessibilityservice;
+ public interface IBrailleDisplayConnection extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IBrailleDisplayConnection {
+ }
+ public void testMethod();
+ }
+ """
+).indented()
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index f68ae2c7e249..fc4a909e761f 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -173,6 +173,10 @@ public class SharedConnectivityManager {
}
}
}
+
+ Executor getExecutor() {
+ return mExecutor;
+ }
}
private ISharedConnectivityService mService;
@@ -188,7 +192,7 @@ public class SharedConnectivityManager {
private final String mServicePackageName;
private final String mIntentAction;
private ServiceConnection mServiceConnection;
- private UserManager mUserManager;
+ private final UserManager mUserManager;
/**
* Creates a new instance of {@link SharedConnectivityManager}.
@@ -316,15 +320,19 @@ public class SharedConnectivityManager {
private void registerCallbackInternal(SharedConnectivityClientCallback callback,
SharedConnectivityCallbackProxy proxy) {
- try {
- mService.registerCallback(proxy);
- synchronized (mProxyDataLock) {
- mProxyMap.put(callback, proxy);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Exception in registerCallback", e);
- callback.onRegisterCallbackFailed(e);
- }
+ proxy.getExecutor().execute(
+ () -> {
+ try {
+ mService.registerCallback(proxy);
+ synchronized (mProxyDataLock) {
+ mProxyMap.put(callback, proxy);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in registerCallback", e);
+ callback.onRegisterCallbackFailed(e);
+ }
+ }
+ );
}
/**