summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp13
-rw-r--r--MEMORY_OWNERS1
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java4
-rw-r--r--api/api.go2
-rw-r--r--core/api/current.txt73
-rw-r--r--core/api/system-current.txt53
-rw-r--r--core/java/android/app/Activity.java6
-rw-r--r--core/java/android/app/ActivityManagerInternal.java6
-rw-r--r--core/java/android/app/AppCompatTaskInfo.java29
-rw-r--r--core/java/android/app/IUserSwitchObserver.aidl12
-rw-r--r--core/java/android/app/Notification.java69
-rw-r--r--core/java/android/app/NotificationManager.java10
-rw-r--r--core/java/android/app/SystemServiceRegistry.java9
-rw-r--r--core/java/android/app/UserSwitchObserver.java6
-rw-r--r--core/java/android/app/contextualsearch/ContextualSearchManager.java39
-rw-r--r--core/java/android/app/contextualsearch/flags.aconfig8
-rw-r--r--core/java/android/app/jank/AppJankStats.java126
-rw-r--r--core/java/android/app/jank/FrameOverrunHistogram.java107
-rw-r--r--core/java/android/app/jank/JankDataProcessor.java9
-rw-r--r--core/java/android/app/jank/RelativeFrameTimeHistogram.java129
-rw-r--r--core/java/android/content/pm/flags.aconfig2
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java9
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java7
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java18
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java24
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java153
-rw-r--r--core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java105
-rw-r--r--core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java13
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java13
-rw-r--r--core/java/android/hardware/camera2/params/SharedSessionConfiguration.java2
-rw-r--r--core/java/android/hardware/contexthub/HubEndpoint.java34
-rw-r--r--core/java/android/hardware/contexthub/HubEndpointDiscoveryCallback.java (renamed from core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java)2
-rw-r--r--core/java/android/hardware/contexthub/HubEndpointLifecycleCallback.java (renamed from core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java)2
-rw-r--r--core/java/android/hardware/contexthub/HubEndpointMessageCallback.java (renamed from core/java/android/hardware/contexthub/IHubEndpointMessageCallback.java)6
-rw-r--r--core/java/android/hardware/contexthub/HubEndpointSession.java2
-rw-r--r--core/java/android/hardware/contexthub/HubEndpointSessionResult.java2
-rw-r--r--core/java/android/hardware/contexthub/HubServiceInfo.java4
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java33
-rw-r--r--core/java/android/hardware/display/DisplayTopology.java33
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig7
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java75
-rw-r--r--core/java/android/net/thread/flags.aconfig2
-rw-r--r--core/java/android/os/BaseBundle.java19
-rw-r--r--core/java/android/os/Bundle.java21
-rw-r--r--core/java/android/os/Parcel.java66
-rw-r--r--core/java/android/os/Process.java11
-rw-r--r--core/java/android/os/TestLooperManager.java16
-rw-r--r--core/java/android/provider/Settings.java16
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletService.java8
-rw-r--r--core/java/android/service/settings/preferences/GetValueRequest.java1
-rw-r--r--core/java/android/service/settings/preferences/GetValueResult.java1
-rw-r--r--core/java/android/service/settings/preferences/MetadataRequest.java1
-rw-r--r--core/java/android/service/settings/preferences/MetadataResult.java1
-rw-r--r--core/java/android/service/settings/preferences/SetValueRequest.java1
-rw-r--r--core/java/android/service/settings/preferences/SetValueResult.java1
-rw-r--r--core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java20
-rw-r--r--core/java/android/service/settings/preferences/SettingsPreferenceValue.java1
-rw-r--r--core/java/android/tracing/flags.aconfig8
-rw-r--r--core/java/android/view/Choreographer.java35
-rw-r--r--core/java/android/view/Display.java4
-rw-r--r--core/java/android/view/DisplayInfo.java22
-rw-r--r--core/java/android/view/KeyCharacterMap.java13
-rw-r--r--core/java/android/view/Surface.java9
-rw-r--r--core/java/android/view/SurfaceControl.java9
-rw-r--r--core/java/android/view/View.java4
-rw-r--r--core/java/android/view/ViewRootImpl.java9
-rw-r--r--core/java/android/view/contentcapture/ChildContentCaptureSession.java5
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureEvent.java11
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java31
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java9
-rw-r--r--core/java/android/view/contentcapture/OWNERS5
-rw-r--r--core/java/android/view/contentcapture/flags/content_capture_flags.aconfig7
-rw-r--r--core/java/android/window/SnapshotDrawerUtils.java240
-rw-r--r--core/java/android/window/StartingWindowInfo.java22
-rw-r--r--core/java/android/window/WindowTokenClientController.java8
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig7
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig11
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig27
-rw-r--r--core/java/com/android/internal/jank/DisplayResolutionTracker.java4
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl3
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java44
-rw-r--r--core/jni/Android.bp13
-rw-r--r--core/jni/android_hardware_camera2_CameraDevice.cpp11
-rw-r--r--core/jni/android_util_Process.cpp23
-rw-r--r--core/jni/platform/darwin/libandroid_runtime_export.exp38
-rw-r--r--core/jni/platform/linux/libandroid_runtime_export.txt43
-rw-r--r--core/proto/android/providers/settings/secure.proto4
-rw-r--r--core/res/AndroidManifest.xml9
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_base.xml40
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_media.xml7
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_messaging.xml6
-rw-r--r--core/res/res/layout/notification_2025_template_header.xml31
-rw-r--r--core/res/res/values-watch/config.xml3
-rw-r--r--core/res/res/values/config.xml11
-rw-r--r--core/res/res/values/config_watch.xml5
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/app/NotificationTest.java208
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java3
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java18
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt86
-rw-r--r--core/tests/coretests/src/android/os/ProcessTest.java18
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java12
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java5
-rw-r--r--core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java179
-rw-r--r--core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java8
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java162
-rw-r--r--libs/WindowManager/Shell/multivalentTests/Android.bp2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java6
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt9
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsWithKeyboard.kt51
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt33
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java3
-rw-r--r--libs/androidfw/Android.bp1
-rw-r--r--libs/androidfw/AssetManager.cpp32
-rw-r--r--libs/androidfw/TypeWrappers.cpp59
-rw-r--r--libs/androidfw/include/androidfw/AssetManager.h22
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h5
-rw-r--r--libs/androidfw/include/androidfw/TypeWrappers.h30
-rw-r--r--libs/androidfw/include/androidfw/misc.h35
-rw-r--r--libs/androidfw/misc.cpp57
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp11
-rw-r--r--libs/androidfw/tests/TypeWrappers_test.cpp127
-rw-r--r--libs/protoutil/Android.bp1
-rw-r--r--location/api/system-current.txt91
-rw-r--r--location/java/android/location/BeidouSatelliteEphemeris.java30
-rw-r--r--location/java/android/location/GalileoSatelliteEphemeris.java149
-rw-r--r--location/java/android/location/GlonassAlmanac.java112
-rw-r--r--location/java/android/location/GlonassSatelliteEphemeris.java183
-rw-r--r--location/java/android/location/GnssAlmanac.java57
-rw-r--r--location/java/android/location/GpsSatelliteEphemeris.java30
-rw-r--r--location/java/android/location/QzssSatelliteEphemeris.java30
-rw-r--r--location/java/android/location/flags/location.aconfig2
-rw-r--r--location/java/android/location/provider/GnssAssistanceProviderBase.java152
-rw-r--r--location/java/android/location/provider/IGnssAssistanceCallback.aidl (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt)16
-rw-r--r--location/java/android/location/provider/IGnssAssistanceProvider.aidl28
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java75
-rw-r--r--nfc/tests/src/android/nfc/tech/NfcATest.java185
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java47
-rw-r--r--packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java47
-rw-r--r--packages/SettingsLib/ButtonPreference/Android.bp4
-rw-r--r--packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java24
-rw-r--r--packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java2
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java23
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java27
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java22
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java37
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java61
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java28
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java7
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java41
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java4
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java58
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java4
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java175
-rw-r--r--packages/SystemUI/Android.bp4
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig22
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java57
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt150
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt81
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt17
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt37
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt20
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt19
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt31
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt10
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt4
-rw-r--r--packages/SystemUI/docs/clock-plugins.md6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt57
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt42
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ControlActionCoordinatorImplTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt)73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt116
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt122
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt86
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt56
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt49
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt46
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt46
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt)86
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java134
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt184
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java379
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt454
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java1
-rw-r--r--packages/SystemUI/res/color/slider_active_track_color.xml19
-rw-r--r--packages/SystemUI/res/color/slider_inactive_track_color.xml19
-rw-r--r--packages/SystemUI/res/color/slider_thumb_color.xml20
-rw-r--r--packages/SystemUI/res/drawable/audio_bars_idle.xml56
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml2
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_slider.xml8
-rw-r--r--packages/SystemUI/res/raw/audio_bars_in.json1
-rw-r--r--packages/SystemUI/res/raw/audio_bars_out.json1
-rw-r--r--packages/SystemUI/res/raw/audio_bars_playing.json1
-rw-r--r--packages/SystemUI/res/values-night/colors.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml9
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml10
-rw-r--r--packages/SystemUI/res/values/styles.xml13
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt37
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt55
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java67
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt62
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt225
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt96
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/FakeShadeDialogContextInteractor.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt97
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java154
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt150
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt197
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt189
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt)26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java8
-rw-r--r--packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt43
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneDataSourceKosmos.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/WindowRootViewKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeAnimationRepositoryKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt)7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt58
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt8
-rw-r--r--packages/Vcn/framework-b/src/android/net/vcn/VcnTransportInfo.java9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java16
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java7
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java8
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java36
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java34
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java16
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java13
-rw-r--r--services/core/java/com/android/server/am/PhantomProcessList.java35
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java6
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java2
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/am/UserController.java174
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java21
-rw-r--r--services/core/java/com/android/server/display/DisplayTopologyCoordinator.java17
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java38
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java97
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2Provider.java37
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java135
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java64
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java36
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java502
-rw-r--r--services/core/java/com/android/server/notification/NotificationDelegate.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java37
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java8
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java2
-rw-r--r--services/core/java/com/android/server/power/Notifier.java28
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java28
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java13
-rw-r--r--services/core/java/com/android/server/storage/CacheQuotaStrategy.java5
-rw-r--r--services/core/java/com/android/server/updates/Android.bp11
-rw-r--r--services/core/java/com/android/server/updates/CertificateTransparencyLogInstallReceiver.java164
-rw-r--r--services/core/java/com/android/server/updates/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java105
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java3
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java7
-rw-r--r--services/core/java/com/android/server/wm/AsyncRotationController.java8
-rw-r--r--services/core/java/com/android/server/wm/ContentRecorder.java12
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java12
-rw-r--r--services/core/java/com/android/server/wm/FadeAnimationController.java26
-rw-r--r--services/core/java/com/android/server/wm/OWNERS1
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java6
-rw-r--r--services/core/java/com/android/server/wm/Task.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java2
-rw-r--r--services/core/jni/Android.bp8
-rw-r--r--services/core/jni/com_android_server_am_PhantomProcessList.cpp49
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp46
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/java/com/android/server/SystemServer.java11
-rw-r--r--services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java11
-rw-r--r--services/profcollect/src/com/android/server/profcollect/Utils.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java33
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt41
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java16
-rw-r--r--services/tests/mockingservicestests/jni/Android.bp1
-rw-r--r--services/tests/mockingservicestests/jni/onload.cpp2
-rw-r--r--services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java72
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java92
-rw-r--r--services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java20
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java4
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java141
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java37
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java19
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java212
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java12
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java3
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java3
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/JankUtils.java18
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/TestWidget.java8
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java2
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java3
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt23
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt4
-rw-r--r--tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt41
-rw-r--r--tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp3
-rw-r--r--tools/aapt/Package.cpp11
-rw-r--r--tools/aapt2/Debug.cpp24
470 files changed, 9929 insertions, 4791 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 2f843f9d6164..8547ec164084 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -1809,12 +1809,25 @@ aconfig_declarations {
name: "aconfig_settingslib_flags",
package: "com.android.settingslib.flags",
container: "system",
+ exportable: true,
srcs: [
"packages/SettingsLib/aconfig/settingslib.aconfig",
],
}
java_aconfig_library {
+ name: "aconfig_settingslib_exported_flags_java_lib",
+ aconfig_declarations: "aconfig_settingslib_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ mode: "exported",
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
+}
+
+java_aconfig_library {
name: "aconfig_settingslib_flags_java_lib",
aconfig_declarations: "aconfig_settingslib_flags",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
diff --git a/MEMORY_OWNERS b/MEMORY_OWNERS
index 89ce5140d8ea..12aa2951bbc9 100644
--- a/MEMORY_OWNERS
+++ b/MEMORY_OWNERS
@@ -2,5 +2,4 @@ surenb@google.com
tjmercier@google.com
kaleshsingh@google.com
jyescas@google.com
-carlosgalo@google.com
jji@google.com
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
index c77528021201..ed669beae1ce 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -63,12 +63,14 @@ public class ZipFilePerfTest {
@Test
@Parameters(method = "getData")
- public void timeZipFileOpenClose(int numEntries) throws Exception {
+ public void timeZipFileOpen(int numEntries) throws Exception {
setUp(numEntries);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
ZipFile zf = new ZipFile(mFile);
+ state.pauseTiming();
zf.close();
+ state.resumeTiming();
}
}
diff --git a/api/api.go b/api/api.go
index e4d783eba4c3..cbdb7e81ab86 100644
--- a/api/api.go
+++ b/api/api.go
@@ -105,7 +105,7 @@ func (a *CombinedApis) DepsMutator(ctx android.BottomUpMutatorContext) {
func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
ctx.WalkDeps(func(child, parent android.Module) bool {
- if _, ok := child.(java.AndroidLibraryDependency); ok && child.Name() != "framework-res" {
+ if _, ok := android.OtherModuleProvider(ctx, child, java.AndroidLibraryInfoProvider); ok && child.Name() != "framework-res" {
// Stubs of BCP and SSCP libraries should not have any dependencies on apps
// This check ensures that we do not run into circular dependencies when UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT=true
ctx.ModuleErrorf(
diff --git a/core/api/current.txt b/core/api/current.txt
index 32507dfef9b6..7c56a5811abb 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9190,37 +9190,37 @@ package android.app.blob {
package android.app.jank {
@FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public final class AppJankStats {
- ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.FrameOverrunHistogram);
- method @NonNull public android.app.jank.FrameOverrunHistogram getFrameOverrunHistogram();
+ ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.RelativeFrameTimeHistogram);
method public long getJankyFrameCount();
+ method @NonNull public android.app.jank.RelativeFrameTimeHistogram getRelativeFrameTimeHistogram();
method public long getTotalFrameCount();
method public int getUid();
method @NonNull public String getWidgetCategory();
method @NonNull public String getWidgetId();
method @NonNull public String getWidgetState();
- field public static final String ANIMATING = "animating";
- field public static final String ANIMATION = "animation";
- field public static final String DRAGGING = "dragging";
- field public static final String FLINGING = "flinging";
- field public static final String KEYBOARD = "keyboard";
- field public static final String MEDIA = "media";
- field public static final String NAVIGATION = "navigation";
- field public static final String NONE = "none";
- field public static final String OTHER = "other";
- field public static final String PLAYBACK = "playback";
- field public static final String PREDICTIVE_BACK = "predictive_back";
- field public static final String SCROLL = "scroll";
- field public static final String SCROLLING = "scrolling";
- field public static final String SWIPING = "swiping";
- field public static final String TAPPING = "tapping";
- field public static final String WIDGET_CATEGORY_UNSPECIFIED = "widget_category_unspecified";
- field public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified";
- field public static final String ZOOMING = "zooming";
- }
-
- @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public class FrameOverrunHistogram {
- ctor public FrameOverrunHistogram();
- method public void addFrameOverrunMillis(int);
+ field public static final String WIDGET_CATEGORY_ANIMATION = "animation";
+ field public static final String WIDGET_CATEGORY_KEYBOARD = "keyboard";
+ field public static final String WIDGET_CATEGORY_MEDIA = "media";
+ field public static final String WIDGET_CATEGORY_NAVIGATION = "navigation";
+ field public static final String WIDGET_CATEGORY_OTHER = "other";
+ field public static final String WIDGET_CATEGORY_SCROLL = "scroll";
+ field public static final String WIDGET_CATEGORY_UNSPECIFIED = "unspecified";
+ field public static final String WIDGET_STATE_ANIMATING = "animating";
+ field public static final String WIDGET_STATE_DRAGGING = "dragging";
+ field public static final String WIDGET_STATE_FLINGING = "flinging";
+ field public static final String WIDGET_STATE_NONE = "none";
+ field public static final String WIDGET_STATE_PLAYBACK = "playback";
+ field public static final String WIDGET_STATE_PREDICTIVE_BACK = "predictive_back";
+ field public static final String WIDGET_STATE_SCROLLING = "scrolling";
+ field public static final String WIDGET_STATE_SWIPING = "swiping";
+ field public static final String WIDGET_STATE_TAPPING = "tapping";
+ field public static final String WIDGET_STATE_UNSPECIFIED = "unspecified";
+ field public static final String WIDGET_STATE_ZOOMING = "zooming";
+ }
+
+ @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public class RelativeFrameTimeHistogram {
+ ctor public RelativeFrameTimeHistogram();
+ method public void addRelativeFrameTimeMillis(int);
method @NonNull public int[] getBucketCounters();
method @NonNull public int[] getBucketEndpointsMillis();
}
@@ -42521,7 +42521,7 @@ package android.service.settings.preferences {
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.GetValueRequest> CREATOR;
}
- public static final class GetValueRequest.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class GetValueRequest.Builder {
ctor public GetValueRequest.Builder(@NonNull String, @NonNull String);
method @NonNull public android.service.settings.preferences.GetValueRequest build();
}
@@ -42542,7 +42542,7 @@ package android.service.settings.preferences {
field public static final int RESULT_UNSUPPORTED = 1; // 0x1
}
- public static final class GetValueResult.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class GetValueResult.Builder {
ctor public GetValueResult.Builder(int);
method @NonNull public android.service.settings.preferences.GetValueResult build();
method @NonNull public android.service.settings.preferences.GetValueResult.Builder setMetadata(@Nullable android.service.settings.preferences.SettingsPreferenceMetadata);
@@ -42555,7 +42555,7 @@ package android.service.settings.preferences {
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.MetadataRequest> CREATOR;
}
- public static final class MetadataRequest.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class MetadataRequest.Builder {
ctor public MetadataRequest.Builder();
method @NonNull public android.service.settings.preferences.MetadataRequest build();
}
@@ -42571,7 +42571,7 @@ package android.service.settings.preferences {
field public static final int RESULT_UNSUPPORTED = 1; // 0x1
}
- public static final class MetadataResult.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class MetadataResult.Builder {
ctor public MetadataResult.Builder(int);
method @NonNull public android.service.settings.preferences.MetadataResult build();
method @NonNull public android.service.settings.preferences.MetadataResult.Builder setMetadataList(@NonNull java.util.List<android.service.settings.preferences.SettingsPreferenceMetadata>);
@@ -42586,7 +42586,7 @@ package android.service.settings.preferences {
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SetValueRequest> CREATOR;
}
- public static final class SetValueRequest.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class SetValueRequest.Builder {
ctor public SetValueRequest.Builder(@NonNull String, @NonNull String, @NonNull android.service.settings.preferences.SettingsPreferenceValue);
method @NonNull public android.service.settings.preferences.SetValueRequest build();
}
@@ -42608,14 +42608,13 @@ package android.service.settings.preferences {
field public static final int RESULT_UNSUPPORTED = 1; // 0x1
}
- public static final class SetValueResult.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class SetValueResult.Builder {
ctor public SetValueResult.Builder(int);
method @NonNull public android.service.settings.preferences.SetValueResult build();
}
@FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SettingsPreferenceMetadata implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public java.util.List<java.lang.String> getBreadcrumbs();
method @NonNull public android.os.Bundle getExtras();
method @NonNull public String getKey();
method @Nullable public android.content.Intent getLaunchIntent();
@@ -42631,17 +42630,16 @@ package android.service.settings.preferences {
method public boolean isWritable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SettingsPreferenceMetadata> CREATOR;
+ field public static final int DEEPLINK_ONLY = 2; // 0x2
field public static final int EXPECT_POST_CONFIRMATION = 1; // 0x1
- field public static final int EXPECT_PRE_CONFIRMATION = 2; // 0x2
field public static final int NO_DIRECT_ACCESS = 3; // 0x3
field public static final int NO_SENSITIVITY = 0; // 0x0
}
- public static final class SettingsPreferenceMetadata.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class SettingsPreferenceMetadata.Builder {
ctor public SettingsPreferenceMetadata.Builder(@NonNull String, @NonNull String);
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata build();
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setAvailable(boolean);
- method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setBreadcrumbs(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setEnabled(boolean);
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setLaunchIntent(@Nullable android.content.Intent);
@@ -42688,7 +42686,7 @@ package android.service.settings.preferences {
field public static final int TYPE_STRING = 3; // 0x3
}
- public static final class SettingsPreferenceValue.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class SettingsPreferenceValue.Builder {
ctor public SettingsPreferenceValue.Builder(int);
method @NonNull public android.service.settings.preferences.SettingsPreferenceValue build();
method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setBooleanValue(boolean);
@@ -53484,9 +53482,9 @@ package android.view {
field public static final int CHANGE_FRAME_RATE_ALWAYS = 1; // 0x1
field public static final int CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0; // 0x0
field @NonNull public static final android.os.Parcelable.Creator<android.view.Surface> CREATOR;
+ field @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_gte_enum") public static final int FRAME_RATE_COMPATIBILITY_AT_LEAST = 2; // 0x2
field public static final int FRAME_RATE_COMPATIBILITY_DEFAULT = 0; // 0x0
field public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1; // 0x1
- field @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_gte_enum") public static final int FRAME_RATE_COMPATIBILITY_GTE = 2; // 0x2
field public static final int ROTATION_0 = 0; // 0x0
field public static final int ROTATION_180 = 2; // 0x2
field public static final int ROTATION_270 = 3; // 0x3
@@ -57120,6 +57118,7 @@ package android.view.contentcapture {
method public void close();
method @NonNull public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext);
method public final void destroy();
+ method @FlaggedApi("android.view.contentcapture.flags.ccapi_baklava_enabled") public void flush();
method @Nullable public final android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
method @NonNull public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6d24e4684e77..d5cb6c034699 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5079,8 +5079,8 @@ package android.hardware.contexthub {
}
@FlaggedApi("android.chre.flags.offload_api") public class HubEndpoint {
- method @Nullable public android.hardware.contexthub.IHubEndpointLifecycleCallback getLifecycleCallback();
- method @Nullable public android.hardware.contexthub.IHubEndpointMessageCallback getMessageCallback();
+ method @Nullable public android.hardware.contexthub.HubEndpointLifecycleCallback getLifecycleCallback();
+ method @Nullable public android.hardware.contexthub.HubEndpointMessageCallback getMessageCallback();
method @NonNull public java.util.Collection<android.hardware.contexthub.HubServiceInfo> getServiceInfoCollection();
method @Nullable public String getTag();
method public int getVersion();
@@ -5095,14 +5095,19 @@ package android.hardware.contexthub {
public static final class HubEndpoint.Builder {
ctor public HubEndpoint.Builder(@NonNull android.content.Context);
method @NonNull public android.hardware.contexthub.HubEndpoint build();
- method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback);
- method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback);
- method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull android.hardware.contexthub.IHubEndpointMessageCallback);
- method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointMessageCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull android.hardware.contexthub.HubEndpointLifecycleCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.HubEndpointLifecycleCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull android.hardware.contexthub.HubEndpointMessageCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.HubEndpointMessageCallback);
method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setServiceInfoCollection(@NonNull java.util.Collection<android.hardware.contexthub.HubServiceInfo>);
method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setTag(@NonNull String);
}
+ @FlaggedApi("android.chre.flags.offload_api") public interface HubEndpointDiscoveryCallback {
+ method public void onEndpointsStarted(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>);
+ method public void onEndpointsStopped(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>, int);
+ }
+
@FlaggedApi("android.chre.flags.offload_api") public final class HubEndpointInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.hardware.contexthub.HubEndpointInfo.HubEndpointIdentifier getIdentifier();
@@ -5126,6 +5131,16 @@ package android.hardware.contexthub {
method public long getHub();
}
+ @FlaggedApi("android.chre.flags.offload_api") public interface HubEndpointLifecycleCallback {
+ method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int);
+ method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable String);
+ method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession);
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public interface HubEndpointMessageCallback {
+ method public void onMessageReceived(@NonNull android.hardware.contexthub.HubEndpointSession, @NonNull android.hardware.contexthub.HubMessage);
+ }
+
@FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSession implements java.lang.AutoCloseable {
method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void close();
method @Nullable public String getServiceDescriptor();
@@ -5174,21 +5189,6 @@ package android.hardware.contexthub {
method @NonNull public android.hardware.contexthub.HubServiceInfo build();
}
- @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointDiscoveryCallback {
- method public void onEndpointsStarted(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>);
- method public void onEndpointsStopped(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>, int);
- }
-
- @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback {
- method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int);
- method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable String);
- method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession);
- }
-
- @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointMessageCallback {
- method public void onMessageReceived(@NonNull android.hardware.contexthub.HubEndpointSession, @NonNull android.hardware.contexthub.HubMessage);
- }
-
}
package android.hardware.devicestate {
@@ -6192,16 +6192,16 @@ package android.hardware.location {
method @Deprecated public int registerCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
method @Deprecated public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpoint(@NonNull android.hardware.contexthub.HubEndpoint);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(long, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(long, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback, @NonNull java.util.concurrent.Executor);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull String, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull String, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback, @NonNull java.util.concurrent.Executor);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback, long);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback, long);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback, @NonNull String);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback, @NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int unloadNanoApp(int);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void unregisterEndpoint(@NonNull android.hardware.contexthub.HubEndpoint);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void unregisterEndpointDiscoveryCallback(@NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void unregisterEndpointDiscoveryCallback(@NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback);
field public static final int AUTHORIZATION_DENIED = 0; // 0x0
field public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; // 0x1
field public static final int AUTHORIZATION_GRANTED = 2; // 0x2
@@ -19091,6 +19091,7 @@ package android.view.contentcapture {
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
+ field @FlaggedApi("android.view.contentcapture.flags.ccapi_baklava_enabled") public static final int TYPE_SESSION_FLUSH = 11; // 0xb
field public static final int TYPE_SESSION_PAUSED = 8; // 0x8
field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fee8cdb1ce51..c3ef104075f2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5834,7 +5834,11 @@ public class Activity extends ContextThemeWrapper
final int size = permissions.length;
int[] results = new int[size];
for (int i = 0; i < size; i++) {
- results[i] = deviceContext.getPermissionRequestState(permissions[i]);
+ if (permissions[i] == null) {
+ results[i] = Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE;
+ } else {
+ results[i] = deviceContext.getPermissionRequestState(permissions[i]);
+ }
}
return results;
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index abdfb53537f8..999db18a1229 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -485,6 +485,11 @@ public abstract class ActivityManagerInternal {
*/
public static final int OOM_ADJ_REASON_FOLLOW_UP = 23;
+ /**
+ * Oom Adj Reason: Update after oom adjuster configuration has changed.
+ */
+ public static final int OOM_ADJ_REASON_RECONFIGURATION = 24;
+
@IntDef(prefix = {"OOM_ADJ_REASON_"}, value = {
OOM_ADJ_REASON_NONE,
OOM_ADJ_REASON_ACTIVITY,
@@ -510,6 +515,7 @@ public abstract class ActivityManagerInternal {
OOM_ADJ_REASON_RESTRICTION_CHANGE,
OOM_ADJ_REASON_COMPONENT_DISABLED,
OOM_ADJ_REASON_FOLLOW_UP,
+ OOM_ADJ_REASON_RECONFIGURATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OomAdjReason {}
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 61b56877589b..599f1a8be233 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -27,6 +27,7 @@ import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Stores App Compat information about a particular Task.
@@ -58,16 +59,11 @@ public class AppCompatTaskInfo implements Parcelable {
public int topActivityLetterboxHeight = PROPERTY_VALUE_UNSET;
/**
- * Contains the current app height of the letterboxed activity if available or
- * {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
+ * Contains the app bounds of the top activity or size compat mode
+ * bounds when in size compat mode. If null, contains bounds.
*/
- public int topActivityLetterboxAppHeight = PROPERTY_VALUE_UNSET;
-
- /**
- * Contains the current app width of the letterboxed activity if available or
- * {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
- */
- public int topActivityLetterboxAppWidth = PROPERTY_VALUE_UNSET;
+ @NonNull
+ public final Rect topActivityAppBounds = new Rect();
/**
* Contains the top activity bounds when the activity is letterboxed.
@@ -350,8 +346,7 @@ public class AppCompatTaskInfo implements Parcelable {
&& topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
- && topActivityLetterboxAppWidth == that.topActivityLetterboxAppWidth
- && topActivityLetterboxAppHeight == that.topActivityLetterboxAppHeight
+ && topActivityAppBounds.equals(that.topActivityAppBounds)
&& topActivityLetterboxHorizontalPosition
== that.topActivityLetterboxHorizontalPosition
&& cameraCompatTaskInfo.equalsForTaskOrganizer(that.cameraCompatTaskInfo);
@@ -371,8 +366,7 @@ public class AppCompatTaskInfo implements Parcelable {
== that.topActivityLetterboxHorizontalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
- && topActivityLetterboxAppWidth == that.topActivityLetterboxAppWidth
- && topActivityLetterboxAppHeight == that.topActivityLetterboxAppHeight
+ && topActivityAppBounds.equals(that.topActivityAppBounds)
&& cameraCompatTaskInfo.equalsForCompatUi(that.cameraCompatTaskInfo);
}
@@ -385,8 +379,7 @@ public class AppCompatTaskInfo implements Parcelable {
topActivityLetterboxHorizontalPosition = source.readInt();
topActivityLetterboxWidth = source.readInt();
topActivityLetterboxHeight = source.readInt();
- topActivityLetterboxAppWidth = source.readInt();
- topActivityLetterboxAppHeight = source.readInt();
+ topActivityAppBounds.set(Objects.requireNonNull(source.readTypedObject(Rect.CREATOR)));
topActivityLetterboxBounds = source.readTypedObject(Rect.CREATOR);
cameraCompatTaskInfo = source.readTypedObject(CameraCompatTaskInfo.CREATOR);
}
@@ -401,8 +394,7 @@ public class AppCompatTaskInfo implements Parcelable {
dest.writeInt(topActivityLetterboxHorizontalPosition);
dest.writeInt(topActivityLetterboxWidth);
dest.writeInt(topActivityLetterboxHeight);
- dest.writeInt(topActivityLetterboxAppWidth);
- dest.writeInt(topActivityLetterboxAppHeight);
+ dest.writeTypedObject(topActivityAppBounds, flags);
dest.writeTypedObject(topActivityLetterboxBounds, flags);
dest.writeTypedObject(cameraCompatTaskInfo, flags);
}
@@ -421,8 +413,7 @@ public class AppCompatTaskInfo implements Parcelable {
+ topActivityLetterboxHorizontalPosition
+ " topActivityLetterboxWidth=" + topActivityLetterboxWidth
+ " topActivityLetterboxHeight=" + topActivityLetterboxHeight
- + " topActivityLetterboxAppWidth=" + topActivityLetterboxAppWidth
- + " topActivityLetterboxAppHeight=" + topActivityLetterboxAppHeight
+ + " topActivityAppBounds=" + topActivityAppBounds
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled()
+ " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled()
+ " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride()
diff --git a/core/java/android/app/IUserSwitchObserver.aidl b/core/java/android/app/IUserSwitchObserver.aidl
index 1ff7a17e578f..d71ee7c712e7 100644
--- a/core/java/android/app/IUserSwitchObserver.aidl
+++ b/core/java/android/app/IUserSwitchObserver.aidl
@@ -19,10 +19,10 @@ package android.app;
import android.os.IRemoteCallback;
/** {@hide} */
-interface IUserSwitchObserver {
- void onBeforeUserSwitching(int newUserId);
- oneway void onUserSwitching(int newUserId, IRemoteCallback reply);
- oneway void onUserSwitchComplete(int newUserId);
- oneway void onForegroundProfileSwitch(int newProfileId);
- oneway void onLockedBootComplete(int newUserId);
+oneway interface IUserSwitchObserver {
+ void onBeforeUserSwitching(int newUserId, IRemoteCallback reply);
+ void onUserSwitching(int newUserId, IRemoteCallback reply);
+ void onUserSwitchComplete(int newUserId);
+ void onForegroundProfileSwitch(int newProfileId);
+ void onLockedBootComplete(int newUserId);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 17638ee76dba..eeb1ebb69b03 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11208,8 +11208,8 @@ public class Notification implements Parcelable
private static final String KEY_SEGMENT_LENGTH = "length";
private static final String KEY_POINT_POSITION = "position";
- private static final int MAX_PROGRESS_SEGMENT_LIMIT = 15;
- private static final int MAX_PROGRESS_STOP_LIMIT = 5;
+ private static final int MAX_PROGRESS_SEGMENT_LIMIT = 10;
+ private static final int MAX_PROGRESS_POINT_LIMIT = 4;
private static final int DEFAULT_PROGRESS_MAX = 100;
private List<Segment> mProgressSegments = new ArrayList<>();
@@ -11286,7 +11286,9 @@ public class Notification implements Parcelable
mProgressSegments = new ArrayList<>();
}
mProgressSegments.clear();
- mProgressSegments.addAll(progressSegments);
+ for (Segment segment : progressSegments) {
+ addProgressSegment(segment);
+ }
return this;
}
@@ -11302,7 +11304,11 @@ public class Notification implements Parcelable
if (mProgressSegments == null) {
mProgressSegments = new ArrayList<>();
}
- mProgressSegments.add(segment);
+ if (segment.getLength() > 0) {
+ mProgressSegments.add(segment);
+ } else {
+ Log.w(TAG, "Dropped the segment. The length is not a positive integer.");
+ }
return this;
}
@@ -11327,7 +11333,14 @@ public class Notification implements Parcelable
* @see Point
*/
public @NonNull ProgressStyle setProgressPoints(@NonNull List<Point> points) {
- mProgressPoints = new ArrayList<>(points);
+ if (mProgressPoints == null) {
+ mProgressPoints = new ArrayList<>();
+ }
+ mProgressPoints.clear();
+
+ for (Point point: points) {
+ addProgressPoint(point);
+ }
return this;
}
@@ -11348,7 +11361,17 @@ public class Notification implements Parcelable
if (mProgressPoints == null) {
mProgressPoints = new ArrayList<>();
}
- mProgressPoints.add(point);
+ if (point.getPosition() >= 0) {
+ mProgressPoints.add(point);
+
+ if (mProgressPoints.size() > MAX_PROGRESS_POINT_LIMIT) {
+ Log.w(TAG, "Progress points limit is reached. First"
+ + MAX_PROGRESS_POINT_LIMIT + " points will be rendered.");
+ }
+
+ } else {
+ Log.w(TAG, "Dropped the point. The position is a negative integer.");
+ }
return this;
}
@@ -11384,8 +11407,7 @@ public class Notification implements Parcelable
} else {
int progressMax = 0;
int validSegmentCount = 0;
- for (int i = 0; i < progressSegment.size()
- && validSegmentCount < MAX_PROGRESS_SEGMENT_LIMIT; i++) {
+ for (int i = 0; i < progressSegment.size(); i++) {
int segmentLength = progressSegment.get(i).getLength();
if (segmentLength > 0) {
try {
@@ -11832,6 +11854,30 @@ public class Notification implements Parcelable
totalLength = DEFAULT_PROGRESS_MAX;
segments.add(sanitizeSegment(new Segment(totalLength), backgroundColor,
defaultProgressColor));
+ } else if (segments.size() > MAX_PROGRESS_SEGMENT_LIMIT) {
+ // If segment limit is exceeded. All segments will be replaced
+ // with a single segment
+ boolean allSameColor = true;
+ int firstSegmentColor = segments.get(0).getColor();
+
+ for (int i = 1; i < segments.size(); i++) {
+ if (segments.get(i).getColor() != firstSegmentColor) {
+ allSameColor = false;
+ break;
+ }
+ }
+
+ // This single segment length has same max as total.
+ final Segment singleSegment = new Segment(totalLength);
+ // Single segment color: if all segments have the same color,
+ // use that color. Otherwise, use 0 / default.
+ singleSegment.setColor(allSameColor ? firstSegmentColor
+ : Notification.COLOR_DEFAULT);
+
+ segments.clear();
+ segments.add(sanitizeSegment(singleSegment,
+ backgroundColor,
+ defaultProgressColor));
}
// Ensure point color contrasts.
@@ -11840,6 +11886,9 @@ public class Notification implements Parcelable
final int position = point.getPosition();
if (position < 0 || position > totalLength) continue;
points.add(sanitizePoint(point, backgroundColor, defaultProgressColor));
+ if (points.size() == MAX_PROGRESS_POINT_LIMIT) {
+ break;
+ }
}
model = new NotificationProgressModel(segments, points,
@@ -11868,8 +11917,10 @@ public class Notification implements Parcelable
* has the same hue as the original color, but is lightened or darkened depending on
* whether the background is dark or light.
*
+ * @hide
*/
- private int sanitizeProgressColor(@ColorInt int color,
+ @VisibleForTesting
+ public static int sanitizeProgressColor(@ColorInt int color,
@ColorInt int bg,
@ColorInt int defaultColor) {
return Builder.ensureColorContrast(
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 8ed66eb7e6c0..e9b889a2f1aa 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -55,6 +55,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -677,9 +678,14 @@ public class NotificationManager {
}
/** {@hide} */
- @UnsupportedAppUsage
- public NotificationManager(Context context, InstantSource clock)
+ public NotificationManager(Context context)
{
+ this(context, SystemClock.elapsedRealtimeClock());
+ }
+
+ /** {@hide} */
+ @UnsupportedAppUsage
+ public NotificationManager(Context context, InstantSource clock) {
mContext = context;
mClock = clock;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 920b19cd8f78..0bbe9434293a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -17,7 +17,7 @@
package android.app;
import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
-import static android.provider.flags.Flags.stageFlagsForBuild;
+import static android.provider.flags.Flags.newStoragePublicApi;
import static android.server.Flags.removeGameManagerServiceFromWear;
import android.accounts.AccountManager;
@@ -289,7 +289,6 @@ import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.policy.PhoneLayoutInflater;
import com.android.internal.util.Preconditions;
-import java.time.InstantSource;
import java.util.Map;
import java.util.Objects;
@@ -625,8 +624,8 @@ public final class SystemServiceRegistry {
com.android.internal.R.style.Theme_Dialog,
com.android.internal.R.style.Theme_Holo_Dialog,
com.android.internal.R.style.Theme_DeviceDefault_Dialog,
- com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),
- InstantSource.system());
+ com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog))
+ );
}});
registerService(Context.PEOPLE_SERVICE, PeopleManager.class,
@@ -1841,7 +1840,7 @@ public final class SystemServiceRegistry {
VirtualizationFrameworkInitializer.registerServiceWrappers();
ConnectivityFrameworkInitializerBaklava.registerServiceWrappers();
- if (stageFlagsForBuild()) {
+ if (newStoragePublicApi()) {
ConfigInfrastructureFrameworkInitializer.registerServiceWrappers();
}
diff --git a/core/java/android/app/UserSwitchObserver.java b/core/java/android/app/UserSwitchObserver.java
index 727799a1f948..1664cfb6f7a8 100644
--- a/core/java/android/app/UserSwitchObserver.java
+++ b/core/java/android/app/UserSwitchObserver.java
@@ -30,7 +30,11 @@ public class UserSwitchObserver extends IUserSwitchObserver.Stub {
}
@Override
- public void onBeforeUserSwitching(int newUserId) throws RemoteException {}
+ public void onBeforeUserSwitching(int newUserId, IRemoteCallback reply) throws RemoteException {
+ if (reply != null) {
+ reply.sendResult(null);
+ }
+ }
@Override
public void onUserSwitching(int newUserId, IRemoteCallback reply) throws RemoteException {
diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java
index 3438cc861661..ad43f271a910 100644
--- a/core/java/android/app/contextualsearch/ContextualSearchManager.java
+++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java
@@ -48,7 +48,9 @@ public final class ContextualSearchManager {
/**
* Key to get the entrypoint from the extras of the activity launched by contextual search.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_ENTRYPOINT =
"android.app.contextualsearch.extra.ENTRYPOINT";
@@ -56,14 +58,18 @@ public final class ContextualSearchManager {
/**
* Key to get the flag_secure value from the extras of the activity launched by contextual
* search. The value will be true if flag_secure is found in any of the visible activities.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_FLAG_SECURE_FOUND =
"android.app.contextualsearch.extra.FLAG_SECURE_FOUND";
/**
* Key to get the screenshot from the extras of the activity launched by contextual search.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_SCREENSHOT =
"android.app.contextualsearch.extra.SCREENSHOT";
@@ -71,7 +77,9 @@ public final class ContextualSearchManager {
/**
* Key to check whether managed profile is visible from the extras of the activity launched by
* contextual search. The value will be true if any one of the visible apps is managed.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_IS_MANAGED_PROFILE_VISIBLE =
"android.app.contextualsearch.extra.IS_MANAGED_PROFILE_VISIBLE";
@@ -79,7 +87,9 @@ public final class ContextualSearchManager {
/**
* Key to get the list of visible packages from the extras of the activity launched by
* contextual search.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_VISIBLE_PACKAGE_NAMES =
"android.app.contextualsearch.extra.VISIBLE_PACKAGE_NAMES";
@@ -87,7 +97,9 @@ public final class ContextualSearchManager {
/**
* Key to get the time the user made the invocation request, based on
* {@link SystemClock#uptimeMillis()}.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*
* TODO: un-hide in W
*
@@ -99,11 +111,24 @@ public final class ContextualSearchManager {
/**
* Key to get the binder token from the extras of the activity launched by contextual search.
* This token is needed to invoke {@link CallbackToken#getContextualSearchState} method.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN";
/**
+ * Key to check whether audio is playing when contextual search is invoked.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
+ *
+ * @hide
+ */
+ public static final String EXTRA_IS_AUDIO_PLAYING =
+ "android.app.contextualsearch.extra.IS_AUDIO_PLAYING";
+
+ /**
* Intent action for contextual search invocation. The app providing the contextual search
* experience must add this intent filter action to the activity it wants to be launched.
* <br>
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index e8cfd79c9cc7..c19921dcdc61 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -8,6 +8,7 @@ flag {
bug: "309689654"
is_exported: true
}
+
flag {
name: "enable_token_refresh"
namespace: "machine_learning"
@@ -27,4 +28,11 @@ flag {
namespace: "sysui_integrations"
description: "Identify live contextual search UI to exclude from contextual search screenshot."
bug: "372510690"
+}
+
+flag {
+ name: "include_audio_playing_status"
+ namespace: "sysui_integrations"
+ description: "Add audio playing status to the contextual search invocation intent."
+ bug: "372935419"
} \ No newline at end of file
diff --git a/core/java/android/app/jank/AppJankStats.java b/core/java/android/app/jank/AppJankStats.java
index eea1d2ba5b9e..6ef6a44ddfbb 100644
--- a/core/java/android/app/jank/AppJankStats.java
+++ b/core/java/android/app/jank/AppJankStats.java
@@ -41,7 +41,8 @@ public final class AppJankStats {
// The id that has been set for the widget.
private String mWidgetId;
- // A general category that the widget applies to.
+ // A general category the widget falls into based on the functions it performs or helps
+ // facilitate.
private String mWidgetCategory;
// The states that the UI elements can report
@@ -53,78 +54,78 @@ public final class AppJankStats {
// Total number of frames determined to be janky during the reported state.
private long mJankyFrames;
- // Histogram of frame duration overruns encoded in predetermined buckets.
- private FrameOverrunHistogram mFrameOverrunHistogram;
+ // Histogram of relative frame times encoded in predetermined buckets.
+ private RelativeFrameTimeHistogram mRelativeFrameTimeHistogram;
/** Used to indicate no widget category has been set. */
- public static final String WIDGET_CATEGORY_UNSPECIFIED =
- "widget_category_unspecified";
+ public static final String WIDGET_CATEGORY_UNSPECIFIED = "unspecified";
/** UI elements that facilitate scrolling. */
- public static final String SCROLL = "scroll";
+ public static final String WIDGET_CATEGORY_SCROLL = "scroll";
/** UI elements that facilitate playing animations. */
- public static final String ANIMATION = "animation";
+ public static final String WIDGET_CATEGORY_ANIMATION = "animation";
/** UI elements that facilitate media playback. */
- public static final String MEDIA = "media";
+ public static final String WIDGET_CATEGORY_MEDIA = "media";
/** UI elements that facilitate in-app navigation. */
- public static final String NAVIGATION = "navigation";
+ public static final String WIDGET_CATEGORY_NAVIGATION = "navigation";
/** UI elements that facilitate displaying, hiding or interacting with keyboard. */
- public static final String KEYBOARD = "keyboard";
-
- /** UI elements that facilitate predictive back gesture navigation. */
- public static final String PREDICTIVE_BACK = "predictive_back";
+ public static final String WIDGET_CATEGORY_KEYBOARD = "keyboard";
/** UI elements that don't fall in one or any of the other categories. */
- public static final String OTHER = "other";
+ public static final String WIDGET_CATEGORY_OTHER = "other";
/** Used to indicate no widget state has been set. */
- public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified";
+ public static final String WIDGET_STATE_UNSPECIFIED = "unspecified";
/** Used to indicate the UI element currently has no state and is idle. */
- public static final String NONE = "none";
+ public static final String WIDGET_STATE_NONE = "none";
/** Used to indicate the UI element is currently scrolling. */
- public static final String SCROLLING = "scrolling";
+ public static final String WIDGET_STATE_SCROLLING = "scrolling";
/** Used to indicate the UI element is currently being flung. */
- public static final String FLINGING = "flinging";
+ public static final String WIDGET_STATE_FLINGING = "flinging";
/** Used to indicate the UI element is currently being swiped. */
- public static final String SWIPING = "swiping";
+ public static final String WIDGET_STATE_SWIPING = "swiping";
/** Used to indicate the UI element is currently being dragged. */
- public static final String DRAGGING = "dragging";
+ public static final String WIDGET_STATE_DRAGGING = "dragging";
/** Used to indicate the UI element is currently zooming. */
- public static final String ZOOMING = "zooming";
+ public static final String WIDGET_STATE_ZOOMING = "zooming";
/** Used to indicate the UI element is currently animating. */
- public static final String ANIMATING = "animating";
+ public static final String WIDGET_STATE_ANIMATING = "animating";
/** Used to indicate the UI element is currently playing media. */
- public static final String PLAYBACK = "playback";
+ public static final String WIDGET_STATE_PLAYBACK = "playback";
/** Used to indicate the UI element is currently being tapped on, for example on a keyboard. */
- public static final String TAPPING = "tapping";
+ public static final String WIDGET_STATE_TAPPING = "tapping";
+
+ /** Used to indicate predictive back navigation is currently being used */
+ public static final String WIDGET_STATE_PREDICTIVE_BACK = "predictive_back";
/**
+ * Provide an organized way to group widgets that have similar purposes or perform related
+ * functions.
* @hide
*/
- @StringDef(value = {
+ @StringDef(prefix = {"WIDGET_CATEGORY_"}, value = {
WIDGET_CATEGORY_UNSPECIFIED,
- SCROLL,
- ANIMATION,
- MEDIA,
- NAVIGATION,
- KEYBOARD,
- PREDICTIVE_BACK,
- OTHER
+ WIDGET_CATEGORY_SCROLL,
+ WIDGET_CATEGORY_ANIMATION,
+ WIDGET_CATEGORY_MEDIA,
+ WIDGET_CATEGORY_NAVIGATION,
+ WIDGET_CATEGORY_KEYBOARD,
+ WIDGET_CATEGORY_OTHER
})
@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@@ -133,17 +134,18 @@ public final class AppJankStats {
/**
* @hide
*/
- @StringDef(value = {
+ @StringDef(prefix = {"WIDGET_STATE_"}, value = {
WIDGET_STATE_UNSPECIFIED,
- NONE,
- SCROLLING,
- FLINGING,
- SWIPING,
- DRAGGING,
- ZOOMING,
- ANIMATING,
- PLAYBACK,
- TAPPING,
+ WIDGET_STATE_NONE,
+ WIDGET_STATE_SCROLLING,
+ WIDGET_STATE_FLINGING,
+ WIDGET_STATE_SWIPING,
+ WIDGET_STATE_DRAGGING,
+ WIDGET_STATE_ZOOMING,
+ WIDGET_STATE_ANIMATING,
+ WIDGET_STATE_PLAYBACK,
+ WIDGET_STATE_TAPPING,
+ WIDGET_STATE_PREDICTIVE_BACK
})
@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@@ -156,31 +158,33 @@ public final class AppJankStats {
*
* @param appUid the Uid of the App that is collecting jank stats.
* @param widgetId the widget id that frames will be associated to.
- * @param widgetCategory a general functionality category that the widget falls into. Must be
- * one of the following: SCROLL, ANIMATION, MEDIA, NAVIGATION, KEYBOARD,
- * PREDICTIVE_BACK, OTHER or will be set to WIDGET_CATEGORY_UNSPECIFIED
- * if no value is passed.
- * @param widgetState the state the widget was in while frames were counted. Must be one of
- * the following: NONE, SCROLLING, FLINGING, SWIPING, DRAGGING, ZOOMING,
- * ANIMATING, PLAYBACK, TAPPING or will be set to WIDGET_STATE_UNSPECIFIED
- * if no value is passed.
+ * @param widgetCategory a category used to organize widgets in a structured way that indicates
+ * they serve a similar purpose or perform related functions. Must be
+ * prefixed with WIDGET_CATEGORY_ and have a suffix of one of the
+ * following:SCROLL, ANIMATION, MEDIA, NAVIGATION, KEYBOARD, OTHER or
+ * will be set to UNSPECIFIED if no value is passed.
+ * @param widgetState the state the widget was in while frames were counted. Must be prefixed
+ * with WIDGET_STATE_ and have a suffix of one of the following:
+ * NONE, SCROLLING, FLINGING, SWIPING, DRAGGING, ZOOMING, ANIMATING,
+ * PLAYBACK, TAPPING, PREDICTIVE_BACK or will be set to
+ * WIDGET_STATE_UNSPECIFIED if no value is passed.
* @param totalFrames the total number of frames that were counted for this stat.
* @param jankyFrames the total number of janky frames that were counted for this stat.
- * @param frameOverrunHistogram the histogram with predefined buckets. See
- * {@link #getFrameOverrunHistogram()} for details.
+ * @param relativeFrameTimeHistogram the histogram with predefined buckets. See
+ * {@link #getRelativeFrameTimeHistogram()} for details.
*
*/
public AppJankStats(int appUid, @NonNull String widgetId,
@Nullable @WidgetCategory String widgetCategory,
@Nullable @WidgetState String widgetState, long totalFrames, long jankyFrames,
- @NonNull FrameOverrunHistogram frameOverrunHistogram) {
+ @NonNull RelativeFrameTimeHistogram relativeFrameTimeHistogram) {
mUid = appUid;
mWidgetId = widgetId;
mWidgetCategory = widgetCategory != null ? widgetCategory : WIDGET_CATEGORY_UNSPECIFIED;
mWidgetState = widgetState != null ? widgetState : WIDGET_STATE_UNSPECIFIED;
mTotalFrames = totalFrames;
mJankyFrames = jankyFrames;
- mFrameOverrunHistogram = frameOverrunHistogram;
+ mRelativeFrameTimeHistogram = relativeFrameTimeHistogram;
}
/**
@@ -203,7 +207,7 @@ public final class AppJankStats {
/**
* Returns the category that the widget's functionality generally falls into, or
- * widget_category_unspecified {@link #WIDGET_CATEGORY_UNSPECIFIED} if no value was passed in.
+ * {@link #WIDGET_CATEGORY_UNSPECIFIED} if no value was passed in.
*
* @return the category that the widget's functionality generally falls into, this value cannot
* be null.
@@ -213,7 +217,7 @@ public final class AppJankStats {
}
/**
- * Returns the widget's state that was reported for this stat, or widget_state_unspecified
+ * Returns the widget's state that was reported for this stat, or
* {@link #WIDGET_STATE_UNSPECIFIED} if no value was passed in.
*
* @return the widget's state that was reported for this stat. This value cannot be null.
@@ -241,13 +245,13 @@ public final class AppJankStats {
}
/**
- * Returns a Histogram containing frame overrun times in millis grouped into predefined buckets.
- * See {@link FrameOverrunHistogram} for more information.
+ * Returns a Histogram containing relative frame times in millis grouped into predefined
+ * buckets. See {@link RelativeFrameTimeHistogram} for more information.
*
- * @return Histogram containing frame overrun times in predefined buckets. This value cannot
+ * @return Histogram containing relative frame times in predefined buckets. This value cannot
* be null.
*/
- public @NonNull FrameOverrunHistogram getFrameOverrunHistogram() {
- return mFrameOverrunHistogram;
+ public @NonNull RelativeFrameTimeHistogram getRelativeFrameTimeHistogram() {
+ return mRelativeFrameTimeHistogram;
}
}
diff --git a/core/java/android/app/jank/FrameOverrunHistogram.java b/core/java/android/app/jank/FrameOverrunHistogram.java
deleted file mode 100644
index 3ad6531a46bf..000000000000
--- a/core/java/android/app/jank/FrameOverrunHistogram.java
+++ /dev/null
@@ -1,107 +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 android.app.jank;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-
-import java.util.Arrays;
-
-/**
- * This class is intended to be used when reporting {@link AppJankStats} back to the system. It's
- * intended to be used by library widgets to help facilitate the reporting of frame overrun times
- * by adding those times into predefined buckets.
- */
-@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
-public class FrameOverrunHistogram {
- private static int[] sBucketEndpoints = new int[]{
- Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20, -18,
- -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 40,
- 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000
- };
- private int[] mBucketCounts;
-
- /**
- * Create a new instance of FrameOverrunHistogram.
- */
- public FrameOverrunHistogram() {
- mBucketCounts = new int[sBucketEndpoints.length];
- }
-
- /**
- * Increases the count by one for the bucket representing the frame overrun duration.
- *
- * @param frameOverrunMillis frame overrun duration in millis, frame overrun is the difference
- * between a frames deadline and when it was rendered.
- */
- public void addFrameOverrunMillis(int frameOverrunMillis) {
- int countsIndex = getIndexForCountsFromOverrunTime(frameOverrunMillis);
- mBucketCounts[countsIndex]++;
- }
-
- /**
- * Returns the counts for the all the frame overrun buckets.
- *
- * @return an array of integers representing the counts of frame overrun times. This value
- * cannot be null.
- */
- public @NonNull int[] getBucketCounters() {
- return Arrays.copyOf(mBucketCounts, mBucketCounts.length);
- }
-
- /**
- * Returns the predefined endpoints for the histogram.
- *
- * @return array of integers representing the endpoints for the predefined histogram count
- * buckets. This value cannot be null.
- */
- public @NonNull int[] getBucketEndpointsMillis() {
- return Arrays.copyOf(sBucketEndpoints, sBucketEndpoints.length);
- }
-
- // This takes the overrun time and returns what bucket it belongs to in the counters array.
- private int getIndexForCountsFromOverrunTime(int overrunTime) {
- if (overrunTime < 20) {
- if (overrunTime >= -20) {
- return (overrunTime + 20) / 2 + 12;
- }
- if (overrunTime >= -30) {
- return (overrunTime + 30) / 5 + 10;
- }
- if (overrunTime >= -100) {
- return (overrunTime + 100) / 10 + 3;
- }
- if (overrunTime >= -200) {
- return (overrunTime + 200) / 50 + 1;
- }
- return 0;
- }
- if (overrunTime < 30) {
- return (overrunTime - 20) / 5 + 32;
- }
- if (overrunTime < 100) {
- return (overrunTime - 30) / 10 + 34;
- }
- if (overrunTime < 200) {
- return (overrunTime - 50) / 100 + 41;
- }
- if (overrunTime < 1000) {
- return (overrunTime - 200) / 100 + 43;
- }
- return sBucketEndpoints.length - 1;
- }
-}
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index c9472598b352..b4c293eeb695 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -111,7 +111,7 @@ public class JankDataProcessor {
pendingStat.mTotalFrames += jankStat.getTotalFrameCount();
mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets,
- jankStat.getFrameOverrunHistogram().getBucketCounters());
+ jankStat.getRelativeFrameTimeHistogram().getBucketCounters());
}
private void mergeNewStat(String stateKey, String activityName, AppJankStats jankStats) {
@@ -136,7 +136,7 @@ public class JankDataProcessor {
pendingStat.mJankyFrames = jankStats.getJankyFrameCount();
mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets,
- jankStats.getFrameOverrunHistogram().getBucketCounters());
+ jankStats.getRelativeFrameTimeHistogram().getBucketCounters());
mPendingJankStats.put(stateKey, pendingStat);
}
@@ -271,7 +271,8 @@ public class JankDataProcessor {
private static final int[] sFrameOverrunHistogramBounds = {
Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20,
-18, -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25,
- 30, 40, 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000
+ 30, 40, 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
+ Integer.MAX_VALUE
};
private final int[] mFrameOverrunBuckets = new int[sFrameOverrunHistogramBounds.length];
@@ -414,7 +415,7 @@ public class JankDataProcessor {
if (overrunTime < 200) {
return (overrunTime - 50) / 100 + 41;
}
- if (overrunTime < 1000) {
+ if (overrunTime <= 1000) {
return (overrunTime - 200) / 100 + 43;
}
return sFrameOverrunHistogramBounds.length - 1;
diff --git a/core/java/android/app/jank/RelativeFrameTimeHistogram.java b/core/java/android/app/jank/RelativeFrameTimeHistogram.java
new file mode 100644
index 000000000000..666f90f89f45
--- /dev/null
+++ b/core/java/android/app/jank/RelativeFrameTimeHistogram.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.jank;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import java.util.Arrays;
+
+/**
+ * A histogram of frame times relative to their deadline.
+ *
+ * This class aids in reporting {@link AppJankStats} to the system and is designed for use by
+ * library widgets. It facilitates the recording of frame times in relation to the frame deadline.
+ * The class records the distribution of time remaining until a frame is considered janky or how
+ * janky the frame was.
+ * <p>
+ * A frame's relative frame time value indicates whether it was delivered early, on time, or late.
+ * A negative relative frame time value indicates the frame was delivered early, a value of zero
+ * indicates the frame was delivered on time and a positive value indicates the frame was delivered
+ * late. The values of the endpoints indicate how early or late a frame was delivered.
+ * <p>
+ * The relative frame times are recorded as a histogram: values are
+ * {@link #addRelativeFrameTimeMillis added} to a bucket by increasing the bucket's counter. The
+ * count of frames with a relative frame time between
+ * {@link #getBucketEndpointsMillis bucket endpoints} {@code i} and {@code i+1} can be obtained
+ * through index {@code i} of {@link #getBucketCounters}.
+ *
+ */
+@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+public class RelativeFrameTimeHistogram {
+ private static int[] sBucketEndpoints = new int[]{
+ Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20, -18,
+ -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 40,
+ 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
+ Integer.MAX_VALUE
+ };
+ //
+ private int[] mBucketCounts;
+
+ /**
+ * Create a new instance of RelativeFrameTimeHistogram.
+ */
+ public RelativeFrameTimeHistogram() {
+ mBucketCounts = new int[sBucketEndpoints.length - 1];
+ }
+
+ /**
+ * Increases the count by one for the bucket representing the relative frame time.
+ *
+ * @param frameTimeMillis relative frame time in millis, relative frame time is the difference
+ * between a frames deadline and when it was rendered.
+ */
+ public void addRelativeFrameTimeMillis(int frameTimeMillis) {
+ int countsIndex = getRelativeFrameTimeBucketIndex(frameTimeMillis);
+ mBucketCounts[countsIndex]++;
+ }
+
+ /**
+ * Returns the counts for the all the relative frame time buckets.
+ *
+ * @return an array of integers representing the counts of relative frame times. This value
+ * cannot be null.
+ */
+ public @NonNull int[] getBucketCounters() {
+ return Arrays.copyOf(mBucketCounts, mBucketCounts.length);
+ }
+
+ /**
+ * Returns the relative frame time endpoints for the histogram.
+ * <p>
+ * Index {@code i} of {@link #getBucketCounters} contains the count of frames that had a
+ * relative frame time between {@code endpoints[i]} (inclusive) and {@code endpoints[i+1]}
+ * (exclusive).
+ *
+ * @return array of integers representing the endpoints for the predefined histogram count
+ * buckets. This value cannot be null.
+ */
+ public @NonNull int[] getBucketEndpointsMillis() {
+ return Arrays.copyOf(sBucketEndpoints, sBucketEndpoints.length);
+ }
+
+ // This takes the relative frame time and returns what bucket it belongs to in the counters
+ // array.
+ private int getRelativeFrameTimeBucketIndex(int relativeFrameTime) {
+ if (relativeFrameTime < 20) {
+ if (relativeFrameTime >= -20) {
+ return (relativeFrameTime + 20) / 2 + 12;
+ }
+ if (relativeFrameTime >= -30) {
+ return (relativeFrameTime + 30) / 5 + 10;
+ }
+ if (relativeFrameTime >= -100) {
+ return (relativeFrameTime + 100) / 10 + 3;
+ }
+ if (relativeFrameTime >= -200) {
+ return (relativeFrameTime + 200) / 50 + 1;
+ }
+ return 0;
+ }
+ if (relativeFrameTime < 30) {
+ return (relativeFrameTime - 20) / 5 + 32;
+ }
+ if (relativeFrameTime < 100) {
+ return (relativeFrameTime - 30) / 10 + 34;
+ }
+ if (relativeFrameTime < 200) {
+ return (relativeFrameTime - 50) / 100 + 41;
+ }
+ if (relativeFrameTime < 1000) {
+ return (relativeFrameTime - 200) / 100 + 43;
+ }
+ return mBucketCounts.length - 1;
+ }
+}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 0d219a901b9d..4c4753872c03 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -152,7 +152,7 @@ flag {
name: "cache_sdk_system_features"
namespace: "system_performance"
description: "Feature flag to enable optimized cache for SDK-defined system feature lookups."
- bug: "375000483"
+ bug: "326623529"
}
flag {
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 852f04793f15..9c6b71b72ec8 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -417,6 +417,7 @@ public abstract class CameraDevice implements AutoCloseable {
* or if any of the output configurations sets a stream use
* case different from {@link
* android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT}.
+ * @throws UnsupportedOperationException if the camera has been opened in shared mode
* @see CameraExtensionCharacteristics#getSupportedExtensions
* @see CameraExtensionCharacteristics#getExtensionSupportedSizes
*/
@@ -1258,7 +1259,8 @@ public abstract class CameraDevice implements AutoCloseable {
* configurations are empty; or the session configuration
* executor is invalid;
* or the output dynamic range combination is
- * invalid/unsupported.
+ * invalid/unsupported; or the session type is not shared when
+ * camera has been opened in shared mode.
* @throws CameraAccessException In case the camera device is no longer connected or has
* encountered a fatal error.
* @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
@@ -1292,6 +1294,8 @@ public abstract class CameraDevice implements AutoCloseable {
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
+ * @throws UnsupportedOperationException if this is not a primary client of a camera opened in
+ * shared mode
*/
@NonNull
public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
@@ -1328,6 +1332,8 @@ public abstract class CameraDevice implements AutoCloseable {
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
+ * @throws UnsupportedOperationException if this is not a primary client of a camera opened in
+ * shared mode
*
* @see #TEMPLATE_PREVIEW
* @see #TEMPLATE_RECORD
@@ -1369,6 +1375,7 @@ public abstract class CameraDevice implements AutoCloseable {
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
+ * @throws UnsupportedOperationException if the camera has been opened in shared mode
*
* @see CaptureRequest.Builder
* @see TotalCaptureResult
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index aba2345f28d8..bfaff941939c 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1375,6 +1375,9 @@ public final class CameraManager {
* @throws SecurityException if the application does not have permission to
* access the camera
*
+ * @throws UnsupportedOperationException if {@link #isCameraDeviceSharingSupported} returns
+ * false for the given {@code cameraId}.
+ *
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*
@@ -1393,6 +1396,10 @@ public final class CameraManager {
if (executor == null) {
throw new IllegalArgumentException("executor was null");
}
+ if (!isCameraDeviceSharingSupported(cameraId)) {
+ throw new UnsupportedOperationException(
+ "CameraDevice sharing is not supported for Camera ID: " + cameraId);
+ }
openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0,
getRotationOverride(mContext), /*sharedMode*/true);
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 496d316eb028..1c65b0882e0f 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -299,6 +299,24 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
return mRequestType;
}
+ /**
+ * Get the stream ids corresponding to the target surfaces.
+ *
+ * @hide
+ */
+ public int[] getStreamIds() {
+ return mStreamIdxArray;
+ };
+
+ /**
+ * Get the surface ids corresponding to the target surfaces.
+ *
+ * @hide
+ */
+ public int[] getSurfaceIds() {
+ return mSurfaceIdxArray;
+ };
+
// If this request is part of constrained high speed request list that was created by
// {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
private boolean mIsPartOfCHSRequestList = false;
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index ce8661e90978..7e0456b22be8 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -340,6 +340,30 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
}
}
+ /**
+ * Shared Camera capture session API which can be used by the clients
+ * to start streaming.
+ *
+ * @hide
+ */
+ public int startStreaming(List<Surface> surfaces, Executor executor,
+ CaptureCallback callback) throws CameraAccessException {
+
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ checkNotClosed();
+
+ executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+ if (DEBUG) {
+ Log.v(TAG, mIdString + "startStreaming callback " + callback + " executor"
+ + " " + executor);
+ }
+
+ return addPendingSequence(mDeviceImpl.startStreaming(surfaces,
+ createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+ }
+ }
+
private void checkRepeatingRequest(CaptureRequest request) {
if (request == null) {
throw new IllegalArgumentException("request must not be null");
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 34c0f7b19da9..89a6b02b56c4 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -451,6 +451,16 @@ public class CameraDeviceImpl extends CameraDevice
}
}
+ /**
+ * When camera device is opened in shared mode, call to check if this is a primary client.
+ *
+ */
+ public boolean isPrimaryClient() {
+ synchronized (mInterfaceLock) {
+ return mIsPrimaryClient;
+ }
+ }
+
private Map<String, CameraCharacteristics> getPhysicalIdToChars() {
if (mPhysicalIdsToChars == null) {
try {
@@ -482,8 +492,19 @@ public class CameraDeviceImpl extends CameraDevice
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
Parcel resultParcel = Parcel.obtain();
- mRemoteDevice.getCaptureResultMetadataQueue().writeToParcel(resultParcel, 0);
+
+ // Passing in PARCELABLE_WRITE_RETURN_VALUE closes the ParcelFileDescriptors
+ // owned by MQDescriptor returned by getCaptureResultMetadataQueue()
+ // Though these will be closed when GC runs, that may not happen for a while.
+ // Also, apps running with StrictMode would get warnings / crash in the case they're not
+ // explicitly closed.
+ mRemoteDevice.getCaptureResultMetadataQueue().writeToParcel(resultParcel,
+ Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
mFMQReader = nativeCreateFMQReader(resultParcel);
+ // Recycle since resultParcel would dup fds from MQDescriptor as well. We don't
+ // need them after the native FMQ reader has been created. That is since the native
+ // creates calls MQDescriptor.readFromParcel() which again dups the fds.
+ resultParcel.recycle();
IBinder remoteDeviceBinder = remoteDevice.asBinder();
// For legacy camera device, remoteDevice is in the same process, and
@@ -847,24 +868,19 @@ public class CameraDeviceImpl extends CameraDevice
List<SharedOutputConfiguration> sharedConfigs =
sharedSessionConfiguration.getOutputStreamsInformation();
for (SharedOutputConfiguration sharedConfig : sharedConfigs) {
- if (outConfig.getConfiguredSize().equals(sharedConfig.getSize())
- && (outConfig.getConfiguredFormat() == sharedConfig.getFormat())
- && (outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE)
- && (outConfig.getSurfaceType() == sharedConfig.getSurfaceType())
+ if ((outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE)
&& (outConfig.getMirrorMode() == sharedConfig.getMirrorMode())
- && (outConfig.getUsage() == sharedConfig.getUsage())
&& (outConfig.isReadoutTimestampEnabled()
== sharedConfig.isReadoutTimestampEnabled())
&& (outConfig.getTimestampBase() == sharedConfig.getTimestampBase())
&& (outConfig.getStreamUseCase() == sharedConfig.getStreamUseCase())
- && (outConfig.getColorSpace().equals(
- sharedSessionConfiguration.getColorSpace()))
&& (outConfig.getDynamicRangeProfile()
== DynamicRangeProfiles.STANDARD)
- && (outConfig.getConfiguredDataspace() == sharedConfig.getDataspace())
&& (Objects.equals(outConfig.getPhysicalCameraId(),
sharedConfig.getPhysicalCameraId()))
&& (outConfig.getSensorPixelModes().isEmpty())
+ && (!outConfig.isMultiResolution())
+ && (!outConfig.isDeferredConfiguration())
&& (!outConfig.isShared())) {
//Found valid config, return true
return true;
@@ -896,14 +912,6 @@ public class CameraDeviceImpl extends CameraDevice
if (config.getExecutor() == null) {
throw new IllegalArgumentException("Invalid executor");
}
- if (mSharedMode) {
- if (config.getSessionType() != SessionConfiguration.SESSION_SHARED) {
- throw new IllegalArgumentException("Invalid session type");
- }
- if (!checkSharedSessionConfiguration(outputConfigs)) {
- throw new IllegalArgumentException("Invalid output configurations");
- }
- }
createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
config.getStateCallback(), config.getExecutor(), config.getSessionType(),
config.getSessionParameters());
@@ -921,17 +929,26 @@ public class CameraDeviceImpl extends CameraDevice
checkIfCameraClosedOrInError();
+ boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE);
+ if (Flags.cameraMultiClient() && mSharedMode) {
+ if (!isSharedSession) {
+ throw new IllegalArgumentException("Invalid session type");
+ }
+ if (!checkSharedSessionConfiguration(outputConfigurations)) {
+ throw new IllegalArgumentException("Invalid output configurations");
+ }
+ if (inputConfig != null) {
+ throw new IllegalArgumentException("Shared capture session doesn't support"
+ + " input configuration yet.");
+ }
+ }
+
boolean isConstrainedHighSpeed =
(operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
if (isConstrainedHighSpeed && inputConfig != null) {
throw new IllegalArgumentException("Constrained high speed session doesn't support"
+ " input configuration yet.");
}
- boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE);
- if (isSharedSession && inputConfig != null) {
- throw new IllegalArgumentException("Shared capture session doesn't support"
- + " input configuration yet.");
- }
if (mCurrentExtensionSession != null) {
mCurrentExtensionSession.commitStats();
@@ -993,8 +1010,7 @@ public class CameraDeviceImpl extends CameraDevice
mCharacteristics);
} else if (isSharedSession) {
newSession = new CameraSharedCaptureSessionImpl(mNextSessionId++,
- callback, executor, this, mDeviceExecutor, configureSuccess,
- mIsPrimaryClient);
+ callback, executor, this, mDeviceExecutor, configureSuccess);
} else {
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
callback, executor, this, mDeviceExecutor, configureSuccess);
@@ -1063,6 +1079,11 @@ public class CameraDeviceImpl extends CameraDevice
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
+ if (Flags.cameraMultiClient() && mSharedMode && !mIsPrimaryClient) {
+ throw new UnsupportedOperationException("In shared session mode,"
+ + "only primary clients can create capture request.");
+ }
+
for (String physicalId : physicalCameraIdSet) {
if (Objects.equals(physicalId, getId())) {
throw new IllegalStateException("Physical id matches the logical id!");
@@ -1089,6 +1110,11 @@ public class CameraDeviceImpl extends CameraDevice
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
+ if (Flags.cameraMultiClient() && mSharedMode && !mIsPrimaryClient) {
+ throw new UnsupportedOperationException("In shared session mode,"
+ + "only primary clients can create capture request.");
+ }
+
CameraMetadataNative templatedRequest = null;
templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
@@ -1108,6 +1134,10 @@ public class CameraDeviceImpl extends CameraDevice
throws CameraAccessException {
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
+ if (Flags.cameraMultiClient() && mSharedMode) {
+ throw new UnsupportedOperationException("In shared session mode,"
+ + "reprocess capture requests are not supported.");
+ }
CameraMetadataNative resultMetadata = new
CameraMetadataNative(inputResult.getNativeCopy());
@@ -1561,6 +1591,74 @@ public class CameraDeviceImpl extends CameraDevice
}
}
+ public int startStreaming(List<Surface> surfaces, CaptureCallback callback,
+ Executor executor) throws CameraAccessException {
+ // Need a valid executor, or current thread needs to have a looper, if
+ // callback is valid
+ executor = checkExecutor(executor, callback);
+ synchronized (mInterfaceLock) {
+ checkIfCameraClosedOrInError();
+ for (Surface surface : surfaces) {
+ if (surface == null) {
+ throw new IllegalArgumentException("Null Surface targets are not allowed");
+ }
+ }
+ // In shared session mode, if there are other active clients streaming then
+ // stoprepeating does not actually send request to HAL to cancel the request.
+ // Cameraservice will use this call to remove this client surfaces provided in its
+ // previous streaming request. If this is the only client for the shared camera device
+ // then camerservice will ask HAL to cancel the previous repeating request
+ stopRepeating();
+
+ // StartStreaming API does not allow capture parameters to be provided through a capture
+ // request. If the primary client has an existing repeating request, the camera service
+ // will either attach the provided surfaces to that request or create a default capture
+ // request if no repeating request is active. A default capture request is created here
+ // for initial use. The capture callback will provide capture results that include the
+ // actual capture parameters used for the streaming.
+ CaptureRequest.Builder builder = createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ for (Surface surface : surfaces) {
+ builder.addTarget(surface);
+ }
+ CaptureRequest request = builder.build();
+ request.convertSurfaceToStreamId(mConfiguredOutputs);
+
+ SubmitInfo requestInfo;
+ requestInfo = mRemoteDevice.startStreaming(request.getStreamIds(),
+ request.getSurfaceIds());
+ request.recoverStreamIdToSurface();
+ List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+ requestList.add(request);
+
+ if (callback != null) {
+ mCaptureCallbackMap.put(requestInfo.getRequestId(),
+ new CaptureCallbackHolder(
+ callback, requestList, executor, true, mNextSessionId - 1));
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
+ }
+ }
+
+ if (mRepeatingRequestId != REQUEST_ID_NONE) {
+ checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId,
+ requestInfo.getLastFrameNumber(), mRepeatingRequestTypes);
+ }
+
+ CaptureRequest[] requestArray = requestList.toArray(
+ new CaptureRequest[requestList.size()]);
+ mRepeatingRequestId = requestInfo.getRequestId();
+ mRepeatingRequestTypes = getRequestTypes(requestArray);
+
+ if (mIdle) {
+ mDeviceExecutor.execute(mCallOnActive);
+ }
+ mIdle = false;
+
+ return requestInfo.getRequestId();
+ }
+ }
+
public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
Executor executor) throws CameraAccessException {
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
@@ -2883,6 +2981,11 @@ public class CameraDeviceImpl extends CameraDevice
@Override
public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
throws CameraAccessException {
+ if (Flags.cameraMultiClient() && mSharedMode) {
+ throw new UnsupportedOperationException("In shared session mode,"
+ + "extension sessions are not supported.");
+ }
+
HashMap<String, CameraCharacteristics> characteristicsMap = new HashMap<>(
getPhysicalIdToChars());
characteristicsMap.put(mCameraId, mCharacteristics);
@@ -2918,4 +3021,4 @@ public class CameraDeviceImpl extends CameraDevice
}
}
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
index a1f31c0ced5e..8c0dcfb2a28c 100644
--- a/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
@@ -19,6 +19,8 @@ import android.annotation.FlaggedApi;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraOfflineSession;
+import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
import android.hardware.camera2.CameraSharedCaptureSession;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.OutputConfiguration;
@@ -28,6 +30,7 @@ import android.view.Surface;
import com.android.internal.camera.flags.Flags;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
@@ -46,7 +49,8 @@ public class CameraSharedCaptureSessionImpl
private static final String TAG = "CameraSharedCaptureSessionImpl";
private final CameraCaptureSessionImpl mSessionImpl;
private final ConditionVariable mInitialized = new ConditionVariable();
- private boolean mIsPrimary;
+ private final android.hardware.camera2.impl.CameraDeviceImpl mCameraDevice;
+ private final Executor mDeviceExecutor;
/**
* Create a new CameraCaptureSession.
@@ -54,24 +58,32 @@ public class CameraSharedCaptureSessionImpl
CameraSharedCaptureSessionImpl(int id,
CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Executor deviceStateExecutor, boolean configureSuccess, boolean isPrimary) {
+ Executor deviceStateExecutor, boolean configureSuccess) {
CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
- mIsPrimary = isPrimary;
+ mCameraDevice = deviceImpl;
+ mDeviceExecutor = deviceStateExecutor;
mInitialized.open();
}
@Override
- public int startStreaming(List<Surface> surfaces, Executor executor, CaptureCallback listener)
+ public int startStreaming(List<Surface> surfaces, Executor executor, CaptureCallback callback)
throws CameraAccessException {
- // Todo: Need to add implementation.
- return 0;
+ if (surfaces.isEmpty()) {
+ throw new IllegalArgumentException("No surfaces provided for streaming");
+ } else if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ } else if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
+ }
+
+ return mSessionImpl.startStreaming(surfaces, executor, callback);
}
@Override
public void stopStreaming() throws CameraAccessException {
- // Todo: Need to add implementation.
+ mSessionImpl.stopRepeating();
}
@Override
@@ -90,16 +102,24 @@ public class CameraSharedCaptureSessionImpl
}
@Override
+ public boolean supportsOfflineProcessing(Surface surface) {
+ return false;
+ }
+
+ @Override
public void abortCaptures() throws CameraAccessException {
- if (mIsPrimary) {
+ if (mCameraDevice.isPrimaryClient()) {
mSessionImpl.abortCaptures();
+ return;
}
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
}
@Override
public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
Handler handler) throws CameraAccessException {
- if (mIsPrimary) {
+ if (mCameraDevice.isPrimaryClient()) {
return mSessionImpl.setRepeatingRequest(request, listener, handler);
}
throw new UnsupportedOperationException("Shared capture session only supports this method"
@@ -107,16 +127,30 @@ public class CameraSharedCaptureSessionImpl
}
@Override
+ public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
+ CaptureCallback listener)
+ throws CameraAccessException {
+ if (mCameraDevice.isPrimaryClient()) {
+ return mSessionImpl.setSingleRepeatingRequest(request, executor, listener);
+ }
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
+ }
+
+ @Override
public void stopRepeating() throws CameraAccessException {
- if (mIsPrimary) {
+ if (mCameraDevice.isPrimaryClient()) {
mSessionImpl.stopRepeating();
+ return;
}
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
}
@Override
public int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
throws CameraAccessException {
- if (mIsPrimary) {
+ if (mCameraDevice.isPrimaryClient()) {
return mSessionImpl.capture(request, listener, handler);
}
throw new UnsupportedOperationException("Shared capture session only supports this method"
@@ -124,6 +158,17 @@ public class CameraSharedCaptureSessionImpl
}
@Override
+ public int captureSingleRequest(CaptureRequest request, Executor executor,
+ CaptureCallback listener)
+ throws CameraAccessException {
+ if (mCameraDevice.isPrimaryClient()) {
+ return mSessionImpl.captureSingleRequest(request, executor, listener);
+ }
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
+ }
+
+ @Override
public void tearDown(Surface surface) throws CameraAccessException {
mSessionImpl.tearDown(surface);
}
@@ -149,48 +194,72 @@ public class CameraSharedCaptureSessionImpl
}
@Override
+ public CameraOfflineSession switchToOffline(Collection<Surface> offlineSurfaces,
+ Executor executor, CameraOfflineSessionCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared capture session do not support this method"
+ );
+ }
+
+ @Override
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
Handler handler) throws CameraAccessException {
- throw new UnsupportedOperationException("Shared Capture session doesn't support"
+ throw new UnsupportedOperationException("Shared Capture session do not support"
+ + " this method");
+ }
+
+ @Override
+ public int setRepeatingBurstRequests(List<CaptureRequest> requests,
+ Executor executor, CaptureCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared Capture session do not support"
+ " this method");
}
@Override
public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
Handler handler) throws CameraAccessException {
- throw new UnsupportedOperationException("Shared Capture session doesn't support"
+ throw new UnsupportedOperationException("Shared Capture session do not support"
+ + " this method");
+ }
+
+ @Override
+ public int captureBurstRequests(List<CaptureRequest> requests,
+ Executor executor, CaptureCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared Capture session do not support"
+ " this method");
}
@Override
public void updateOutputConfiguration(OutputConfiguration config)
throws CameraAccessException {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
@Override
public void finalizeOutputConfigurations(List<OutputConfiguration> deferredOutputConfigs)
throws CameraAccessException {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
@Override
public void prepare(Surface surface) throws CameraAccessException {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
@Override
public void prepare(int maxCount, Surface surface) throws CameraAccessException {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
@Override
public void closeWithoutDraining() {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index a79e084b7f41..0b8e9c2687c3 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -65,6 +65,17 @@ public class ICameraDeviceUserWrapper {
}
}
+ public SubmitInfo startStreaming(int[] streamIdxArray, int[] surfaceIdxArray)
+ throws CameraAccessException {
+ try {
+ return mRemoteDevice.startStreaming(streamIdxArray, surfaceIdxArray);
+ } catch (ServiceSpecificException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ } catch (RemoteException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ }
+ }
+
public SubmitInfo submitRequest(CaptureRequest request, boolean streaming)
throws CameraAccessException {
try {
@@ -325,4 +336,4 @@ public class ICameraDeviceUserWrapper {
}
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index e12c46322d8c..d394154a2c0e 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1803,6 +1803,19 @@ public final class OutputConfiguration implements Parcelable {
}
/**
+ * Get the flag indicating if this {@link OutputConfiguration} is for a multi-resolution output
+ * with a MultiResolutionImageReader.
+ *
+ * @return true if this {@link OutputConfiguration} is for a multi-resolution output with a
+ * MultiResolutionImageReader.
+ *
+ * @hide
+ */
+ public boolean isMultiResolution() {
+ return mIsMultiResolution;
+ }
+
+ /**
* Get the physical camera ID associated with this {@link OutputConfiguration}.
*
* <p>If this OutputConfiguration isn't targeting a physical camera of a logical
diff --git a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
index cdcc92ce4404..365f870ba22d 100644
--- a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
@@ -212,7 +212,7 @@ public final class SharedSessionConfiguration {
}
public @Nullable String getPhysicalCameraId() {
- return mPhysicalCameraId;
+ return mPhysicalCameraId.isEmpty() ? null : mPhysicalCameraId;
}
}
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 99f331f43450..de88895ba55c 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -99,8 +99,8 @@ public class HubEndpoint {
private final Object mLock = new Object();
private final HubEndpointInfo mPendingHubEndpointInfo;
- @Nullable private final IHubEndpointLifecycleCallback mLifecycleCallback;
- @Nullable private final IHubEndpointMessageCallback mMessageCallback;
+ @Nullable private final HubEndpointLifecycleCallback mLifecycleCallback;
+ @Nullable private final HubEndpointMessageCallback mMessageCallback;
@NonNull private final Executor mLifecycleCallbackExecutor;
@NonNull private final Executor mMessageCallbackExecutor;
@@ -335,9 +335,9 @@ public class HubEndpoint {
private HubEndpoint(
@NonNull HubEndpointInfo pendingEndpointInfo,
- @Nullable IHubEndpointLifecycleCallback endpointLifecycleCallback,
+ @Nullable HubEndpointLifecycleCallback endpointLifecycleCallback,
@NonNull Executor lifecycleCallbackExecutor,
- @Nullable IHubEndpointMessageCallback endpointMessageCallback,
+ @Nullable HubEndpointMessageCallback endpointMessageCallback,
@NonNull Executor messageCallbackExecutor) {
mPendingHubEndpointInfo = pendingEndpointInfo;
mLifecycleCallback = endpointLifecycleCallback;
@@ -485,12 +485,12 @@ public class HubEndpoint {
}
@Nullable
- public IHubEndpointLifecycleCallback getLifecycleCallback() {
+ public HubEndpointLifecycleCallback getLifecycleCallback() {
return mLifecycleCallback;
}
@Nullable
- public IHubEndpointMessageCallback getMessageCallback() {
+ public HubEndpointMessageCallback getMessageCallback() {
return mMessageCallback;
}
@@ -498,11 +498,11 @@ public class HubEndpoint {
public static final class Builder {
private final String mPackageName;
- @Nullable private IHubEndpointLifecycleCallback mLifecycleCallback;
+ @Nullable private HubEndpointLifecycleCallback mLifecycleCallback;
@NonNull private Executor mLifecycleCallbackExecutor;
- @Nullable private IHubEndpointMessageCallback mMessageCallback;
+ @Nullable private HubEndpointMessageCallback mMessageCallback;
@NonNull private Executor mMessageCallbackExecutor;
private int mVersion;
@@ -539,10 +539,13 @@ public class HubEndpoint {
return this;
}
- /** Attach a callback interface for lifecycle events for this Endpoint */
+ /**
+ * Attach a callback interface for lifecycle events for this Endpoint. Callback will be
+ * posted to the main thread.
+ */
@NonNull
public Builder setLifecycleCallback(
- @NonNull IHubEndpointLifecycleCallback lifecycleCallback) {
+ @NonNull HubEndpointLifecycleCallback lifecycleCallback) {
mLifecycleCallback = lifecycleCallback;
return this;
}
@@ -554,15 +557,18 @@ public class HubEndpoint {
@NonNull
public Builder setLifecycleCallback(
@NonNull @CallbackExecutor Executor executor,
- @NonNull IHubEndpointLifecycleCallback lifecycleCallback) {
+ @NonNull HubEndpointLifecycleCallback lifecycleCallback) {
mLifecycleCallbackExecutor = executor;
mLifecycleCallback = lifecycleCallback;
return this;
}
- /** Attach a callback interface for message events for this Endpoint */
+ /**
+ * Attach a callback interface for message events for this Endpoint. Callback will be posted
+ * to the main thread.
+ */
@NonNull
- public Builder setMessageCallback(@NonNull IHubEndpointMessageCallback messageCallback) {
+ public Builder setMessageCallback(@NonNull HubEndpointMessageCallback messageCallback) {
mMessageCallback = messageCallback;
return this;
}
@@ -574,7 +580,7 @@ public class HubEndpoint {
@NonNull
public Builder setMessageCallback(
@NonNull @CallbackExecutor Executor executor,
- @NonNull IHubEndpointMessageCallback messageCallback) {
+ @NonNull HubEndpointMessageCallback messageCallback) {
mMessageCallbackExecutor = executor;
mMessageCallback = messageCallback;
return this;
diff --git a/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java b/core/java/android/hardware/contexthub/HubEndpointDiscoveryCallback.java
index a61a7ebd0de9..4672bbb74170 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java
+++ b/core/java/android/hardware/contexthub/HubEndpointDiscoveryCallback.java
@@ -30,7 +30,7 @@ import java.util.List;
*/
@SystemApi
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
-public interface IHubEndpointDiscoveryCallback {
+public interface HubEndpointDiscoveryCallback {
/**
* Called when a list of hub endpoints have started.
*
diff --git a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java b/core/java/android/hardware/contexthub/HubEndpointLifecycleCallback.java
index 698ed0adfd80..6d75010711cc 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
+++ b/core/java/android/hardware/contexthub/HubEndpointLifecycleCallback.java
@@ -29,7 +29,7 @@ import android.chre.flags.Flags;
*/
@SystemApi
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
-public interface IHubEndpointLifecycleCallback {
+public interface HubEndpointLifecycleCallback {
/**
* Called when an endpoint is requesting a session be opened with another endpoint.
*
diff --git a/core/java/android/hardware/contexthub/IHubEndpointMessageCallback.java b/core/java/android/hardware/contexthub/HubEndpointMessageCallback.java
index fde7017b5e76..5db54efc8893 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointMessageCallback.java
+++ b/core/java/android/hardware/contexthub/HubEndpointMessageCallback.java
@@ -26,18 +26,18 @@ import android.chre.flags.Flags;
* <p>This interface can be attached to an endpoint through {@link
* HubEndpoint.Builder#setMessageCallback} method. Methods in this interface will only be called
* when the endpoint is currently registered and has an open session. The endpoint will receive
- * session lifecycle callbacks through {@link IHubEndpointLifecycleCallback}.
+ * session lifecycle callbacks through {@link HubEndpointLifecycleCallback}.
*
* @hide
*/
@SystemApi
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
-public interface IHubEndpointMessageCallback {
+public interface HubEndpointMessageCallback {
/**
* Callback interface for receiving messages for a particular endpoint session.
*
* @param session The session this message is sent through. Previously specified in a {@link
- * IHubEndpointLifecycleCallback#onSessionOpened(HubEndpointSession)} call.
+ * HubEndpointLifecycleCallback#onSessionOpened(HubEndpointSession)} call.
* @param message The {@link HubMessage} object representing a message received by the endpoint
* that registered this callback interface. This message is constructed by the
*/
diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java
index 77f937ebeabc..f7f5636264e4 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSession.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSession.java
@@ -137,7 +137,7 @@ public class HubEndpointSession implements AutoCloseable {
* no service associated to this session.
*
* <p>For hub initiated sessions, the object was previously used in as an argument for open
- * request in {@link IHubEndpointLifecycleCallback#onSessionOpenRequest}.
+ * request in {@link HubEndpointLifecycleCallback#onSessionOpenRequest}.
*
* <p>For app initiated sessions, the object was previously used in an open request in {@link
* android.hardware.location.ContextHubManager#openSession}
diff --git a/core/java/android/hardware/contexthub/HubEndpointSessionResult.java b/core/java/android/hardware/contexthub/HubEndpointSessionResult.java
index 1f2bdb985008..b193d00467aa 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSessionResult.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSessionResult.java
@@ -23,7 +23,7 @@ import android.annotation.SystemApi;
import android.chre.flags.Flags;
/**
- * Return type of {@link IHubEndpointLifecycleCallback#onSessionOpenRequest}. The value determines
+ * Return type of {@link HubEndpointLifecycleCallback#onSessionOpenRequest}. The value determines
* whether a open session request from the remote is accepted or not.
*
* @hide
diff --git a/core/java/android/hardware/contexthub/HubServiceInfo.java b/core/java/android/hardware/contexthub/HubServiceInfo.java
index 2f33e8f8872b..651be3b17c33 100644
--- a/core/java/android/hardware/contexthub/HubServiceInfo.java
+++ b/core/java/android/hardware/contexthub/HubServiceInfo.java
@@ -112,6 +112,7 @@ public final class HubServiceInfo implements Parcelable {
* <p>The value can be one of {@link HubServiceInfo#FORMAT_CUSTOM}, {@link
* HubServiceInfo#FORMAT_AIDL} or {@link HubServiceInfo#FORMAT_PW_RPC_PROTOBUF}.
*/
+ @ServiceFormat
public int getFormat() {
return mFormat;
}
@@ -178,7 +179,8 @@ public final class HubServiceInfo implements Parcelable {
* <li>Pigweed RPC with Protobuf: com.example.proto.ExampleService
* </ol>
*
- * @param serviceDescriptor The service descriptor.
+ * @param serviceDescriptor The service descriptor for the interface, provided by the
+ * vendor.
* @param format One of {@link HubServiceInfo#FORMAT_CUSTOM}, {@link
* HubServiceInfo#FORMAT_AIDL} or {@link HubServiceInfo#FORMAT_PW_RPC_PROTOBUF}.
* @param majorVersion Breaking changes should be a major version bump.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index ffa546067eff..9030810a1c1a 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -106,7 +106,7 @@ public final class DisplayManagerGlobal {
@IntDef(prefix = {"EVENT_DISPLAY_"}, flag = true, value = {
EVENT_DISPLAY_ADDED,
- EVENT_DISPLAY_CHANGED,
+ EVENT_DISPLAY_BASIC_CHANGED,
EVENT_DISPLAY_REMOVED,
EVENT_DISPLAY_BRIGHTNESS_CHANGED,
EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED,
@@ -119,7 +119,8 @@ public final class DisplayManagerGlobal {
public @interface DisplayEvent {}
public static final int EVENT_DISPLAY_ADDED = 1;
- public static final int EVENT_DISPLAY_CHANGED = 2;
+ public static final int EVENT_DISPLAY_BASIC_CHANGED = 2;
+
public static final int EVENT_DISPLAY_REMOVED = 3;
public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4;
public static final int EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED = 5;
@@ -130,7 +131,7 @@ public final class DisplayManagerGlobal {
@LongDef(prefix = {"INTERNAL_EVENT_FLAG_"}, flag = true, value = {
INTERNAL_EVENT_FLAG_DISPLAY_ADDED,
- INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED,
INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED,
INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
@@ -143,7 +144,7 @@ public final class DisplayManagerGlobal {
public @interface InternalEventFlag {}
public static final long INTERNAL_EVENT_FLAG_DISPLAY_ADDED = 1L << 0;
- public static final long INTERNAL_EVENT_FLAG_DISPLAY_CHANGED = 1L << 1;
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED = 1L << 1;
public static final long INTERNAL_EVENT_FLAG_DISPLAY_REMOVED = 1L << 2;
public static final long INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED = 1L << 3;
public static final long INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED = 1L << 4;
@@ -485,7 +486,7 @@ public final class DisplayManagerGlobal {
// There can be racing condition between DMS and WMS callbacks, so force triggering the
// listener to make sure the client can get the onDisplayChanged callback even if
// DisplayInfo is not changed (Display read from both DisplayInfo and WindowConfiguration).
- handleDisplayEvent(displayId, EVENT_DISPLAY_CHANGED, true /* forceUpdate */);
+ handleDisplayEvent(displayId, EVENT_DISPLAY_BASIC_CHANGED, true /* forceUpdate */);
}
private static Looper getLooperForHandler(@Nullable Handler handler) {
@@ -518,7 +519,8 @@ public final class DisplayManagerGlobal {
}
if (mDispatchNativeCallbacks) {
mask |= INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
}
if (!mTopologyListeners.isEmpty()) {
@@ -571,7 +573,8 @@ public final class DisplayManagerGlobal {
}
info = getDisplayInfoLocked(displayId);
- if (event == EVENT_DISPLAY_CHANGED && mDispatchNativeCallbacks) {
+ if ((event == EVENT_DISPLAY_BASIC_CHANGED
+ || event == EVENT_DISPLAY_REFRESH_RATE_CHANGED) && mDispatchNativeCallbacks) {
// Choreographer only supports a single display, so only dispatch refresh rate
// changes for the default display.
if (displayId == Display.DEFAULT_DISPLAY) {
@@ -1492,9 +1495,9 @@ public final class DisplayManagerGlobal {
mListener.onDisplayAdded(displayId);
}
break;
- case EVENT_DISPLAY_CHANGED:
- if ((mInternalEventFlagsMask & INTERNAL_EVENT_FLAG_DISPLAY_CHANGED)
- != 0) {
+ case EVENT_DISPLAY_BASIC_CHANGED:
+ if ((mInternalEventFlagsMask
+ & INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED) != 0) {
if (info != null && (forceUpdate || !info.equals(mDisplayInfo))) {
if (extraLogging()) {
Slog.i(TAG, "Sending onDisplayChanged: Display Changed. Info: "
@@ -1691,8 +1694,8 @@ public final class DisplayManagerGlobal {
switch (event) {
case EVENT_DISPLAY_ADDED:
return "ADDED";
- case EVENT_DISPLAY_CHANGED:
- return "CHANGED";
+ case EVENT_DISPLAY_BASIC_CHANGED:
+ return "BASIC_CHANGED";
case EVENT_DISPLAY_REMOVED:
return "REMOVED";
case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
@@ -1763,7 +1766,11 @@ public final class DisplayManagerGlobal {
}
if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
- baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_CHANGED;
+ // For backward compatibility, a client subscribing to
+ // DisplayManager.EVENT_FLAG_DISPLAY_CHANGED will be enrolled to both Basic and
+ // RR changes
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
}
if ((eventFlags
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index 211aefffa34c..ba5dfc094afb 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -129,14 +129,38 @@ public final class DisplayTopology implements Parcelable {
}
/**
+ * Update the size of a display and normalize the topology.
+ * @param displayId The logical display ID
+ * @param width The new width
+ * @param height The new height
+ * @return True if the topology has changed.
+ */
+ public boolean updateDisplay(int displayId, float width, float height) {
+ TreeNode display = findDisplay(displayId, mRoot);
+ if (display == null) {
+ return false;
+ }
+ if (floatEquals(display.mWidth, width) && floatEquals(display.mHeight, height)) {
+ return false;
+ }
+ display.mWidth = width;
+ display.mHeight = height;
+ normalize();
+ Slog.i(TAG, "Display with ID " + displayId + " updated, new width: " + width
+ + ", new height: " + height);
+ return true;
+ }
+
+ /**
* Remove a display from the topology.
* The default topology is created from the remaining displays, as if they were reconnected
* one by one.
* @param displayId The logical display ID
+ * @return True if the display was present in the topology and removed.
*/
- public void removeDisplay(int displayId) {
+ public boolean removeDisplay(int displayId) {
if (findDisplay(displayId, mRoot) == null) {
- return;
+ return false;
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(mRoot);
@@ -159,6 +183,7 @@ public final class DisplayTopology implements Parcelable {
} else {
Slog.i(TAG, "Display with ID " + displayId + " removed");
}
+ return true;
}
/**
@@ -685,12 +710,12 @@ public final class DisplayTopology implements Parcelable {
/**
* The width of the display in density-independent pixels (dp).
*/
- private final float mWidth;
+ private float mWidth;
/**
* The height of the display in density-independent pixels (dp).
*/
- private final float mHeight;
+ private float mHeight;
/**
* The position of this display relative to its parent.
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 313bad50e88e..c4d11cd8aff7 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -204,3 +204,10 @@ flag {
description: "Allows the user to disable input scrolling acceleration for mouse."
bug: "383555305"
}
+
+flag {
+ name: "remove_fallback_modifiers"
+ namespace: "input"
+ description: "Removes modifiers from the original key event that activated the fallback, ensuring that only the intended fallback event is sent."
+ bug: "382545048"
+}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 1e0cc94612dd..0cd320981c93 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -36,11 +36,11 @@ import android.content.pm.PackageManager;
import android.hardware.contexthub.ErrorCode;
import android.hardware.contexthub.HubDiscoveryInfo;
import android.hardware.contexthub.HubEndpoint;
+import android.hardware.contexthub.HubEndpointDiscoveryCallback;
import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubEndpointLifecycleCallback;
import android.hardware.contexthub.HubServiceInfo;
import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback;
-import android.hardware.contexthub.IHubEndpointDiscoveryCallback;
-import android.hardware.contexthub.IHubEndpointLifecycleCallback;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
@@ -207,7 +207,7 @@ public final class ContextHubManager {
private Handler mCallbackHandler;
/** A map of endpoint discovery callbacks currently registered */
- private Map<IHubEndpointDiscoveryCallback, IContextHubEndpointDiscoveryCallback>
+ private Map<HubEndpointDiscoveryCallback, IContextHubEndpointDiscoveryCallback>
mDiscoveryCallbacks = new ConcurrentHashMap<>();
/**
@@ -718,7 +718,19 @@ public final class ContextHubManager {
/**
* Find a list of endpoints that provides a specific service.
*
- * @param serviceDescriptor Statically generated ID for an endpoint.
+ * <p>Service descriptor should uniquely identify the interface (scoped to type). Convention of
+ * the descriptor depend on interface type.
+ *
+ * <p>Examples:
+ *
+ * <ol>
+ * <li>AOSP-defined AIDL: android.hardware.something.IFoo/default
+ * <li>Vendor-defined AIDL: com.example.something.IBar/default
+ * <li>Pigweed RPC with Protobuf: com.example.proto.ExampleService
+ * </ol>
+ *
+ * @param serviceDescriptor The service descriptor for a service provided by the hub. The value
+ * cannot be null or empty.
* @return A list of {@link HubDiscoveryInfo} objects that represents the result of discovery.
* @throws IllegalArgumentException if the serviceDescriptor is empty/null.
*/
@@ -750,14 +762,15 @@ public final class ContextHubManager {
/**
* Creates an interface to invoke endpoint discovery callbacks to send down to the service.
*
- * @param callback the callback to invoke at the client process
* @param executor the executor to invoke callbacks for this client
+ * @param callback the callback to invoke at the client process
+ * @param serviceDescriptor an optional descriptor to match discovery list with
* @return the callback interface
*/
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
private IContextHubEndpointDiscoveryCallback createDiscoveryCallback(
- IHubEndpointDiscoveryCallback callback,
Executor executor,
+ HubEndpointDiscoveryCallback callback,
@Nullable String serviceDescriptor) {
return new IContextHubEndpointDiscoveryCallback.Stub() {
@Override
@@ -829,36 +842,36 @@ public final class ContextHubManager {
}
/**
- * Equivalent to {@link #registerEndpointDiscoveryCallback(long, IHubEndpointDiscoveryCallback,
- * Executor)} with the default executor in the main thread.
+ * Equivalent to {@link #registerEndpointDiscoveryCallback(Executor,
+ * HubEndpointDiscoveryCallback, long)} with the default executor in the main thread.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void registerEndpointDiscoveryCallback(
- long endpointId, @NonNull IHubEndpointDiscoveryCallback callback) {
+ @NonNull HubEndpointDiscoveryCallback callback, long endpointId) {
registerEndpointDiscoveryCallback(
- endpointId, callback, new HandlerExecutor(Handler.getMain()));
+ new HandlerExecutor(Handler.getMain()), callback, endpointId);
}
/**
* Registers a callback to be notified when the hub endpoint with the corresponding endpoint ID
* has started or stopped.
*
- * @param endpointId The identifier of the hub endpoint.
- * @param callback The callback to be invoked.
* @param executor The executor to invoke the callback on.
+ * @param callback The callback to be invoked.
+ * @param endpointId The identifier of the hub endpoint.
* @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void registerEndpointDiscoveryCallback(
- long endpointId,
- @NonNull IHubEndpointDiscoveryCallback callback,
- @NonNull Executor executor) {
- Objects.requireNonNull(callback, "callback cannot be null");
+ @NonNull Executor executor,
+ @NonNull HubEndpointDiscoveryCallback callback,
+ long endpointId) {
Objects.requireNonNull(executor, "executor cannot be null");
+ Objects.requireNonNull(callback, "callback cannot be null");
IContextHubEndpointDiscoveryCallback iCallback =
- createDiscoveryCallback(callback, executor, null);
+ createDiscoveryCallback(executor, callback, null);
try {
mService.registerEndpointDiscoveryCallbackId(endpointId, iCallback);
} catch (RemoteException e) {
@@ -869,42 +882,42 @@ public final class ContextHubManager {
}
/**
- * Equivalent to {@link #registerEndpointDiscoveryCallback(String,
- * IHubEndpointDiscoveryCallback, Executor)} with the default executor in the main thread.
+ * Equivalent to {@link #registerEndpointDiscoveryCallback(Executor,
+ * HubEndpointDiscoveryCallback, String)} with the default executor in the main thread.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void registerEndpointDiscoveryCallback(
- @NonNull String serviceDescriptor, @NonNull IHubEndpointDiscoveryCallback callback) {
+ @NonNull HubEndpointDiscoveryCallback callback, @NonNull String serviceDescriptor) {
registerEndpointDiscoveryCallback(
- serviceDescriptor, callback, new HandlerExecutor(Handler.getMain()));
+ new HandlerExecutor(Handler.getMain()), callback, serviceDescriptor);
}
/**
* Registers a callback to be notified when the hub endpoint with the corresponding service
* descriptor has started or stopped.
*
+ * @param executor The executor to invoke the callback on.
* @param serviceDescriptor The service descriptor of the hub endpoint.
* @param callback The callback to be invoked.
- * @param executor The executor to invoke the callback on.
* @throws IllegalArgumentException if the serviceDescriptor is empty.
* @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void registerEndpointDiscoveryCallback(
- @NonNull String serviceDescriptor,
- @NonNull IHubEndpointDiscoveryCallback callback,
- @NonNull Executor executor) {
- Objects.requireNonNull(serviceDescriptor, "serviceDescriptor cannot be null");
- Objects.requireNonNull(callback, "callback cannot be null");
+ @NonNull Executor executor,
+ @NonNull HubEndpointDiscoveryCallback callback,
+ @NonNull String serviceDescriptor) {
Objects.requireNonNull(executor, "executor cannot be null");
+ Objects.requireNonNull(callback, "callback cannot be null");
+ Objects.requireNonNull(serviceDescriptor, "serviceDescriptor cannot be null");
if (serviceDescriptor.isBlank()) {
throw new IllegalArgumentException("Invalid service descriptor: " + serviceDescriptor);
}
IContextHubEndpointDiscoveryCallback iCallback =
- createDiscoveryCallback(callback, executor, serviceDescriptor);
+ createDiscoveryCallback(executor, callback, serviceDescriptor);
try {
mService.registerEndpointDiscoveryCallbackDescriptor(serviceDescriptor, iCallback);
} catch (RemoteException e) {
@@ -924,7 +937,7 @@ public final class ContextHubManager {
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void unregisterEndpointDiscoveryCallback(
- @NonNull IHubEndpointDiscoveryCallback callback) {
+ @NonNull HubEndpointDiscoveryCallback callback) {
Objects.requireNonNull(callback, "callback cannot be null");
IContextHubEndpointDiscoveryCallback iCallback = mDiscoveryCallbacks.remove(callback);
if (iCallback == null) {
@@ -1291,7 +1304,7 @@ public final class ContextHubManager {
* service.
*
* <p>Context Hub Service will create the endpoint session and notify the registered endpoint.
- * The registered endpoint will receive callbacks on its {@link IHubEndpointLifecycleCallback}
+ * The registered endpoint will receive callbacks on its {@link HubEndpointLifecycleCallback}
* object regarding the lifecycle events of the session.
*
* @param hubEndpoint {@link HubEndpoint} object previously registered via {@link
@@ -1311,7 +1324,7 @@ public final class ContextHubManager {
* described by a {@link HubServiceInfo} object.
*
* <p>Context Hub Service will create the endpoint session and notify the registered endpoint.
- * The registered endpoint will receive callbacks on its {@link IHubEndpointLifecycleCallback}
+ * The registered endpoint will receive callbacks on its {@link HubEndpointLifecycleCallback}
* object regarding the lifecycle events of the session.
*
* @param hubEndpoint {@link HubEndpoint} object previously registered via {@link
diff --git a/core/java/android/net/thread/flags.aconfig b/core/java/android/net/thread/flags.aconfig
index afb982ba64ca..100d50d8e70c 100644
--- a/core/java/android/net/thread/flags.aconfig
+++ b/core/java/android/net/thread/flags.aconfig
@@ -17,5 +17,5 @@ flag {
is_exported: true
namespace: "thread_network"
description: "Controls whether the Android Thread feature is enabled"
- bug: "301473012"
+ bug: "384596973"
}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 50121278f0e6..ecd90e46e432 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -45,7 +45,8 @@ import java.util.function.BiFunction;
* {@link PersistableBundle} subclass.
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
-public class BaseBundle {
+@SuppressWarnings("HiddenSuperclass")
+public class BaseBundle implements Parcel.ClassLoaderProvider {
/** @hide */
protected static final String TAG = "Bundle";
static final boolean DEBUG = false;
@@ -299,8 +300,9 @@ public class BaseBundle {
/**
* Return the ClassLoader currently associated with this Bundle.
+ * @hide
*/
- ClassLoader getClassLoader() {
+ public ClassLoader getClassLoader() {
return mClassLoader;
}
@@ -405,6 +407,9 @@ public class BaseBundle {
if ((mFlags & Bundle.FLAG_VERIFY_TOKENS_PRESENT) != 0) {
Intent.maybeMarkAsMissingCreatorToken(object);
}
+ } else if (object instanceof Bundle) {
+ Bundle bundle = (Bundle) object;
+ bundle.setClassLoaderSameAsContainerBundleWhenRetrievedFirstTime(this);
}
return (clazz != null) ? clazz.cast(object) : (T) object;
}
@@ -475,10 +480,10 @@ public class BaseBundle {
map.erase();
map.ensureCapacity(count);
}
- int numLazyValues = 0;
+ int[] numLazyValues = new int[]{0};
try {
- numLazyValues = parcelledData.readArrayMap(map, count, !parcelledByNative,
- /* lazy */ ownsParcel, mClassLoader);
+ parcelledData.readArrayMap(map, count, !parcelledByNative,
+ /* lazy */ ownsParcel, this, numLazyValues);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -489,14 +494,14 @@ public class BaseBundle {
} finally {
mWeakParcelledData = null;
if (ownsParcel) {
- if (numLazyValues == 0) {
+ if (numLazyValues[0] == 0) {
recycleParcel(parcelledData);
} else {
mWeakParcelledData = new WeakReference<>(parcelledData);
}
}
- mLazyValues = numLazyValues;
+ mLazyValues = numLazyValues[0];
mParcelledByNative = false;
mMap = map;
// Set field last as it is volatile
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 819d58d9f059..55bfd451d97a 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -141,6 +141,8 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
STRIPPED.putInt("STRIPPED", 1);
}
+ private boolean isFirstRetrievedFromABundle = false;
+
/**
* Constructs a new, empty Bundle.
*/
@@ -1020,7 +1022,9 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
return null;
}
try {
- return (Bundle) o;
+ Bundle bundle = (Bundle) o;
+ bundle.setClassLoaderSameAsContainerBundleWhenRetrievedFirstTime(this);
+ return bundle;
} catch (ClassCastException e) {
typeWarning(key, o, "Bundle", e);
return null;
@@ -1028,6 +1032,21 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
/**
+ * Set the ClassLoader of a bundle to its container bundle. This is necessary so that when a
+ * bundle's ClassLoader is changed, it can be propagated to its children. Do this only when it
+ * is retrieved from the container bundle first time though. Once it is accessed outside of its
+ * container, its ClassLoader should no longer be changed by its container anymore.
+ *
+ * @param containerBundle the bundle this bundle is retrieved from.
+ */
+ void setClassLoaderSameAsContainerBundleWhenRetrievedFirstTime(BaseBundle containerBundle) {
+ if (!isFirstRetrievedFromABundle) {
+ setClassLoader(containerBundle.getClassLoader());
+ isFirstRetrievedFromABundle = true;
+ }
+ }
+
+ /**
* Returns the value associated with the given key, or {@code null} if
* no mapping of the desired type exists for the given key or a {@code null}
* value is explicitly associated with the key.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index cf473ec9c3ea..5ba6553a58c9 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4650,7 +4650,7 @@ public final class Parcel {
* @hide
*/
@Nullable
- public Object readLazyValue(@Nullable ClassLoader loader) {
+ private Object readLazyValue(@Nullable ClassLoaderProvider loaderProvider) {
int start = dataPosition();
int type = readInt();
if (isLengthPrefixed(type)) {
@@ -4661,12 +4661,17 @@ public final class Parcel {
int end = MathUtils.addOrThrow(dataPosition(), objectLength);
int valueLength = end - start;
setDataPosition(end);
- return new LazyValue(this, start, valueLength, type, loader);
+ return new LazyValue(this, start, valueLength, type, loaderProvider);
} else {
- return readValue(type, loader, /* clazz */ null);
+ return readValue(type, getClassLoader(loaderProvider), /* clazz */ null);
}
}
+ @Nullable
+ private static ClassLoader getClassLoader(@Nullable ClassLoaderProvider loaderProvider) {
+ return loaderProvider == null ? null : loaderProvider.getClassLoader();
+ }
+
private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> {
/**
@@ -4680,7 +4685,12 @@ public final class Parcel {
private final int mPosition;
private final int mLength;
private final int mType;
- @Nullable private final ClassLoader mLoader;
+ // this member is set when a bundle that includes a LazyValue is unparceled. But it is used
+ // when apply method is called. Between these 2 events, the bundle's ClassLoader could have
+ // changed. Let the bundle be a ClassLoaderProvider allows the bundle provides its current
+ // ClassLoader at the time apply method is called.
+ @NonNull
+ private final ClassLoaderProvider mLoaderProvider;
@Nullable private Object mObject;
/**
@@ -4691,12 +4701,13 @@ public final class Parcel {
*/
@Nullable private volatile Parcel mSource;
- LazyValue(Parcel source, int position, int length, int type, @Nullable ClassLoader loader) {
+ LazyValue(Parcel source, int position, int length, int type,
+ @NonNull ClassLoaderProvider loaderProvider) {
mSource = requireNonNull(source);
mPosition = position;
mLength = length;
mType = type;
- mLoader = loader;
+ mLoaderProvider = loaderProvider;
}
@Override
@@ -4709,7 +4720,8 @@ public final class Parcel {
int restore = source.dataPosition();
try {
source.setDataPosition(mPosition);
- mObject = source.readValue(mLoader, clazz, itemTypes);
+ mObject = source.readValue(mLoaderProvider.getClassLoader(), clazz,
+ itemTypes);
} finally {
source.setDataPosition(restore);
}
@@ -4782,7 +4794,8 @@ public final class Parcel {
return Objects.equals(mObject, value.mObject);
}
// Better safely fail here since this could mean we get different objects.
- if (!Objects.equals(mLoader, value.mLoader)) {
+ if (!Objects.equals(mLoaderProvider.getClassLoader(),
+ value.mLoaderProvider.getClassLoader())) {
return false;
}
// Otherwise compare metadata prior to comparing payload.
@@ -4796,10 +4809,24 @@ public final class Parcel {
@Override
public int hashCode() {
// Accessing mSource first to provide memory barrier for mObject
- return Objects.hash(mSource == null, mObject, mLoader, mType, mLength);
+ return Objects.hash(mSource == null, mObject, mLoaderProvider.getClassLoader(), mType,
+ mLength);
}
}
+ /**
+ * Provides a ClassLoader.
+ * @hide
+ */
+ public interface ClassLoaderProvider {
+ /**
+ * Returns a ClassLoader.
+ *
+ * @return ClassLoader
+ */
+ ClassLoader getClassLoader();
+ }
+
/** Same as {@link #readValue(ClassLoader, Class, Class[])} without any item types. */
private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
// Avoids allocating Class[0] array
@@ -5537,8 +5564,8 @@ public final class Parcel {
}
private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
- int size, @Nullable ClassLoader loader) {
- readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
+ int size, @Nullable ClassLoaderProvider loaderProvider) {
+ readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loaderProvider, null);
}
/**
@@ -5548,17 +5575,17 @@ public final class Parcel {
* @param lazy Whether to populate the map with lazy {@link Function} objects for
* length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
* details.
- * @return a count of the lazy values in the map
+ * @param lazyValueCount number of lazy values added here
* @hide
*/
- int readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
- boolean lazy, @Nullable ClassLoader loader) {
- int lazyValues = 0;
+ void readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
+ boolean lazy, @Nullable ClassLoaderProvider loaderProvider, int[] lazyValueCount) {
while (size > 0) {
String key = readString();
- Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
+ Object value = (lazy) ? readLazyValue(loaderProvider) : readValue(
+ getClassLoader(loaderProvider));
if (value instanceof LazyValue) {
- lazyValues++;
+ lazyValueCount[0]++;
}
if (sorted) {
map.append(key, value);
@@ -5570,7 +5597,6 @@ public final class Parcel {
if (sorted) {
map.validate();
}
- return lazyValues;
}
/**
@@ -5578,12 +5604,12 @@ public final class Parcel {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
- @Nullable ClassLoader loader) {
+ @Nullable ClassLoaderProvider loaderProvider) {
final int N = readInt();
if (N < 0) {
return;
}
- readArrayMapInternal(outVal, N, loader);
+ readArrayMapInternal(outVal, N, loaderProvider);
}
/**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 907d96834857..0c5d9e97a77d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1221,6 +1221,17 @@ public class Process {
*/
public static final native int[] getExclusiveCores();
+
+ /**
+ * Get the CPU affinity masks from sched_getaffinity.
+ *
+ * @param tid The identifier of the thread/process to get the sched affinity.
+ * @return an array of CPU affinity masks, of which the size will be dynamic and just enough to
+ * include all bit masks for all currently online and possible CPUs of the device.
+ * @hide
+ */
+ public static final native long[] getSchedAffinity(int tid);
+
/**
* Set the priority of the calling thread, based on Linux priorities. See
* {@link #setThreadPriority(int, int)} for more information.
diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java
index e2169925fdd3..289b98c9b1d4 100644
--- a/core/java/android/os/TestLooperManager.java
+++ b/core/java/android/os/TestLooperManager.java
@@ -41,6 +41,7 @@ public class TestLooperManager {
private boolean mReleased;
private boolean mLooperBlocked;
+ private final boolean mLooperIsMyLooper;
/**
* @hide
@@ -54,8 +55,11 @@ public class TestLooperManager {
}
mLooper = looper;
mQueue = mLooper.getQueue();
- // Post a message that will keep the looper blocked as long as we are dispatching.
- new Handler(looper).post(new LooperHolder());
+ mLooperIsMyLooper = Looper.myLooper() == looper;
+ if (!mLooperIsMyLooper) {
+ // Post a message that will keep the looper blocked as long as we are dispatching.
+ new Handler(looper).post(new LooperHolder());
+ }
}
/**
@@ -82,7 +86,7 @@ public class TestLooperManager {
public Message next() {
// Wait for the looper block to come up, to make sure we don't accidentally get
// the message for the block.
- while (!mLooperBlocked) {
+ while (!mLooperIsMyLooper && !mLooperBlocked) {
synchronized (this) {
try {
wait();
@@ -114,9 +118,6 @@ public class TestLooperManager {
* should be executed by this queue.
* If the queue is empty or no messages are deliverable, returns null.
* This method never blocks.
- *
- * <p>Callers should always call {@link #recycle(Message)} on the message when all interactions
- * with it have completed.
*/
@FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
@SuppressWarnings("AutoBoxing") // box the primitive long, or return null to indicate no value
@@ -165,6 +166,9 @@ public class TestLooperManager {
// This is being called from the thread it should be executed on, we can just dispatch.
message.target.dispatchMessage(message);
} else {
+ if (mLooperIsMyLooper) {
+ throw new RuntimeException("Cannot call execute from non Looper thread");
+ }
MessageExecution execution = new MessageExecution();
execution.m = message;
synchronized (execution) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c3a49305af87..6898fcef23ab 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1296,6 +1296,22 @@ public final class Settings {
public static final String ACTION_LOCKSCREEN_SETTINGS = "android.settings.LOCK_SCREEN_SETTINGS";
/**
+ * Activity Action: Show settings of notifications on lockscreen.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_LOCKSCREEN_NOTIFICATIONS_SETTINGS =
+ "android.settings.LOCK_SCREEN_NOTIFICATIONS_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow pairing bluetooth devices.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
index 90136ae00f6a..ffe8086ca4a1 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
@@ -93,6 +93,10 @@ import android.util.Log;
* must do its own state management (keeping in mind that the service's process might be killed
* by the Android System when unbound; for example, if the device is running low in memory).
*
+ * <p> The service also provides pending intents to override the system's Quick Access activities
+ * via the {@link #getTargetActivityPendingIntent} and the
+ * {@link #getGestureTargetActivityPendingIntent} method.
+ *
* <p>
* <a name="ErrorHandling"></a>
* <h3>Error handling</h3>
@@ -384,6 +388,10 @@ public abstract class QuickAccessWalletService extends Service {
*
* <p>The pending intent will be sent when the user performs a gesture to open Wallet.
* The pending intent should launch an activity.
+ *
+ * <p> If the gesture is performed and this method returns null, the system will launch the
+ * activity specified by the {@link #getTargetActivityPendingIntent} method. If that method
+ * also returns null, the system will launch the system-provided card switcher activity.
*/
@Nullable
@FlaggedApi(Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
diff --git a/core/java/android/service/settings/preferences/GetValueRequest.java b/core/java/android/service/settings/preferences/GetValueRequest.java
index 4f82800d1855..db5c57c49595 100644
--- a/core/java/android/service/settings/preferences/GetValueRequest.java
+++ b/core/java/android/service/settings/preferences/GetValueRequest.java
@@ -108,6 +108,7 @@ public final class GetValueRequest implements Parcelable {
/**
* Builder to construct {@link GetValueRequest}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
private final String mScreenKey;
private final String mPreferenceKey;
diff --git a/core/java/android/service/settings/preferences/GetValueResult.java b/core/java/android/service/settings/preferences/GetValueResult.java
index 369dea77cc85..791131588034 100644
--- a/core/java/android/service/settings/preferences/GetValueResult.java
+++ b/core/java/android/service/settings/preferences/GetValueResult.java
@@ -170,6 +170,7 @@ public final class GetValueResult implements Parcelable {
/**
* Builder to construct {@link GetValueResult}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
@ResultCode
private final int mResultCode;
diff --git a/core/java/android/service/settings/preferences/MetadataRequest.java b/core/java/android/service/settings/preferences/MetadataRequest.java
index ffecc6bec5b2..e0417152eedc 100644
--- a/core/java/android/service/settings/preferences/MetadataRequest.java
+++ b/core/java/android/service/settings/preferences/MetadataRequest.java
@@ -65,6 +65,7 @@ public final class MetadataRequest implements Parcelable {
/**
* Builder to construct {@link MetadataRequest}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
/** Constructs an immutable {@link MetadataRequest} object. */
@NonNull
diff --git a/core/java/android/service/settings/preferences/MetadataResult.java b/core/java/android/service/settings/preferences/MetadataResult.java
index 6a65dcc9c757..e62fa8f38c93 100644
--- a/core/java/android/service/settings/preferences/MetadataResult.java
+++ b/core/java/android/service/settings/preferences/MetadataResult.java
@@ -131,6 +131,7 @@ public final class MetadataResult implements Parcelable {
/**
* Builder to construct {@link MetadataResult}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
@ResultCode
private final int mResultCode;
diff --git a/core/java/android/service/settings/preferences/SetValueRequest.java b/core/java/android/service/settings/preferences/SetValueRequest.java
index f7600aecdfaf..77581d9deffe 100644
--- a/core/java/android/service/settings/preferences/SetValueRequest.java
+++ b/core/java/android/service/settings/preferences/SetValueRequest.java
@@ -123,6 +123,7 @@ public final class SetValueRequest implements Parcelable {
/**
* Builder to construct {@link SetValueRequest}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
private final String mScreenKey;
private final String mPreferenceKey;
diff --git a/core/java/android/service/settings/preferences/SetValueResult.java b/core/java/android/service/settings/preferences/SetValueResult.java
index cb1776abd3bc..513f7a7d5bcc 100644
--- a/core/java/android/service/settings/preferences/SetValueResult.java
+++ b/core/java/android/service/settings/preferences/SetValueResult.java
@@ -156,6 +156,7 @@ public final class SetValueResult implements Parcelable {
/**
* Builder to construct {@link SetValueResult}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
@ResultCode
private final int mResultCode;
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
index ea7d4a675713..30631f2fd71d 100644
--- a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
@@ -102,6 +102,7 @@ public final class SettingsPreferenceMetadata implements Parcelable {
/**
* Returns the breadcrumbs (navigation context) of Preference.
* <p>May be empty.
+ * @hide restrict to platform; may be opened wider in the future
*/
@NonNull
public List<String> getBreadcrumbs() {
@@ -189,33 +190,32 @@ public final class SettingsPreferenceMetadata implements Parcelable {
@IntDef(value = {
NO_SENSITIVITY,
EXPECT_POST_CONFIRMATION,
- EXPECT_PRE_CONFIRMATION,
+ DEEPLINK_ONLY,
NO_DIRECT_ACCESS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WriteSensitivity {}
/**
- * Indicates preference is not sensitive.
+ * Indicates preference is not write-sensitive.
* <p>Its value is writable without explicit consent, assuming all necessary permissions are
* granted.
*/
public static final int NO_SENSITIVITY = 0;
/**
- * Indicates preference is mildly sensitive.
+ * Indicates preference is mildly write-sensitive.
* <p>In addition to necessary permissions, after writing its value the user should be
* given the option to revert back.
*/
public static final int EXPECT_POST_CONFIRMATION = 1;
/**
- * Indicates preference is sensitive.
- * <p>In addition to necessary permissions, the user should be prompted for confirmation prior
- * to making a change. Otherwise it is suggested to provide a deeplink to the Preference's page
- * instead, accessible via {@link #getLaunchIntent}.
+ * Indicates preference is write-sensitive.
+ * <p>This preference cannot be changed through this API; instead a deeplink to the Preference's
+ * page should be used instead, accessible via {@link #getLaunchIntent}.
*/
- public static final int EXPECT_PRE_CONFIRMATION = 2;
+ public static final int DEEPLINK_ONLY = 2;
/**
- * Indicates preference is highly sensitivity and carries significant user-risk.
+ * Indicates preference is highly write-sensitivity and carries significant user-risk.
* <p>This Preference cannot be changed through this API and no direct deeplink is available.
* Other Metadata is still available.
*/
@@ -303,6 +303,7 @@ public final class SettingsPreferenceMetadata implements Parcelable {
/**
* Builder to construct {@link SettingsPreferenceMetadata}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
private final String mScreenKey;
private final String mKey;
@@ -355,6 +356,7 @@ public final class SettingsPreferenceMetadata implements Parcelable {
/**
* Sets the preference breadcrumbs (navigation context).
+ * @hide
*/
@NonNull
public Builder setBreadcrumbs(@NonNull List<String> breadcrumbs) {
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceValue.java b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
index 08826ca9776b..eea93b321e47 100644
--- a/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
@@ -170,6 +170,7 @@ public final class SettingsPreferenceValue implements Parcelable {
/**
* Builder to construct {@link SettingsPreferenceValue}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
@Type
private final int mType;
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index fb1bd1703ce6..6116d599baa0 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -70,3 +70,11 @@ flag {
is_fixed_read_only: true
bug: "352538294"
}
+
+flag {
+ name: "system_server_large_perfetto_shmem_buffer"
+ namespace: "windowing_tools"
+ description: "Large perfetto shmem buffer"
+ is_fixed_read_only: true
+ bug: "382369925"
+}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 089b5c256b6e..6c50b5f945a5 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.flags.Flags.bufferStuffingRecovery;
import static android.view.flags.Flags.FLAG_EXPECTED_PRESENTATION_TIME_API;
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
@@ -965,22 +966,24 @@ public final class Choreographer {
// Evaluate if buffer stuffing recovery needs to start or end, and
// what actions need to be taken for recovery.
- switch (updateBufferStuffingState(frameTimeNanos, vsyncEventData)) {
- case NONE:
- // Without buffer stuffing recovery, offsetFrameTimeNanos is
- // synonymous with frameTimeNanos.
- break;
- case OFFSET:
- // Add animation offset. Used to update frame timeline with
- // offset before jitter is calculated.
- offsetFrameTimeNanos = frameTimeNanos - frameIntervalNanos;
- break;
- case DELAY_FRAME:
- // Intentional frame delay to help reduce queued buffer count.
- scheduleVsyncLocked();
- return;
- default:
- break;
+ if (bufferStuffingRecovery()) {
+ switch (updateBufferStuffingState(frameTimeNanos, vsyncEventData)) {
+ case NONE:
+ // Without buffer stuffing recovery, offsetFrameTimeNanos is
+ // synonymous with frameTimeNanos.
+ break;
+ case OFFSET:
+ // Add animation offset. Used to update frame timeline with
+ // offset before jitter is calculated.
+ offsetFrameTimeNanos = frameTimeNanos - frameIntervalNanos;
+ break;
+ case DELAY_FRAME:
+ // Intentional frame delay to help reduce queued buffer count.
+ scheduleVsyncLocked();
+ return;
+ default:
+ break;
+ }
}
try {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0c8a0d60a96a..ca0959af3ff8 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1597,7 +1597,9 @@ public final class Display {
// Although we only care about the HDR/SDR ratio changing, that can also come in the
// form of the larger DISPLAY_CHANGED event
mGlobal.registerDisplayListener(toRegister, executor,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| DisplayManagerGlobal
.INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
ActivityThread.currentPackageName());
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index ba098eb53246..e75b1b0bd17a 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -447,7 +447,18 @@ public final class DisplayInfo implements Parcelable {
}
public boolean equals(DisplayInfo other) {
- return other != null
+ return equals(other, /* compareRefreshRate */ true);
+ }
+
+ /**
+ * Compares if the two DisplayInfo objects are equal or not
+ * @param other The other DisplayInfo against which the comparison is to be done
+ * @param compareRefreshRate Indicates if the refresh rate is also to be considered in
+ * comparison
+ * @return
+ */
+ public boolean equals(DisplayInfo other, boolean compareRefreshRate) {
+ boolean isEqualWithoutRefreshRate = other != null
&& layerStack == other.layerStack
&& flags == other.flags
&& type == other.type
@@ -466,7 +477,6 @@ public final class DisplayInfo implements Parcelable {
&& logicalHeight == other.logicalHeight
&& Objects.equals(displayCutout, other.displayCutout)
&& rotation == other.rotation
- && modeId == other.modeId
&& hasArrSupport == other.hasArrSupport
&& Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate)
&& Arrays.equals(supportedRefreshRates, other.supportedRefreshRates)
@@ -490,7 +500,6 @@ public final class DisplayInfo implements Parcelable {
&& ownerUid == other.ownerUid
&& Objects.equals(ownerPackageName, other.ownerPackageName)
&& removeMode == other.removeMode
- && getRefreshRate() == other.getRefreshRate()
&& brightnessMinimum == other.brightnessMinimum
&& brightnessMaximum == other.brightnessMaximum
&& brightnessDefault == other.brightnessDefault
@@ -504,6 +513,13 @@ public final class DisplayInfo implements Parcelable {
&& Objects.equals(
thermalBrightnessThrottlingDataId, other.thermalBrightnessThrottlingDataId)
&& canHostTasks == other.canHostTasks;
+
+ if (compareRefreshRate) {
+ return isEqualWithoutRefreshRate
+ && (getRefreshRate() == other.getRefreshRate())
+ && (modeId == other.modeId);
+ }
+ return isEqualWithoutRefreshRate;
}
@Override
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index a8d4e2d2c70a..48dfdd4a95f4 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,9 @@
package android.view;
+
+import static com.android.hardware.input.Flags.removeFallbackModifiers;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -458,7 +461,15 @@ public class KeyCharacterMap implements Parcelable {
FallbackAction action = FallbackAction.obtain();
metaState = KeyEvent.normalizeMetaState(metaState);
if (nativeGetFallbackAction(mPtr, keyCode, metaState, action)) {
- action.metaState = KeyEvent.normalizeMetaState(action.metaState);
+ if (removeFallbackModifiers()) {
+ // Strip all modifiers. This is safe to do since only exact keyCode + metaState
+ // modifiers will trigger a fallback.
+ // E.g. Ctrl + Space -> language_switch (fallback generated)
+ // Ctrl + Alt + Space -> Ctrl + Alt + Space (no fallback generated)
+ action.metaState = 0;
+ } else {
+ action.metaState = KeyEvent.normalizeMetaState(action.metaState);
+ }
return action;
}
action.recycle();
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 6e6e87bb9403..4fc1cfc0ca82 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -206,7 +206,8 @@ public class Surface implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FRAME_RATE_COMPATIBILITY_"},
value = {FRAME_RATE_COMPATIBILITY_DEFAULT, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
- FRAME_RATE_COMPATIBILITY_GTE})
+ FRAME_RATE_COMPATIBILITY_AT_LEAST, FRAME_RATE_COMPATIBILITY_EXACT,
+ FRAME_RATE_COMPATIBILITY_MIN})
public @interface FrameRateCompatibility {}
// From native_window.h. Keep these in sync.
@@ -219,7 +220,7 @@ public class Surface implements Parcelable {
* In Android version {@link Build.VERSION_CODES#BAKLAVA} and above, use
* {@link FRAME_RATE_COMPATIBILITY_DEFAULT} for game content.
* For other cases, see {@link FRAME_RATE_COMPATIBILITY_FIXED_SOURCE} and
- * {@link FRAME_RATE_COMPATIBILITY_GTE}.
+ * {@link FRAME_RATE_COMPATIBILITY_AT_LEAST}.
*/
public static final int FRAME_RATE_COMPATIBILITY_DEFAULT = 0;
@@ -234,7 +235,7 @@ public class Surface implements Parcelable {
public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1;
/**
- * The surface requests a frame rate that is greater than or equal to the specified frame rate.
+ * The surface requests a frame rate that is at least the specified frame rate.
* This value should be used for UIs, animations, scrolling and fling, and anything that is not
* a game or video.
*
@@ -242,7 +243,7 @@ public class Surface implements Parcelable {
* {@link FRAME_RATE_COMPATIBILITY_DEFAULT}.
*/
@FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_GTE_ENUM)
- public static final int FRAME_RATE_COMPATIBILITY_GTE = 2;
+ public static final int FRAME_RATE_COMPATIBILITY_AT_LEAST = 2;
/**
* This surface belongs to an app on the High Refresh Rate Deny list, and needs the display
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index f22505b80948..833f2d98554e 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -22,7 +22,6 @@ import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
-import static android.view.flags.Flags.bufferStuffingRecovery;
import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.LAYER_ID;
import static android.view.SurfaceControlProto.NAME;
@@ -5118,11 +5117,9 @@ public final class SurfaceControl implements Parcelable {
*/
@NonNull
public Transaction setRecoverableFromBufferStuffing(@NonNull SurfaceControl sc) {
- if (bufferStuffingRecovery()) {
- checkPreconditions(sc);
- nativeSetFlags(mNativeObject, sc.mNativeObject, RECOVERABLE_FROM_BUFFER_STUFFING,
- RECOVERABLE_FROM_BUFFER_STUFFING);
- }
+ checkPreconditions(sc);
+ nativeSetFlags(mNativeObject, sc.mNativeObject, RECOVERABLE_FROM_BUFFER_STUFFING,
+ RECOVERABLE_FROM_BUFFER_STUFFING);
return this;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d13f0e21bf80..8f8bfe2865a9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -27,7 +27,7 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
-import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.accessibility.Flags.FLAG_DEPRECATE_ACCESSIBILITY_ANNOUNCEMENT_APIS;
@@ -34251,7 +34251,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
frameRateToSet = frameRate;
} else {
- compatibility = FRAME_RATE_COMPATIBILITY_GTE;
+ compatibility = FRAME_RATE_COMPATIBILITY_AT_LEAST;
frameRateToSet = velocityFrameRate;
}
viewRootImpl.votePreferredFrameRate(frameRateToSet, compatibility);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1d27574eca8c..16cdb64f62cc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -38,7 +38,7 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
-import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_BOOST;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_CONFLICTED;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
@@ -1828,7 +1828,8 @@ public final class ViewRootImpl implements ViewParent,
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_STATE
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED
: DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
DisplayManagerGlobal
.getInstance()
@@ -13271,7 +13272,7 @@ public final class ViewRootImpl implements ViewParent,
* We set category to HIGH if the maximum frame rate is greater than 60.
* Otherwise, we set category to NORMAL.
*
- * Use FRAME_RATE_COMPATIBILITY_GTE for velocity and FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+ * Use FRAME_RATE_COMPATIBILITY_AT_LEAST for velocity and FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
* for TextureView video play and user requested frame rate.
*
* @param frameRate the preferred frame rate of a View
@@ -13282,7 +13283,7 @@ public final class ViewRootImpl implements ViewParent,
if (frameRate <= 0) {
return;
}
- if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && !mIsPressedGesture) {
+ if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_AT_LEAST && !mIsPressedGesture) {
mIsTouchBoosting = false;
mIsFrameRateBoosting = false;
if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) {
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 70c899f1efc7..8baa55f8e377 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -142,6 +142,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession {
}
@Override
+ void internalNotifySessionFlushEvent(int sessionId) {
+ getMainCaptureSession().internalNotifySessionFlushEvent(sessionId);
+ }
+
+ @Override
boolean isContentCaptureEnabled() {
return getMainCaptureSession().isContentCaptureEnabled();
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index db4ac5de0b49..efd39163c3b8 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -18,7 +18,9 @@ package android.view.contentcapture;
import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
import static android.view.contentcapture.ContentCaptureManager.DEBUG;
import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
+import static android.view.contentcapture.flags.Flags.FLAG_CCAPI_BAKLAVA_ENABLED;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -137,6 +139,12 @@ public final class ContentCaptureEvent implements Parcelable {
*/
public static final int TYPE_WINDOW_BOUNDS_CHANGED = 10;
+ /**
+ * Called to flush a semantics meaningful view changes status to Intelligence Service.
+ */
+ @FlaggedApi(FLAG_CCAPI_BAKLAVA_ENABLED)
+ public static final int TYPE_SESSION_FLUSH = 11;
+
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_VIEW_APPEARED,
@@ -149,6 +157,7 @@ public final class ContentCaptureEvent implements Parcelable {
TYPE_SESSION_RESUMED,
TYPE_VIEW_INSETS_CHANGED,
TYPE_WINDOW_BOUNDS_CHANGED,
+ TYPE_SESSION_FLUSH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType{}
@@ -697,6 +706,8 @@ public final class ContentCaptureEvent implements Parcelable {
return "VIEW_INSETS_CHANGED";
case TYPE_WINDOW_BOUNDS_CHANGED:
return "TYPE_WINDOW_BOUNDS_CHANGED";
+ case TYPE_SESSION_FLUSH:
+ return "TYPE_SESSION_FLUSH";
default:
return "UKNOWN_TYPE: " + type;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 0ca36ba28e3a..9aeec20ec9b7 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -19,8 +19,10 @@ import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
+import static android.view.contentcapture.flags.Flags.FLAG_CCAPI_BAKLAVA_ENABLED;
import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -548,6 +550,35 @@ public abstract class ContentCaptureSession implements AutoCloseable {
abstract void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets);
+ /**
+ * Flushes an internal buffer of UI events and signals System Intelligence (SI) that a
+ * semantically meaningful state has been reached. SI uses this signal to potentially
+ * rebuild the view hierarchy and understand the current state of the UI.
+ *
+ * <p>UI events are often batched together for performance reasons. A semantic batch
+ * represents a series of events that, when applied sequentially, result in a
+ * meaningful and complete UI state.
+ *
+ * <p>It is crucial to call {@code flush()} after completing a semantic batch to ensure
+ * SI can accurately reconstruct the view hierarchy.
+ *
+ * <p><b>Premature Flushing:</b> Calling {@code flush()} within a semantic batch may
+ * lead to SI failing to rebuild the view hierarchy correctly. This could manifest as
+ * incorrect ordering of sibling nodes.
+ *
+ * <p><b>Delayed Flushing:</b> While not immediately flushing after a semantic batch is
+ * generally safe, it's recommended to do so as soon as possible. In the worst-case
+ * scenario where a {@code flush()} is never called, SI will attempt to process the
+ * events after a short delay based on view appearance and disappearance events.
+ */
+ @FlaggedApi(FLAG_CCAPI_BAKLAVA_ENABLED)
+ public void flush() {
+ internalNotifySessionFlushEvent(mId);
+ }
+
+ /** @hide */
+ abstract void internalNotifySessionFlushEvent(int sessionId);
+
/** @hide */
public void notifyViewTreeEvent(boolean started) {
internalNotifyViewTreeEvent(mId, started);
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index eb827dd5258d..2fb78c038ca2 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -17,6 +17,7 @@ package android.view.contentcapture;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FLUSH;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_PAUSED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUMED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
@@ -623,6 +624,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@Override
public void flush(@FlushReason int reason) {
+ // TODO: b/380381249 renaming the internal APIs to prevent confusions between this and the
+ // public API.
runOnContentCaptureThread(() -> flushImpl(reason));
}
@@ -890,6 +893,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
enqueueEvent(event);
}
+ @Override
+ void internalNotifySessionFlushEvent(int sessionId) {
+ final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_FLUSH);
+ enqueueEvent(event, FORCE_FLUSH);
+ }
+
private List<ContentCaptureEvent> clearBufferEvents() {
final ArrayList<ContentCaptureEvent> bufferEvents = new ArrayList<>();
ContentCaptureEvent event;
diff --git a/core/java/android/view/contentcapture/OWNERS b/core/java/android/view/contentcapture/OWNERS
index 9ac273f515e7..30f4cae4bf19 100644
--- a/core/java/android/view/contentcapture/OWNERS
+++ b/core/java/android/view/contentcapture/OWNERS
@@ -1,4 +1,5 @@
# Bug component: 544200
-hackz@google.com
-shivanker@google.com
+dariofreni@google.com
+klikli@google.com
+shikhamalhotra@google.com
diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
index 416a877d87ab..f709ed7f57cd 100644
--- a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
+++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
@@ -7,3 +7,10 @@ flag {
description: "Feature flag for running content capture tasks on background thread"
bug: "309411951"
}
+
+flag {
+ name: "ccapi_baklava_enabled"
+ namespace: "machine_learning"
+ description: "Feature flag for baklava content capture API"
+ bug: "380381249"
+}
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 5397da11eb36..435c8c79122f 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -44,20 +44,16 @@ import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_AT
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.getNavigationBarRect;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.GraphicBuffer;
import android.graphics.Paint;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.util.Log;
-import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -66,7 +62,6 @@ import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.DecorView;
-import com.android.window.flags.Flags;
/**
* Utils class to help draw a snapshot on a surface.
@@ -103,8 +98,6 @@ public class SnapshotDrawerUtils {
| FLAG_SECURE
| FLAG_DIM_BEHIND;
- private static final Paint sBackgroundPaint = new Paint();
-
/**
* The internal object to hold the surface and drawing on it.
*/
@@ -115,54 +108,29 @@ public class SnapshotDrawerUtils {
private final TaskSnapshot mSnapshot;
private final CharSequence mTitle;
- private SystemBarBackgroundPainter mSystemBarBackgroundPainter;
- private final Rect mFrame = new Rect();
- private final Rect mSystemBarInsets = new Rect();
private final int mSnapshotW;
private final int mSnapshotH;
- private boolean mSizeMismatch;
+ private final int mContainerW;
+ private final int mContainerH;
public SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot,
- CharSequence title) {
+ Rect windowBounds, CharSequence title) {
mRootSurface = rootSurface;
mSnapshot = snapshot;
mTitle = title;
final HardwareBuffer hwBuffer = snapshot.getHardwareBuffer();
mSnapshotW = hwBuffer.getWidth();
mSnapshotH = hwBuffer.getHeight();
+ mContainerW = windowBounds.width();
+ mContainerH = windowBounds.height();
}
- /**
- * Initiate system bar painter to draw the system bar background.
- */
- @VisibleForTesting
- public void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags,
- int appearance, ActivityManager.TaskDescription taskDescription,
- @WindowInsets.Type.InsetsType int requestedVisibleTypes) {
- mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
- windowPrivateFlags, appearance, taskDescription, 1f, requestedVisibleTypes);
- int backgroundColor = taskDescription.getBackgroundColor();
- sBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
- }
-
- /**
- * Set frame size that the snapshot should fill. It is the bounds of a task or activity.
- */
- @VisibleForTesting
- public void setFrames(Rect frame, Rect systemBarInsets) {
- mFrame.set(frame);
+ private void drawSnapshot(boolean releaseAfterDraw) {
final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
- mSizeMismatch = (mFrame.width() != mSnapshotW || mFrame.height() != mSnapshotH)
+ final boolean sizeMismatch = mContainerW != mSnapshotW || mContainerH != mSnapshotH
|| letterboxInsets.left != 0 || letterboxInsets.top != 0;
- if (!Flags.drawSnapshotAspectRatioMatch() && systemBarInsets != null) {
- mSystemBarInsets.set(systemBarInsets);
- mSystemBarBackgroundPainter.setInsets(systemBarInsets);
- }
- }
-
- private void drawSnapshot(boolean releaseAfterDraw) {
- Log.v(TAG, "Drawing snapshot surface sizeMismatch=" + mSizeMismatch);
- if (mSizeMismatch) {
+ Log.v(TAG, "Drawing snapshot surface sizeMismatch=" + sizeMismatch);
+ if (sizeMismatch) {
// The dimensions of the buffer and the window don't match, so attaching the buffer
// will fail. Better create a child window with the exact dimensions and fill the
// parent window with the background color!
@@ -189,11 +157,6 @@ public class SnapshotDrawerUtils {
private void drawSizeMismatchSnapshot() {
final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
- // We consider nearly matched dimensions as there can be rounding errors and the user
- // won't notice very minute differences from scaling one dimension more than the other
- boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH)
- && !Flags.drawSnapshotAspectRatioMatch();
-
// Keep a reference to it such that it doesn't get destroyed when finalized.
SurfaceControl childSurfaceControl = new SurfaceControl.Builder()
.setName(mTitle + " - task-snapshot-surface")
@@ -203,166 +166,28 @@ public class SnapshotDrawerUtils {
.setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot")
.build();
- final Rect frame;
final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
float offsetX = letterboxInsets.left;
float offsetY = letterboxInsets.top;
// We can just show the surface here as it will still be hidden as the parent is
// still hidden.
mTransaction.show(childSurfaceControl);
- if (aspectRatioMismatch) {
- Rect crop = null;
- if (letterboxInsets.left != 0 || letterboxInsets.top != 0
- || letterboxInsets.right != 0 || letterboxInsets.bottom != 0) {
- // Clip off letterbox.
- crop = calculateSnapshotCrop(letterboxInsets);
- // If the snapshot can cover the frame, then no need to draw background.
- aspectRatioMismatch = !isAspectRatioMatch(mFrame, crop);
- }
- // if letterbox doesn't match window frame, try crop by content insets
- if (aspectRatioMismatch) {
- // Clip off ugly navigation bar.
- final Rect contentInsets = mSnapshot.getContentInsets();
- crop = calculateSnapshotCrop(contentInsets);
- offsetX = contentInsets.left;
- offsetY = contentInsets.top;
- }
- frame = calculateSnapshotFrame(crop);
- mTransaction.setCrop(childSurfaceControl, crop);
- } else {
- frame = null;
- }
// Align the snapshot with content area.
if (offsetX != 0f || offsetY != 0f) {
mTransaction.setPosition(childSurfaceControl,
- -offsetX * mFrame.width() / mSnapshot.getTaskSize().x,
- -offsetY * mFrame.height() / mSnapshot.getTaskSize().y);
+ -offsetX * mContainerW / mSnapshot.getTaskSize().x,
+ -offsetY * mContainerH / mSnapshot.getTaskSize().y);
}
// Scale the mismatch dimensions to fill the target frame.
- final float scaleX = (float) mFrame.width() / mSnapshotW;
- final float scaleY = (float) mFrame.height() / mSnapshotH;
+ final float scaleX = (float) mContainerW / mSnapshotW;
+ final float scaleY = (float) mContainerH / mSnapshotH;
mTransaction.setScale(childSurfaceControl, scaleX, scaleY);
mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
-
- if (aspectRatioMismatch) {
- GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
- PixelFormat.RGBA_8888,
- GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
- | GraphicBuffer.USAGE_SW_WRITE_RARELY);
- final Canvas c = background != null ? background.lockCanvas() : null;
- if (c == null) {
- Log.e(TAG, "Unable to draw snapshot: failed to allocate graphic buffer for "
- + mTitle);
- mTransaction.clear();
- childSurfaceControl.release();
- return;
- }
- drawBackgroundAndBars(c, frame);
- background.unlockCanvasAndPost(c);
- mTransaction.setBuffer(mRootSurface,
- HardwareBuffer.createFromGraphicBuffer(background));
- }
mTransaction.apply();
childSurfaceControl.release();
}
-
- /**
- * Calculates the snapshot crop in snapshot coordinate space.
- * @param insets Content insets or Letterbox insets
- * @return crop rect in snapshot coordinate space.
- */
- @VisibleForTesting
- public Rect calculateSnapshotCrop(@NonNull Rect insets) {
- final Rect rect = new Rect();
- rect.set(0, 0, mSnapshotW, mSnapshotH);
-
- final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
- final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y;
-
- // Let's remove all system decorations except the status bar, but only if the task is at
- // the very top of the screen.
- final boolean isTop = mFrame.top == 0;
- rect.inset((int) (insets.left * scaleX),
- isTop ? 0 : (int) (insets.top * scaleY),
- (int) (insets.right * scaleX),
- (int) (insets.bottom * scaleY));
- return rect;
- }
-
- /**
- * Calculates the snapshot frame in window coordinate space from crop.
- *
- * @param crop rect that is in snapshot coordinate space.
- */
- @VisibleForTesting
- public Rect calculateSnapshotFrame(Rect crop) {
- final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
- final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y;
-
- // Rescale the frame from snapshot to window coordinate space
- final Rect frame = new Rect(0, 0,
- (int) (crop.width() / scaleX + 0.5f),
- (int) (crop.height() / scaleY + 0.5f)
- );
-
- // However, we also need to make space for the navigation bar on the left side.
- frame.offset(mSystemBarInsets.left, 0);
- return frame;
- }
-
- /**
- * Draw status bar and navigation bar background.
- */
- @VisibleForTesting
- public void drawBackgroundAndBars(Canvas c, Rect frame) {
- final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
- final boolean fillHorizontally = c.getWidth() > frame.right;
- final boolean fillVertically = c.getHeight() > frame.bottom;
- if (fillHorizontally) {
- c.drawRect(frame.right, alpha(mSystemBarBackgroundPainter.mStatusBarColor) == 0xFF
- ? statusBarHeight : 0, c.getWidth(), fillVertically
- ? frame.bottom : c.getHeight(), sBackgroundPaint);
- }
- if (fillVertically) {
- c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), sBackgroundPaint);
- }
- mSystemBarBackgroundPainter.drawDecors(c, frame);
- }
-
- /**
- * Ask system bar background painter to draw status bar background.
- */
- @VisibleForTesting
- public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
- mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame,
- mSystemBarBackgroundPainter.getStatusBarColorViewHeight());
- }
-
- /**
- * Ask system bar background painter to draw navigation bar background.
- */
- @VisibleForTesting
- public void drawNavigationBarBackground(Canvas c) {
- mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
- }
- }
-
- private static boolean isAspectRatioMatch(Rect frame, int w, int h) {
- if (frame.isEmpty()) {
- return false;
- }
- return Math.abs(((float) w / h) - ((float) frame.width() / frame.height())) <= 0.01f;
- }
-
- private static boolean isAspectRatioMatch(Rect frame1, Rect frame2) {
- if (frame1.isEmpty() || frame2.isEmpty()) {
- return false;
- }
- return Math.abs(
- ((float) frame2.width() / frame2.height())
- - ((float) frame1.width() / frame1.height())) <= 0.01f;
}
/**
@@ -383,28 +208,15 @@ public class SnapshotDrawerUtils {
/**
* Help method to draw the snapshot on a surface.
*/
- public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp,
+ public static void drawSnapshotOnSurface(WindowManager.LayoutParams lp,
SurfaceControl rootSurface, TaskSnapshot snapshot,
- Rect windowBounds, InsetsState topWindowInsetsState,
- boolean releaseAfterDraw) {
+ Rect windowBounds, boolean releaseAfterDraw) {
if (windowBounds.isEmpty()) {
Log.e(TAG, "Unable to draw snapshot on an empty windowBounds");
return;
}
final SnapshotSurface drawSurface = new SnapshotSurface(
- rootSurface, snapshot, lp.getTitle());
- final WindowManager.LayoutParams attrs = Flags.drawSnapshotAspectRatioMatch()
- ? info.mainWindowLayoutParams : info.topOpaqueWindowLayoutParams;
- final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
- final ActivityManager.TaskDescription taskDescription =
- getOrCreateTaskDescription(runningTaskInfo);
- Rect systemBarInsets = null;
- if (!Flags.drawSnapshotAspectRatioMatch()) {
- drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
- attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
- systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
- }
- drawSurface.setFrames(windowBounds, systemBarInsets);
+ rootSurface, snapshot, windowBounds, lp.getTitle());
drawSurface.drawSnapshot(releaseAfterDraw);
}
@@ -414,10 +226,8 @@ public class SnapshotDrawerUtils {
public static WindowManager.LayoutParams createLayoutParameters(StartingWindowInfo info,
CharSequence title, @WindowManager.LayoutParams.WindowType int windowType,
int pixelFormat, IBinder token) {
- final WindowManager.LayoutParams attrs = Flags.drawSnapshotAspectRatioMatch()
- ? info.mainWindowLayoutParams : info.topOpaqueWindowLayoutParams;
- final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams;
- if (attrs == null || mainWindowParams == null) {
+ final WindowManager.LayoutParams attrs = info.mainWindowLayoutParams;
+ if (attrs == null) {
Log.w(TAG, "unable to create taskSnapshot surface ");
return null;
}
@@ -427,9 +237,9 @@ public class SnapshotDrawerUtils {
final int windowFlags = attrs.flags;
final int windowPrivateFlags = attrs.privateFlags;
- layoutParams.packageName = mainWindowParams.packageName;
- layoutParams.windowAnimations = mainWindowParams.windowAnimations;
- layoutParams.dimAmount = mainWindowParams.dimAmount;
+ layoutParams.packageName = attrs.packageName;
+ layoutParams.windowAnimations = attrs.windowAnimations;
+ layoutParams.dimAmount = attrs.dimAmount;
layoutParams.type = windowType;
layoutParams.format = pixelFormat;
layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
@@ -460,14 +270,6 @@ public class SnapshotDrawerUtils {
return layoutParams;
}
- static Rect getSystemBarInsets(Rect frame, @Nullable InsetsState state) {
- if (state == null) {
- return new Rect();
- }
- return state.calculateInsets(frame, WindowInsets.Type.systemBars(),
- false /* ignoreVisibility */).toRect();
- }
-
/**
* Helper class to draw the background of the system bars in regions the task snapshot isn't
* filling the window.
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 72df343a2dbe..80ccbddbd1d9 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -27,7 +27,6 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
-import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
@@ -100,20 +99,6 @@ public final class StartingWindowInfo implements Parcelable {
public ActivityInfo targetActivityInfo;
/**
- * InsetsState of TopFullscreenOpaqueWindow
- * @hide
- */
- @Nullable
- public InsetsState topOpaqueWindowInsetsState;
-
- /**
- * LayoutParams of TopFullscreenOpaqueWindow
- * @hide
- */
- @Nullable
- public WindowManager.LayoutParams topOpaqueWindowLayoutParams;
-
- /**
* LayoutParams of MainWindow
* @hide
*/
@@ -263,8 +248,6 @@ public final class StartingWindowInfo implements Parcelable {
taskBounds.writeToParcel(dest, flags);
dest.writeTypedObject(targetActivityInfo, flags);
dest.writeInt(startingWindowTypeParameter);
- dest.writeTypedObject(topOpaqueWindowInsetsState, flags);
- dest.writeTypedObject(topOpaqueWindowLayoutParams, flags);
dest.writeTypedObject(mainWindowLayoutParams, flags);
dest.writeInt(splashScreenThemeResId);
dest.writeBoolean(isKeyguardOccluded);
@@ -280,9 +263,6 @@ public final class StartingWindowInfo implements Parcelable {
taskBounds.readFromParcel(source);
targetActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
startingWindowTypeParameter = source.readInt();
- topOpaqueWindowInsetsState = source.readTypedObject(InsetsState.CREATOR);
- topOpaqueWindowLayoutParams = source.readTypedObject(
- WindowManager.LayoutParams.CREATOR);
mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
splashScreenThemeResId = source.readInt();
isKeyguardOccluded = source.readBoolean();
@@ -302,8 +282,6 @@ public final class StartingWindowInfo implements Parcelable {
+ " topActivityType=" + taskInfo.topActivityType
+ " preferredStartingWindowType="
+ Integer.toHexString(startingWindowTypeParameter)
- + " insetsState=" + topOpaqueWindowInsetsState
- + " topWindowLayoutParams=" + topOpaqueWindowLayoutParams
+ " mainWindowLayoutParams=" + mainWindowLayoutParams
+ " splashScreenThemeResId " + Integer.toHexString(splashScreenThemeResId);
}
diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java
index 1ec05b65861d..fcd7dfbb1769 100644
--- a/core/java/android/window/WindowTokenClientController.java
+++ b/core/java/android/window/WindowTokenClientController.java
@@ -35,6 +35,7 @@ import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
/**
* Singleton controller to manage the attached {@link WindowTokenClient}s, and to dispatch
@@ -137,7 +138,9 @@ public class WindowTokenClientController {
// is initialized later, the SystemUiContext will start reporting from
// DisplayContent#registerSystemUiContext, and WindowTokenClientController can report
// the Configuration to the correct client.
- recordWindowContextToken(client);
+ if (Flags.trackSystemUiContextBeforeWms()) {
+ recordWindowContextToken(client);
+ }
return false;
}
final WindowContextInfo info;
@@ -145,6 +148,9 @@ public class WindowTokenClientController {
info = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed attachToDisplayContent", e);
+ return false;
}
if (info == null) {
return false;
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 801698caff0e..0d04961569e6 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -58,13 +58,6 @@ flag {
}
flag {
- name: "user_min_aspect_ratio_app_default"
- namespace: "large_screen_experiences_app_compat"
- description: "Whether the API PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT is available"
- bug: "310816437"
-}
-
-flag {
name: "allow_hide_scm_button"
namespace: "large_screen_experiences_app_compat"
description: "Whether we should allow hiding the size compat restart button"
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 30668a6c6b1d..9d11d149b0ed 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -243,17 +243,6 @@ flag {
}
flag {
- name: "draw_snapshot_aspect_ratio_match"
- namespace: "windowing_frontend"
- description: "The aspect ratio should always match when drawing snapshot"
- bug: "341020277"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "system_ui_post_animation_end"
namespace: "windowing_frontend"
description: "Run AnimatorListener#onAnimationEnd on next frame for SystemUI"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 5a092b8522db..abd93cfaf179 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -105,6 +105,13 @@ flag {
flag {
namespace: "windowing_sdk"
+ name: "activity_embedding_support_for_connected_displays"
+ description: "Enables activity embedding support for connected displays, including enabling AE optimization for Settings."
+ bug: "369438353"
+}
+
+flag {
+ namespace: "windowing_sdk"
name: "wlinfo_oncreate"
description: "Makes WindowLayoutInfo accessible without racing in the Activity#onCreate()"
bug: "337820752"
@@ -138,3 +145,23 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "normalize_home_intent"
+ description: "To ensure home is started in correct intent"
+ bug: "378505461"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "condense_configuration_change_for_simple_mode"
+ description: "Condense configuration change for simple mode"
+ bug: "356738240"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/jank/DisplayResolutionTracker.java b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
index 0c2fd4bbd7ae..5d66b3c10197 100644
--- a/core/java/com/android/internal/jank/DisplayResolutionTracker.java
+++ b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
@@ -148,8 +148,8 @@ public class DisplayResolutionTracker {
public void registerDisplayListener(DisplayManager.DisplayListener listener) {
manager.registerDisplayListener(listener, handler,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal
- .INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
ActivityThread.currentPackageName());
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 3e2f30118b2a..f14e1f63cdf6 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -236,4 +236,7 @@ interface IStatusBarService
/** Shows rear display educational dialog */
void showRearDisplayDialog(int currentBaseState);
+
+ /** Unbundle a categorized notification */
+ void unbundleNotification(String key);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9bd52372e6c4..39ddea614ee4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
import static android.security.Flags.reportPrimaryAuthAttempts;
import static android.security.Flags.shouldTrustManagerListenForPrimaryAuth;
+import static com.android.internal.widget.flags.Flags.hideLastCharWithPhysicalInput;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,6 +44,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.hardware.input.InputManagerGlobal;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -59,6 +62,7 @@ import android.util.Log;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.view.InputDevice;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
@@ -1097,12 +1101,20 @@ public class LockPatternUtils {
return type == CREDENTIAL_TYPE_PATTERN;
}
+ private boolean hasActivePointerDeviceAttached() {
+ return !getEnabledNonTouchInputDevices(InputDevice.SOURCE_CLASS_POINTER).isEmpty();
+ }
+
/**
* @return Whether the visible pattern is enabled.
*/
@UnsupportedAppUsage
public boolean isVisiblePatternEnabled(int userId) {
- return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, true, userId);
+ boolean defaultValue = true;
+ if (hideLastCharWithPhysicalInput()) {
+ defaultValue = !hasActivePointerDeviceAttached();
+ }
+ return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, defaultValue, userId);
}
/**
@@ -1116,11 +1128,39 @@ public class LockPatternUtils {
return getString(Settings.Secure.LOCK_PATTERN_VISIBLE, userId) != null;
}
+ private List<InputDevice> getEnabledNonTouchInputDevices(int source) {
+ final InputManagerGlobal inputManager = InputManagerGlobal.getInstance();
+ final int[] inputIds = inputManager.getInputDeviceIds();
+ List<InputDevice> matchingDevices = new ArrayList<InputDevice>();
+ for (final int deviceId : inputIds) {
+ final InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+ if (!inputDevice.isEnabled()) continue;
+ if (inputDevice.supportsSource(InputDevice.SOURCE_TOUCHSCREEN)) continue;
+ if (inputDevice.isVirtual()) continue;
+ if (!inputDevice.supportsSource(source)) continue;
+ matchingDevices.add(inputDevice);
+ }
+ return matchingDevices;
+ }
+
+ private boolean hasPhysicalKeyboardActive() {
+ final List<InputDevice> keyboards =
+ getEnabledNonTouchInputDevices(InputDevice.SOURCE_KEYBOARD);
+ for (final InputDevice keyboard : keyboards) {
+ if (keyboard.isFullKeyboard()) return true;
+ }
+ return false;
+ }
+
/**
* @return Whether enhanced pin privacy is enabled.
*/
public boolean isPinEnhancedPrivacyEnabled(int userId) {
- return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, false, userId);
+ boolean defaultValue = false;
+ if (hideLastCharWithPhysicalInput()) {
+ defaultValue = hasPhysicalKeyboardActive();
+ }
+ return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, defaultValue, userId);
}
/**
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 027113a75f6b..5acdf32a9f84 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -482,11 +482,22 @@ cc_library_shared_for_libandroid_runtime {
"libbinder",
"libhidlbase", // libhwbinder is in here
],
+ version_script: "platform/linux/libandroid_runtime_export.txt",
+ },
+ darwin: {
+ host_ldlibs: [
+ "-framework AppKit",
+ ],
+ dist: {
+ targets: ["layoutlib_jni"],
+ dir: "layoutlib_native/darwin",
+ },
+ exported_symbols_list: "platform/darwin/libandroid_runtime_export.exp",
},
linux_glibc_x86_64: {
ldflags: ["-static-libgcc"],
dist: {
- targets: ["layoutlib"],
+ targets: ["layoutlib_jni"],
dir: "layoutlib_native/linux",
tag: "stripped_all",
},
diff --git a/core/jni/android_hardware_camera2_CameraDevice.cpp b/core/jni/android_hardware_camera2_CameraDevice.cpp
index 493c7073416c..04cfed581750 100644
--- a/core/jni/android_hardware_camera2_CameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_CameraDevice.cpp
@@ -30,6 +30,7 @@
#include <nativehelper/JNIHelp.h>
#include "android_os_Parcel.h"
#include "core_jni_helpers.h"
+#include <android/binder_auto_utils.h>
#include <android/binder_parcel_jni.h>
#include <android/hardware/camera2/ICameraDeviceUser.h>
#include <aidl/android/hardware/common/fmq/MQDescriptor.h>
@@ -40,6 +41,7 @@
using namespace android;
using ::android::AidlMessageQueue;
+using ndk::ScopedAParcel;
using ResultMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
class FMQReader {
@@ -75,15 +77,16 @@ extern "C" {
static jlong CameraDevice_createFMQReader(JNIEnv *env, jclass thiz,
jobject resultParcel) {
- AParcel *resultAParcel = AParcel_fromJavaParcel(env, resultParcel);
- if (resultAParcel == nullptr) {
+ ScopedAParcel sResultAParcel(AParcel_fromJavaParcel(env, resultParcel));
+ if (sResultAParcel.get() == nullptr) {
ALOGE("%s: Error creating result parcel", __FUNCTION__);
return 0;
}
- AParcel_setDataPosition(resultAParcel, 0);
+
+ AParcel_setDataPosition(sResultAParcel.get(), 0);
MQDescriptor<int8_t, SynchronizedReadWrite> resultMQ;
- if (resultMQ.readFromParcel(resultAParcel) != OK) {
+ if (resultMQ.readFromParcel(sResultAParcel.get()) != OK) {
ALOGE("%s: read from result parcel failed", __FUNCTION__);
return 0;
}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index dc7253954d44..67c97258a01d 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -490,6 +490,28 @@ jintArray android_os_Process_getExclusiveCores(JNIEnv* env, jobject clazz) {
return cpus;
}
+jlongArray android_os_Process_getSchedAffinity(JNIEnv* env, jobject clazz, jint pid) {
+ // sched_getaffinity will do memset 0 for the unset bits within set_alloc_size_byte
+ cpu_set_t cpu_set;
+ if (sched_getaffinity(pid, sizeof(cpu_set_t), &cpu_set) != 0) {
+ signalExceptionForError(env, errno, pid);
+ return nullptr;
+ }
+ int cpu_cnt = std::min(CPU_SETSIZE, get_nprocs_conf());
+ int masks_len = (int)(CPU_ALLOC_SIZE(cpu_cnt) / sizeof(__CPU_BITTYPE));
+ jlongArray masks = env->NewLongArray(masks_len);
+ if (masks == nullptr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+ return nullptr;
+ }
+ jlong* mask_elements = env->GetLongArrayElements(masks, 0);
+ for (int i = 0; i < masks_len; i++) {
+ mask_elements[i] = cpu_set.__bits[i];
+ }
+ env->ReleaseLongArrayElements(masks, mask_elements, 0);
+ return masks;
+}
+
static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz, jboolean bgOk) {
// Establishes the calling thread as illegal to put into the background.
// Typically used only for the system process's main looper.
@@ -1370,6 +1392,7 @@ static const JNINativeMethod methods[] = {
{"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup},
{"createProcessGroup", "(II)I", (void*)android_os_Process_createProcessGroup},
{"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores},
+ {"getSchedAffinity", "(I)[J", (void*)android_os_Process_getSchedAffinity},
{"setArgV0Native", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
{"setUid", "(I)I", (void*)android_os_Process_setUid},
{"setGid", "(I)I", (void*)android_os_Process_setGid},
diff --git a/core/jni/platform/darwin/libandroid_runtime_export.exp b/core/jni/platform/darwin/libandroid_runtime_export.exp
new file mode 100644
index 000000000000..00a7585719ea
--- /dev/null
+++ b/core/jni/platform/darwin/libandroid_runtime_export.exp
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+# symbols needed for the JNI operations
+_JNI_OnLoad
+_ANativeWindow*
+
+# symbols needed to link with layoutlib_jni
+___android_log*
+__ZNK7android7RefBase*
+__ZN7android4base9SetLogger*
+__ZN7android4base10SetAborter*
+__ZN7android4base11GetProperty*
+__ZN7android4Rect*
+__ZN7android5Fence*
+__ZN7android7RefBase*
+__ZN7android7String*
+__ZN7android10VectorImpl*
+__ZN7android11BufferQueue*
+__ZN7android14AndroidRuntime*
+__ZN7android14sp_report_raceEv*
+__ZN7android15KeyCharacterMap*
+__ZN7android15InputDeviceInfo*
+__ZN7android31android_view_InputDevice_create*
+__ZN7android53android_view_Surface_createFromIGraphicBufferProducer*
diff --git a/core/jni/platform/linux/libandroid_runtime_export.txt b/core/jni/platform/linux/libandroid_runtime_export.txt
new file mode 100644
index 000000000000..19e3478d5d38
--- /dev/null
+++ b/core/jni/platform/linux/libandroid_runtime_export.txt
@@ -0,0 +1,43 @@
+#
+# 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.
+#
+
+{
+ global:
+ # symbols needed for the JNI operations
+ JNI_OnLoad;
+ ANativeWindow*;
+
+ # symbols needed to link with layoutlib_jni
+ __android_log*;
+ _ZNK7android7RefBase*;
+ _ZN7android4base9SetLogger*;
+ _ZN7android4base10SetAborter*;
+ _ZN7android4base11GetProperty*;
+ _ZN7android4Rect*;
+ _ZN7android5Fence*;
+ _ZN7android7RefBase*;
+ _ZN7android7String*;
+ _ZN7android10VectorImpl*;
+ _ZN7android11BufferQueue*;
+ _ZN7android14AndroidRuntime*;
+ _ZN7android14sp_report_raceEv*;
+ _ZN7android15KeyCharacterMap*;
+ _ZN7android15InputDeviceInfo*;
+ _ZN7android31android_view_InputDevice_create*;
+ _ZN7android53android_view_Surface_createFromIGraphicBufferProducer*;
+ local:
+ *;
+};
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 7e9d62315ef1..c901ee1e3f8f 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -567,6 +567,8 @@ message SecureSettingsProto {
// value.
optional SettingProto rtt_calling_mode = 69 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto screen_off_udfps_enabled = 104 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
message Screensaver {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -744,5 +746,5 @@ message SecureSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 104;
+ // Next tag = 105;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e133ca48d0d7..df989527efe4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -9164,15 +9164,6 @@
</intent-filter>
</receiver>
- <receiver android:name="com.android.server.updates.CertificateTransparencyLogInstallReceiver"
- android:exported="true"
- android:permission="android.permission.UPDATE_CONFIG">
- <intent-filter>
- <action android:name="android.intent.action.UPDATE_CT_LOGS" />
- <data android:scheme="content" android:host="*" android:mimeType="*/*" />
- </intent-filter>
- </receiver>
-
<receiver android:name="com.android.server.updates.LangIdInstallReceiver"
android:exported="true"
android:permission="android.permission.UPDATE_CONFIG">
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index 76c810bdb2c1..e91e1115ac1c 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -157,39 +157,27 @@
android:maxDrawableHeight="@dimen/notification_right_icon_size"
/>
- <LinearLayout
- android:id="@+id/notification_buttons_column"
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:orientation="vertical"
+ android:minWidth="@dimen/notification_content_margin_end"
>
- <include layout="@layout/notification_close_button"
- android:layout_width="@dimen/notification_close_button_size"
- android:layout_height="@dimen/notification_close_button_size"
- android:layout_gravity="end"
- android:layout_marginEnd="20dp"
- />
-
- <FrameLayout
- android:id="@+id/expand_button_touch_container"
+ <include layout="@layout/notification_expand_button"
android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:minWidth="@dimen/notification_content_margin_end"
- >
-
- <include layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
- />
-
- </FrameLayout>
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
- </LinearLayout>
+ </FrameLayout>
</LinearLayout>
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 2e0a7afc3cd1..2d367337bb6f 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -194,4 +194,11 @@
</FrameLayout>
</LinearLayout>
+
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
</com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index f644adefda9d..fbecb8c30b9c 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -199,6 +199,12 @@
</LinearLayout>
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
</com.android.internal.widget.NotificationMaxHeightFrameLayout>
<LinearLayout
diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml
index fc727e1c72f5..2d30d8a8bbb6 100644
--- a/core/res/res/layout/notification_2025_template_header.xml
+++ b/core/res/res/layout/notification_2025_template_header.xml
@@ -60,7 +60,7 @@
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
- android:layout_toStartOf="@id/notification_buttons_column"
+ android:layout_toStartOf="@id/expand_button"
android:layout_alignWithParentIfMissing="true"
android:clipChildren="false"
android:gravity="center_vertical"
@@ -81,28 +81,17 @@
android:focusable="false"
/>
- <LinearLayout
- android:id="@+id/notification_buttons_column"
+ <include layout="@layout/notification_expand_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:orientation="vertical"
- >
-
- <include layout="@layout/notification_close_button"
- android:layout_width="@dimen/notification_close_button_size"
- android:layout_height="@dimen/notification_close_button_size"
- android:layout_gravity="end"
- android:layout_marginEnd="20dp"
- />
-
- <include layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- />
+ android:layout_centerVertical="true"
+ android:layout_alignParentEnd="true" />
- </LinearLayout>
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentEnd="true" />
</NotificationHeaderView>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index e28b6462bad7..e6295ea06177 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -100,4 +100,7 @@
<!-- Whether to enable scaling and fading animation to scrollviews while scrolling.
P.S this is a change only intended for wear devices. -->
<bool name="config_enableViewGroupScalingFading">true</bool>
+
+ <!-- Allow the gesture to double tap the power button to trigger a target action. -->
+ <bool name="config_doubleTapPowerGestureEnabled">false</bool>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 565e28e0cc87..45a5d85a097d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2165,6 +2165,17 @@
config_enableGeofenceOverlay is false. -->
<string name="config_geofenceProviderPackageName" translatable="false">@null</string>
+ <!-- Whether to enable GNSS assistance overlay which allows GnssAssistanceProvider to be
+ replaced by an app at run-time. When disabled, only the
+ config_gnssAssistanceProviderPackageName package will be searched for
+ GnssAssistanceProvider, otherwise any system package is eligible. Anyone who wants to
+ disable the overlay mechanism can set it to false.
+ -->
+ <bool name="config_enableGnssAssistanceOverlay" translatable="false">true</bool>
+ <!-- Package name providing GNSS assistance API support. Used only when
+ config_enableGnssAssistanceOverlay is false. -->
+ <string name="config_gnssAssistanceProviderPackageName" translatable="false">@null</string>
+
<!-- Whether to enable Hardware Activity-Recognition overlay which allows Hardware
Activity-Recognition to be replaced by an app at run-time. When disabled, only the
config_activityRecognitionHardwarePackageName package will be searched for
diff --git a/core/res/res/values/config_watch.xml b/core/res/res/values/config_watch.xml
index 629a343f1280..bcb1e0941b5a 100644
--- a/core/res/res/values/config_watch.xml
+++ b/core/res/res/values/config_watch.xml
@@ -15,8 +15,7 @@
-->
<resources>
- <!-- TODO(b/382103556): use predefined Material3 token -->
<!-- For Wear Material3 -->
- <dimen name="config_wearMaterial3_buttonCornerRadius">26dp</dimen>
- <dimen name="config_wearMaterial3_bottomDialogCornerRadius">18dp</dimen>
+ <dimen name="config_wearMaterial3_buttonCornerRadius">@dimen/config_shapeCornerRadiusLarge</dimen>
+ <dimen name="config_wearMaterial3_bottomDialogCornerRadius">@dimen/config_shapeCornerRadiusMedium</dimen>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 73e06f6a2520..8195d38993c8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2040,6 +2040,7 @@
<java-symbol type="bool" name="config_useGnssHardwareProvider" />
<java-symbol type="bool" name="config_enableGeocoderOverlay" />
<java-symbol type="bool" name="config_enableGeofenceOverlay" />
+ <java-symbol type="bool" name="config_enableGnssAssistanceOverlay" />
<java-symbol type="bool" name="config_enableNetworkLocationOverlay" />
<java-symbol type="bool" name="config_sf_limitedAlpha" />
<java-symbol type="bool" name="config_unplugTurnsOnScreen" />
@@ -2223,6 +2224,7 @@
<java-symbol type="string" name="config_gnssLocationProviderPackageName" />
<java-symbol type="string" name="config_geocoderProviderPackageName" />
<java-symbol type="string" name="config_geofenceProviderPackageName" />
+ <java-symbol type="string" name="config_gnssAssistanceProviderPackageName" />
<java-symbol type="string" name="config_networkLocationProviderPackageName" />
<java-symbol type="string" name="config_wimaxManagerClassname" />
<java-symbol type="string" name="config_wimaxNativeLibLocation" />
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 9effeec23890..ca6ad6fae46e 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -105,6 +105,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.widget.NotificationProgressModel;
import junit.framework.Assert;
@@ -2414,7 +2415,7 @@ public class NotificationTest {
@Test
@EnableFlags(Flags.FLAG_API_RICH_ONGOING)
- public void progressStyle_getProgressMax_nooSegments_returnsDefault() {
+ public void progressStyle_getProgressMax_noSegments_returnsDefault() {
final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
progressStyle.setProgressSegments(Collections.emptyList());
assertThat(progressStyle.getProgressMax()).isEqualTo(100);
@@ -2459,6 +2460,211 @@ public class NotificationTest {
@Test
@EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_getProgressMax_onSegmentLimitExceeded_returnsSumOfSegmentLength() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // limit is 10 for ProgressStyle
+ for (int i = 0; i < 30; i++) {
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(10));
+ }
+
+ // THEN
+ assertThat(progressStyle.getProgressMax()).isEqualTo(300);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_addProgressSegment_dropsInvalidSegments() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // Segments should be a positive integer.
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(0));
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(-1));
+
+ // THEN
+ assertThat(progressStyle.getProgressSegments()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_setProgressSegment_dropsInvalidSegments() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // Segments should be a positive integer.
+ progressStyle
+ .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(0),
+ new Notification.ProgressStyle.Segment(-1)));
+
+ // THEN
+ assertThat(progressStyle.getProgressSegments()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_addProgressPoint_dropsNegativePoints() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // Points should not be a negative integer.
+ progressStyle
+ .addProgressPoint(new Notification.ProgressStyle.Point(-1))
+ .addProgressPoint(new Notification.ProgressStyle.Point(-100));
+
+ // THEN
+ assertThat(progressStyle.getProgressPoints()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_setProgressPoint_dropsNegativePoints() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // Points should not be a negative integer.
+ progressStyle
+ .setProgressPoints(List.of(new Notification.ProgressStyle.Point(-1),
+ new Notification.ProgressStyle.Point(-100)));
+
+ // THEN
+ assertThat(progressStyle.getProgressPoints()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_ignoresPointsExceedingMax() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ progressStyle.addProgressSegment(new Notification.ProgressStyle.Segment(100));
+ // Points should not larger than progress maximum.
+ progressStyle
+ .addProgressPoint(new Notification.ProgressStyle.Point(101))
+ .addProgressPoint(new Notification.ProgressStyle.Point(500));
+
+ // THEN
+ assertThat(progressStyle.createProgressModel(Color.BLUE, Color.RED).getPoints()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_ignoresOverLimitPoints() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ progressStyle.addProgressSegment(new Notification.ProgressStyle.Segment(100));
+
+ // maximum 4 points are going to be rendered.
+ progressStyle
+ .addProgressPoint(new Notification.ProgressStyle.Point(0))
+ .addProgressPoint(new Notification.ProgressStyle.Point(20))
+ .addProgressPoint(new Notification.ProgressStyle.Point(150))
+ .addProgressPoint(new Notification.ProgressStyle.Point(50))
+ .addProgressPoint(new Notification.ProgressStyle.Point(70))
+ .addProgressPoint(new Notification.ProgressStyle.Point(80))
+ .addProgressPoint(new Notification.ProgressStyle.Point(90))
+ .addProgressPoint(new Notification.ProgressStyle.Point(95))
+ .addProgressPoint(new Notification.ProgressStyle.Point(100));
+ final int backgroundColor = Color.RED;
+ final int defaultProgressColor = Color.BLUE;
+ final int expectedProgressColor = Notification.ProgressStyle.sanitizeProgressColor(
+ /* color = */Notification.COLOR_DEFAULT,
+ /* bg = */backgroundColor,
+ /* defaultColor = */defaultProgressColor);
+
+ // THEN
+ assertThat(progressStyle.createProgressModel(defaultProgressColor, backgroundColor)
+ .getPoints()).isEqualTo(
+ List.of(new Notification.ProgressStyle.Point(0)
+ .setColor(expectedProgressColor),
+ new Notification.ProgressStyle.Point(20)
+ .setColor(expectedProgressColor),
+ new Notification.ProgressStyle.Point(50)
+ .setColor(expectedProgressColor),
+ new Notification.ProgressStyle.Point(70)
+ .setColor(expectedProgressColor)
+ )
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_mergeSegmentsOnOverflow() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+
+ for (int i = 0; i < 15; i++) {
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(10));
+ }
+
+ final NotificationProgressModel progressModel = progressStyle.createProgressModel(
+ Color.BLUE, Color.RED);
+
+ // THEN
+ assertThat(progressModel.getSegments().size()).isEqualTo(1);
+ assertThat(progressModel.getProgressMax()).isEqualTo(150);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_useSegmentColorWhenAllMatch() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ final int segmentColor = Color.YELLOW;
+ final int defaultProgressColor = Color.BLUE;
+ final int backgroundColor = Color.RED;
+ // contrast ensured color for segmentColor.
+ final int expectedSegmentColor = Notification.ProgressStyle.sanitizeProgressColor(
+ /* color = */ segmentColor,
+ /* bg = */ backgroundColor,
+ /* defaultColor = */ defaultProgressColor);
+
+ for (int i = 0; i < 15; i++) {
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(10)
+ .setColor(segmentColor));
+ }
+
+ final NotificationProgressModel progressModel = progressStyle.createProgressModel(
+ defaultProgressColor, backgroundColor);
+
+ // THEN
+ assertThat(progressModel.getSegments())
+ .isEqualTo(List.of(new Notification.ProgressStyle.Segment(150)
+ .setColor(expectedSegmentColor)));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_useDefaultColorWhenAllNotMatch() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ final int defaultProgressColor = Color.BLUE;
+ final int backgroundColor = Color.RED;
+ // contrast ensured color for default progress color.
+ final int expectedSegmentColor = Notification.ProgressStyle.sanitizeProgressColor(
+ /* color = */ defaultProgressColor,
+ /* bg = */ backgroundColor,
+ /* defaultColor = */ defaultProgressColor);
+
+ for (int i = 0; i < 15; i++) {
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(5)
+ .setColor(Color.BLUE))
+ .addProgressSegment(new Notification.ProgressStyle.Segment(5)
+ .setColor(Color.CYAN));
+ }
+
+ final NotificationProgressModel progressModel = progressStyle.createProgressModel(
+ defaultProgressColor, backgroundColor);
+
+ // THEN
+ assertThat(progressModel.getSegments())
+ .isEqualTo(List.of(new Notification.ProgressStyle.Segment(150)
+ .setColor(expectedSegmentColor)));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
public void progressStyle_indeterminate_defaultValueFalse() {
final Notification.ProgressStyle progressStyle1 = new Notification.ProgressStyle();
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 911b7ce22741..10a85bcbf488 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -120,7 +120,8 @@ public class ClientTransactionListenerControllerTest {
doReturn(newDisplayInfo).when(mIDisplayManager).getDisplayInfo(123);
mDisplayManager.registerDisplayListener(mListener, mHandler,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
null /* packageName */);
mController.onDisplayChanged(123);
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 7a5b3064b3a3..a270848b98a3 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -73,9 +73,13 @@ public class DisplayManagerGlobalTest {
public final CheckFlagsRule mCheckFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
+ private static final long DISPLAY_CHANGE_EVENTS =
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
+
private static final long ALL_DISPLAY_EVENTS =
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DISPLAY_CHANGE_EVENTS
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
@Mock
@@ -127,7 +131,7 @@ public class DisplayManagerGlobalTest {
final DisplayInfo newDisplayInfo = new DisplayInfo();
newDisplayInfo.rotation++;
doReturn(newDisplayInfo).when(mDisplayManager).getDisplayInfo(displayId);
- callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
Mockito.verify(mDisplayListener).onDisplayChanged(eq(displayId));
Mockito.verifyNoMoreInteractions(mDisplayListener);
@@ -186,8 +190,8 @@ public class DisplayManagerGlobalTest {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED, null);
- callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ & ~DISPLAY_CHANGE_EVENTS, null);
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
@@ -257,8 +261,7 @@ public class DisplayManagerGlobalTest {
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
null /* packageName */);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener2, mHandler,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
- null /* packageName */);
+ DISPLAY_CHANGE_EVENTS, null /* packageName */);
mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(321);
waitForHandler();
@@ -304,8 +307,7 @@ public class DisplayManagerGlobalTest {
assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED,
mDisplayManagerGlobal
.mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_ADDED, 0));
- assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
- mDisplayManagerGlobal
+ assertEquals(DISPLAY_CHANGE_EVENTS, mDisplayManagerGlobal
.mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, 0));
assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
mDisplayManagerGlobal
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
index 4a227d8ff1ef..255d09b854bd 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -86,7 +86,7 @@ class DisplayTopologyTest {
verifyDisplay(display1, displayId1, width1, height1, noOfChildren = 1)
val display2 = display1.children[0]
- verifyDisplay(display1.children[0], displayId2, width2, height2, POSITION_TOP,
+ verifyDisplay(display2, displayId2, width2, height2, POSITION_TOP,
offset = width1 / 2 - width2 / 2, noOfChildren = 1)
var display = display2
@@ -99,6 +99,76 @@ class DisplayTopologyTest {
}
@Test
+ fun updateDisplay() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+
+ val newWidth = 1000f
+ val newHeight = 500f
+ topology.addDisplay(displayId, width, height)
+ assertThat(topology.updateDisplay(displayId, newWidth, newHeight)).isTrue()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+ verifyDisplay(topology.root!!, displayId, newWidth, newHeight, noOfChildren = 0)
+ }
+
+ @Test
+ fun updateDisplay_notUpdated() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+ topology.addDisplay(displayId, width, height)
+
+ // Same size
+ assertThat(topology.updateDisplay(displayId, width, height)).isFalse()
+
+ // Display doesn't exist
+ assertThat(topology.updateDisplay(/* displayId= */ 100, width, height)).isFalse()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+ verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
+ }
+
+ @Test
+ fun updateDisplayDoesNotAffectDefaultTopology() {
+ val width1 = 700f
+ val height = 600f
+ topology.addDisplay(/* displayId= */ 1, width1, height)
+
+ val width2 = 800f
+ val noOfDisplays = 30
+ for (i in 2..noOfDisplays) {
+ topology.addDisplay(/* displayId= */ i, width2, height)
+ }
+
+ val displaysToUpdate = arrayOf(3, 7, 18)
+ val newWidth = 1000f
+ val newHeight = 1500f
+ for (i in displaysToUpdate) {
+ assertThat(topology.updateDisplay(/* displayId= */ i, newWidth, newHeight)).isTrue()
+ }
+
+ assertThat(topology.primaryDisplayId).isEqualTo(1)
+
+ val display1 = topology.root!!
+ verifyDisplay(display1, id = 1, width1, height, noOfChildren = 1)
+
+ val display2 = display1.children[0]
+ verifyDisplay(display2, id = 2, width2, height, POSITION_TOP,
+ offset = width1 / 2 - width2 / 2, noOfChildren = 1)
+
+ var display = display2
+ for (i in 3..noOfDisplays) {
+ display = display.children[0]
+ // The last display should have no children
+ verifyDisplay(display, id = i, if (i in displaysToUpdate) newWidth else width2,
+ if (i in displaysToUpdate) newHeight else height, POSITION_RIGHT, offset = 0f,
+ noOfChildren = if (i < noOfDisplays) 1 else 0)
+ }
+ }
+
+ @Test
fun removeDisplays() {
val displayId1 = 1
val width1 = 800f
@@ -117,7 +187,7 @@ class DisplayTopologyTest {
}
var removedDisplays = arrayOf(20)
- topology.removeDisplay(20)
+ assertThat(topology.removeDisplay(20)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
@@ -139,11 +209,11 @@ class DisplayTopologyTest {
noOfChildren = if (i < noOfDisplays) 1 else 0)
}
- topology.removeDisplay(22)
+ assertThat(topology.removeDisplay(22)).isTrue()
removedDisplays += 22
- topology.removeDisplay(23)
+ assertThat(topology.removeDisplay(23)).isTrue()
removedDisplays += 23
- topology.removeDisplay(25)
+ assertThat(topology.removeDisplay(25)).isTrue()
removedDisplays += 25
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
@@ -174,7 +244,7 @@ class DisplayTopologyTest {
val height = 600f
topology.addDisplay(displayId, width, height)
- topology.removeDisplay(displayId)
+ assertThat(topology.removeDisplay(displayId)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(Display.INVALID_DISPLAY)
assertThat(topology.root).isNull()
@@ -187,7 +257,7 @@ class DisplayTopologyTest {
val height = 600f
topology.addDisplay(displayId, width, height)
- topology.removeDisplay(3)
+ assertThat(topology.removeDisplay(3)).isFalse()
assertThat(topology.primaryDisplayId).isEqualTo(displayId)
verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
@@ -203,7 +273,7 @@ class DisplayTopologyTest {
topology = DisplayTopology(/* root= */ null, displayId2)
topology.addDisplay(displayId1, width, height)
topology.addDisplay(displayId2, width, height)
- topology.removeDisplay(displayId2)
+ assertThat(topology.removeDisplay(displayId2)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
verifyDisplay(topology.root!!, displayId1, width, height, noOfChildren = 0)
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
index 310baa371163..ea39db7b0057 100644
--- a/core/tests/coretests/src/android/os/ProcessTest.java
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -18,6 +18,7 @@
package android.os;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.IgnoreUnderRavenwood;
@@ -26,6 +27,8 @@ import android.platform.test.ravenwood.RavenwoodRule;
import org.junit.Rule;
import org.junit.Test;
+import java.util.Arrays;
+
@IgnoreUnderRavenwood(blockedBy = Process.class)
public class ProcessTest {
@Rule
@@ -92,4 +95,19 @@ public class ProcessTest {
assertTrue(Process.getAdvertisedMem() > 0);
assertTrue(Process.getTotalMemory() <= Process.getAdvertisedMem());
}
+
+ @Test
+ public void testGetSchedAffinity() {
+ long[] tidMasks = Process.getSchedAffinity(Process.myTid());
+ long[] pidMasks = Process.getSchedAffinity(Process.myPid());
+ checkAffinityMasks(tidMasks);
+ checkAffinityMasks(pidMasks);
+ }
+
+ static void checkAffinityMasks(long[] masks) {
+ assertNotNull(masks);
+ assertTrue(masks.length > 0);
+ assertTrue("At least one of the affinity mask should be non-zero but got "
+ + Arrays.toString(masks), Arrays.stream(masks).anyMatch(value -> value > 0));
+ }
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index ed9fc1c9e547..18ab52dba8f3 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -25,7 +25,7 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
-import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -861,10 +861,10 @@ public class ViewRootImplTest {
assertEquals(mViewRootImpl.getFrameRateCompatibility(),
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
assertFalse(mViewRootImpl.isFrameRateConflicted());
- mViewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_GTE);
+ mViewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_AT_LEAST);
if (toolkitFrameRateVelocityMappingReadOnly()) {
assertEquals(24, mViewRootImpl.getPreferredFrameRate(), 0.1);
- assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
+ assertEquals(FRAME_RATE_COMPATIBILITY_AT_LEAST,
mViewRootImpl.getFrameRateCompatibility());
assertFalse(mViewRootImpl.isFrameRateConflicted());
} else {
@@ -888,10 +888,10 @@ public class ViewRootImplTest {
sInstrumentation.runOnMainSync(() -> {
assertFalse(mViewRootImpl.isFrameRateConflicted());
- mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
+ mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_AT_LEAST);
if (toolkitFrameRateVelocityMappingReadOnly()) {
assertEquals(60, mViewRootImpl.getPreferredFrameRate(), 0.1);
- assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
+ assertEquals(FRAME_RATE_COMPATIBILITY_AT_LEAST,
mViewRootImpl.getFrameRateCompatibility());
} else {
assertEquals(FRAME_RATE_CATEGORY_HIGH,
@@ -904,7 +904,7 @@ public class ViewRootImplTest {
mViewRootImpl.getFrameRateCompatibility());
// Should be false since 60 is a divisor of 120.
assertFalse(mViewRootImpl.isFrameRateConflicted());
- mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
+ mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_AT_LEAST);
assertEquals(120, mViewRootImpl.getPreferredFrameRate(), 0.1);
// compatibility should be remained the same (FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
// since the frame rate 60 is smaller than 120.
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 5a4561d7c6ea..f87b6994900f 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -266,6 +266,11 @@ public class ContentCaptureSessionTest {
}
@Override
+ void internalNotifySessionFlushEvent(int sessionId) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
@NonNull ContentCaptureContext clientContext) {
throw new UnsupportedOperationException("should not have been called");
diff --git a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
index fdc00ba65255..610758d378de 100644
--- a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
+++ b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
@@ -16,8 +16,6 @@
package android.window;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static org.junit.Assert.assertEquals;
@@ -30,15 +28,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager.TaskDescription;
-import android.content.ComponentName;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorSpace;
-import android.graphics.Point;
import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.view.Surface;
-import android.view.SurfaceControl;
import android.view.WindowInsets;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -54,7 +46,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class SnapshotDrawerUtilsTest {
- private SnapshotDrawerUtils.SnapshotSurface mSnapshotSurface;
+ private SnapshotDrawerUtils.SystemBarBackgroundPainter mSystemBarBackgroundPainter;
private void setupSurface(int width, int height) {
setupSurface(width, height, new Rect(), FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
@@ -70,31 +62,14 @@ public class SnapshotDrawerUtilsTest {
// taskBounds
assertEquals(width, taskBounds.width());
assertEquals(height, taskBounds.height());
- Point taskSize = new Point(taskBounds.width(), taskBounds.height());
- final TaskSnapshot snapshot = createTaskSnapshot(width, height, taskSize, contentInsets);
TaskDescription taskDescription = createTaskDescription(Color.WHITE,
Color.RED, Color.BLUE);
- mSnapshotSurface = new SnapshotDrawerUtils.SnapshotSurface(
- new SurfaceControl(), snapshot, "Test");
- mSnapshotSurface.initiateSystemBarPainter(windowFlags, 0, 0,
- taskDescription, WindowInsets.Type.defaultVisible());
- }
-
- private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
- Rect contentInsets) {
- final HardwareBuffer buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888,
- 1, HardwareBuffer.USAGE_CPU_READ_RARELY);
- return new TaskSnapshot(
- System.currentTimeMillis(),
- 0 /* captureTime */,
- new ComponentName("", ""), buffer,
- ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- Surface.ROTATION_0, taskSize, contentInsets, new Rect() /* letterboxInsets */,
- false, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
- 0 /* systemUiVisibility */, false /* isTranslucent */, false /* hasImeSurface */,
- 0 /* uiMode */);
+ mSystemBarBackgroundPainter = new SnapshotDrawerUtils.SystemBarBackgroundPainter(
+ windowFlags, 0 /* windowPrivateFlags */, 0 /* appearance */,
+ taskDescription, 1f /* scale */, WindowInsets.Type.defaultVisible());
+ mSystemBarBackgroundPainter.setInsets(contentInsets);
}
private static TaskDescription createTaskDescription(int background,
@@ -107,134 +82,14 @@ public class SnapshotDrawerUtilsTest {
}
@Test
- public void fillEmptyBackground_fillHorizontally() {
- setupSurface(200, 100);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(200);
- when(mockCanvas.getHeight()).thenReturn(100);
- mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200));
- verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
- }
-
- @Test
- public void fillEmptyBackground_fillVertically() {
- setupSurface(100, 200);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(200);
- mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100));
- verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any());
- }
-
- @Test
- public void fillEmptyBackground_fillBoth() {
- setupSurface(200, 200);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(200);
- when(mockCanvas.getHeight()).thenReturn(200);
- mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
- verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
- verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any());
- }
-
- @Test
- public void fillEmptyBackground_dontFill_sameSize() {
- setupSurface(100, 100);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
- verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
- }
-
- @Test
- public void fillEmptyBackground_dontFill_bitmapLarger() {
- setupSurface(100, 100);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200));
- verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
- }
-
- @Test
- public void testCalculateSnapshotCrop() {
- final Rect contentInsets = new Rect(0, 10, 0, 10);
- setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(0, 0, 100, 90),
- mSnapshotSurface.calculateSnapshotCrop(contentInsets));
- }
-
- @Test
- public void testCalculateSnapshotCrop_taskNotOnTop() {
- final Rect contentInsets = new Rect(0, 10, 0, 10);
- final Rect bounds = new Rect(0, 50, 100, 150);
- setupSurface(100, 100, contentInsets, 0, bounds);
- mSnapshotSurface.setFrames(bounds, contentInsets);
- assertEquals(new Rect(0, 10, 100, 90),
- mSnapshotSurface.calculateSnapshotCrop(contentInsets));
- }
-
- @Test
- public void testCalculateSnapshotCrop_navBarLeft() {
- final Rect contentInsets = new Rect(10, 0, 0, 0);
- setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(10, 0, 100, 100),
- mSnapshotSurface.calculateSnapshotCrop(contentInsets));
- }
-
- @Test
- public void testCalculateSnapshotCrop_navBarRight() {
- final Rect contentInsets = new Rect(0, 10, 10, 0);
- setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(0, 0, 90, 100),
- mSnapshotSurface.calculateSnapshotCrop(contentInsets));
- }
-
- @Test
- public void testCalculateSnapshotCrop_waterfall() {
- final Rect contentInsets = new Rect(5, 10, 5, 10);
- setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(5, 0, 95, 90),
- mSnapshotSurface.calculateSnapshotCrop(contentInsets));
- }
-
- @Test
- public void testCalculateSnapshotFrame() {
- setupSurface(100, 100);
- final Rect insets = new Rect(0, 10, 0, 10);
- mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
- assertEquals(new Rect(0, 0, 100, 80),
- mSnapshotSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
- }
-
- @Test
- public void testCalculateSnapshotFrame_navBarLeft() {
- setupSurface(100, 100);
- final Rect insets = new Rect(10, 10, 0, 0);
- mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
- assertEquals(new Rect(10, 0, 100, 90),
- mSnapshotSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
- }
-
- @Test
- public void testCalculateSnapshotFrame_waterfall() {
- setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, new Rect(0, 0, 100, 100));
- final Rect insets = new Rect(0, 10, 0, 10);
- mSnapshotSurface.setFrames(new Rect(5, 0, 95, 100), insets);
- assertEquals(new Rect(0, 0, 90, 90),
- mSnapshotSurface.calculateSnapshotFrame(new Rect(5, 0, 95, 90)));
- }
-
- @Test
public void testDrawStatusBarBackground() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 10, 0);
- mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
+ mSystemBarBackgroundPainter.setInsets(insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSnapshotSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100));
+ mSystemBarBackgroundPainter.drawDecors(mockCanvas, new Rect(0, 0, 50, 100));
verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
}
@@ -242,11 +97,11 @@ public class SnapshotDrawerUtilsTest {
public void testDrawStatusBarBackground_nullFrame() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 10, 0);
- mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
+ mSystemBarBackgroundPainter.setInsets(insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSnapshotSurface.drawStatusBarBackground(mockCanvas, null);
+ mSystemBarBackgroundPainter.drawDecors(mockCanvas, null /* alreadyDrawnFrame */);
verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
}
@@ -254,11 +109,11 @@ public class SnapshotDrawerUtilsTest {
public void testDrawStatusBarBackground_nope() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 10, 0);
- mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
+ mSystemBarBackgroundPainter.setInsets(insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSnapshotSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100));
+ mSystemBarBackgroundPainter.drawDecors(mockCanvas, new Rect(0, 0, 100, 100));
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
}
@@ -267,11 +122,11 @@ public class SnapshotDrawerUtilsTest {
final Rect insets = new Rect(0, 10, 0, 10);
setupSurface(100, 100, insets, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, 100, 100));
- mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
+ mSystemBarBackgroundPainter.setInsets(insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSnapshotSurface.drawNavigationBarBackground(mockCanvas);
+ mSystemBarBackgroundPainter.drawDecors(mockCanvas, null /* alreadyDrawnFrame */);
verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any());
}
@@ -280,11 +135,11 @@ public class SnapshotDrawerUtilsTest {
final Rect insets = new Rect(10, 10, 0, 0);
setupSurface(100, 100, insets, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, 100, 100));
- mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
+ mSystemBarBackgroundPainter.setInsets(insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSnapshotSurface.drawNavigationBarBackground(mockCanvas);
+ mSystemBarBackgroundPainter.drawDecors(mockCanvas, null /* alreadyDrawnFrame */);
verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any());
}
@@ -293,11 +148,11 @@ public class SnapshotDrawerUtilsTest {
final Rect insets = new Rect(0, 10, 10, 0);
setupSurface(100, 100, insets, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, 100, 100));
- mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
+ mSystemBarBackgroundPainter.setInsets(insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSnapshotSurface.drawNavigationBarBackground(mockCanvas);
+ mSystemBarBackgroundPainter.drawDecors(mockCanvas, null /* alreadyDrawnFrame */);
verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any());
}
}
diff --git a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
index bb2fe1bcfc64..84ff40f0dcf0 100644
--- a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
@@ -33,11 +33,15 @@ import android.app.ActivityThread;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.IWindowManager;
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -58,6 +62,9 @@ public class WindowTokenClientControllerTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public SetFlagsRule setFlagsRule = new SetFlagsRule();
+
@Mock
private IWindowManager mWindowManagerService;
@Mock
@@ -161,6 +168,7 @@ public class WindowTokenClientControllerTest {
verify(mWindowManagerService).detachWindowContext(mWindowTokenClient);
}
+ @EnableFlags(Flags.FLAG_TRACK_SYSTEM_UI_CONTEXT_BEFORE_WMS)
@Test
public void testAttachToDisplayContent_keepTrackWithoutWMS() {
// WMS is not initialized
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
index 00b4f464de50..d1fbc77cbd46 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -31,6 +31,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
@@ -44,20 +45,27 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.UserInfo;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManagerGlobal;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
+import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.widget.flags.Flags;
import com.google.android.collect.Lists;
@@ -76,6 +84,8 @@ import java.util.concurrent.CompletableFuture;
public class LockPatternUtilsTest {
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private ILockSettings mLockSettings;
private static final int USER_ID = 1;
@@ -395,4 +405,156 @@ public class LockPatternUtilsTest {
}
};
}
+
+ private InputManagerGlobal.TestSession configureExternalHardwareTest(InputDevice[] devices)
+ throws RemoteException {
+ final Context context = new ContextWrapper(InstrumentationRegistry.getTargetContext());
+ final ILockSettings ils = mock(ILockSettings.class);
+ when(ils.getBoolean(anyString(), anyBoolean(), anyInt())).thenThrow(RemoteException.class);
+ mLockPatternUtils = new LockPatternUtils(context, ils);
+
+ IInputManager inputManagerMock = mock(IInputManager.class);
+
+ int[] deviceIds = new int[devices.length];
+
+ for (int i = 0; i < devices.length; i++) {
+ when(inputManagerMock.getInputDevice(i)).thenReturn(devices[i]);
+ }
+
+ when(inputManagerMock.getInputDeviceIds()).thenReturn(deviceIds);
+ InputManagerGlobal.TestSession session =
+ InputManagerGlobal.createTestSession(inputManagerMock);
+
+ return session;
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_noDevicesAttached() throws RemoteException {
+ InputManagerGlobal.TestSession session = configureExternalHardwareTest(new InputDevice[0]);
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_noEnabledDeviceAttached() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder.setEnabled(false);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withoutHwKeyboard() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder.setEnabled(true).setSources(InputDevice.SOURCE_TOUCHSCREEN);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withoutFullHwKeyboard() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withHwKeyboardOldDefault() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withHwKeyboard() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_noDevices() throws RemoteException {
+ InputManagerGlobal.TestSession session = configureExternalHardwareTest(new InputDevice[0]);
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_noEnabledDevices() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder.setEnabled(false);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_noPointingDevices() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_TOUCHSCREEN);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_externalPointingDevice() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_CLASS_POINTER);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_externalPointingDeviceOldDefault() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_CLASS_POINTER);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
}
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
index 41d1b5c15369..eecf199a3ec2 100644
--- a/libs/WindowManager/Shell/multivalentTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -55,6 +55,7 @@ android_robolectric_test {
"truth",
"flag-junit-base",
"flag-junit",
+ "testables",
],
auto_gen_config: true,
}
@@ -77,6 +78,7 @@ android_test {
"truth",
"platform-test-annotations",
"platform-test-rules",
+ "testables",
],
libs: [
"android.test.base.stubs.system",
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
index 0d8f80935f5a..3e01256fd67c 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.bubbles.bar
+import android.animation.AnimatorTestRule
+import android.app.ActivityManager
import android.content.Context
import android.graphics.Insets
import android.graphics.Rect
@@ -23,7 +25,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
-import androidx.core.animation.AnimatorTestRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -36,27 +37,34 @@ import com.android.wm.shell.bubbles.BubbleExpandedViewManager
import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubbleOverflow
import com.android.wm.shell.bubbles.BubblePositioner
+import com.android.wm.shell.bubbles.BubbleTaskView
import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.FakeBubbleFactory
-import com.android.wm.shell.bubbles.FakeBubbleTaskViewFactory
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import org.junit.After
import org.junit.Before
-import org.junit.ClassRule
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
/** Tests for [BubbleBarAnimationHelper] */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleBarAnimationHelperTest {
- companion object {
- @JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
+ @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
+ companion object {
const val SCREEN_WIDTH = 2000
const val SCREEN_HEIGHT = 1000
}
@@ -148,6 +156,26 @@ class BubbleBarAnimationHelperTest {
}
@Test
+ fun animateSwitch_bubbleToBubble_updateTaskBounds() {
+ val fromBubble = createBubble("from").initialize(container)
+ val toBubbleTaskController = mock<TaskViewTaskController>()
+ val toBubble = createBubble("to", toBubbleTaskController).initialize(container)
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(fromBubble, toBubble) {}
+ // Start the animation, but don't finish
+ animatorTestRule.advanceTimeBy(100)
+ }
+ getInstrumentation().waitForIdleSync()
+ // Clear invocations to ensure that bounds update happens after animation ends
+ clearInvocations(toBubbleTaskController)
+ getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(900) }
+ getInstrumentation().waitForIdleSync()
+
+ verify(toBubbleTaskController).setWindowBounds(any())
+ }
+
+ @Test
fun animateSwitch_bubbleToOverflow_oldHiddenNewShown() {
val fromBubble = createBubble(key = "from").initialize(container)
val overflow = createOverflow().initialize(container)
@@ -193,13 +221,43 @@ class BubbleBarAnimationHelperTest {
assertThat(toBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
}
- private fun createBubble(key: String): Bubble {
+ @Test
+ fun animateToRestPosition_updateTaskBounds() {
+ val taskController = mock<TaskViewTaskController>()
+ val bubble = createBubble("key", taskController).initialize(container)
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateExpansion(bubble) {}
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateToRestPosition()
+ animatorTestRule.advanceTimeBy(100)
+ }
+ // Clear invocations to ensure that bounds update happens after animation ends
+ clearInvocations(taskController)
+ getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(900) }
+ getInstrumentation().waitForIdleSync()
+
+ verify(taskController).setWindowBounds(any())
+ }
+
+ private fun createBubble(
+ key: String,
+ taskViewTaskController: TaskViewTaskController = mock<TaskViewTaskController>(),
+ ): Bubble {
+ val taskView = TaskView(context, taskViewTaskController)
+ val taskInfo = mock<ActivityManager.RunningTaskInfo>()
+ whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
+ val bubbleTaskView = BubbleTaskView(taskView, mainExecutor)
+
val bubbleBarExpandedView =
FakeBubbleFactory.createExpandedView(
context,
bubblePositioner,
expandedViewManager,
- FakeBubbleTaskViewFactory(context, mainExecutor).create(),
+ bubbleTaskView,
mainExecutor,
bgExecutor,
bubbleLogger,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 3e8a9b64dac6..3188e5b9c6d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -463,6 +463,7 @@ public class BubbleBarAnimationHelper {
super.onAnimationEnd(animation);
bbev.resetPivot();
bbev.setDragging(false);
+ updateExpandedView(bbev);
}
});
startNewAnimator(animatorSet);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index c74bf53268f9..9ebb7f5aa270 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -643,7 +643,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
t.setPosition(animatingLeash, x, endY);
t.setAlpha(animatingLeash, 1.f);
}
- dispatchEndPositioning(mDisplayId, mCancelled, t);
+ if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+ dispatchEndPositioning(mDisplayId, mCancelled, t);
+ }
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
@@ -659,6 +661,14 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
ImeTracker.forLogging().onCancelled(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
}
+ if (android.view.inputmethod.Flags.refactorInsetsController()) {
+ // In split screen, we also set {@link
+ // WindowContainer#mExcludeInsetsTypes} but this should only happen after
+ // the IME client visibility was set. Otherwise the insets will we
+ // dispatched too early, and we get a flicker. Thus, only dispatching it
+ // after reporting that the IME is hidden to system server.
+ dispatchEndPositioning(mDisplayId, mCancelled, t);
+ }
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
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 21c44c9b92ee..4bcec702281d 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
@@ -571,9 +571,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// For flexible split, expand app offscreen as well
if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
- bounds1.top = bounds1.bottom - bounds2.width();
+ bounds1.top = bounds1.bottom - bounds2.height();
} else {
- bounds2.bottom = bounds2.top + bounds1.width();
+ bounds2.bottom = bounds2.top + bounds1.height();
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index dfa2d9b6bb63..9a60cfeed7c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -38,6 +38,7 @@ import androidx.core.util.putAll
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
@@ -236,6 +237,7 @@ class DesktopModeLoggerTransitionObserver(
) {
// Sessions is finishing, log task updates followed by an exit event
identifyAndLogTaskUpdates(
+ transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
)
@@ -252,12 +254,14 @@ class DesktopModeLoggerTransitionObserver(
desktopModeEventLogger.logSessionEnter(getEnterReason(transitionInfo))
identifyAndLogTaskUpdates(
+ transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
)
} else if (isSessionActive) {
// Session is neither starting, nor finishing, log task updates if there are any
identifyAndLogTaskUpdates(
+ transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
)
@@ -270,6 +274,7 @@ class DesktopModeLoggerTransitionObserver(
/** Compare the old and new state of taskInfos and identify and log the changes */
private fun identifyAndLogTaskUpdates(
+ transitionInfo: TransitionInfo,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
) {
@@ -304,9 +309,19 @@ class DesktopModeLoggerTransitionObserver(
// find old tasks that were removed
preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
- desktopModeEventLogger.logTaskRemoved(
- buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size())
- )
+ val minimizeReason =
+ if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) {
+ MinimizeReason.MINIMIZE_BUTTON
+ } else {
+ null
+ }
+ val taskUpdate =
+ buildTaskUpdateForTask(
+ taskInfo,
+ postTransitionVisibleFreeformTasks.size(),
+ minimizeReason,
+ )
+ desktopModeEventLogger.logTaskRemoved(taskUpdate)
Trace.setCounter(
Trace.TRACE_TAG_WINDOW_MANAGER,
VISIBLE_TASKS_COUNTER_NAME,
@@ -320,7 +335,11 @@ class DesktopModeLoggerTransitionObserver(
}
}
- private fun buildTaskUpdateForTask(taskInfo: TaskInfo, visibleTasks: Int): TaskUpdate {
+ private fun buildTaskUpdateForTask(
+ taskInfo: TaskInfo,
+ visibleTasks: Int,
+ minimizeReason: MinimizeReason? = null,
+ ): TaskUpdate {
val screenBounds = taskInfo.configuration.windowConfiguration.bounds
val positionInParent = taskInfo.positionInParent
return TaskUpdate(
@@ -331,6 +350,7 @@ class DesktopModeLoggerTransitionObserver(
taskX = positionInParent.x,
taskY = positionInParent.y,
visibleTaskCount = visibleTasks,
+ minimizeReason = minimizeReason,
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index 606a729305b4..90191345147c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -82,7 +82,7 @@ fun calculateInitialBounds(
// For portrait resizeable activities, respect apps fullscreen width but
// apply ideal size height.
Size(
- taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth,
+ taskInfo.appCompatTaskInfo.topActivityAppBounds.width(),
idealSize.height,
)
} else {
@@ -104,7 +104,7 @@ fun calculateInitialBounds(
// apply custom app width.
Size(
customPortraitWidthForLandscapeApp,
- taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight,
+ taskInfo.appCompatTaskInfo.topActivityAppBounds.height(),
)
} else {
// For portrait resizeable activities, simply apply ideal size.
@@ -196,13 +196,8 @@ fun maximizeSizeGivenAspectRatio(
/** Calculates the aspect ratio of an activity from its fullscreen bounds. */
fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
- val appLetterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth
- val appLetterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight
- if (taskInfo.appCompatTaskInfo.isTopActivityLetterboxed || !taskInfo.canChangeAspectRatio) {
- return maxOf(appLetterboxWidth, appLetterboxHeight) /
- minOf(appLetterboxWidth, appLetterboxHeight).toFloat()
- }
- val appBounds = taskInfo.configuration.windowConfiguration.appBounds ?: return 1f
+ if (taskInfo.appCompatTaskInfo.topActivityAppBounds.isEmpty) return 1f
+ val appBounds = taskInfo.appCompatTaskInfo.topActivityAppBounds
return maxOf(appBounds.height(), appBounds.width()) /
minOf(appBounds.height(), appBounds.width()).toFloat()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
index a611fe1db2ce..c4ff87d175a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
@@ -74,7 +74,7 @@ public class DragSession {
mInitialDragData = data;
mInitialDragFlags = dragFlags;
displayLayout = dispLayout;
- hideDragSourceTaskId = data.getDescription().getExtras() != null
+ hideDragSourceTaskId = data != null && data.getDescription().getExtras() != null
? data.getDescription().getExtras().getInt(EXTRA_HIDE_DRAG_SOURCE_TASK_ID, -1)
: -1;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 27472493a8bc..0519a5e055be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -89,8 +89,6 @@ public class TaskSnapshotWindow {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"create taskSnapshot surface for task: %d", taskId);
- final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
-
final WindowManager.LayoutParams layoutParams = SnapshotDrawerUtils.createLayoutParameters(
info, TITLE_FORMAT + taskId, TYPE_APPLICATION_STARTING,
snapshot.getHardwareBuffer().getFormat(), appToken);
@@ -152,8 +150,8 @@ public class TaskSnapshotWindow {
return null;
}
- SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot,
- info.taskBounds, topWindowInsetsState, true /* releaseAfterDraw */);
+ SnapshotDrawerUtils.drawSnapshotOnSurface(layoutParams, surfaceControl, snapshot,
+ info.taskBounds, true /* releaseAfterDraw */);
snapshotSurface.mHasDrawn = true;
snapshotSurface.reportDrawn();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
index 2a22d4dd0cb5..34d1011bac0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
@@ -26,7 +26,6 @@ import android.content.Context;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.view.Display;
-import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.WindowManager;
@@ -77,12 +76,11 @@ class WindowlessSnapshotWindowCreator {
final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost(
mContext, display, wlw, "WindowlessSnapshotWindowCreator");
final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds();
- final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
final FrameLayout rootLayout = new FrameLayout(
mSplashscreenContentDrawer.createViewContextWrapper(mContext));
mViewHost.setView(rootLayout, lp);
- SnapshotDrawerUtils.drawSnapshotOnSurface(info, lp, wlw.mChildSurface, snapshot,
- windowBounds, topWindowInsetsState, false /* releaseAfterDraw */);
+ SnapshotDrawerUtils.drawSnapshotOnSurface(lp, wlw.mChildSurface, snapshot,
+ windowBounds, false /* releaseAfterDraw */);
final ActivityManager.TaskDescription taskDescription =
SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo);
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index 9972247df46d..ab1ac1a0efa3 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -44,6 +44,7 @@ import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart
import android.tools.flicker.assertors.assertions.AppWindowRemainInsideDisplayBounds
import android.tools.flicker.assertors.assertions.AppWindowReturnsToStartBoundsAndPosition
import android.tools.flicker.assertors.assertions.LauncherWindowReplacesAppAsTopWindow
+import android.tools.flicker.assertors.assertions.VisibleLayersShownMoreThanOneConsecutiveEntry
import android.tools.flicker.config.AssertionTemplates
import android.tools.flicker.config.FlickerConfigEntry
import android.tools.flicker.config.ScenarioId
@@ -463,7 +464,9 @@ class DesktopModeFlickerScenarios {
}
),
assertions =
- AssertionTemplates.COMMON_ASSERTIONS +
+ AssertionTemplates.COMMON_ASSERTIONS.toMutableMap().also {
+ it.remove(VisibleLayersShownMoreThanOneConsecutiveEntry())
+ } +
listOf(
AppWindowOnTopAtStart(DESKTOP_MODE_APP),
AppWindowBecomesInvisible(DESKTOP_MODE_APP),
@@ -489,7 +492,9 @@ class DesktopModeFlickerScenarios {
}
),
assertions =
- AssertionTemplates.COMMON_ASSERTIONS +
+ AssertionTemplates.COMMON_ASSERTIONS.toMutableMap().also {
+ it.remove(VisibleLayersShownMoreThanOneConsecutiveEntry())
+ } +
listOf(
AppWindowOnTopAtStart(DESKTOP_MODE_APP),
AppWindowBecomesInvisible(DESKTOP_MODE_APP),
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsWithKeyboard.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsWithKeyboard.kt
new file mode 100644
index 000000000000..56f1dcbf838c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsWithKeyboard.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_APP
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_LAST_APP
+import com.android.wm.shell.scenarios.MinimizeAppWindows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Minimize app windows by pressing META + -.
+ *
+ * Assert that the app windows gets hidden.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MinimizeAppsWithKeyboard : MinimizeAppWindows(usingKeyboard = true) {
+ @ExpectedScenarios(["MINIMIZE_APP", "MINIMIZE_LAST_APP"])
+ @Test
+ override fun minimizeAllAppWindows() = super.minimizeAllAppWindows()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(MINIMIZE_APP)
+ .use(MINIMIZE_LAST_APP)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
index 971637b62604..835559cd0936 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
@@ -43,7 +43,10 @@ import org.junit.Test
*/
@Ignore("Test Base Class")
abstract class MinimizeAppWindows
-constructor(private val rotation: Rotation = Rotation.ROTATION_0) {
+constructor(
+ private val rotation: Rotation = Rotation.ROTATION_0,
+ private val usingKeyboard: Boolean = false
+) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
private val wmHelper = WindowManagerStateHelper(instrumentation)
@@ -68,9 +71,9 @@ constructor(private val rotation: Rotation = Rotation.ROTATION_0) {
@Test
open fun minimizeAllAppWindows() {
- testApp3.minimizeDesktopApp(wmHelper, device)
- testApp2.minimizeDesktopApp(wmHelper, device)
- testApp1.minimizeDesktopApp(wmHelper, device)
+ testApp3.minimizeDesktopApp(wmHelper, device, usingKeyboard = usingKeyboard)
+ testApp2.minimizeDesktopApp(wmHelper, device, usingKeyboard = usingKeyboard)
+ testApp1.minimizeDesktopApp(wmHelper, device, usingKeyboard = usingKeyboard)
}
@After
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index abd707817621..c0ff2f0652b3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -45,6 +45,7 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_M
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
import com.google.common.truth.Truth.assertThat
+import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -77,6 +78,12 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
doReturn(DISPLAY_HEIGHT).whenever(displayLayout).height()
}
+ @After
+ fun tearDown() {
+ clearInvocations(staticMockMarker(FrameworkStatsLog::class.java))
+ clearInvocations(staticMockMarker(EventLogTags::class.java))
+ }
+
@Test
fun logSessionEnter_logsEnterReasonWithNewSessionId() {
desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 43684fb92b64..0154ff31c989 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -46,6 +46,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
@@ -566,7 +567,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
assertFalse(transitionObserver.isSessionActive)
verify(desktopModeEventLogger, times(1)).logSessionExit(eq(ExitReason.TASK_MINIMIZED))
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(DEFAULT_TASK_UPDATE))
+ verify(desktopModeEventLogger, times(1))
+ .logTaskRemoved(
+ eq(DEFAULT_TASK_UPDATE.copy(minimizeReason = MinimizeReason.MINIMIZE_BUTTON))
+ )
verifyZeroInteractions(desktopModeEventLogger)
}
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 4f37180baa37..e1c2153014fa 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
@@ -4160,8 +4160,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
configuration.windowConfiguration.appBounds = bounds
}
- appCompatTaskInfo.topActivityLetterboxAppWidth = bounds.width()
- appCompatTaskInfo.topActivityLetterboxAppHeight = bounds.height()
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, bounds.width(), bounds.height())
isResizeable = false
}
@@ -4879,15 +4878,19 @@ class DesktopTasksControllerTest : ShellTestCase() {
appCompatTaskInfo.isSystemFullscreenOverrideEnabled = enableSystemFullscreenOverride
if (deviceOrientation == ORIENTATION_LANDSCAPE) {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_LONG
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_SHORT
+ appCompatTaskInfo.topActivityAppBounds.set(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_LONG,
+ DISPLAY_DIMENSION_SHORT,
+ )
} else {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_SHORT
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_LONG
+ appCompatTaskInfo.topActivityAppBounds.set(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_SHORT,
+ DISPLAY_DIMENSION_LONG,
+ )
}
if (shouldLetterbox) {
@@ -4897,17 +4900,15 @@ class DesktopTasksControllerTest : ShellTestCase() {
screenOrientation == SCREEN_ORIENTATION_PORTRAIT
) {
// Letterbox to portrait size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1200
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1600
+ appCompatTaskInfo.isTopActivityLetterboxed = true
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, 1200, 1600)
} else if (
deviceOrientation == ORIENTATION_PORTRAIT &&
screenOrientation == SCREEN_ORIENTATION_LANDSCAPE
) {
// Letterbox to landscape size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1600
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1200
+ appCompatTaskInfo.isTopActivityLetterboxed = true
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, 1600, 1200)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt
index 3d59342f62d8..8ccca07142aa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt
@@ -59,6 +59,13 @@ class DragSessionTest : ShellTestCase() {
}
@Test
+ fun testNullClipData() {
+ // Start a new drag session with null data
+ val session = DragSession(activityTaskManager, displayLayout, null, 0)
+ assertThat(session.hideDragSourceTaskId).isEqualTo(-1)
+ }
+
+ @Test
fun testGetRunningTask() {
// Set up running tasks
val runningTasks = listOf(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index ce482cdd9944..4b01d841b824 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -55,7 +55,6 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
import android.testing.TestableContext;
-import android.view.InsetsState;
import android.view.Surface;
import android.view.WindowManager;
import android.view.WindowMetrics;
@@ -338,9 +337,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
windowInfo.appToken = appToken;
windowInfo.targetActivityInfo = info;
windowInfo.taskInfo = taskInfo;
- windowInfo.topOpaqueWindowInsetsState = new InsetsState();
windowInfo.mainWindowLayoutParams = new WindowManager.LayoutParams();
- windowInfo.topOpaqueWindowLayoutParams = new WindowManager.LayoutParams();
return windowInfo;
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 1bc15d72bacc..cc4a29b31996 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -199,6 +199,7 @@ cc_test {
// This is to suppress warnings/errors from gtest
"-Wno-unnamed-type-template-args",
],
+ require_root: true,
srcs: [
// Helpers/infra for testing.
"tests/CommonHelpers.cpp",
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index e6182454ad8a..5955915c9fcd 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -1420,18 +1420,20 @@ void AssetManager::mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo
Mutex AssetManager::SharedZip::gLock;
DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen;
-AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
- : mPath(path), mZipFile(NULL), mModWhen(modWhen),
- mResourceTableAsset(NULL), mResourceTable(NULL)
-{
- if (kIsDebug) {
- ALOGI("Creating SharedZip %p %s\n", this, mPath.c_str());
- }
- ALOGV("+++ opening zip '%s'\n", mPath.c_str());
- mZipFile = ZipFileRO::open(mPath.c_str());
- if (mZipFile == NULL) {
- ALOGD("failed to open Zip archive '%s'\n", mPath.c_str());
- }
+AssetManager::SharedZip::SharedZip(const String8& path, ModDate modWhen)
+ : mPath(path),
+ mZipFile(NULL),
+ mModWhen(modWhen),
+ mResourceTableAsset(NULL),
+ mResourceTable(NULL) {
+ if (kIsDebug) {
+ ALOGI("Creating SharedZip %p %s\n", this, mPath.c_str());
+ }
+ ALOGV("+++ opening zip '%s'\n", mPath.c_str());
+ mZipFile = ZipFileRO::open(mPath.c_str());
+ if (mZipFile == NULL) {
+ ALOGD("failed to open Zip archive '%s'\n", mPath.c_str());
+ }
}
AssetManager::SharedZip::SharedZip(int fd, const String8& path)
@@ -1453,7 +1455,7 @@ sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path,
bool createIfNotPresent)
{
AutoMutex _l(gLock);
- time_t modWhen = getFileModDate(path.c_str());
+ auto modWhen = getFileModDate(path.c_str());
sp<SharedZip> zip = gOpen.valueFor(path).promote();
if (zip != NULL && zip->mModWhen == modWhen) {
return zip;
@@ -1520,8 +1522,8 @@ ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res)
bool AssetManager::SharedZip::isUpToDate()
{
- time_t modWhen = getFileModDate(mPath.c_str());
- return mModWhen == modWhen;
+ auto modWhen = getFileModDate(mPath.c_str());
+ return mModWhen == modWhen;
}
void AssetManager::SharedZip::addOverlay(const asset_path& ap)
diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp
index 70d14a11830e..970463492c1a 100644
--- a/libs/androidfw/TypeWrappers.cpp
+++ b/libs/androidfw/TypeWrappers.cpp
@@ -16,8 +16,6 @@
#include <androidfw/TypeWrappers.h>
-#include <algorithm>
-
namespace android {
TypeVariant::TypeVariant(const ResTable_type* data) : data(data), mLength(dtohl(data->entryCount)) {
@@ -31,30 +29,44 @@ TypeVariant::TypeVariant(const ResTable_type* data) : data(data), mLength(dtohl(
ALOGE("Type's entry indices extend beyond its boundaries");
mLength = 0;
} else {
- mLength = ResTable_sparseTypeEntry{entryIndices[entryCount - 1]}.idx + 1;
+ mLength = dtohs(ResTable_sparseTypeEntry{entryIndices[entryCount - 1]}.idx) + 1;
}
}
}
TypeVariant::iterator& TypeVariant::iterator::operator++() {
- mIndex++;
+ ++mIndex;
if (mIndex > mTypeVariant->mLength) {
mIndex = mTypeVariant->mLength;
}
- return *this;
-}
-static bool keyCompare(uint32_t entry, uint16_t index) {
- return dtohs(ResTable_sparseTypeEntry{entry}.idx) < index;
+ const ResTable_type* type = mTypeVariant->data;
+ if ((type->flags & ResTable_type::FLAG_SPARSE) == 0) {
+ return *this;
+ }
+
+ // Need to adjust |mSparseIndex| as well if we've passed its current element.
+ const uint32_t entryCount = dtohl(type->entryCount);
+ const auto entryIndices = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
+ if (mSparseIndex >= entryCount) {
+ return *this; // done
+ }
+ const auto element = (const ResTable_sparseTypeEntry*)(entryIndices + mSparseIndex);
+ if (mIndex > dtohs(element->idx)) {
+ ++mSparseIndex;
+ }
+
+ return *this;
}
const ResTable_entry* TypeVariant::iterator::operator*() const {
- const ResTable_type* type = mTypeVariant->data;
if (mIndex >= mTypeVariant->mLength) {
- return NULL;
+ return nullptr;
}
- const uint32_t entryCount = dtohl(mTypeVariant->data->entryCount);
+ const ResTable_type* type = mTypeVariant->data;
+ const uint32_t entryCount = dtohl(type->entryCount);
const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type)
+ dtohl(type->header.size);
const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
@@ -63,18 +75,19 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
sizeof(uint16_t) : sizeof(uint32_t);
if (reinterpret_cast<uintptr_t>(entryIndices) + (indexSize * entryCount) > containerEnd) {
ALOGE("Type's entry indices extend beyond its boundaries");
- return NULL;
+ return nullptr;
}
uint32_t entryOffset;
if (type->flags & ResTable_type::FLAG_SPARSE) {
- auto iter = std::lower_bound(entryIndices, entryIndices + entryCount, mIndex, keyCompare);
- if (iter == entryIndices + entryCount
- || dtohs(ResTable_sparseTypeEntry{*iter}.idx) != mIndex) {
- return NULL;
+ if (mSparseIndex >= entryCount) {
+ return nullptr;
}
-
- entryOffset = static_cast<uint32_t>(dtohs(ResTable_sparseTypeEntry{*iter}.offset)) * 4u;
+ const auto element = (const ResTable_sparseTypeEntry*)(entryIndices + mSparseIndex);
+ if (dtohs(element->idx) != mIndex) {
+ return nullptr;
+ }
+ entryOffset = static_cast<uint32_t>(dtohs(element->offset)) * 4u;
} else if (type->flags & ResTable_type::FLAG_OFFSET16) {
auto entryIndices16 = reinterpret_cast<const uint16_t*>(entryIndices);
entryOffset = offset_from16(entryIndices16[mIndex]);
@@ -83,25 +96,25 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
}
if (entryOffset == ResTable_type::NO_ENTRY) {
- return NULL;
+ return nullptr;
}
if ((entryOffset & 0x3) != 0) {
ALOGE("Index %u points to entry with unaligned offset 0x%08x", mIndex, entryOffset);
- return NULL;
+ return nullptr;
}
const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
reinterpret_cast<uintptr_t>(type) + dtohl(type->entriesStart) + entryOffset);
if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) {
ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex);
- return NULL;
+ return nullptr;
} else if (reinterpret_cast<uintptr_t>(entry) + entry->size() > containerEnd) {
ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex);
- return NULL;
+ return nullptr;
} else if (entry->size() < sizeof(*entry)) {
ALOGE("Entry at index %u is too small (%zu)", mIndex, entry->size());
- return NULL;
+ return nullptr;
}
return entry;
}
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index ce0985b38986..376c881ea376 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -280,21 +280,21 @@ private:
~SharedZip();
private:
- SharedZip(const String8& path, time_t modWhen);
- SharedZip(int fd, const String8& path);
- SharedZip(); // <-- not implemented
+ SharedZip(const String8& path, ModDate modWhen);
+ SharedZip(int fd, const String8& path);
+ SharedZip(); // <-- not implemented
- String8 mPath;
- ZipFileRO* mZipFile;
- time_t mModWhen;
+ String8 mPath;
+ ZipFileRO* mZipFile;
+ ModDate mModWhen;
- Asset* mResourceTableAsset;
- ResTable* mResourceTable;
+ Asset* mResourceTableAsset;
+ ResTable* mResourceTable;
- Vector<asset_path> mOverlays;
+ Vector<asset_path> mOverlays;
- static Mutex gLock;
- static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
+ static Mutex gLock;
+ static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
};
/*
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index e213fbd22ab0..ac75eb3bb98c 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -25,8 +25,9 @@
#include "android-base/macros.h"
#include "android-base/unique_fd.h"
#include "androidfw/ConfigDescription.h"
-#include "androidfw/StringPiece.h"
#include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
+#include "androidfw/misc.h"
#include "utils/ByteOrder.h"
namespace android {
@@ -213,7 +214,7 @@ class LoadedIdmap {
android::base::unique_fd idmap_fd_;
std::string_view overlay_apk_path_;
std::string_view target_apk_path_;
- time_t idmap_last_mod_time_;
+ ModDate idmap_last_mod_time_;
private:
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
index fb2fad619011..db641b78a4e4 100644
--- a/libs/androidfw/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -27,24 +27,14 @@ struct TypeVariant {
class iterator {
public:
- iterator& operator=(const iterator& rhs) {
- mTypeVariant = rhs.mTypeVariant;
- mIndex = rhs.mIndex;
- return *this;
- }
-
bool operator==(const iterator& rhs) const {
return mTypeVariant == rhs.mTypeVariant && mIndex == rhs.mIndex;
}
- bool operator!=(const iterator& rhs) const {
- return mTypeVariant != rhs.mTypeVariant || mIndex != rhs.mIndex;
- }
-
iterator operator++(int) {
- uint32_t prevIndex = mIndex;
+ iterator prev = *this;
operator++();
- return iterator(mTypeVariant, prevIndex);
+ return prev;
}
const ResTable_entry* operator->() const {
@@ -60,18 +50,26 @@ struct TypeVariant {
private:
friend struct TypeVariant;
- iterator(const TypeVariant* tv, uint32_t index)
- : mTypeVariant(tv), mIndex(index) {}
+
+ enum class Kind { Begin, End };
+ iterator(const TypeVariant* tv, Kind kind)
+ : mTypeVariant(tv) {
+ mSparseIndex = mIndex = kind == Kind::Begin ? 0 : tv->mLength;
+ // mSparseIndex here is technically past the number of sparse entries, but it is still
+ // ok as it is enough to infer that this is the end iterator.
+ }
+
const TypeVariant* mTypeVariant;
uint32_t mIndex;
+ uint32_t mSparseIndex;
};
iterator beginEntries() const {
- return iterator(this, 0);
+ return iterator(this, iterator::Kind::Begin);
}
iterator endEntries() const {
- return iterator(this, mLength);
+ return iterator(this, iterator::Kind::End);
}
const ResTable_type* data;
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index 077609d20d55..c9ba8a01a5e9 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-#include <sys/types.h>
+#include <time.h>
//
// Handy utility functions and portability code.
//
-#ifndef _LIBS_ANDROID_FW_MISC_H
-#define _LIBS_ANDROID_FW_MISC_H
namespace android {
@@ -41,15 +40,35 @@ typedef enum FileType {
} FileType;
/* get the file's type; follows symlinks */
FileType getFileType(const char* fileName);
-/* get the file's modification date; returns -1 w/errno set on failure */
-time_t getFileModDate(const char* fileName);
+
+// MinGW doesn't support nanosecond resolution in stat() modification time, and given
+// that it only matters on the device it's ok to keep it at a seconds level there.
+#ifdef _WIN32
+using ModDate = time_t;
+inline constexpr ModDate kInvalidModDate = ModDate(-1);
+inline constexpr unsigned long long kModDateResolutionNs = 1ull * 1000 * 1000 * 1000;
+inline time_t toTimeT(ModDate m) {
+ return m;
+}
+#else
+using ModDate = timespec;
+inline constexpr ModDate kInvalidModDate = {-1, -1};
+inline constexpr unsigned long long kModDateResolutionNs = 1;
+inline time_t toTimeT(ModDate m) {
+ return m.tv_sec;
+}
+#endif
+
+/* get the file's modification date; returns kInvalidModDate w/errno set on failure */
+ModDate getFileModDate(const char* fileName);
/* same, but also returns -1 if the file has already been deleted */
-time_t getFileModDate(int fd);
+ModDate getFileModDate(int fd);
// Check if |path| or |fd| resides on a readonly filesystem.
bool isReadonlyFilesystem(const char* path);
bool isReadonlyFilesystem(int fd);
-}; // namespace android
+} // namespace android
-#endif // _LIBS_ANDROID_FW_MISC_H
+// Whoever uses getFileModDate() will need this as well
+bool operator==(const timespec& l, const timespec& r);
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 93dcaf549a90..32f3624a3aee 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -28,11 +28,13 @@
#include <sys/vfs.h>
#endif // __linux__
-#include <cstring>
-#include <cstdio>
#include <errno.h>
#include <sys/stat.h>
+#include <cstdio>
+#include <cstring>
+#include <tuple>
+
namespace android {
/*
@@ -73,27 +75,34 @@ FileType getFileType(const char* fileName)
}
}
-/*
- * Get a file's modification date.
- */
-time_t getFileModDate(const char* fileName) {
- struct stat sb;
- if (stat(fileName, &sb) < 0) {
- return (time_t)-1;
- }
- return sb.st_mtime;
+static ModDate getModDate(const struct stat& st) {
+#ifdef _WIN32
+ return st.st_mtime;
+#elif defined(__APPLE__)
+ return st.st_mtimespec;
+#else
+ return st.st_mtim;
+#endif
}
-time_t getFileModDate(int fd) {
- struct stat sb;
- if (fstat(fd, &sb) < 0) {
- return (time_t)-1;
- }
- if (sb.st_nlink <= 0) {
- errno = ENOENT;
- return (time_t)-1;
- }
- return sb.st_mtime;
+ModDate getFileModDate(const char* fileName) {
+ struct stat sb;
+ if (stat(fileName, &sb) < 0) {
+ return kInvalidModDate;
+ }
+ return getModDate(sb);
+}
+
+ModDate getFileModDate(int fd) {
+ struct stat sb;
+ if (fstat(fd, &sb) < 0) {
+ return kInvalidModDate;
+ }
+ if (sb.st_nlink <= 0) {
+ errno = ENOENT;
+ return kInvalidModDate;
+ }
+ return getModDate(sb);
}
#ifndef __linux__
@@ -124,4 +133,8 @@ bool isReadonlyFilesystem(int fd) {
}
#endif // __linux__
-}; // namespace android
+} // namespace android
+
+bool operator==(const timespec& l, const timespec& r) {
+ return std::tie(l.tv_sec, l.tv_nsec) == std::tie(r.tv_sec, l.tv_nsec);
+}
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 60aa7d88925d..cb2e56f5f5e4 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <chrono>
+#include <thread>
+
#include "android-base/file.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/AssetManager2.h"
@@ -27,6 +30,7 @@
#include "data/overlayable/R.h"
#include "data/system/R.h"
+using namespace std::chrono_literals;
using ::testing::NotNull;
namespace overlay = com::android::overlay;
@@ -218,10 +222,13 @@ TEST_F(IdmapTest, OverlayAssetsIsUpToDate) {
unlink(temp_file.path);
ASSERT_FALSE(apk_assets->IsUpToDate());
- sleep(2);
+
+ const auto sleep_duration =
+ std::chrono::nanoseconds(std::max(kModDateResolutionNs, 1'000'000ull));
+ std::this_thread::sleep_for(sleep_duration);
base::WriteStringToFile("hello", temp_file.path);
- sleep(2);
+ std::this_thread::sleep_for(sleep_duration);
ASSERT_FALSE(apk_assets->IsUpToDate());
}
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
index ed30904ec179..d66e05805484 100644
--- a/libs/androidfw/tests/TypeWrappers_test.cpp
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -14,28 +14,42 @@
* limitations under the License.
*/
-#include <algorithm>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
-#include <utils/String8.h>
+#include <androidfw/Util.h>
+
+#include <optional>
+#include <vector>
#include <gtest/gtest.h>
namespace android {
-// create a ResTable_type in memory with a vector of Res_value*
-static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
- bool compact_entry = false,
- bool short_offsets = false)
+using ResValueVector = std::vector<std::optional<Res_value>>;
+
+// create a ResTable_type in memory
+static util::unique_cptr<ResTable_type> createTypeTable(
+ const ResValueVector& in_values, bool compact_entry, bool short_offsets, bool sparse)
{
+ ResValueVector sparse_values;
+ if (sparse) {
+ std::ranges::copy_if(in_values, std::back_inserter(sparse_values),
+ [](auto&& v) { return v.has_value(); });
+ }
+ const ResValueVector& values = sparse ? sparse_values : in_values;
+
ResTable_type t{};
t.header.type = RES_TABLE_TYPE_TYPE;
t.header.headerSize = sizeof(t);
t.header.size = sizeof(t);
t.id = 1;
- t.flags = short_offsets ? ResTable_type::FLAG_OFFSET16 : 0;
+ t.flags = sparse
+ ? ResTable_type::FLAG_SPARSE
+ : short_offsets ? ResTable_type::FLAG_OFFSET16 : 0;
- t.header.size += values.size() * (short_offsets ? sizeof(uint16_t) : sizeof(uint32_t));
+ t.header.size += values.size() *
+ (sparse ? sizeof(ResTable_sparseTypeEntry) :
+ short_offsets ? sizeof(uint16_t) : sizeof(uint32_t));
t.entriesStart = t.header.size;
t.entryCount = values.size();
@@ -53,9 +67,18 @@ static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
memcpy(p_header, &t, sizeof(t));
size_t i = 0, entry_offset = 0;
- uint32_t k = 0;
- for (auto const& v : values) {
- if (short_offsets) {
+ uint32_t sparse_index = 0;
+
+ for (auto const& v : in_values) {
+ if (sparse) {
+ if (!v) {
+ ++i;
+ continue;
+ }
+ const auto p = reinterpret_cast<ResTable_sparseTypeEntry*>(p_offsets) + sparse_index++;
+ p->idx = i;
+ p->offset = (entry_offset >> 2) & 0xffffu;
+ } else if (short_offsets) {
uint16_t *p = reinterpret_cast<uint16_t *>(p_offsets) + i;
*p = v ? (entry_offset >> 2) & 0xffffu : 0xffffu;
} else {
@@ -83,62 +106,92 @@ static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
}
i++;
}
- return reinterpret_cast<ResTable_type*>(data);
+ return util::unique_cptr<ResTable_type>{reinterpret_cast<ResTable_type*>(data)};
}
TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
- std::vector<Res_value *> values;
-
- Res_value *v1 = new Res_value{};
- values.push_back(v1);
-
- values.push_back(nullptr);
-
- Res_value *v2 = new Res_value{};
- values.push_back(v2);
-
- Res_value *v3 = new Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x12345678};
- values.push_back(v3);
+ ResValueVector values;
+
+ values.push_back(std::nullopt);
+ values.push_back(Res_value{});
+ values.push_back(std::nullopt);
+ values.push_back(Res_value{});
+ values.push_back(Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x12345678});
+ values.push_back(std::nullopt);
+ values.push_back(std::nullopt);
+ values.push_back(std::nullopt);
+ values.push_back(Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x87654321});
// test for combinations of compact_entry and short_offsets
- for (size_t i = 0; i < 4; i++) {
- bool compact_entry = i & 0x1, short_offsets = i & 0x2;
- ResTable_type* data = createTypeTable(values, compact_entry, short_offsets);
- TypeVariant v(data);
+ for (size_t i = 0; i < 8; i++) {
+ bool compact_entry = i & 0x1, short_offsets = i & 0x2, sparse = i & 0x4;
+ auto data = createTypeTable(values, compact_entry, short_offsets, sparse);
+ TypeVariant v(data.get());
TypeVariant::iterator iter = v.beginEntries();
ASSERT_EQ(uint32_t(0), iter.index());
- ASSERT_TRUE(NULL != *iter);
- ASSERT_EQ(uint32_t(0), iter->key());
+ ASSERT_TRUE(NULL == *iter);
ASSERT_NE(v.endEntries(), iter);
- iter++;
+ ++iter;
ASSERT_EQ(uint32_t(1), iter.index());
- ASSERT_TRUE(NULL == *iter);
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(1), iter->key());
ASSERT_NE(v.endEntries(), iter);
iter++;
ASSERT_EQ(uint32_t(2), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
+ ++iter;
+
+ ASSERT_EQ(uint32_t(3), iter.index());
ASSERT_TRUE(NULL != *iter);
- ASSERT_EQ(uint32_t(2), iter->key());
+ ASSERT_EQ(uint32_t(3), iter->key());
ASSERT_NE(v.endEntries(), iter);
iter++;
- ASSERT_EQ(uint32_t(3), iter.index());
+ ASSERT_EQ(uint32_t(4), iter.index());
ASSERT_TRUE(NULL != *iter);
ASSERT_EQ(iter->is_compact(), compact_entry);
- ASSERT_EQ(uint32_t(3), iter->key());
+ ASSERT_EQ(uint32_t(4), iter->key());
ASSERT_EQ(uint32_t(0x12345678), iter->value().data);
ASSERT_EQ(Res_value::TYPE_STRING, iter->value().dataType);
+ ++iter;
+
+ ASSERT_EQ(uint32_t(5), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
+ ++iter;
+
+ ASSERT_EQ(uint32_t(6), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
+ ++iter;
+
+ ASSERT_EQ(uint32_t(7), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
iter++;
- ASSERT_EQ(v.endEntries(), iter);
+ ASSERT_EQ(uint32_t(8), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(iter->is_compact(), compact_entry);
+ ASSERT_EQ(uint32_t(8), iter->key());
+ ASSERT_EQ(uint32_t(0x87654321), iter->value().data);
+ ASSERT_EQ(Res_value::TYPE_STRING, iter->value().dataType);
- free(data);
+ ++iter;
+
+ ASSERT_EQ(v.endEntries(), iter);
}
}
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index 8af4b7e8f4c8..4fecf4de0312 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -59,7 +59,6 @@ cc_library {
apex_available: [
"//apex_available:platform",
"com.android.os.statsd",
- "test_com.android.os.statsd",
"com.android.uprobestats",
],
}
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index ba4224137cd4..023bad26e4f5 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -35,11 +35,11 @@ package android.location {
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class BeidouSatelliteEphemeris implements android.os.Parcelable {
method public int describeContents();
- method @IntRange(from=1, to=63) public int getPrn();
method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel getSatelliteClockModel();
method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime getSatelliteEphemerisTime();
method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth getSatelliteHealth();
method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method @IntRange(from=1, to=63) public int getSvid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris> CREATOR;
}
@@ -104,11 +104,11 @@ package android.location {
public static final class BeidouSatelliteEphemeris.Builder {
ctor public BeidouSatelliteEphemeris.Builder();
method @NonNull public android.location.BeidouSatelliteEphemeris build();
- method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setPrn(int);
method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel);
method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime);
method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth);
method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSvid(int);
}
public final class CorrelationVector implements android.os.Parcelable {
@@ -195,10 +195,10 @@ package android.location {
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GalileoSatelliteEphemeris implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel> getSatelliteClockModels();
- method @IntRange(from=1, to=36) public int getSatelliteCodeNumber();
method @NonNull public android.location.SatelliteEphemerisTime getSatelliteEphemerisTime();
method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth getSatelliteHealth();
method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method @IntRange(from=1, to=36) public int getSvid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoSatelliteEphemeris> CREATOR;
}
@@ -207,10 +207,10 @@ package android.location {
ctor public GalileoSatelliteEphemeris.Builder();
method @NonNull public android.location.GalileoSatelliteEphemeris build();
method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteClockModels(@NonNull java.util.List<android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel>);
- method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteCodeNumber(@IntRange(from=1, to=36) int);
method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.SatelliteEphemerisTime);
method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.GalileoSatelliteEphemeris.GalileoSvHealth);
method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSvid(@IntRange(from=1, to=36) int);
}
public static final class GalileoSatelliteEphemeris.GalileoSatelliteClockModel implements android.os.Parcelable {
@@ -243,25 +243,31 @@ package android.location {
public static final class GalileoSatelliteEphemeris.GalileoSvHealth implements android.os.Parcelable {
method public int describeContents();
- method @IntRange(from=0, to=1) public int getDataValidityStatusE1b();
- method @IntRange(from=0, to=1) public int getDataValidityStatusE5a();
- method @IntRange(from=0, to=1) public int getDataValidityStatusE5b();
- method @IntRange(from=0, to=3) public int getSignalHealthStatusE1b();
- method @IntRange(from=0, to=3) public int getSignalHealthStatusE5a();
- method @IntRange(from=0, to=3) public int getSignalHealthStatusE5b();
+ method public int getDataValidityStatusE1b();
+ method public int getDataValidityStatusE5a();
+ method public int getDataValidityStatusE5b();
+ method public int getSignalHealthStatusE1b();
+ method public int getSignalHealthStatusE5a();
+ method public int getSignalHealthStatusE5b();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoSatelliteEphemeris.GalileoSvHealth> CREATOR;
+ field public static final int DATA_STATUS_DATA_VALID = 0; // 0x0
+ field public static final int DATA_STATUS_WORKING_WITHOUT_GUARANTEE = 1; // 0x1
+ field public static final int HEALTH_STATUS_EXTENDED_OPERATION_MODE = 2; // 0x2
+ field public static final int HEALTH_STATUS_IN_TEST = 3; // 0x3
+ field public static final int HEALTH_STATUS_OK = 0; // 0x0
+ field public static final int HEALTH_STATUS_OUT_OF_SERVICE = 1; // 0x1
}
public static final class GalileoSatelliteEphemeris.GalileoSvHealth.Builder {
ctor public GalileoSatelliteEphemeris.GalileoSvHealth.Builder();
method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth build();
- method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE1b(@IntRange(from=0, to=1) int);
- method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE5a(@IntRange(from=0, to=1) int);
- method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE5b(@IntRange(from=0, to=1) int);
- method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE1b(@IntRange(from=0, to=3) int);
- method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE5a(@IntRange(from=0, to=3) int);
- method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE5b(@IntRange(from=0, to=3) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE1b(int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE5a(int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE5b(int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE1b(int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE5a(int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE5b(int);
}
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GlonassAlmanac implements android.os.Parcelable {
@@ -275,17 +281,19 @@ package android.location {
public static final class GlonassAlmanac.GlonassSatelliteAlmanac implements android.os.Parcelable {
method public int describeContents();
+ method @IntRange(from=1, to=1461) public int getCalendarDayNumber();
method @FloatRange(from=-0.067F, to=0.067f) public double getDeltaI();
method @FloatRange(from=-3600.0F, to=3600.0f) public double getDeltaT();
method @FloatRange(from=-0.004F, to=0.004f) public double getDeltaTDot();
method @FloatRange(from=0.0f, to=0.03f) public double getEccentricity();
- method @IntRange(from=0, to=31) public int getFreqChannel();
+ method @IntRange(from=0, to=31) public int getFrequencyChannelNumber();
+ method public int getHealthState();
method @FloatRange(from=-1.0F, to=1.0f) public double getLambda();
method @FloatRange(from=-1.0F, to=1.0f) public double getOmega();
method @IntRange(from=1, to=25) public int getSlotNumber();
- method @IntRange(from=0, to=1) public int getSvHealth();
method @FloatRange(from=0.0f, to=44100.0f) public double getTLambda();
method @FloatRange(from=-0.0019F, to=0.0019f) public double getTau();
+ method public boolean isGlonassM();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassAlmanac.GlonassSatelliteAlmanac> CREATOR;
}
@@ -293,15 +301,17 @@ package android.location {
public static final class GlonassAlmanac.GlonassSatelliteAlmanac.Builder {
ctor public GlonassAlmanac.GlonassSatelliteAlmanac.Builder();
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac build();
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setCalendarDayNumber(@IntRange(from=1, to=1461) int);
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setDeltaI(@FloatRange(from=-0.067F, to=0.067f) double);
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setDeltaT(@FloatRange(from=-3600.0F, to=3600.0f) double);
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setDeltaTDot(@FloatRange(from=-0.004F, to=0.004f) double);
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setEccentricity(@FloatRange(from=0.0f, to=0.03f) double);
- method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setFreqChannel(@IntRange(from=0, to=31) int);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setFrequencyChannelNumber(@IntRange(from=0, to=31) int);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setGlonassM(boolean);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setHealthState(int);
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setLambda(@FloatRange(from=-1.0F, to=1.0f) double);
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setOmega(@FloatRange(from=-1.0F, to=1.0f) double);
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setSlotNumber(@IntRange(from=1, to=25) int);
- method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setSvHealth(@IntRange(from=0, to=1) int);
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setTLambda(@FloatRange(from=0.0f, to=44100.0f) double);
method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setTau(@FloatRange(from=-0.0019F, to=0.0019f) double);
}
@@ -331,12 +341,17 @@ package android.location {
method public int describeContents();
method @IntRange(from=0, to=31) public int getAgeInDays();
method @FloatRange(from=0.0f) public double getFrameTimeSeconds();
- method @IntRange(from=0, to=1) public int getHealthState();
+ method public int getHealthState();
method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel getSatelliteClockModel();
method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel getSatelliteOrbitModel();
method @IntRange(from=1, to=25) public int getSlotNumber();
+ method @IntRange(from=0, to=60) public int getUpdateIntervalMinutes();
+ method public boolean isGlonassM();
+ method public boolean isUpdateIntervalOdd();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassSatelliteEphemeris> CREATOR;
+ field public static final int HEALTH_STATUS_HEALTHY = 0; // 0x0
+ field public static final int HEALTH_STATUS_UNHEALTHY = 1; // 0x1
}
public static final class GlonassSatelliteEphemeris.Builder {
@@ -344,18 +359,23 @@ package android.location {
method @NonNull public android.location.GlonassSatelliteEphemeris build();
method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setAgeInDays(@IntRange(from=0, to=31) int);
method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setFrameTimeSeconds(@FloatRange(from=0.0f) double);
- method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setHealthState(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setGlonassM(boolean);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setHealthState(int);
method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel);
method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel);
method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setSlotNumber(@IntRange(from=1, to=25) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setUpdateIntervalMinutes(@IntRange(from=0, to=60) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setUpdateIntervalOdd(boolean);
}
public static final class GlonassSatelliteEphemeris.GlonassSatelliteClockModel implements android.os.Parcelable {
method public int describeContents();
method @FloatRange(from=-0.002F, to=0.002f) public double getClockBias();
method @FloatRange(from=-9.32E-10F, to=9.32E-10f) public double getFrequencyBias();
- method @IntRange(from=0xfffffff9, to=6) public int getFrequencyNumber();
+ method @IntRange(from=0xfffffff9, to=6) public int getFrequencyChannelNumber();
+ method @FloatRange(from=-1.4E-8F, to=1.4E-8f) public double getGroupDelayDiffSeconds();
method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method public boolean isGroupDelayDiffSecondsAvailable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel> CREATOR;
}
@@ -365,7 +385,9 @@ package android.location {
method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel build();
method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setClockBias(@FloatRange(from=-0.002F, to=0.002f) double);
method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setFrequencyBias(@FloatRange(from=-9.32E-10F, to=9.32E-10f) double);
- method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setFrequencyNumber(@IntRange(from=0xfffffff9, to=6) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setFrequencyChannelNumber(@IntRange(from=0xfffffff9, to=6) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setGroupDelayDiffSeconds(@FloatRange(from=-1.4E-8F, to=1.4E-8f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setGroupDelayDiffSecondsAvailable(boolean);
method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
}
@@ -401,10 +423,11 @@ package android.location {
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GnssAlmanac implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.location.GnssAlmanac.GnssSatelliteAlmanac> getGnssSatelliteAlmanacs();
- method @IntRange(from=0) public int getIod();
+ method @IntRange(from=0) public int getIoda();
method @IntRange(from=0) public long getIssueDateMillis();
method @IntRange(from=0, to=604800) public int getToaSeconds();
method @IntRange(from=0) public int getWeekNumber();
+ method public boolean isCompleteAlmanacProvided();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAlmanac> CREATOR;
}
@@ -412,8 +435,9 @@ package android.location {
public static final class GnssAlmanac.Builder {
ctor public GnssAlmanac.Builder();
method @NonNull public android.location.GnssAlmanac build();
+ method @NonNull public android.location.GnssAlmanac.Builder setCompleteAlmanacProvided(boolean);
method @NonNull public android.location.GnssAlmanac.Builder setGnssSatelliteAlmanacs(@NonNull java.util.List<android.location.GnssAlmanac.GnssSatelliteAlmanac>);
- method @NonNull public android.location.GnssAlmanac.Builder setIod(@IntRange(from=0) int);
+ method @NonNull public android.location.GnssAlmanac.Builder setIoda(@IntRange(from=0) int);
method @NonNull public android.location.GnssAlmanac.Builder setIssueDateMillis(@IntRange(from=0) long);
method @NonNull public android.location.GnssAlmanac.Builder setToaSeconds(@IntRange(from=0, to=604800) int);
method @NonNull public android.location.GnssAlmanac.Builder setWeekNumber(@IntRange(from=0) int);
@@ -916,11 +940,11 @@ package android.location {
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GpsSatelliteEphemeris implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params getGpsL2Params();
- method @IntRange(from=1, to=32) public int getPrn();
method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel getSatelliteClockModel();
method @NonNull public android.location.SatelliteEphemerisTime getSatelliteEphemerisTime();
method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth getSatelliteHealth();
method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method @IntRange(from=1, to=32) public int getSvid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsSatelliteEphemeris> CREATOR;
}
@@ -929,11 +953,11 @@ package android.location {
ctor public GpsSatelliteEphemeris.Builder();
method @NonNull public android.location.GpsSatelliteEphemeris build();
method @NonNull public android.location.GpsSatelliteEphemeris.Builder setGpsL2Params(@NonNull android.location.GpsSatelliteEphemeris.GpsL2Params);
- method @NonNull public android.location.GpsSatelliteEphemeris.Builder setPrn(@IntRange(from=1, to=32) int);
method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel);
method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.SatelliteEphemerisTime);
method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteHealth);
method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSvid(@IntRange(from=1, to=32) int);
}
public static final class GpsSatelliteEphemeris.GpsL2Params implements android.os.Parcelable {
@@ -1225,11 +1249,11 @@ package android.location {
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class QzssSatelliteEphemeris implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params getGpsL2Params();
- method @IntRange(from=183, to=206) public int getPrn();
method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel getSatelliteClockModel();
method @NonNull public android.location.SatelliteEphemerisTime getSatelliteEphemerisTime();
method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth getSatelliteHealth();
method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method @IntRange(from=183, to=206) public int getSvid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.QzssSatelliteEphemeris> CREATOR;
}
@@ -1238,11 +1262,11 @@ package android.location {
ctor public QzssSatelliteEphemeris.Builder();
method @NonNull public android.location.QzssSatelliteEphemeris build();
method @NonNull public android.location.QzssSatelliteEphemeris.Builder setGpsL2Params(@NonNull android.location.GpsSatelliteEphemeris.GpsL2Params);
- method @NonNull public android.location.QzssSatelliteEphemeris.Builder setPrn(@IntRange(from=183, to=206) int);
method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel);
method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.SatelliteEphemerisTime);
method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteHealth);
method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSvid(@IntRange(from=183, to=206) int);
}
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class RealTimeIntegrityModel implements android.os.Parcelable {
@@ -1435,6 +1459,13 @@ package android.location.provider {
field public static final String ACTION_GEOCODE_PROVIDER = "com.android.location.service.GeocodeProvider";
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public abstract class GnssAssistanceProviderBase {
+ ctor public GnssAssistanceProviderBase(@NonNull android.content.Context, @NonNull String);
+ method @NonNull public final android.os.IBinder getBinder();
+ method public abstract void onRequest(@NonNull android.os.OutcomeReceiver<android.location.GnssAssistance,java.lang.Throwable>);
+ field public static final String ACTION_GNSS_ASSISTANCE_PROVIDER = "android.location.provider.action.GNSS_ASSISTANCE_PROVIDER";
+ }
+
public abstract class LocationProviderBase {
ctor public LocationProviderBase(@NonNull android.content.Context, @NonNull String, @NonNull android.location.provider.ProviderProperties);
method @Nullable public final android.os.IBinder getBinder();
diff --git a/location/java/android/location/BeidouSatelliteEphemeris.java b/location/java/android/location/BeidouSatelliteEphemeris.java
index 6bd91e3318c3..3382c20964d9 100644
--- a/location/java/android/location/BeidouSatelliteEphemeris.java
+++ b/location/java/android/location/BeidouSatelliteEphemeris.java
@@ -35,8 +35,8 @@ import com.android.internal.util.Preconditions;
@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
@SystemApi
public final class BeidouSatelliteEphemeris implements Parcelable {
- /** The PRN number of the Beidou satellite. */
- private final int mPrn;
+ /** The PRN or satellite ID number for the Beidou satellite. */
+ private final int mSvid;
/** Satellite clock model. */
private final BeidouSatelliteClockModel mSatelliteClockModel;
@@ -51,8 +51,8 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
private final BeidouSatelliteEphemerisTime mSatelliteEphemerisTime;
private BeidouSatelliteEphemeris(Builder builder) {
- // Allow PRN beyond the range to support potential future extensibility.
- Preconditions.checkArgument(builder.mPrn >= 1);
+ // Allow Svid beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvid >= 1);
Preconditions.checkNotNull(builder.mSatelliteClockModel,
"SatelliteClockModel cannot be null");
Preconditions.checkNotNull(builder.mSatelliteOrbitModel,
@@ -61,17 +61,17 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
"SatelliteHealth cannot be null");
Preconditions.checkNotNull(builder.mSatelliteEphemerisTime,
"SatelliteEphemerisTime cannot be null");
- mPrn = builder.mPrn;
+ mSvid = builder.mSvid;
mSatelliteClockModel = builder.mSatelliteClockModel;
mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
mSatelliteHealth = builder.mSatelliteHealth;
mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
}
- /** Returns the PRN of the satellite. */
+ /** Returns the PRN or satellite ID number for the Beidou satellite. */
@IntRange(from = 1, to = 63)
- public int getPrn() {
- return mPrn;
+ public int getSvid() {
+ return mSvid;
}
/** Returns the satellite clock model. */
@@ -105,7 +105,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
public BeidouSatelliteEphemeris createFromParcel(Parcel in) {
final BeidouSatelliteEphemeris.Builder beidouSatelliteEphemeris =
new Builder()
- .setPrn(in.readInt())
+ .setSvid(in.readInt())
.setSatelliteClockModel(
in.readTypedObject(BeidouSatelliteClockModel.CREATOR))
.setSatelliteOrbitModel(
@@ -131,7 +131,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mPrn);
+ parcel.writeInt(mSvid);
parcel.writeTypedObject(mSatelliteClockModel, flags);
parcel.writeTypedObject(mSatelliteOrbitModel, flags);
parcel.writeTypedObject(mSatelliteHealth, flags);
@@ -142,7 +142,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
@NonNull
public String toString() {
StringBuilder builder = new StringBuilder("BeidouSatelliteEphemeris[");
- builder.append("prn = ").append(mPrn);
+ builder.append("svid = ").append(mSvid);
builder.append(", satelliteClockModel = ").append(mSatelliteClockModel);
builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
builder.append(", satelliteHealth = ").append(mSatelliteHealth);
@@ -153,16 +153,16 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
/** Builder for {@link BeidouSatelliteEphemeris} */
public static final class Builder {
- private int mPrn;
+ private int mSvid;
private BeidouSatelliteClockModel mSatelliteClockModel;
private KeplerianOrbitModel mSatelliteOrbitModel;
private BeidouSatelliteHealth mSatelliteHealth;
private BeidouSatelliteEphemerisTime mSatelliteEphemerisTime;
- /** Sets the PRN of the satellite. */
+ /** Sets the PRN or satellite ID number for the Beidou satellite. */
@NonNull
- public Builder setPrn(int prn) {
- mPrn = prn;
+ public Builder setSvid(int svid) {
+ mSvid = svid;
return this;
}
diff --git a/location/java/android/location/GalileoSatelliteEphemeris.java b/location/java/android/location/GalileoSatelliteEphemeris.java
index 7dd371176267..08218f4bf83e 100644
--- a/location/java/android/location/GalileoSatelliteEphemeris.java
+++ b/location/java/android/location/GalileoSatelliteEphemeris.java
@@ -43,8 +43,8 @@ import java.util.List;
@SystemApi
public final class GalileoSatelliteEphemeris implements Parcelable {
- /** Satellite code number. */
- private int mSatelliteCodeNumber;
+ /** PRN or satellite ID number for the Galileo satellite. */
+ private int mSvid;
/** Array of satellite clock model. */
@NonNull private final List<GalileoSatelliteClockModel> mSatelliteClockModels;
@@ -59,8 +59,8 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
@NonNull private final SatelliteEphemerisTime mSatelliteEphemerisTime;
private GalileoSatelliteEphemeris(Builder builder) {
- // Allow satelliteCodeNumber beyond the range to support potential future extensibility.
- Preconditions.checkArgument(builder.mSatelliteCodeNumber >= 1);
+ // Allow svid beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvid >= 1);
Preconditions.checkNotNull(
builder.mSatelliteClockModels, "SatelliteClockModels cannot be null");
Preconditions.checkNotNull(
@@ -68,7 +68,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
Preconditions.checkNotNull(builder.mSatelliteHealth, "SatelliteHealth cannot be null");
Preconditions.checkNotNull(
builder.mSatelliteEphemerisTime, "SatelliteEphemerisTime cannot be null");
- mSatelliteCodeNumber = builder.mSatelliteCodeNumber;
+ mSvid = builder.mSvid;
final List<GalileoSatelliteClockModel> satelliteClockModels = builder.mSatelliteClockModels;
mSatelliteClockModels = Collections.unmodifiableList(new ArrayList<>(satelliteClockModels));
mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
@@ -76,10 +76,10 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
}
- /** Returns the satellite code number. */
+ /** Returns the PRN or satellite ID number for the Galileo satellite. */
@IntRange(from = 1, to = 36)
- public int getSatelliteCodeNumber() {
- return mSatelliteCodeNumber;
+ public int getSvid() {
+ return mSvid;
}
/** Returns the list of satellite clock models. */
@@ -113,7 +113,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
public GalileoSatelliteEphemeris createFromParcel(Parcel in) {
final GalileoSatelliteEphemeris.Builder galileoSatelliteEphemeris =
new Builder();
- galileoSatelliteEphemeris.setSatelliteCodeNumber(in.readInt());
+ galileoSatelliteEphemeris.setSvid(in.readInt());
List<GalileoSatelliteClockModel> satelliteClockModels = new ArrayList<>();
in.readTypedList(satelliteClockModels, GalileoSatelliteClockModel.CREATOR);
galileoSatelliteEphemeris.setSatelliteClockModels(satelliteClockModels);
@@ -139,7 +139,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mSatelliteCodeNumber);
+ parcel.writeInt(mSvid);
parcel.writeTypedList(mSatelliteClockModels, flags);
parcel.writeTypedObject(mSatelliteOrbitModel, flags);
parcel.writeTypedObject(mSatelliteHealth, flags);
@@ -150,7 +150,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
@NonNull
public String toString() {
StringBuilder builder = new StringBuilder("GalileoSatelliteEphemeris[");
- builder.append("satelliteCodeNumber = ").append(mSatelliteCodeNumber);
+ builder.append("svid = ").append(mSvid);
builder.append(", satelliteClockModels = ").append(mSatelliteClockModels);
builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
builder.append(", satelliteHealth = ").append(mSatelliteHealth);
@@ -161,17 +161,16 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
/** Builder for {@link GalileoSatelliteEphemeris}. */
public static final class Builder {
- private int mSatelliteCodeNumber;
+ private int mSvid;
private List<GalileoSatelliteClockModel> mSatelliteClockModels;
private KeplerianOrbitModel mSatelliteOrbitModel;
private GalileoSvHealth mSatelliteHealth;
private SatelliteEphemerisTime mSatelliteEphemerisTime;
- /** Sets the satellite code number. */
+ /** Sets the PRN or satellite ID number for the Galileo satellite. */
@NonNull
- public Builder setSatelliteCodeNumber(
- @IntRange(from = 1, to = 36) int satelliteCodeNumber) {
- mSatelliteCodeNumber = satelliteCodeNumber;
+ public Builder setSvid(@IntRange(from = 1, to = 36) int svid) {
+ mSvid = svid;
return this;
}
@@ -218,37 +217,107 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
* <p>This is defined in Galileo-OS-SIS-ICD 5.1.9.3.
*/
public static final class GalileoSvHealth implements Parcelable {
+
+ /**
+ * Galileo data validity status.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DATA_STATUS_DATA_VALID, DATA_STATUS_WORKING_WITHOUT_GUARANTEE})
+ public @interface GalileoDataValidityStatus {}
+
+ /**
+ * The following enumerations must be in sync with the values declared in
+ * GalileoHealthDataVaidityType in GalileoSatelliteEphemeris.aidl.
+ */
+
+ /** Data validity status is data valid. */
+ public static final int DATA_STATUS_DATA_VALID = 0;
+
+ /** Data validity status is working without guarantee. */
+ public static final int DATA_STATUS_WORKING_WITHOUT_GUARANTEE = 1;
+
+ /**
+ * Galileo signal health status.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ HEALTH_STATUS_OK,
+ HEALTH_STATUS_OUT_OF_SERVICE,
+ HEALTH_STATUS_EXTENDED_OPERATION_MODE,
+ HEALTH_STATUS_IN_TEST
+ })
+ public @interface GalileoHealthStatus {}
+
+ /**
+ * The following enumerations must be in sync with the values declared in
+ * GalileoHealthStatusType in GalileoSatelliteEphemeris.aidl.
+ */
+
+ /** Health status is ok. */
+ public static final int HEALTH_STATUS_OK = 0;
+
+ /** Health status is out of service. */
+ public static final int HEALTH_STATUS_OUT_OF_SERVICE = 1;
+
+ /** Health status is in extended operation mode. */
+ public static final int HEALTH_STATUS_EXTENDED_OPERATION_MODE = 2;
+
+ /** Health status is in test mode. */
+ public static final int HEALTH_STATUS_IN_TEST = 3;
+
/** E1-B data validity status. */
- private int mDataValidityStatusE1b;
+ private @GalileoDataValidityStatus int mDataValidityStatusE1b;
/** E1-B/C signal health status. */
- private int mSignalHealthStatusE1b;
+ private @GalileoHealthStatus int mSignalHealthStatusE1b;
/** E5a data validity status. */
- private int mDataValidityStatusE5a;
+ private @GalileoDataValidityStatus int mDataValidityStatusE5a;
/** E5a signal health status. */
- private int mSignalHealthStatusE5a;
+ private @GalileoHealthStatus int mSignalHealthStatusE5a;
/** E5b data validity status. */
- private int mDataValidityStatusE5b;
+ private @GalileoDataValidityStatus int mDataValidityStatusE5b;
/** E5b signal health status. */
- private int mSignalHealthStatusE5b;
+ private @GalileoHealthStatus int mSignalHealthStatusE5b;
private GalileoSvHealth(Builder builder) {
Preconditions.checkArgumentInRange(
- builder.mDataValidityStatusE1b, 0, 1, "DataValidityStatusE1b");
+ builder.mDataValidityStatusE1b,
+ DATA_STATUS_DATA_VALID,
+ DATA_STATUS_WORKING_WITHOUT_GUARANTEE,
+ "DataValidityStatusE1b");
Preconditions.checkArgumentInRange(
- builder.mSignalHealthStatusE1b, 0, 3, "SignalHealthStatusE1b");
+ builder.mSignalHealthStatusE1b,
+ HEALTH_STATUS_OK,
+ HEALTH_STATUS_IN_TEST,
+ "SignalHealthStatusE1b");
Preconditions.checkArgumentInRange(
- builder.mDataValidityStatusE5a, 0, 1, "DataValidityStatusE5a");
+ builder.mDataValidityStatusE5a,
+ DATA_STATUS_DATA_VALID,
+ DATA_STATUS_WORKING_WITHOUT_GUARANTEE,
+ "DataValidityStatusE5a");
Preconditions.checkArgumentInRange(
- builder.mSignalHealthStatusE5a, 0, 3, "SignalHealthStatusE5a");
+ builder.mSignalHealthStatusE5a,
+ HEALTH_STATUS_OK,
+ HEALTH_STATUS_IN_TEST,
+ "SignalHealthStatusE5a");
Preconditions.checkArgumentInRange(
- builder.mDataValidityStatusE5b, 0, 1, "DataValidityStatusE5b");
+ builder.mDataValidityStatusE5b,
+ DATA_STATUS_DATA_VALID,
+ DATA_STATUS_WORKING_WITHOUT_GUARANTEE,
+ "DataValidityStatusE5b");
Preconditions.checkArgumentInRange(
- builder.mSignalHealthStatusE5b, 0, 3, "SignalHealthStatusE5b");
+ builder.mSignalHealthStatusE5b,
+ HEALTH_STATUS_OK,
+ HEALTH_STATUS_IN_TEST,
+ "SignalHealthStatusE5b");
mDataValidityStatusE1b = builder.mDataValidityStatusE1b;
mSignalHealthStatusE1b = builder.mSignalHealthStatusE1b;
mDataValidityStatusE5a = builder.mDataValidityStatusE5a;
@@ -258,37 +327,37 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
}
/** Returns the E1-B data validity status. */
- @IntRange(from = 0, to = 1)
+ @GalileoDataValidityStatus
public int getDataValidityStatusE1b() {
return mDataValidityStatusE1b;
}
/** Returns the E1-B/C signal health status. */
- @IntRange(from = 0, to = 3)
+ @GalileoHealthStatus
public int getSignalHealthStatusE1b() {
return mSignalHealthStatusE1b;
}
/** Returns the E5a data validity status. */
- @IntRange(from = 0, to = 1)
+ @GalileoDataValidityStatus
public int getDataValidityStatusE5a() {
return mDataValidityStatusE5a;
}
/** Returns the E5a signal health status. */
- @IntRange(from = 0, to = 3)
+ @GalileoHealthStatus
public int getSignalHealthStatusE5a() {
return mSignalHealthStatusE5a;
}
/** Returns the E5b data validity status. */
- @IntRange(from = 0, to = 1)
+ @GalileoDataValidityStatus
public int getDataValidityStatusE5b() {
return mDataValidityStatusE5b;
}
/** Returns the E5b signal health status. */
- @IntRange(from = 0, to = 3)
+ @GalileoHealthStatus
public int getSignalHealthStatusE5b() {
return mSignalHealthStatusE5b;
}
@@ -355,7 +424,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
/** Sets the E1-B data validity status. */
@NonNull
public Builder setDataValidityStatusE1b(
- @IntRange(from = 0, to = 1) int dataValidityStatusE1b) {
+ @GalileoDataValidityStatus int dataValidityStatusE1b) {
mDataValidityStatusE1b = dataValidityStatusE1b;
return this;
}
@@ -363,7 +432,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
/** Sets the E1-B/C signal health status. */
@NonNull
public Builder setSignalHealthStatusE1b(
- @IntRange(from = 0, to = 3) int signalHealthStatusE1b) {
+ @GalileoHealthStatus int signalHealthStatusE1b) {
mSignalHealthStatusE1b = signalHealthStatusE1b;
return this;
}
@@ -371,7 +440,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
/** Sets the E5a data validity status. */
@NonNull
public Builder setDataValidityStatusE5a(
- @IntRange(from = 0, to = 1) int dataValidityStatusE5a) {
+ @GalileoDataValidityStatus int dataValidityStatusE5a) {
mDataValidityStatusE5a = dataValidityStatusE5a;
return this;
}
@@ -379,7 +448,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
/** Sets the E5a signal health status. */
@NonNull
public Builder setSignalHealthStatusE5a(
- @IntRange(from = 0, to = 3) int signalHealthStatusE5a) {
+ @GalileoHealthStatus int signalHealthStatusE5a) {
mSignalHealthStatusE5a = signalHealthStatusE5a;
return this;
}
@@ -387,7 +456,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
/** Sets the E5b data validity status. */
@NonNull
public Builder setDataValidityStatusE5b(
- @IntRange(from = 0, to = 1) int dataValidityStatusE5b) {
+ @GalileoDataValidityStatus int dataValidityStatusE5b) {
mDataValidityStatusE5b = dataValidityStatusE5b;
return this;
}
@@ -395,7 +464,7 @@ public final class GalileoSatelliteEphemeris implements Parcelable {
/** Sets the E5b signal health status. */
@NonNull
public Builder setSignalHealthStatusE5b(
- @IntRange(from = 0, to = 3) int signalHealthStatusE5b) {
+ @GalileoHealthStatus int signalHealthStatusE5b) {
mSignalHealthStatusE5b = signalHealthStatusE5b;
return this;
}
diff --git a/location/java/android/location/GlonassAlmanac.java b/location/java/android/location/GlonassAlmanac.java
index 861dc5d257c4..37657435b98a 100644
--- a/location/java/android/location/GlonassAlmanac.java
+++ b/location/java/android/location/GlonassAlmanac.java
@@ -21,6 +21,7 @@ import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.location.GlonassSatelliteEphemeris.GlonassHealthStatus;
import android.location.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -121,11 +122,17 @@ public final class GlonassAlmanac implements Parcelable {
/** Slot number. */
private final int mSlotNumber;
- /** Satellite health information (0=healthy, 1=unhealthy). */
- private final int mSvHealth;
+ /** Satellite health status. */
+ private final @GlonassHealthStatus int mHealthState;
/** Frequency channel number. */
- private final int mFreqChannel;
+ private final int mFrequencyChannelNumber;
+
+ /** Calendar day number within the four-year period beginning since the leap year. */
+ private final int mCalendarDayNumber;
+
+ /** Flag to indicates if the satellite is a GLONASS-M satellitee. */
+ private final boolean mGlonassM;
/** Coarse value of satellite time correction to GLONASS time in seconds. */
private final double mTau;
@@ -148,15 +155,18 @@ public final class GlonassAlmanac implements Parcelable {
/** Eccentricity. */
private final double mEccentricity;
- /** Argument of perigee in radians. */
+ /** Argument of perigee in semi-circles. */
private final double mOmega;
private GlonassSatelliteAlmanac(Builder builder) {
// Allow slotNumber beyond the range to support potential future extensibility.
Preconditions.checkArgument(builder.mSlotNumber >= 1);
- // Allow svHealth beyond the range to support potential future extensibility.
- Preconditions.checkArgument(builder.mSvHealth >= 0);
- Preconditions.checkArgumentInRange(builder.mFreqChannel, 0, 31, "FreqChannel");
+ // Allow healthState beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mHealthState >= 0);
+ Preconditions.checkArgumentInRange(
+ builder.mFrequencyChannelNumber, 0, 31, "FrequencyChannelNumber");
+ Preconditions.checkArgumentInRange(
+ builder.mCalendarDayNumber, 1, 1461, "CalendarDayNumber");
Preconditions.checkArgumentInRange(builder.mTau, -1.9e-3f, 1.9e-3f, "Tau");
Preconditions.checkArgumentInRange(builder.mTLambda, 0.0f, 44100.0f, "TLambda");
Preconditions.checkArgumentInRange(builder.mLambda, -1.0f, 1.0f, "Lambda");
@@ -166,8 +176,10 @@ public final class GlonassAlmanac implements Parcelable {
Preconditions.checkArgumentInRange(builder.mEccentricity, 0.0f, 0.03f, "Eccentricity");
Preconditions.checkArgumentInRange(builder.mOmega, -1.0f, 1.0f, "Omega");
mSlotNumber = builder.mSlotNumber;
- mSvHealth = builder.mSvHealth;
- mFreqChannel = builder.mFreqChannel;
+ mHealthState = builder.mHealthState;
+ mFrequencyChannelNumber = builder.mFrequencyChannelNumber;
+ mCalendarDayNumber = builder.mCalendarDayNumber;
+ mGlonassM = builder.mGlonassM;
mTau = builder.mTau;
mTLambda = builder.mTLambda;
mLambda = builder.mLambda;
@@ -184,16 +196,29 @@ public final class GlonassAlmanac implements Parcelable {
return mSlotNumber;
}
- /** Returns the Satellite health information (0=healthy, 1=unhealthy). */
- @IntRange(from = 0, to = 1)
- public int getSvHealth() {
- return mSvHealth;
+ /** Returns the satellite health status. */
+ public @GlonassHealthStatus int getHealthState() {
+ return mHealthState;
}
/** Returns the frequency channel number. */
@IntRange(from = 0, to = 31)
- public int getFreqChannel() {
- return mFreqChannel;
+ public int getFrequencyChannelNumber() {
+ return mFrequencyChannelNumber;
+ }
+
+ /**
+ * Returns the calendar day number within the four-year period beginning since the leap
+ * year.
+ */
+ @IntRange(from = 1, to = 1461)
+ public int getCalendarDayNumber() {
+ return mCalendarDayNumber;
+ }
+
+ /** Returns true if the satellite is a GLONASS-M satellitee, false otherwise. */
+ public boolean isGlonassM() {
+ return mGlonassM;
}
/** Returns the coarse value of satellite time correction to GLONASS time in seconds. */
@@ -241,7 +266,7 @@ public final class GlonassAlmanac implements Parcelable {
return mEccentricity;
}
- /** Returns the argument of perigee in radians. */
+ /** Returns the Argument of perigee in semi-circles. */
@FloatRange(from = -1.0f, to = 1.0f)
public double getOmega() {
return mOmega;
@@ -255,8 +280,10 @@ public final class GlonassAlmanac implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mSlotNumber);
- dest.writeInt(mSvHealth);
- dest.writeInt(mFreqChannel);
+ dest.writeInt(mHealthState);
+ dest.writeInt(mFrequencyChannelNumber);
+ dest.writeInt(mCalendarDayNumber);
+ dest.writeBoolean(mGlonassM);
dest.writeDouble(mTau);
dest.writeDouble(mTLambda);
dest.writeDouble(mLambda);
@@ -273,8 +300,10 @@ public final class GlonassAlmanac implements Parcelable {
public GlonassSatelliteAlmanac createFromParcel(@NonNull Parcel source) {
return new GlonassSatelliteAlmanac.Builder()
.setSlotNumber(source.readInt())
- .setSvHealth(source.readInt())
- .setFreqChannel(source.readInt())
+ .setHealthState(source.readInt())
+ .setFrequencyChannelNumber(source.readInt())
+ .setCalendarDayNumber(source.readInt())
+ .setGlonassM(source.readBoolean())
.setTau(source.readDouble())
.setTLambda(source.readDouble())
.setLambda(source.readDouble())
@@ -297,8 +326,10 @@ public final class GlonassAlmanac implements Parcelable {
public String toString() {
StringBuilder builder = new StringBuilder("GlonassSatelliteAlmanac[");
builder.append("slotNumber = ").append(mSlotNumber);
- builder.append(", svHealth = ").append(mSvHealth);
- builder.append(", freqChannel = ").append(mFreqChannel);
+ builder.append(", healthState = ").append(mHealthState);
+ builder.append(", frequencyChannelNumber = ").append(mFrequencyChannelNumber);
+ builder.append(", calendarDayNumber = ").append(mCalendarDayNumber);
+ builder.append(", glonassM = ").append(mGlonassM);
builder.append(", tau = ").append(mTau);
builder.append(", tLambda = ").append(mTLambda);
builder.append(", lambda = ").append(mLambda);
@@ -314,8 +345,10 @@ public final class GlonassAlmanac implements Parcelable {
/** Builder for {@link GlonassSatelliteAlmanac}. */
public static final class Builder {
private int mSlotNumber;
- private int mSvHealth;
- private int mFreqChannel;
+ private int mHealthState;
+ private int mFrequencyChannelNumber;
+ private int mCalendarDayNumber;
+ private boolean mGlonassM;
private double mTau;
private double mTLambda;
private double mLambda;
@@ -332,17 +365,36 @@ public final class GlonassAlmanac implements Parcelable {
return this;
}
- /** Sets the Satellite health information (0=healthy, 1=unhealthy). */
+ /** Sets the satellite health status. */
@NonNull
- public Builder setSvHealth(@IntRange(from = 0, to = 1) int svHealth) {
- mSvHealth = svHealth;
+ public Builder setHealthState(@GlonassHealthStatus int healthState) {
+ mHealthState = healthState;
return this;
}
/** Sets the frequency channel number. */
@NonNull
- public Builder setFreqChannel(@IntRange(from = 0, to = 31) int freqChannel) {
- mFreqChannel = freqChannel;
+ public Builder setFrequencyChannelNumber(
+ @IntRange(from = 0, to = 31) int frequencyChannelNumber) {
+ mFrequencyChannelNumber = frequencyChannelNumber;
+ return this;
+ }
+
+ /**
+ * Sets the calendar day number within the four-year period beginning since the leap
+ * year.
+ */
+ @NonNull
+ public Builder setCalendarDayNumber(
+ @IntRange(from = 1, to = 1461) int calendarDayNumber) {
+ mCalendarDayNumber = calendarDayNumber;
+ return this;
+ }
+
+ /** Sets to true if the satellite is a GLONASS-M satellitee, false otherwise. */
+ @NonNull
+ public Builder setGlonassM(boolean isGlonassM) {
+ this.mGlonassM = isGlonassM;
return this;
}
@@ -401,7 +453,7 @@ public final class GlonassAlmanac implements Parcelable {
return this;
}
- /** Sets the argument of perigee in radians. */
+ /** Sets the Argument of perigee in semi-circles. */
@NonNull
public Builder setOmega(@FloatRange(from = -1.0f, to = 1.0f) double omega) {
mOmega = omega;
diff --git a/location/java/android/location/GlonassSatelliteEphemeris.java b/location/java/android/location/GlonassSatelliteEphemeris.java
index 77a6ebb50cfb..bee6047f71c8 100644
--- a/location/java/android/location/GlonassSatelliteEphemeris.java
+++ b/location/java/android/location/GlonassSatelliteEphemeris.java
@@ -18,6 +18,7 @@ package android.location;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -27,6 +28,9 @@ import android.os.Parcelable;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A class contains ephemeris parameters specific to Glonass satellites.
*
@@ -38,11 +42,31 @@ import com.android.internal.util.Preconditions;
@SystemApi
public final class GlonassSatelliteEphemeris implements Parcelable {
+ /**
+ * Glonass signal health status.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({HEALTH_STATUS_HEALTHY, HEALTH_STATUS_UNHEALTHY})
+ public @interface GlonassHealthStatus {}
+
+ /**
+ * The following enumerations must be in sync with the values declared in
+ * GlonassSatelliteEphemeris.aidl
+ */
+
+ /** Health status is healthy. */
+ public static final int HEALTH_STATUS_HEALTHY = 0;
+
+ /** Health status is unhealthy. */
+ public static final int HEALTH_STATUS_UNHEALTHY = 1;
+
/** L1/Satellite system (R), satellite number (slot number in sat. constellation). */
private final int mSlotNumber;
- /** Health state (0=healthy, 1=unhealthy). */
- private final int mHealthState;
+ /** Health state. */
+ private final @GlonassHealthStatus int mHealthState;
/** Message frame time in seconds of the UTC week (tk+nd*86400). */
private final double mFrameTimeSeconds;
@@ -50,6 +74,15 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
/** Age of current information in days (E). */
private final int mAgeInDays;
+ /** Update and validity interval in minutes (P1) */
+ private final int mUpdateIntervalMinutes;
+
+ /** Flag to indicate if the update interval is odd or even (P2). */
+ private final boolean mUpdateIntervalOdd;
+
+ /** Flag to indicates if the satellite is a Glonass-M satellitee (M). */
+ private final boolean mGlonassM;
+
/** Satellite clock model. */
@NonNull private final GlonassSatelliteClockModel mSatelliteClockModel;
@@ -63,6 +96,8 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
Preconditions.checkArgument(builder.mHealthState >= 0);
Preconditions.checkArgument(builder.mFrameTimeSeconds >= 0.0f);
Preconditions.checkArgumentInRange(builder.mAgeInDays, 0, 31, "AgeInDays");
+ Preconditions.checkArgumentInRange(
+ builder.mUpdateIntervalMinutes, 0, 60, "UpdateIntervalMinutes");
Preconditions.checkNotNull(
builder.mSatelliteClockModel, "SatelliteClockModel cannot be null");
Preconditions.checkNotNull(
@@ -71,6 +106,9 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
mHealthState = builder.mHealthState;
mFrameTimeSeconds = builder.mFrameTimeSeconds;
mAgeInDays = builder.mAgeInDays;
+ mUpdateIntervalMinutes = builder.mUpdateIntervalMinutes;
+ mUpdateIntervalOdd = builder.mUpdateIntervalOdd;
+ mGlonassM = builder.mGlonassM;
mSatelliteClockModel = builder.mSatelliteClockModel;
mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
}
@@ -83,9 +121,8 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
return mSlotNumber;
}
- /** Returns the health state (0=healthy, 1=unhealthy). */
- @IntRange(from = 0, to = 1)
- public int getHealthState() {
+ /** Returns the health state. */
+ public @GlonassHealthStatus int getHealthState() {
return mHealthState;
}
@@ -101,6 +138,22 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
return mAgeInDays;
}
+ /** Returns the update interval in minutes (P1). */
+ @IntRange(from = 0, to = 60)
+ public int getUpdateIntervalMinutes() {
+ return mUpdateIntervalMinutes;
+ }
+
+ /** Returns true if the update interval (P2) is odd, false otherwise (P2). */
+ public boolean isUpdateIntervalOdd() {
+ return mUpdateIntervalOdd;
+ }
+
+ /** Returns true if the satellite is a Glonass-M satellitee (M), false otherwise. */
+ public boolean isGlonassM() {
+ return mGlonassM;
+ }
+
/** Returns the satellite clock model. */
@NonNull
public GlonassSatelliteClockModel getSatelliteClockModel() {
@@ -124,6 +177,9 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
dest.writeInt(mHealthState);
dest.writeDouble(mFrameTimeSeconds);
dest.writeInt(mAgeInDays);
+ dest.writeInt(mUpdateIntervalMinutes);
+ dest.writeBoolean(mUpdateIntervalOdd);
+ dest.writeBoolean(mGlonassM);
dest.writeTypedObject(mSatelliteClockModel, flags);
dest.writeTypedObject(mSatelliteOrbitModel, flags);
}
@@ -137,6 +193,9 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
.setHealthState(source.readInt())
.setFrameTimeSeconds(source.readDouble())
.setAgeInDays(source.readInt())
+ .setUpdateIntervalMinutes(source.readInt())
+ .setUpdateIntervalOdd(source.readBoolean())
+ .setGlonassM(source.readBoolean())
.setSatelliteClockModel(
source.readTypedObject(GlonassSatelliteClockModel.CREATOR))
.setSatelliteOrbitModel(
@@ -158,6 +217,9 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
builder.append(", healthState = ").append(mHealthState);
builder.append(", frameTimeSeconds = ").append(mFrameTimeSeconds);
builder.append(", ageInDays = ").append(mAgeInDays);
+ builder.append(", updateIntervalMinutes = ").append(mUpdateIntervalMinutes);
+ builder.append(", isUpdateIntervalOdd = ").append(mUpdateIntervalOdd);
+ builder.append(", isGlonassM = ").append(mGlonassM);
builder.append(", satelliteClockModel = ").append(mSatelliteClockModel);
builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
builder.append("]");
@@ -170,6 +232,9 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
private int mHealthState;
private double mFrameTimeSeconds;
private int mAgeInDays;
+ private int mUpdateIntervalMinutes;
+ private boolean mUpdateIntervalOdd;
+ private boolean mGlonassM;
private GlonassSatelliteClockModel mSatelliteClockModel;
private GlonassSatelliteOrbitModel mSatelliteOrbitModel;
@@ -182,9 +247,9 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
return this;
}
- /** Sets the health state (0=healthy, 1=unhealthy). */
+ /** Sets the health state. */
@NonNull
- public Builder setHealthState(@IntRange(from = 0, to = 1) int healthState) {
+ public Builder setHealthState(@GlonassHealthStatus int healthState) {
mHealthState = healthState;
return this;
}
@@ -219,6 +284,28 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
return this;
}
+ /** Sets the update interval in minutes (P1). */
+ @NonNull
+ public Builder setUpdateIntervalMinutes(
+ @IntRange(from = 0, to = 60) int updateIntervalMinutes) {
+ mUpdateIntervalMinutes = updateIntervalMinutes;
+ return this;
+ }
+
+ /** Sets to true if the update interval (P2) is odd, false otherwise. */
+ @NonNull
+ public Builder setUpdateIntervalOdd(boolean isUpdateIntervalOdd) {
+ mUpdateIntervalOdd = isUpdateIntervalOdd;
+ return this;
+ }
+
+ /** Sets to true if the satellite is a Glonass-M satellitee (M), false otherwise. */
+ @NonNull
+ public Builder setGlonassM(boolean isGlonassM) {
+ mGlonassM = isGlonassM;
+ return this;
+ }
+
/** Builds a {@link GlonassSatelliteEphemeris}. */
@NonNull
public GlonassSatelliteEphemeris build() {
@@ -246,19 +333,36 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
/** Frequency bias (+GammaN). */
private final double mFrequencyBias;
- /** Frequency number. */
- private final int mFrequencyNumber;
+ /** Frequency channel number. */
+ private final int mFrequencyChannelNumber;
+
+ /* L1/L2 group delay difference in seconds (DeltaTau). */
+ private final double mGroupDelayDiffSeconds;
+
+ /**
+ * Whether the L1/L2 group delay difference in seconds (DeltaTau) is available.
+ *
+ * <p>It is set to true if available, otherwise false.
+ */
+ private final boolean mGroupDelayDiffSecondsAvailable;
private GlonassSatelliteClockModel(Builder builder) {
Preconditions.checkArgument(builder.mTimeOfClockSeconds >= 0);
Preconditions.checkArgumentInRange(builder.mClockBias, -0.002f, 0.002f, "ClockBias");
Preconditions.checkArgumentInRange(
builder.mFrequencyBias, -9.32e-10f, 9.32e-10f, "FrequencyBias");
- Preconditions.checkArgumentInRange(builder.mFrequencyNumber, -7, 6, "FrequencyNumber");
+ Preconditions.checkArgumentInRange(
+ builder.mFrequencyChannelNumber, -7, 6, "FrequencyChannelNumber");
+ if (builder.mGroupDelayDiffSecondsAvailable) {
+ Preconditions.checkArgumentInRange(
+ builder.mGroupDelayDiffSeconds, -1.4e-8f, 1.4e-8f, "GroupDelayDiffSeconds");
+ }
mTimeOfClockSeconds = builder.mTimeOfClockSeconds;
mClockBias = builder.mClockBias;
mFrequencyBias = builder.mFrequencyBias;
- mFrequencyNumber = builder.mFrequencyNumber;
+ mFrequencyChannelNumber = builder.mFrequencyChannelNumber;
+ mGroupDelayDiffSeconds = builder.mGroupDelayDiffSeconds;
+ mGroupDelayDiffSecondsAvailable = builder.mGroupDelayDiffSecondsAvailable;
}
/** Returns the time of clock in seconds (UTC). */
@@ -279,10 +383,21 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
return mFrequencyBias;
}
- /** Returns the frequency number. */
+ /** Returns the Frequency channel number. */
@IntRange(from = -7, to = 6)
- public int getFrequencyNumber() {
- return mFrequencyNumber;
+ public int getFrequencyChannelNumber() {
+ return mFrequencyChannelNumber;
+ }
+
+ /** Returns the L1/L2 group delay difference in seconds (DeltaTau). */
+ @FloatRange(from = -1.4e-8f, to = 1.4e-8f)
+ public double getGroupDelayDiffSeconds() {
+ return mGroupDelayDiffSeconds;
+ }
+
+ /** Returns whether the L1/L2 group delay difference in seconds (DeltaTau) is available. */
+ public boolean isGroupDelayDiffSecondsAvailable() {
+ return mGroupDelayDiffSecondsAvailable;
}
@Override
@@ -295,7 +410,9 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
dest.writeLong(mTimeOfClockSeconds);
dest.writeDouble(mClockBias);
dest.writeDouble(mFrequencyBias);
- dest.writeInt(mFrequencyNumber);
+ dest.writeInt(mFrequencyChannelNumber);
+ dest.writeDouble(mGroupDelayDiffSeconds);
+ dest.writeBoolean(mGroupDelayDiffSecondsAvailable);
}
public static final @NonNull Parcelable.Creator<GlonassSatelliteClockModel> CREATOR =
@@ -306,7 +423,9 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
.setTimeOfClockSeconds(source.readLong())
.setClockBias(source.readDouble())
.setFrequencyBias(source.readDouble())
- .setFrequencyNumber(source.readInt())
+ .setFrequencyChannelNumber(source.readInt())
+ .setGroupDelayDiffSeconds(source.readDouble())
+ .setGroupDelayDiffSecondsAvailable(source.readBoolean())
.build();
}
@@ -323,7 +442,10 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
builder.append("timeOfClockSeconds = ").append(mTimeOfClockSeconds);
builder.append(", clockBias = ").append(mClockBias);
builder.append(", frequencyBias = ").append(mFrequencyBias);
- builder.append(", frequencyNumber = ").append(mFrequencyNumber);
+ builder.append(", frequencyChannelNumber = ").append(mFrequencyChannelNumber);
+ if (mGroupDelayDiffSecondsAvailable) {
+ builder.append(", groupDelayDiffSeconds = ").append(mGroupDelayDiffSeconds);
+ }
builder.append("]");
return builder.toString();
}
@@ -333,7 +455,9 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
private long mTimeOfClockSeconds;
private double mClockBias;
private double mFrequencyBias;
- private int mFrequencyNumber;
+ private double mGroupDelayDiffSeconds;
+ private int mFrequencyChannelNumber;
+ private boolean mGroupDelayDiffSecondsAvailable;
/** Sets the time of clock in seconds (UTC). */
@NonNull
@@ -357,10 +481,27 @@ public final class GlonassSatelliteEphemeris implements Parcelable {
return this;
}
- /** Sets the frequency number. */
+ /** Sets the Frequency channel number. */
+ @NonNull
+ public Builder setFrequencyChannelNumber(
+ @IntRange(from = -7, to = 6) int frequencyChannelNumber) {
+ mFrequencyChannelNumber = frequencyChannelNumber;
+ return this;
+ }
+
+ /** Sets the L1/L2 group delay difference in seconds (DeltaTau). */
+ @NonNull
+ public Builder setGroupDelayDiffSeconds(
+ @FloatRange(from = -1.4e-8f, to = 1.4e-8f) double groupDelayDiffSeconds) {
+ mGroupDelayDiffSeconds = groupDelayDiffSeconds;
+ return this;
+ }
+
+ /** Sets whether the L1/L2 group delay difference in seconds (DeltaTau) is available. */
@NonNull
- public Builder setFrequencyNumber(@IntRange(from = -7, to = 6) int frequencyNumber) {
- mFrequencyNumber = frequencyNumber;
+ public Builder setGroupDelayDiffSecondsAvailable(
+ boolean isGroupDelayDiffSecondsAvailable) {
+ mGroupDelayDiffSecondsAvailable = isGroupDelayDiffSecondsAvailable;
return this;
}
diff --git a/location/java/android/location/GnssAlmanac.java b/location/java/android/location/GnssAlmanac.java
index 6466e45a965e..c16ad91f130a 100644
--- a/location/java/android/location/GnssAlmanac.java
+++ b/location/java/android/location/GnssAlmanac.java
@@ -59,7 +59,7 @@ public final class GnssAlmanac implements Parcelable {
*
* <p>This is unused for GPS/QZSS/Baidou.
*/
- private final int mIod;
+ private final int mIoda;
/**
* Almanac reference week number.
@@ -75,20 +75,27 @@ public final class GnssAlmanac implements Parcelable {
/** Almanac reference time in seconds. */
private final int mToaSeconds;
+ /**
+ * Flag to indicate if the satelliteAlmanacs contains complete GNSS
+ * constellation indicated by svid.
+ */
+ private final boolean mCompleteAlmanacProvided;
+
/** The list of GnssSatelliteAlmanacs. */
@NonNull private final List<GnssSatelliteAlmanac> mGnssSatelliteAlmanacs;
private GnssAlmanac(Builder builder) {
Preconditions.checkArgument(builder.mIssueDateMillis >= 0);
- Preconditions.checkArgument(builder.mIod >= 0);
+ Preconditions.checkArgument(builder.mIoda >= 0);
Preconditions.checkArgument(builder.mWeekNumber >= 0);
Preconditions.checkArgumentInRange(builder.mToaSeconds, 0, 604800, "ToaSeconds");
Preconditions.checkNotNull(
builder.mGnssSatelliteAlmanacs, "GnssSatelliteAlmanacs cannot be null");
mIssueDateMillis = builder.mIssueDateMillis;
- mIod = builder.mIod;
+ mIoda = builder.mIoda;
mWeekNumber = builder.mWeekNumber;
mToaSeconds = builder.mToaSeconds;
+ mCompleteAlmanacProvided = builder.mCompleteAlmanacProvided;
mGnssSatelliteAlmanacs =
Collections.unmodifiableList(new ArrayList<>(builder.mGnssSatelliteAlmanacs));
}
@@ -101,8 +108,8 @@ public final class GnssAlmanac implements Parcelable {
/** Returns the almanac issue of data. */
@IntRange(from = 0)
- public int getIod() {
- return mIod;
+ public int getIoda() {
+ return mIoda;
}
/**
@@ -125,6 +132,14 @@ public final class GnssAlmanac implements Parcelable {
return mToaSeconds;
}
+ /**
+ * Returns the flag to indicate if the satelliteAlmanacs contains complete GNSS
+ * constellation indicated by svid.
+ */
+ public boolean isCompleteAlmanacProvided() {
+ return mCompleteAlmanacProvided;
+ }
+
/** Returns the list of GnssSatelliteAlmanacs. */
@NonNull
public List<GnssSatelliteAlmanac> getGnssSatelliteAlmanacs() {
@@ -139,9 +154,10 @@ public final class GnssAlmanac implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(mIssueDateMillis);
- dest.writeInt(mIod);
+ dest.writeInt(mIoda);
dest.writeInt(mWeekNumber);
dest.writeInt(mToaSeconds);
+ dest.writeBoolean(mCompleteAlmanacProvided);
dest.writeTypedList(mGnssSatelliteAlmanacs);
}
@@ -151,9 +167,10 @@ public final class GnssAlmanac implements Parcelable {
public GnssAlmanac createFromParcel(Parcel in) {
GnssAlmanac.Builder gnssAlmanac = new GnssAlmanac.Builder();
gnssAlmanac.setIssueDateMillis(in.readLong());
- gnssAlmanac.setIod(in.readInt());
+ gnssAlmanac.setIoda(in.readInt());
gnssAlmanac.setWeekNumber(in.readInt());
gnssAlmanac.setToaSeconds(in.readInt());
+ gnssAlmanac.setCompleteAlmanacProvided(in.readBoolean());
List<GnssSatelliteAlmanac> satelliteAlmanacs = new ArrayList<>();
in.readTypedList(satelliteAlmanacs, GnssSatelliteAlmanac.CREATOR);
gnssAlmanac.setGnssSatelliteAlmanacs(satelliteAlmanacs);
@@ -170,9 +187,10 @@ public final class GnssAlmanac implements Parcelable {
public String toString() {
StringBuilder builder = new StringBuilder("GnssAlmanac[");
builder.append("issueDateMillis=").append(mIssueDateMillis);
- builder.append(", iod=").append(mIod);
+ builder.append(", ioda=").append(mIoda);
builder.append(", weekNumber=").append(mWeekNumber);
builder.append(", toaSeconds=").append(mToaSeconds);
+ builder.append(", completeAlmanacProvided=").append(mCompleteAlmanacProvided);
builder.append(", satelliteAlmanacs=").append(mGnssSatelliteAlmanacs);
builder.append("]");
return builder.toString();
@@ -181,9 +199,10 @@ public final class GnssAlmanac implements Parcelable {
/** Builder for {@link GnssAlmanac}. */
public static final class Builder {
private long mIssueDateMillis;
- private int mIod;
+ private int mIoda;
private int mWeekNumber;
private int mToaSeconds;
+ private boolean mCompleteAlmanacProvided;
private List<GnssSatelliteAlmanac> mGnssSatelliteAlmanacs;
/** Sets the almanac issue date in milliseconds (UTC). */
@@ -195,8 +214,8 @@ public final class GnssAlmanac implements Parcelable {
/** Sets the almanac issue of data. */
@NonNull
- public Builder setIod(@IntRange(from = 0) int iod) {
- mIod = iod;
+ public Builder setIoda(@IntRange(from = 0) int ioda) {
+ mIoda = ioda;
return this;
}
@@ -222,6 +241,16 @@ public final class GnssAlmanac implements Parcelable {
return this;
}
+ /**
+ * Sets to true if the satelliteAlmanacs contains complete GNSS
+ * constellation indicated by svid, false otherwise.
+ */
+ @NonNull
+ public Builder setCompleteAlmanacProvided(boolean isCompleteAlmanacProvided) {
+ this.mCompleteAlmanacProvided = isCompleteAlmanacProvided;
+ return this;
+ }
+
/** Sets the list of GnssSatelliteAlmanacs. */
@NonNull
public Builder setGnssSatelliteAlmanacs(
@@ -249,7 +278,7 @@ public final class GnssAlmanac implements Parcelable {
* <p>For Galileo, this is defined in Galileo-OS-SIS-ICD-v2.1 section 5.1.10.
*/
public static final class GnssSatelliteAlmanac implements Parcelable {
- /** The PRN number of the GNSS satellite. */
+ /** The PRN or satellite ID number for the GNSS satellite. */
private final int mSvid;
/**
@@ -332,7 +361,7 @@ public final class GnssAlmanac implements Parcelable {
mAf1 = builder.mAf1;
}
- /** Returns the PRN number of the GNSS satellite. */
+ /** Returns the PRN or satellite ID number of the GNSS satellite. */
@IntRange(from = 1)
public int getSvid() {
return mSvid;
@@ -503,7 +532,7 @@ public final class GnssAlmanac implements Parcelable {
private double mAf0;
private double mAf1;
- /** Sets the PRN number of the GNSS satellite. */
+ /** Sets the PRN or satellite ID number of the GNSS satellite. */
@NonNull
public Builder setSvid(@IntRange(from = 1) int svid) {
mSvid = svid;
diff --git a/location/java/android/location/GpsSatelliteEphemeris.java b/location/java/android/location/GpsSatelliteEphemeris.java
index ec6bc59dc69c..0abdc30d2f19 100644
--- a/location/java/android/location/GpsSatelliteEphemeris.java
+++ b/location/java/android/location/GpsSatelliteEphemeris.java
@@ -37,8 +37,8 @@ import com.android.internal.util.Preconditions;
@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
@SystemApi
public final class GpsSatelliteEphemeris implements Parcelable {
- /** Satellite PRN */
- private final int mPrn;
+ /** PRN or satellite ID number for the GPS satellite. */
+ private final int mSvid;
/** L2 parameters. */
@NonNull private final GpsL2Params mGpsL2Params;
@@ -56,8 +56,8 @@ public final class GpsSatelliteEphemeris implements Parcelable {
@NonNull private final SatelliteEphemerisTime mSatelliteEphemerisTime;
private GpsSatelliteEphemeris(Builder builder) {
- // Allow PRN beyond the range to support potential future extensibility.
- Preconditions.checkArgument(builder.mPrn >= 1);
+ // Allow svid beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvid >= 1);
Preconditions.checkNotNull(builder.mGpsL2Params, "GPSL2Params cannot be null");
Preconditions.checkNotNull(builder.mSatelliteClockModel,
"SatelliteClockModel cannot be null");
@@ -67,7 +67,7 @@ public final class GpsSatelliteEphemeris implements Parcelable {
"SatelliteHealth cannot be null");
Preconditions.checkNotNull(builder.mSatelliteEphemerisTime,
"SatelliteEphemerisTime cannot be null");
- mPrn = builder.mPrn;
+ mSvid = builder.mSvid;
mGpsL2Params = builder.mGpsL2Params;
mSatelliteClockModel = builder.mSatelliteClockModel;
mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
@@ -75,10 +75,10 @@ public final class GpsSatelliteEphemeris implements Parcelable {
mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
}
- /** Returns the PRN of the satellite. */
+ /** Returns the svid of the satellite. */
@IntRange(from = 1, to = 32)
- public int getPrn() {
- return mPrn;
+ public int getSvid() {
+ return mSvid;
}
/** Returns the L2 parameters of the satellite. */
@@ -118,7 +118,7 @@ public final class GpsSatelliteEphemeris implements Parcelable {
public GpsSatelliteEphemeris createFromParcel(Parcel in) {
final GpsSatelliteEphemeris.Builder gpsSatelliteEphemeris =
new Builder()
- .setPrn(in.readInt())
+ .setSvid(in.readInt())
.setGpsL2Params(in.readTypedObject(GpsL2Params.CREATOR))
.setSatelliteClockModel(
in.readTypedObject(GpsSatelliteClockModel.CREATOR))
@@ -144,7 +144,7 @@ public final class GpsSatelliteEphemeris implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mPrn);
+ parcel.writeInt(mSvid);
parcel.writeTypedObject(mGpsL2Params, flags);
parcel.writeTypedObject(mSatelliteClockModel, flags);
parcel.writeTypedObject(mSatelliteOrbitModel, flags);
@@ -156,7 +156,7 @@ public final class GpsSatelliteEphemeris implements Parcelable {
@NonNull
public String toString() {
StringBuilder builder = new StringBuilder("GpsSatelliteEphemeris[");
- builder.append("prn = ").append(mPrn);
+ builder.append("Svid = ").append(mSvid);
builder.append(", gpsL2Params = ").append(mGpsL2Params);
builder.append(", satelliteClockModel = ").append(mSatelliteClockModel);
builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
@@ -168,17 +168,17 @@ public final class GpsSatelliteEphemeris implements Parcelable {
/** Builder for {@link GpsSatelliteEphemeris} */
public static final class Builder {
- private int mPrn = 0;
+ private int mSvid = 0;
private GpsL2Params mGpsL2Params;
private GpsSatelliteClockModel mSatelliteClockModel;
private KeplerianOrbitModel mSatelliteOrbitModel;
private GpsSatelliteHealth mSatelliteHealth;
private SatelliteEphemerisTime mSatelliteEphemerisTime;
- /** Sets the PRN of the satellite. */
+ /** Sets the PRN or satellite ID number for the GPS satellite.. */
@NonNull
- public Builder setPrn(@IntRange(from = 1, to = 32) int prn) {
- mPrn = prn;
+ public Builder setSvid(@IntRange(from = 1, to = 32) int svid) {
+ mSvid = svid;
return this;
}
diff --git a/location/java/android/location/QzssSatelliteEphemeris.java b/location/java/android/location/QzssSatelliteEphemeris.java
index 96203d9588c8..dd9f408f53be 100644
--- a/location/java/android/location/QzssSatelliteEphemeris.java
+++ b/location/java/android/location/QzssSatelliteEphemeris.java
@@ -39,8 +39,8 @@ import com.android.internal.util.Preconditions;
@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
@SystemApi
public final class QzssSatelliteEphemeris implements Parcelable {
- /** Satellite PRN. */
- private final int mPrn;
+ /** PRN or satellite ID number for the Qzss satellite. */
+ private final int mSvid;
/** L2 parameters. */
@NonNull private final GpsL2Params mGpsL2Params;
@@ -57,10 +57,10 @@ public final class QzssSatelliteEphemeris implements Parcelable {
/** Ephemeris time. */
@NonNull private final SatelliteEphemerisTime mSatelliteEphemerisTime;
- /** Returns the PRN of the satellite. */
+ /** Returns the PRN or satellite ID number for the Qzss satellite. */
@IntRange(from = 183, to = 206)
- public int getPrn() {
- return mPrn;
+ public int getSvid() {
+ return mSvid;
}
/** Returns the L2 parameters of the satellite. */
@@ -95,7 +95,7 @@ public final class QzssSatelliteEphemeris implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mPrn);
+ parcel.writeInt(mSvid);
parcel.writeTypedObject(mGpsL2Params, flags);
parcel.writeTypedObject(mSatelliteClockModel, flags);
parcel.writeTypedObject(mSatelliteOrbitModel, flags);
@@ -104,8 +104,8 @@ public final class QzssSatelliteEphemeris implements Parcelable {
}
private QzssSatelliteEphemeris(Builder builder) {
- // Allow PRN beyond the range to support potential future extensibility.
- Preconditions.checkArgument(builder.mPrn >= 1);
+ // Allow Svid beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvid >= 1);
Preconditions.checkNotNull(builder.mGpsL2Params, "GpsL2Params cannot be null");
Preconditions.checkNotNull(builder.mSatelliteClockModel,
"SatelliteClockModel cannot be null");
@@ -115,7 +115,7 @@ public final class QzssSatelliteEphemeris implements Parcelable {
"SatelliteHealth cannot be null");
Preconditions.checkNotNull(builder.mSatelliteEphemerisTime,
"SatelliteEphemerisTime cannot be null");
- mPrn = builder.mPrn;
+ mSvid = builder.mSvid;
mGpsL2Params = builder.mGpsL2Params;
mSatelliteClockModel = builder.mSatelliteClockModel;
mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
@@ -130,7 +130,7 @@ public final class QzssSatelliteEphemeris implements Parcelable {
public QzssSatelliteEphemeris createFromParcel(Parcel in) {
final QzssSatelliteEphemeris.Builder qzssSatelliteEphemeris =
new Builder()
- .setPrn(in.readInt())
+ .setSvid(in.readInt())
.setGpsL2Params(in.readTypedObject(GpsL2Params.CREATOR))
.setSatelliteClockModel(
in.readTypedObject(GpsSatelliteClockModel.CREATOR))
@@ -158,7 +158,7 @@ public final class QzssSatelliteEphemeris implements Parcelable {
@NonNull
public String toString() {
StringBuilder builder = new StringBuilder("QzssSatelliteEphemeris[");
- builder.append("prn=").append(mPrn);
+ builder.append("Svid=").append(mSvid);
builder.append(", gpsL2Params=").append(mGpsL2Params);
builder.append(", satelliteClockModel=").append(mSatelliteClockModel);
builder.append(", satelliteOrbitModel=").append(mSatelliteOrbitModel);
@@ -170,17 +170,17 @@ public final class QzssSatelliteEphemeris implements Parcelable {
/** Builder for {@link QzssSatelliteEphemeris}. */
public static final class Builder {
- private int mPrn;
+ private int mSvid;
private GpsL2Params mGpsL2Params;
private GpsSatelliteClockModel mSatelliteClockModel;
private KeplerianOrbitModel mSatelliteOrbitModel;
private GpsSatelliteHealth mSatelliteHealth;
private SatelliteEphemerisTime mSatelliteEphemerisTime;
- /** Sets the PRN of the satellite. */
+ /** Sets the PRN or satellite ID number for the Qzss satellite. */
@NonNull
- public Builder setPrn(@IntRange(from = 183, to = 206) int prn) {
- mPrn = prn;
+ public Builder setSvid(@IntRange(from = 183, to = 206) int svid) {
+ mSvid = svid;
return this;
}
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index c02cc808d60c..1b38982f48c1 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -167,4 +167,4 @@ flag {
namespace: "location"
description: "Flag for GNSS assistance interface"
bug: "209078566"
-} \ No newline at end of file
+}
diff --git a/location/java/android/location/provider/GnssAssistanceProviderBase.java b/location/java/android/location/provider/GnssAssistanceProviderBase.java
new file mode 100644
index 000000000000..f4b26d5033a5
--- /dev/null
+++ b/location/java/android/location/provider/GnssAssistanceProviderBase.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.location.GnssAssistance;
+import android.location.flags.Flags;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+
+/**
+ * Base class for GNSS assistance providers outside the system server.
+ *
+ * <p>GNSS assistance providers should be wrapped in a non-exported service which returns the result
+ * of {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The
+ * service should not be exported so that components other than the system server cannot bind to it.
+ * Alternatively, the service may be guarded by a permission that only system server can obtain. The
+ * service may specify metadata on its capabilities:
+ *
+ * <ul>
+ * <li>"serviceVersion": An integer version code to help tie break if multiple services are
+ * capable of implementing the geocode provider. All else equal, the service with the highest
+ * version code will be chosen. Assumed to be 0 if not specified.
+ * <li>"serviceIsMultiuser": A boolean property, indicating if the service wishes to take
+ * responsibility for handling changes to the current user on the device. If true, the service
+ * will always be bound from the system user. If false, the service will always be bound from
+ * the current user. If the current user changes, the old binding will be released, and a new
+ * binding established under the new user. Assumed to be false if not specified.
+ * </ul>
+ *
+ * <p>The service should have an intent filter in place for the GNSS assistance provider as
+ * specified by the constant in this class.
+ *
+ * <p>GNSS assistance providers are identified by their UID / package name / attribution tag. Based
+ * on this identity, geocode providers may be given some special privileges.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public abstract class GnssAssistanceProviderBase {
+
+ /**
+ * The action the wrapping service should have in its intent filter to implement the GNSS
+ * Assistance provider.
+ */
+ public static final String ACTION_GNSS_ASSISTANCE_PROVIDER =
+ "android.location.provider.action.GNSS_ASSISTANCE_PROVIDER";
+
+ final String mTag;
+ @Nullable
+ final String mAttributionTag;
+ final IBinder mBinder;
+
+ /**
+ * Subclasses should pass in a context and an arbitrary tag that may be used for logcat logging
+ * of errors, and thus should uniquely identify the class.
+ */
+ public GnssAssistanceProviderBase(@NonNull Context context, @NonNull String tag) {
+ mTag = tag;
+ mAttributionTag = context.getAttributionTag();
+ mBinder = new GnssAssistanceProviderBase.Service();
+ }
+
+ /**
+ * Returns the IBinder instance that should be returned from the {@link
+ * android.app.Service#onBind(Intent)} method of the wrapping service.
+ */
+ @NonNull
+ public final IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Requests GNSS assistance data of the given arguments. The given callback must be invoked
+ * once.
+ */
+ public abstract void onRequest(
+ @NonNull OutcomeReceiver<GnssAssistance, Throwable> callback);
+
+ private class Service extends IGnssAssistanceProvider.Stub {
+ @Override
+ public void request(IGnssAssistanceCallback callback) {
+ try {
+ onRequest(new GnssAssistanceProviderBase.SingleUseCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+ }
+
+ private static class SingleUseCallback implements
+ OutcomeReceiver<GnssAssistance, Throwable> {
+
+ private final AtomicReference<IGnssAssistanceCallback> mCallback;
+
+ SingleUseCallback(IGnssAssistanceCallback callback) {
+ mCallback = new AtomicReference<>(callback);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onError();
+ } catch (RemoteException r) {
+ throw r.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onResult(GnssAssistance result) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onResult(result);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt b/location/java/android/location/provider/IGnssAssistanceCallback.aidl
index 360e9e93f18d..ea38d08df6c2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt
+++ b/location/java/android/location/provider/IGnssAssistanceCallback.aidl
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification
+package android.location.provider;
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
-import org.mockito.kotlin.mock
+import android.location.GnssAssistance;
-val Kosmos.visualInterruptionDecisionProvider by
- Kosmos.Fixture { mock<VisualInterruptionDecisionProvider>() }
+/**
+ * Binder interface for GNSS assistance callbacks.
+ * @hide
+ */
+oneway interface IGnssAssistanceCallback {
+ void onError();
+ void onResult(in GnssAssistance result);
+}
diff --git a/location/java/android/location/provider/IGnssAssistanceProvider.aidl b/location/java/android/location/provider/IGnssAssistanceProvider.aidl
new file mode 100644
index 000000000000..1796e9edb347
--- /dev/null
+++ b/location/java/android/location/provider/IGnssAssistanceProvider.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.provider.IGnssAssistanceCallback;
+
+/**
+ * Binder interface for services that implement GNSS assistance providers. Do not implement this
+ * directly, extend {@link GnssAssistanceProviderBase} instead.
+ * @hide
+ */
+oneway interface IGnssAssistanceProvider {
+ void request(in IGnssAssistanceCallback callback);
+}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 09f40e005b4c..f42017dc835a 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -226,6 +226,10 @@ public abstract class MediaRoute2ProviderService extends Service {
@GuardedBy("mSessionLock")
private final ArrayMap<String, MediaStreams> mOngoingMediaStreams = new ArrayMap<>();
+ @GuardedBy("mSessionLock")
+ private final ArrayMap<String, RoutingSessionInfo> mPendingSystemSessionReleases =
+ new ArrayMap<>();
+
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
}
@@ -358,7 +362,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* @return a {@link MediaStreams} instance that holds the media streams to route as part of the
* newly created routing session. May be null if system media capture failed, in which case
* you can ignore the return value, as you will receive a call to {@link #onReleaseSession}
- * where you can clean up this session
+ * where you can clean up this session. {@link AudioRecord#startRecording()} must be called
+ * immediately on {@link MediaStreams#getAudioRecord()} after calling this method, in order
+ * to start streaming audio to the receiver.
* @hide
*/
// TODO: b/362507305 - Unhide once the implementation and CTS are in place.
@@ -417,7 +423,7 @@ public abstract class MediaRoute2ProviderService extends Service {
}
AudioFormat audioFormat = formats.mAudioFormat;
- var mediaStreamsBuilder = new MediaStreams.Builder();
+ var mediaStreamsBuilder = new MediaStreams.Builder(sessionInfo);
if (audioFormat != null) {
populateAudioStream(audioFormat, uid, mediaStreamsBuilder);
}
@@ -458,7 +464,6 @@ public abstract class MediaRoute2ProviderService extends Service {
if (uid != Process.INVALID_UID) {
audioMixingRuleBuilder.addMixRule(AudioMixingRule.RULE_MATCH_UID, uid);
}
-
AudioMix mix =
new AudioMix.Builder(audioMixingRuleBuilder.build())
.setFormat(audioFormat)
@@ -471,7 +476,11 @@ public abstract class MediaRoute2ProviderService extends Service {
Log.e(TAG, "Couldn't fetch the audio manager.");
return;
}
- audioManager.registerAudioPolicy(audioPolicy);
+ int audioPolicyResult = audioManager.registerAudioPolicy(audioPolicy);
+ if (audioPolicyResult != AudioManager.SUCCESS) {
+ Log.e(TAG, "Failed to register the audio policy.");
+ return;
+ }
var audioRecord = audioPolicy.createAudioRecordSink(mix);
if (audioRecord == null) {
Log.e(TAG, "Audio record creation failed.");
@@ -521,8 +530,14 @@ public abstract class MediaRoute2ProviderService extends Service {
RoutingSessionInfo sessionInfo;
synchronized (mSessionLock) {
sessionInfo = mSessionInfos.remove(sessionId);
- maybeReleaseMediaStreams(sessionId);
-
+ if (Flags.enableMirroringInMediaRouter2()) {
+ if (sessionInfo == null) {
+ sessionInfo = maybeReleaseMediaStreams(sessionId);
+ }
+ if (sessionInfo == null) {
+ sessionInfo = mPendingSystemSessionReleases.remove(sessionId);
+ }
+ }
if (sessionInfo == null) {
Log.w(TAG, "notifySessionReleased: Ignoring unknown session info.");
return;
@@ -539,18 +554,26 @@ public abstract class MediaRoute2ProviderService extends Service {
}
}
- /** Releases any system media routing resources associated with the given {@code sessionId}. */
- private void maybeReleaseMediaStreams(String sessionId) {
+ /**
+ * Releases any system media routing resources associated with the given {@code sessionId}.
+ *
+ * @return The {@link RoutingSessionInfo} that corresponds to the released media streams, or
+ * null if no streams were released.
+ */
+ @Nullable
+ private RoutingSessionInfo maybeReleaseMediaStreams(String sessionId) {
if (!Flags.enableMirroringInMediaRouter2()) {
- return;
+ return null;
}
synchronized (mSessionLock) {
var streams = mOngoingMediaStreams.remove(sessionId);
if (streams != null) {
releaseAudioStream(streams.mAudioPolicy, streams.mAudioRecord);
// TODO: b/380431086: Release the video stream once implemented.
+ return streams.mSessionInfo;
}
}
+ return null;
}
// We cannot reach the code that requires MODIFY_AUDIO_ROUTING without holding it.
@@ -1019,12 +1042,16 @@ public abstract class MediaRoute2ProviderService extends Service {
if (!checkCallerIsSystem()) {
return;
}
- if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
- return;
+ synchronized (mSessionLock) {
+ // We proactively release the system media routing session resources when the
+ // system requests it, to ensure it happens immediately.
+ RoutingSessionInfo releasedSession = maybeReleaseMediaStreams(sessionId);
+ if (releasedSession != null) {
+ mPendingSystemSessionReleases.put(sessionId, releasedSession);
+ } else if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
+ return;
+ }
}
- // We proactively release the system media routing once the system requests it, to
- // ensure it happens immediately.
- maybeReleaseMediaStreams(sessionId);
addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
@@ -1047,9 +1074,19 @@ public abstract class MediaRoute2ProviderService extends Service {
@Nullable private final AudioPolicy mAudioPolicy;
@Nullable private final AudioRecord mAudioRecord;
+ /**
+ * Holds the last {@link RoutingSessionInfo} associated with these streams.
+ *
+ * @hide
+ */
+ @GuardedBy("MediaRoute2ProviderService.this.mSessionLock")
+ @NonNull
+ private RoutingSessionInfo mSessionInfo;
+
// TODO: b/380431086: Add the video equivalent.
private MediaStreams(Builder builder) {
+ this.mSessionInfo = builder.mSessionInfo;
this.mAudioPolicy = builder.mAudioPolicy;
this.mAudioRecord = builder.mAudioRecord;
}
@@ -1070,9 +1107,19 @@ public abstract class MediaRoute2ProviderService extends Service {
*/
public static final class Builder {
+ @NonNull private RoutingSessionInfo mSessionInfo;
@Nullable private AudioPolicy mAudioPolicy;
@Nullable private AudioRecord mAudioRecord;
+ /**
+ * Constructor.
+ *
+ * @param sessionInfo The {@link RoutingSessionInfo} associated with these streams.
+ */
+ Builder(@NonNull RoutingSessionInfo sessionInfo) {
+ mSessionInfo = requireNonNull(sessionInfo);
+ }
+
/** Populates system media audio-related structures. */
public Builder setAudioStream(
@NonNull AudioPolicy audioPolicy, @NonNull AudioRecord audioRecord) {
diff --git a/nfc/tests/src/android/nfc/tech/NfcATest.java b/nfc/tests/src/android/nfc/tech/NfcATest.java
new file mode 100644
index 000000000000..40076ebd0a0a
--- /dev/null
+++ b/nfc/tests/src/android/nfc/tech/NfcATest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import static android.nfc.tech.NfcA.EXTRA_ATQA;
+import static android.nfc.tech.NfcA.EXTRA_SAK;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.nfc.ErrorCodes;
+import android.nfc.INfcTag;
+import android.nfc.Tag;
+import android.nfc.TransceiveResult;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+
+public class NfcATest {
+ @Mock
+ private Tag mMockTag;
+ @Mock
+ private INfcTag mMockTagService;
+ @Mock
+ private Bundle mMockBundle;
+ private NfcA mNfcA;
+ private final byte[] mSampleArray = new byte[] {1, 2, 3};
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ when(mMockBundle.getShort(EXTRA_SAK)).thenReturn((short) 1);
+ when(mMockBundle.getByteArray(EXTRA_ATQA)).thenReturn(mSampleArray);
+ when(mMockTag.getTechExtras(TagTechnology.NFC_A)).thenReturn(mMockBundle);
+
+ mNfcA = new NfcA(mMockTag);
+ }
+
+ @Test
+ public void testGetNfcAWithTech() {
+ Tag mockTag = mock(Tag.class);
+ when(mockTag.getTechExtras(TagTechnology.NFC_A)).thenReturn(mMockBundle);
+ when(mockTag.hasTech(TagTechnology.NFC_A)).thenReturn(true);
+
+ assertNotNull(NfcA.get(mockTag));
+ verify(mockTag).getTechExtras(TagTechnology.NFC_A);
+ verify(mockTag).hasTech(TagTechnology.NFC_A);
+ }
+
+ @Test
+ public void testGetNfcAWithoutTech() {
+ when(mMockTag.hasTech(TagTechnology.NFC_A)).thenReturn(false);
+ assertNull(NfcA.get(mMockTag));
+ }
+
+ @Test
+ public void testGetAtga() {
+ assertNotNull(mNfcA.getAtqa());
+ }
+
+ @Test
+ public void testGetSak() {
+ assertEquals((short) 1, mNfcA.getSak());
+ }
+
+ @Test
+ public void testTransceive() throws IOException, RemoteException {
+ TransceiveResult mockTransceiveResult = mock(TransceiveResult.class);
+ when(mMockTag.getConnectedTechnology()).thenReturn(TagTechnology.NFC_A);
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ when(mMockTag.getServiceHandle()).thenReturn(1);
+ when(mMockTagService.transceive(1, mSampleArray, true))
+ .thenReturn(mockTransceiveResult);
+ when(mockTransceiveResult.getResponseOrThrow()).thenReturn(mSampleArray);
+
+ mNfcA.transceive(mSampleArray);
+ verify(mMockTag).getTagService();
+ verify(mMockTag).getServiceHandle();
+ }
+
+ @Test
+ public void testGetMaxTransceiveLength() throws RemoteException {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ when(mMockTagService.getMaxTransceiveLength(TagTechnology.NFC_A)).thenReturn(1);
+
+ mNfcA.getMaxTransceiveLength();
+ verify(mMockTag).getTagService();
+ }
+
+ @Test
+ public void testSetTimeout() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.setTimeout(TagTechnology.NFC_A, 1000)).thenReturn(
+ ErrorCodes.SUCCESS);
+
+ mNfcA.setTimeout(1000);
+ verify(mMockTag).getTagService();
+ verify(mMockTagService).setTimeout(TagTechnology.NFC_A, 1000);
+ } catch (Exception e) {
+ fail("Unexpected exception during valid setTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testSetTimeoutInvalidTimeout() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.setTimeout(TagTechnology.NFC_A, -1)).thenReturn(
+ ErrorCodes.ERROR_TIMEOUT);
+
+ assertThrows(IllegalArgumentException.class, () -> mNfcA.setTimeout(-1));
+ } catch (Exception e) {
+ fail("Unexpected exception during invalid setTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testSetTimeoutRemoteException() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.setTimeout(TagTechnology.NFC_A, 1000)).thenThrow(
+ new RemoteException());
+
+ mNfcA.setTimeout(1000); // Should not throw an exception but log it
+ verify(mMockTag).getTagService();
+ verify(mMockTagService).setTimeout(TagTechnology.NFC_A, 1000);
+ } catch (Exception e) {
+ fail("Unexpected exception during RemoteException in setTimeout: " + e.getMessage());
+ }
+
+ }
+
+ @Test
+ public void testGetTimeout() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.getTimeout(TagTechnology.NFC_A)).thenReturn(2000);
+
+ assertEquals(2000, mNfcA.getTimeout());
+ verify(mMockTag).getTagService();
+ verify(mMockTagService).getTimeout(TagTechnology.NFC_A);
+ } catch (Exception e) {
+ fail("Unexpected exception during valid getTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGetTimeoutRemoteException() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.getTimeout(TagTechnology.NFC_A)).thenThrow(new RemoteException());
+
+ assertEquals(0, mNfcA.getTimeout());
+ } catch (Exception e) {
+ fail("Unexpected exception during RemoteException in getTimeout: " + e.getMessage());
+ }
+ }
+}
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index 31e1eb36ad8d..70f5bb32a5d5 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -2074,15 +2074,19 @@ public class PackageWatchdog {
bootMitigationCounts.put(observer.name, observer.getBootMitigationCount());
}
+ FileOutputStream fileStream = null;
+ ObjectOutputStream objectStream = null;
try {
- FileOutputStream fileStream = new FileOutputStream(new File(filePath));
- ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
+ fileStream = new FileOutputStream(new File(filePath));
+ objectStream = new ObjectOutputStream(fileStream);
objectStream.writeObject(bootMitigationCounts);
objectStream.flush();
- objectStream.close();
- fileStream.close();
} catch (Exception e) {
Slog.i(TAG, "Could not save observers metadata to file: " + e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(objectStream);
+ IoUtils.closeQuietly(fileStream);
}
}
@@ -2233,23 +2237,32 @@ public class PackageWatchdog {
void readAllObserversBootMitigationCountIfNecessary(String filePath) {
File metadataFile = new File(filePath);
if (metadataFile.exists()) {
+ FileInputStream fileStream = null;
+ ObjectInputStream objectStream = null;
+ HashMap<String, Integer> bootMitigationCounts = null;
try {
- FileInputStream fileStream = new FileInputStream(metadataFile);
- ObjectInputStream objectStream = new ObjectInputStream(fileStream);
- HashMap<String, Integer> bootMitigationCounts =
+ fileStream = new FileInputStream(metadataFile);
+ objectStream = new ObjectInputStream(fileStream);
+ bootMitigationCounts =
(HashMap<String, Integer>) objectStream.readObject();
- objectStream.close();
- fileStream.close();
-
- for (int i = 0; i < mAllObservers.size(); i++) {
- final ObserverInternal observer = mAllObservers.valueAt(i);
- if (bootMitigationCounts.containsKey(observer.name)) {
- observer.setBootMitigationCount(
- bootMitigationCounts.get(observer.name));
- }
- }
} catch (Exception e) {
Slog.i(TAG, "Could not read observer metadata file: " + e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(objectStream);
+ IoUtils.closeQuietly(fileStream);
+ }
+
+ if (bootMitigationCounts == null || bootMitigationCounts.isEmpty()) {
+ Slog.i(TAG, "No observer in metadata file");
+ return;
+ }
+ for (int i = 0; i < mAllObservers.size(); i++) {
+ final ObserverInternal observer = mAllObservers.valueAt(i);
+ if (bootMitigationCounts.containsKey(observer.name)) {
+ observer.setBootMitigationCount(
+ bootMitigationCounts.get(observer.name));
+ }
}
}
}
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index ffae5176cebf..ac815f8aca85 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -2021,15 +2021,19 @@ public class PackageWatchdog {
bootMitigationCounts.put(observer.name, observer.getBootMitigationCount());
}
+ FileOutputStream fileStream = null;
+ ObjectOutputStream objectStream = null;
try {
- FileOutputStream fileStream = new FileOutputStream(new File(filePath));
- ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
+ fileStream = new FileOutputStream(new File(filePath));
+ objectStream = new ObjectOutputStream(fileStream);
objectStream.writeObject(bootMitigationCounts);
objectStream.flush();
- objectStream.close();
- fileStream.close();
} catch (Exception e) {
Slog.i(TAG, "Could not save observers metadata to file: " + e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(objectStream);
+ IoUtils.closeQuietly(fileStream);
}
}
@@ -2180,23 +2184,32 @@ public class PackageWatchdog {
void readAllObserversBootMitigationCountIfNecessary(String filePath) {
File metadataFile = new File(filePath);
if (metadataFile.exists()) {
+ FileInputStream fileStream = null;
+ ObjectInputStream objectStream = null;
+ HashMap<String, Integer> bootMitigationCounts = null;
try {
- FileInputStream fileStream = new FileInputStream(metadataFile);
- ObjectInputStream objectStream = new ObjectInputStream(fileStream);
- HashMap<String, Integer> bootMitigationCounts =
+ fileStream = new FileInputStream(metadataFile);
+ objectStream = new ObjectInputStream(fileStream);
+ bootMitigationCounts =
(HashMap<String, Integer>) objectStream.readObject();
- objectStream.close();
- fileStream.close();
-
- for (int i = 0; i < mAllObservers.size(); i++) {
- final ObserverInternal observer = mAllObservers.valueAt(i);
- if (bootMitigationCounts.containsKey(observer.name)) {
- observer.setBootMitigationCount(
- bootMitigationCounts.get(observer.name));
- }
- }
} catch (Exception e) {
Slog.i(TAG, "Could not read observer metadata file: " + e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(objectStream);
+ IoUtils.closeQuietly(fileStream);
+ }
+
+ if (bootMitigationCounts == null || bootMitigationCounts.isEmpty()) {
+ Slog.i(TAG, "No observer in metadata file");
+ return;
+ }
+ for (int i = 0; i < mAllObservers.size(); i++) {
+ final ObserverInternal observer = mAllObservers.valueAt(i);
+ if (bootMitigationCounts.containsKey(observer.name)) {
+ observer.setBootMitigationCount(
+ bootMitigationCounts.get(observer.name));
+ }
}
}
}
diff --git a/packages/SettingsLib/ButtonPreference/Android.bp b/packages/SettingsLib/ButtonPreference/Android.bp
index 0382829b2652..08dd27fd25ce 100644
--- a/packages/SettingsLib/ButtonPreference/Android.bp
+++ b/packages/SettingsLib/ButtonPreference/Android.bp
@@ -24,4 +24,8 @@ android_library {
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.healthfitness",
+ ],
}
diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
index 993555e78bea..be711accd542 100644
--- a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
+++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
@@ -247,4 +247,28 @@ public class ButtonPreference extends Preference implements GroupSectionDividerM
mButton.setLayoutParams(lp);
}
}
+
+ /**
+ * Sets the style of the button.
+ *
+ * @param type Specifies the button's type, which sets the attribute `buttonPreferenceType`.
+ * Possible values are:
+ * <ul>
+ * <li>0: filled</li>
+ * <li>1: tonal</li>
+ * <li>2: outline</li>
+ * </ul>
+ * @param size Specifies the button's size, which sets the attribute `buttonPreferenceSize`.
+ * Possible values are:
+ * <ul>
+ * <li>0: normal</li>
+ * <li>1: large</li>
+ * <li>2: extra large</li>
+ * </ul>
+ */
+ public void setButtonStyle(int type, int size) {
+ int layoutId = ButtonStyle.getLayoutId(type, size);
+ setLayoutResource(layoutId);
+ notifyChanged();
+ }
}
diff --git a/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
index 49f045f4423c..5df617c37945 100644
--- a/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
+++ b/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
@@ -41,7 +41,7 @@ import androidx.preference.PreferenceViewHolder;
* xxxxxxx:allowDividerBelow="true"
*
*/
-public class LayoutPreference extends Preference {
+public class LayoutPreference extends Preference implements GroupSectionDividerMixin {
private final View.OnClickListener mClickListener = v -> performClick(v);
private boolean mAllowDividerAbove;
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
index 03a2101544be..218983a55e1b 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
@@ -31,7 +31,6 @@ import androidx.preference.CheckBoxPreference;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.widget.preference.selector.R;
-import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
/**
* Selector preference (checkbox or radio button) with an optional additional widget.
@@ -180,10 +179,8 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
: getContext().getString(R.string.settings_label));
}
- if (Flags.allowSetTitleMaxLines()) {
- TextView title = (TextView) holder.findViewById(android.R.id.title);
- title.setMaxLines(mTitleMaxLines);
- }
+ TextView title = (TextView) holder.findViewById(android.R.id.title);
+ title.setMaxLines(mTitleMaxLines);
}
/**
@@ -244,16 +241,12 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
setLayoutResource(R.layout.preference_selector_with_widget);
setIconSpaceReserved(false);
- if (Flags.allowSetTitleMaxLines()) {
- final TypedArray a =
- context.obtainStyledAttributes(
- attrs, R.styleable.SelectorWithWidgetPreference, defStyleAttr,
- defStyleRes);
- mTitleMaxLines =
- a.getInt(R.styleable.SelectorWithWidgetPreference_titleMaxLines,
- DEFAULT_MAX_LINES);
- a.recycle();
- }
+ final TypedArray a =
+ context.obtainStyledAttributes(
+ attrs, R.styleable.SelectorWithWidgetPreference, defStyleAttr, defStyleRes);
+ mTitleMaxLines =
+ a.getInt(R.styleable.SelectorWithWidgetPreference_titleMaxLines, DEFAULT_MAX_LINES);
+ a.recycle();
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 1a043d5015b2..d82b58ec1358 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -97,6 +97,7 @@ flag {
name: "settings_catalyst"
namespace: "android_settings"
description: "Settings catalyst project migration"
+ is_exported: true
bug: "323791114"
is_exported: true
}
@@ -106,6 +107,7 @@ flag {
is_fixed_read_only: true
namespace: "android_settings"
description: "Enable WRITE_SYSTEM_PREFERENCE permission and appop"
+ is_exported: true
bug: "375193223"
is_exported: true
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index cf452314163f..c9aac91f1320 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -21,6 +21,7 @@ import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_
import android.annotation.IntDef;
import android.annotation.MainThread;
+import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -1643,7 +1644,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
CharSequence appLabel = "";
ApplicationInfo appInfo = null;
try {
- int userId = UserHandle.getUserId(UserHandle.USER_CURRENT);
+ int userId = ActivityManager.getCurrentUser();
appInfo = packageManager.getApplicationInfoAsUser(packageName, 0 /* flags */, userId);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Failed to get app info", e);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
index 2b8b3b74dab9..c939c770b63d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
@@ -21,9 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import android.app.Application;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -33,10 +30,8 @@ import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.widget.preference.selector.R;
-import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
@@ -45,7 +40,6 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class SelectorWithWidgetPreferenceTest {
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Application mContext;
private SelectorWithWidgetPreference mPreference;
@@ -128,26 +122,6 @@ public class SelectorWithWidgetPreferenceTest {
}
@Test
- @DisableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
- public void onBindViewHolder_titleMaxLinesSet_flagOff_titleMaxLinesMatchesDefault() {
- final int titleMaxLines = 5;
- AttributeSet attributeSet = Robolectric.buildAttributeSet()
- .addAttribute(R.attr.titleMaxLines, String.valueOf(titleMaxLines))
- .build();
- mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
- View view = LayoutInflater.from(mContext)
- .inflate(mPreference.getLayoutResource(), null /* root */);
- PreferenceViewHolder preferenceViewHolder =
- PreferenceViewHolder.createInstanceForTests(view);
-
- mPreference.onBindViewHolder(preferenceViewHolder);
-
- TextView title = (TextView) preferenceViewHolder.findViewById(android.R.id.title);
- assertThat(title.getMaxLines()).isEqualTo(SelectorWithWidgetPreference.DEFAULT_MAX_LINES);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
public void onBindViewHolder_noTitleMaxLinesSet_titleMaxLinesMatchesDefault() {
AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
@@ -163,7 +137,6 @@ public class SelectorWithWidgetPreferenceTest {
}
@Test
- @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
public void onBindViewHolder_titleMaxLinesSet_titleMaxLinesUpdated() {
final int titleMaxLines = 5;
AttributeSet attributeSet = Robolectric.buildAttributeSet()
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 91ac34ac8233..de7c450d8d39 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -148,27 +148,7 @@ public final class DeviceConfigService extends Binder {
// TODO(b/364399200): use filter to skip instead?
return;
}
-
- ArrayList<String> missingFiles = new ArrayList<String>();
- for (String fileName : sAconfigTextProtoFilesOnDevice) {
- File aconfigFile = new File(fileName);
- if (!aconfigFile.exists()) {
- missingFiles.add(fileName);
- }
- }
-
- if (missingFiles.isEmpty()) {
- pw.println("\nAconfig flags:");
- for (String name : MyShellCommand.listAllAconfigFlags(iprovider)) {
- pw.println(name);
- }
- } else {
- pw.println("\nFailed to dump aconfig flags due to missing files:");
- for (String fileName : missingFiles) {
- pw.println(fileName);
- }
- }
- }
+ }
private static HashSet<String> getAconfigFlagNamesInDeviceConfig() {
HashSet<String> nameSet = new HashSet<String>();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 1c4def39eaa0..e01cb84f60ae 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -16,6 +16,19 @@
package com.android.providers.settings;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_DEVICE_SPECIFIC_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_GLOBAL;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_LOCALE;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_LOCK_SETTINGS;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_NETWORK_POLICIES;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SECURE;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS_2;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SYSTEM;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_NEW_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_SETTINGS_BACKUP_DATA;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -99,22 +112,6 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private static final int NULL_SIZE = -1;
private static final float FONT_SCALE_DEF_VALUE = 1.0f;
- private static final String KEY_SYSTEM = "system";
- private static final String KEY_SECURE = "secure";
- private static final String KEY_GLOBAL = "global";
- private static final String KEY_LOCALE = "locale";
- private static final String KEY_LOCK_SETTINGS = "lock_settings";
- private static final String KEY_SOFTAP_CONFIG = "softap_config";
- private static final String KEY_NETWORK_POLICIES = "network_policies";
- private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
- private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
- private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
- // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
- // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
- // restoring this data.
- private static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
- private static final String KEY_WIFI_SETTINGS_BACKUP_DATA = "wifi_settings_backup_data";
-
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
private static final int STATE_VERSION = 9;
@@ -257,6 +254,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (com.android.server.backup.Flags.enableMetricsSettingsBackupAgents()) {
mBackupRestoreEventLogger = this.getBackupRestoreEventLogger();
+ mSettingsHelper.setBackupRestoreEventLogger(mBackupRestoreEventLogger);
numberOfSettingsPerKey = new HashMap<>();
areAgentMetricsEnabled = true;
}
@@ -412,9 +410,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
mSettingsHelper
.setLocaleData(
localeData,
- size,
- mBackupRestoreEventLogger,
- KEY_LOCALE);
+ size);
break;
case KEY_WIFI_CONFIG :
@@ -552,8 +548,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
if (nBytes > buffer.length) buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
mSettingsHelper
- .setLocaleData(
- buffer, nBytes, mBackupRestoreEventLogger, KEY_LOCALE);
+ .setLocaleData(buffer, nBytes);
// Restore older backups performing the necessary migrations.
if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java
new file mode 100644
index 000000000000..745c2fb5409d
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 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.providers.settings;
+
+import android.net.Uri;
+import android.provider.Settings;
+
+/**
+ * Class to store the keys used for backup and restore.
+ */
+final class SettingsBackupRestoreKeys {
+ static final String KEY_UNKNOWN = "unknown";
+ static final String KEY_SYSTEM = "system";
+ static final String KEY_SECURE = "secure";
+ static final String KEY_GLOBAL = "global";
+ static final String KEY_LOCALE = "locale";
+ static final String KEY_LOCK_SETTINGS = "lock_settings";
+ static final String KEY_SOFTAP_CONFIG = "softap_config";
+ static final String KEY_NETWORK_POLICIES = "network_policies";
+ static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
+ static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+ static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
+ // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
+ // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
+ // restoring this data.
+ static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
+ static final String KEY_WIFI_SETTINGS_BACKUP_DATA = "wifi_settings_backup_data";
+
+ /**
+ * Returns the key corresponding to the given URI.
+ *
+ * @param uri The URI of the setting's destination.
+ * @return The key corresponding to the given URI, or KEY_UNKNOWN if the URI is not recognized.
+ */
+ static String getKeyFromUri(Uri uri) {
+ if (uri.equals(Settings.Secure.CONTENT_URI)) {
+ return KEY_SECURE;
+ } else if (uri.equals(Settings.System.CONTENT_URI)) {
+ return KEY_SYSTEM;
+ } else if (uri.equals(Settings.Global.CONTENT_URI)) {
+ return KEY_GLOBAL;
+ } else {
+ return KEY_UNKNOWN;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 924c151a99a0..ab8d739feb43 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -17,6 +17,7 @@
package com.android.providers.settings;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.backup.BackupRestoreEventLogger;
@@ -84,6 +85,7 @@ public class SettingsHelper {
private Context mContext;
private AudioManager mAudioManager;
private TelephonyManager mTelephonyManager;
+ @Nullable private BackupRestoreEventLogger mBackupRestoreEventLogger;
/**
* A few settings elements are special in that a restore of those values needs to
@@ -741,11 +743,8 @@ public class SettingsHelper {
*
* @param data the comma separated BCP-47 language tags in bytes.
* @param size the size of the data in bytes.
- * @param backupRestoreEventLogger the logger to log the restore event.
- * @param dataType the data type of the setting for logging purposes.
*/
- /* package */ void setLocaleData(
- byte[] data, int size, BackupRestoreEventLogger backupRestoreEventLogger, String dataType) {
+ /* package */ void setLocaleData(byte[] data, int size) {
final Configuration conf = mContext.getResources().getConfiguration();
// Replace "_" with "-" to deal with older backups.
@@ -772,15 +771,15 @@ public class SettingsHelper {
am.updatePersistentConfigurationWithAttribution(config, mContext.getOpPackageName(),
mContext.getAttributionTag());
- if (Flags.enableMetricsSettingsBackupAgents()) {
- backupRestoreEventLogger
- .logItemsRestored(dataType, localeList.size());
+ if (Flags.enableMetricsSettingsBackupAgents() && mBackupRestoreEventLogger != null) {
+ mBackupRestoreEventLogger
+ .logItemsRestored(SettingsBackupRestoreKeys.KEY_LOCALE, localeList.size());
}
} catch (RemoteException e) {
- if (Flags.enableMetricsSettingsBackupAgents()) {
- backupRestoreEventLogger
+ if (Flags.enableMetricsSettingsBackupAgents() && mBackupRestoreEventLogger != null) {
+ mBackupRestoreEventLogger
.logItemsRestoreFailed(
- dataType,
+ SettingsBackupRestoreKeys.KEY_LOCALE,
localeList.size(),
ERROR_REMOTE_EXCEPTION_SETTING_LOCALE_DATA);
}
@@ -795,4 +794,13 @@ public class SettingsHelper {
AudioManager am = new AudioManager(mContext);
am.reloadAudioSettings();
}
+
+ /**
+ * Sets the backup restore event logger.
+ *
+ * @param backupRestoreEventLogger the logger to log B&R metrics.
+ */
+ void setBackupRestoreEventLogger(BackupRestoreEventLogger backupRestoreEventLogger) {
+ mBackupRestoreEventLogger = backupRestoreEventLogger;
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f9c64422b0db..661a09553914 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2520,6 +2520,13 @@ class SettingsProtoDumpUtil {
Settings.Secure.RTT_CALLING_MODE,
SecureSettingsProto.RTT_CALLING_MODE);
+ final long screenoffudfpsenabledToken = p.start(
+ SecureSettingsProto.SCREEN_OFF_UDFPS_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
+ SecureSettingsProto.SCREEN_OFF_UDFPS_ENABLED);
+ p.end(screenoffudfpsenabledToken);
+
final long screensaverToken = p.start(SecureSettingsProto.SCREENSAVER);
dumpSetting(s, p,
Settings.Secure.SCREENSAVER_ENABLED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 55f48e3e367f..f1f03c31f718 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -120,6 +120,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
@@ -2914,6 +2915,14 @@ public class SettingsProvider extends ContentProvider {
};
}
+ @VisibleForTesting
+ void injectServices(UserManager userManager, IPackageManager packageManager,
+ SystemConfigManager sysConfigManager) {
+ mUserManager = userManager;
+ mPackageManager = packageManager;
+ mSysConfigManager = sysConfigManager;
+ }
+
private static final class Arguments {
private static final Pattern WHERE_PATTERN_WITH_PARAM_NO_BRACKETS =
Pattern.compile("[\\s]*name[\\s]*=[\\s]*\\?[\\s]*");
@@ -3080,6 +3089,7 @@ public class SettingsProvider extends ContentProvider {
private static final String SSAID_USER_KEY = "userkey";
+ @GuardedBy("mLock")
private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
private GenerationRegistry mGenerationRegistry;
@@ -3992,6 +4002,14 @@ public class SettingsProvider extends ContentProvider {
}
}
+ @VisibleForTesting
+ void injectSettings(SettingsState settings, int type, int userId) {
+ int key = makeKey(type, userId);
+ synchronized (mLock) {
+ mSettingsStates.put(key, settings);
+ }
+ }
+
private final class MyHandler extends Handler {
private static final int MSG_NOTIFY_URI_CHANGED = 1;
private static final int MSG_NOTIFY_DATA_CHANGED = 2;
@@ -4023,12 +4041,21 @@ public class SettingsProvider extends ContentProvider {
}
}
- private final class UpgradeController {
+ @VisibleForTesting
+ final class UpgradeController {
private static final int SETTINGS_VERSION = 226;
private final int mUserId;
+ private final Injector mInjector;
+
public UpgradeController(int userId) {
+ this(/* injector= */ null, userId);
+ }
+
+ @VisibleForTesting
+ UpgradeController(Injector injector, int userId) {
+ mInjector = injector == null ? new Injector() : injector;
mUserId = userId;
}
@@ -6136,8 +6163,8 @@ public class SettingsProvider extends ContentProvider {
systemSettings.getSettingLocked(Settings.System.PEAK_REFRESH_RATE);
final Setting minRefreshRateSetting =
systemSettings.getSettingLocked(Settings.System.MIN_REFRESH_RATE);
- float highestRefreshRate = RefreshRateSettingsUtils
- .findHighestRefreshRateForDefaultDisplay(getContext());
+ float highestRefreshRate =
+ mInjector.findHighestRefreshRateForDefaultDisplay(getContext());
if (!peakRefreshRateSetting.isNull()) {
try {
@@ -6318,6 +6345,14 @@ public class SettingsProvider extends ContentProvider {
private long getBitMask(int capability) {
return 1 << (capability - 1);
}
+
+ @VisibleForTesting
+ static class Injector {
+ float findHighestRefreshRateForDefaultDisplay(Context context) {
+ return RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(
+ context);
+ }
+ }
}
/**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 5cd534e62ea9..bf3afeda448e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -107,7 +107,7 @@ import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
* the same lock to grab the current state to write to disk.
* </p>
*/
-final class SettingsState {
+public class SettingsState {
private static final boolean DEBUG = false;
private static final boolean DEBUG_PERSISTENCE = false;
@@ -1838,7 +1838,7 @@ final class SettingsState {
}
}
- class Setting {
+ public class Setting {
private String name;
private String value;
private String defaultValue;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java
new file mode 100644
index 000000000000..ef537e8c7fc0
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 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.providers.settings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link SettingsBackupRestoreKeys}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SettingsBackupRestoreKeysTest {
+
+ @Test
+ public void getKeyFromUri_secureUri_returnsSecureKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.Secure.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_SECURE);
+ }
+
+ @Test
+ public void getKeyFromUri_systemUri_returnsSystemKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.System.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_SYSTEM);
+ }
+
+ @Test
+ public void getKeyFromUri_globalUri_returnsGlobalKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.Global.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_GLOBAL);
+ }
+
+ @Test
+ public void getKeyFromUri_unknownUri_returnsUnknownKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Uri.parse("content://unknown")))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_UNKNOWN);
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
index 9cce43160b52..119b2870b622 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
@@ -16,7 +16,7 @@
package com.android.providers.settings;
-import static android.provider.Settings.Secure.ACCESSIBILITY_ENABLED;
+import static android.provider.Settings.Secure.CONTENT_CAPTURE_ENABLED;
import static android.provider.Settings.Secure.SYNC_PARENT_SOUNDS;
import static android.provider.Settings.System.RINGTONE;
@@ -67,7 +67,7 @@ public class SettingsProviderMultiUsersTest {
private static final String SPACE_SYSTEM = "system";
private static final String SPACE_SECURE = "secure";
- private static final String CLONE_TO_MANAGED_PROFILE_SETTING = ACCESSIBILITY_ENABLED;
+ private static final String CLONE_TO_MANAGED_PROFILE_SETTING = CONTENT_CAPTURE_ENABLED;
private static final String CLONE_FROM_PARENT_SETTINGS = RINGTONE;
private static final String SYNC_FROM_PARENT_SETTINGS = SYNC_PARENT_SOUNDS;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java
new file mode 100644
index 000000000000..26ff376f828e
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.providers.settings;
+
+import static android.provider.Settings.System.MIN_REFRESH_RATE;
+import static android.provider.Settings.System.PEAK_REFRESH_RATE;
+
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_GLOBAL;
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_SECURE;
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_SYSTEM;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.IPackageManager;
+import android.os.Looper;
+import android.os.SystemConfigManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class UpgradeControllerTest {
+ private static final int USER_ID = UserHandle.USER_SYSTEM;
+ private static final float HIGHEST_REFRESH_RATE = 130f;
+
+ private final Context mContext =
+ spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ private final SettingsProvider.SettingsRegistry.UpgradeController.Injector mInjector =
+ new SettingsProvider.SettingsRegistry.UpgradeController.Injector() {
+ @Override
+ float findHighestRefreshRateForDefaultDisplay(Context context) {
+ return HIGHEST_REFRESH_RATE;
+ }
+ };
+ private final SettingsProvider mSettingsProvider = new SettingsProvider() {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+ };
+ private final SettingsProvider.SettingsRegistry mSettingsRegistry =
+ mSettingsProvider.new SettingsRegistry(Looper.getMainLooper());
+ private final SettingsProvider.SettingsRegistry.UpgradeController mUpgradeController =
+ mSettingsRegistry.new UpgradeController(mInjector, USER_ID);
+
+ @Mock
+ private UserManager mUserManager;
+
+ @Mock
+ private IPackageManager mPackageManager;
+
+ @Mock
+ private SystemConfigManager mSysConfigManager;
+
+ @Mock
+ private SettingsState mSystemSettings;
+
+ @Mock
+ private SettingsState mSecureSettings;
+
+ @Mock
+ private SettingsState mGlobalSettings;
+
+ @Mock
+ private SettingsState.Setting mMockSetting;
+
+ @Mock
+ private SettingsState.Setting mPeakRefreshRateSetting;
+
+ @Mock
+ private SettingsState.Setting mMinRefreshRateSetting;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSettingsProvider.attachInfoForTesting(mContext, /* info= */ null);
+ mSettingsProvider.injectServices(mUserManager, mPackageManager, mSysConfigManager);
+ when(mSystemSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mSecureSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mGlobalSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mMockSetting.isNull()).thenReturn(true);
+ when(mMockSetting.getValue()).thenReturn("0");
+
+ when(mSystemSettings.getSettingLocked(PEAK_REFRESH_RATE))
+ .thenReturn(mPeakRefreshRateSetting);
+ when(mSystemSettings.getSettingLocked(MIN_REFRESH_RATE))
+ .thenReturn(mMinRefreshRateSetting);
+
+ mSettingsRegistry.injectSettings(mSystemSettings, SETTINGS_TYPE_SYSTEM, USER_ID);
+ mSettingsRegistry.injectSettings(mSecureSettings, SETTINGS_TYPE_SECURE, USER_ID);
+ mSettingsRegistry.injectSettings(mGlobalSettings, SETTINGS_TYPE_GLOBAL, USER_ID);
+
+ // Lowest version so that all upgrades are run
+ when(mSecureSettings.getVersionLocked()).thenReturn(118);
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_defaultValues() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(true);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(true);
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Should remain unchanged
+ verify(mSystemSettings, never()).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ verify(mSystemSettings, never()).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_enabled() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(false);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(false);
+ when(mPeakRefreshRateSetting.getValue()).thenReturn(String.valueOf(HIGHEST_REFRESH_RATE));
+ when(mMinRefreshRateSetting.getValue()).thenReturn(String.valueOf(HIGHEST_REFRESH_RATE));
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Highest refresh rate gets converted to infinity
+ verify(mSystemSettings).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ eq(String.valueOf(Float.POSITIVE_INFINITY)), /* tag= */ any(),
+ /* makeDefault= */ anyBoolean(), /* packageName= */ any());
+ verify(mSystemSettings).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ eq(String.valueOf(Float.POSITIVE_INFINITY)), /* tag= */ any(),
+ /* makeDefault= */ anyBoolean(), /* packageName= */ any());
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_disabled() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(false);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(false);
+ when(mPeakRefreshRateSetting.getValue()).thenReturn("70f");
+ when(mMinRefreshRateSetting.getValue()).thenReturn("70f");
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Should remain unchanged
+ verify(mSystemSettings, never()).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ verify(mSystemSettings, never()).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ }
+}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 3d250fd82473..0600fb3abd6f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -85,6 +85,7 @@ filegroup {
filegroup {
name: "SystemUI-tests-broken-robofiles-run",
srcs: [
+ "tests/src/**/systemui/dreams/touch/CommunalTouchHandlerTest.java",
"tests/src/**/systemui/shade/NotificationShadeWindowViewControllerTest.kt",
"tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt",
@@ -418,6 +419,9 @@ android_library {
"androidx.slice_slice-view",
],
manifest: "AndroidManifest-res.xml",
+ flags_packages: [
+ "com_android_systemui_flags",
+ ],
}
android_library {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 1b1c91de1e56..5ff2d1b07347 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -496,6 +496,13 @@ flag {
}
flag {
+ name: "status_bar_popup_chips"
+ namespace: "systemui"
+ description: "Show rich ongoing processes as chips in the status bar"
+ bug: "372964148"
+}
+
+flag {
name: "promote_notifications_automatically"
namespace: "systemui"
description: "Flag to automatically turn certain notifications into promoted notifications so "
@@ -1229,6 +1236,21 @@ flag {
}
flag {
+ name: "glanceable_hub_v2_resources"
+ namespace: "systemui"
+ description: "Read only flag for rolling out glanceable hub v2 resource values"
+ bug: "375689917"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "glanceable_hub_back_action"
+ namespace: "systemui"
+ description: "Support back action from glanceable hub"
+ bug: "382771533"
+}
+
+flag {
name: "dream_overlay_updated_font"
namespace: "systemui"
description: "Flag to enable updated font settings for dream overlay"
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index ca2b9578f2be..7d27a562f536 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -195,7 +195,10 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
// Create the origin leash and add to the transition root leash.
mOriginLeash =
new SurfaceControl.Builder().setName("OriginTransition-origin-leash").build();
- mStartTransaction
+
+ // Create temporary transaction to build
+ final SurfaceControl.Transaction tmpTransaction = new SurfaceControl.Transaction();
+ tmpTransaction
.reparent(mOriginLeash, rootLeash)
.show(mOriginLeash)
.setCornerRadius(mOriginLeash, windowRadius)
@@ -208,14 +211,14 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
int mode = change.getMode();
SurfaceControl leash = change.getLeash();
// Reparent leash to the transition root.
- mStartTransaction.reparent(leash, rootLeash);
+ tmpTransaction.reparent(leash, rootLeash);
if (TransitionUtil.isOpeningMode(mode)) {
openingSurfaces.add(change.getLeash());
// For opening surfaces, ending bounds are base bound. Apply corner radius if
// it's full screen.
Rect bounds = change.getEndAbsBounds();
if (displayBounds.equals(bounds)) {
- mStartTransaction
+ tmpTransaction
.setCornerRadius(leash, windowRadius)
.setWindowCrop(leash, bounds.width(), bounds.height());
}
@@ -226,28 +229,53 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
// it's full screen.
Rect bounds = change.getStartAbsBounds();
if (displayBounds.equals(bounds)) {
- mStartTransaction
+ tmpTransaction
.setCornerRadius(leash, windowRadius)
.setWindowCrop(leash, bounds.width(), bounds.height());
}
}
}
+ if (openingSurfaces.isEmpty() && closingSurfaces.isEmpty()) {
+ logD("prepareUIs: no opening/closing surfaces available, nothing to prepare.");
+ return false;
+ }
+
// Set relative order:
// ---- App1 ----
// ---- origin ----
// ---- App2 ----
+
if (mIsEntry) {
- mStartTransaction
- .setRelativeLayer(mOriginLeash, closingSurfaces.get(0), 1)
- .setRelativeLayer(
- openingSurfaces.get(openingSurfaces.size() - 1), mOriginLeash, 1);
+ if (!closingSurfaces.isEmpty()) {
+ tmpTransaction
+ .setRelativeLayer(mOriginLeash, closingSurfaces.get(0), 1);
+ } else {
+ logW("Missing closing surface is entry transition");
+ }
+ if (!openingSurfaces.isEmpty()) {
+ tmpTransaction
+ .setRelativeLayer(
+ openingSurfaces.get(openingSurfaces.size() - 1), mOriginLeash, 1);
+ } else {
+ logW("Missing opening surface is entry transition");
+ }
+
} else {
- mStartTransaction
- .setRelativeLayer(mOriginLeash, openingSurfaces.get(0), 1)
- .setRelativeLayer(
- closingSurfaces.get(closingSurfaces.size() - 1), mOriginLeash, 1);
+ if (!openingSurfaces.isEmpty()) {
+ tmpTransaction
+ .setRelativeLayer(mOriginLeash, openingSurfaces.get(0), 1);
+ } else {
+ logW("Missing opening surface is exit transition");
+ }
+ if (!closingSurfaces.isEmpty()) {
+ tmpTransaction.setRelativeLayer(
+ closingSurfaces.get(closingSurfaces.size() - 1), mOriginLeash, 1);
+ } else {
+ logW("Missing closing surface is exit transition");
+ }
}
+ mStartTransaction.merge(tmpTransaction);
// Attach origin UIComponent to origin leash.
mOriginTransaction = mOrigin.newTransaction();
@@ -300,6 +328,7 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
}
private void cancel() {
+ logD("cancel()");
if (mAnimator != null) {
mAnimator.cancel();
}
@@ -311,6 +340,10 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
}
}
+ private static void logW(String msg) {
+ Log.w(TAG, msg);
+ }
+
private static void logE(String msg) {
Log.e(TAG, msg);
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 41a00f5237f7..b0c7ac09551a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -17,7 +17,6 @@
package com.android.systemui.animation
import android.app.ActivityManager
-import android.app.ActivityOptions
import android.app.ActivityTaskManager
import android.app.PendingIntent
import android.app.TaskInfo
@@ -292,7 +291,7 @@ constructor(
?: throw IllegalStateException(
"ActivityTransitionAnimator.callback must be set before using this animator"
)
- val runner = createRunner(controller)
+ val runner = createEphemeralRunner(controller)
val runnerDelegate = runner.delegate
val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen
@@ -416,7 +415,7 @@ constructor(
var cleanUpRunnable: Runnable? = null
val returnRunner =
- createRunner(
+ createEphemeralRunner(
object : DelegateTransitionAnimatorController(launchController) {
override val isLaunching = false
@@ -458,11 +457,7 @@ constructor(
"${launchController.transitionCookie}_returnTransition",
)
- transitionRegister?.register(
- filter,
- transition,
- includeTakeover = longLivedReturnAnimationsEnabled(),
- )
+ transitionRegister?.register(filter, transition, includeTakeover = false)
cleanUpRunnable = Runnable { transitionRegister?.unregister(transition) }
}
@@ -476,12 +471,14 @@ constructor(
listeners.remove(listener)
}
- /** Create a new animation [Runner] controlled by [controller]. */
+ /**
+ * Create a new animation [Runner] controlled by [controller].
+ *
+ * This method must only be used for ephemeral (launch or return) transitions. Otherwise, use
+ * [createLongLivedRunner].
+ */
@VisibleForTesting
- @JvmOverloads
- fun createRunner(controller: Controller, longLived: Boolean = false): Runner {
- if (longLived) assertLongLivedReturnAnimations()
-
+ fun createEphemeralRunner(controller: Controller): Runner {
// Make sure we use the modified timings when animating a dialog into an app.
val transitionAnimator =
if (controller.isDialogLaunch) {
@@ -490,7 +487,22 @@ constructor(
transitionAnimator
}
- return Runner(controller, callback!!, transitionAnimator, lifecycleListener, longLived)
+ return Runner(controller, callback!!, transitionAnimator, lifecycleListener)
+ }
+
+ /**
+ * Create a new animation [Runner] controlled by the [Controller] that [controllerFactory] can
+ * create based on [forLaunch].
+ *
+ * This method must only be used for long-lived registrations. Otherwise, use
+ * [createEphemeralRunner].
+ */
+ @VisibleForTesting
+ fun createLongLivedRunner(controllerFactory: ControllerFactory, forLaunch: Boolean): Runner {
+ assertLongLivedReturnAnimations()
+ return Runner(callback!!, transitionAnimator, lifecycleListener) {
+ controllerFactory.createController(forLaunch)
+ }
}
interface PendingIntentStarter {
@@ -537,6 +549,23 @@ constructor(
}
/**
+ * A factory used to create instances of [Controller] linked to a specific cookie [cookie] and
+ * [component].
+ */
+ abstract class ControllerFactory(
+ val cookie: TransitionCookie,
+ val component: ComponentName?,
+ val launchCujType: Int? = null,
+ val returnCujType: Int? = null,
+ ) {
+ /**
+ * Creates a [Controller] for launching or returning from the activity linked to [cookie]
+ * and [component].
+ */
+ abstract fun createController(forLaunch: Boolean): Controller
+ }
+
+ /**
* A controller that takes care of applying the animation to an expanding view.
*
* Note that all callbacks (onXXX methods) are all called on the main thread.
@@ -656,13 +685,13 @@ constructor(
}
/**
- * Registers [controller] as a long-lived transition handler for launch and return animations.
+ * Registers [controllerFactory] as a long-lived transition handler for launch and return
+ * animations.
*
- * The [controller] will only be used for transitions matching the [TransitionCookie] defined
- * within it, or the [ComponentName] if the cookie matching fails. Both fields are mandatory for
- * this registration.
+ * The [Controller]s created by [controllerFactory] will only be used for transitions matching
+ * the [cookie], or the [ComponentName] defined within it if the cookie matching fails.
*/
- fun register(controller: Controller) {
+ fun register(cookie: TransitionCookie, controllerFactory: ControllerFactory) {
assertLongLivedReturnAnimations()
if (transitionRegister == null) {
@@ -672,13 +701,8 @@ constructor(
)
}
- val cookie =
- controller.transitionCookie
- ?: throw IllegalStateException(
- "A cookie must be defined in order to use long-lived animations"
- )
val component =
- controller.component
+ controllerFactory.component
?: throw IllegalStateException(
"A component must be defined in order to use long-lived animations"
)
@@ -699,15 +723,11 @@ constructor(
}
val launchRemoteTransition =
RemoteTransition(
- OriginTransition(createRunner(controller, longLived = true)),
+ OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = true)),
"${cookie}_launchTransition",
)
transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = true)
- val returnController =
- object : Controller by controller {
- override val isLaunching: Boolean = false
- }
// Cross-task close transitions should not use this animation, so we only register it for
// when the opening window is Launcher.
val returnFilter =
@@ -727,7 +747,7 @@ constructor(
}
val returnRemoteTransition =
RemoteTransition(
- OriginTransition(createRunner(returnController, longLived = true)),
+ OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = false)),
"${cookie}_returnTransition",
)
transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = true)
@@ -918,32 +938,61 @@ constructor(
}
@VisibleForTesting
- inner class Runner(
+ inner class Runner
+ private constructor(
/**
* This can hold a reference to a view, so it needs to be cleaned up and can't be held on to
- * forever when ![longLived].
+ * forever. In case of a long-lived [Runner], this must be null and [controllerFactory] must
+ * be defined instead.
*/
private var controller: Controller?,
+ /**
+ * Reusable factory to generate single-use controllers. In case of an ephemeral [Runner],
+ * this must be null and [controller] must be defined instead.
+ */
+ private val controllerFactory: (() -> Controller)?,
private val callback: Callback,
/** The animator to use to animate the window transition. */
private val transitionAnimator: TransitionAnimator,
/** Listener for animation lifecycle events. */
- private val listener: Listener? = null,
- /**
- * Whether the internal should be kept around after execution for later usage. IMPORTANT:
- * should always be false if this [Runner] is to be used directly with [ActivityOptions]
- * (i.e. for ephemeral launches), or the controller will leak its view.
- */
- private val longLived: Boolean = false,
+ private val listener: Listener?,
) : IRemoteAnimationRunner.Stub() {
+ constructor(
+ controller: Controller,
+ callback: Callback,
+ transitionAnimator: TransitionAnimator,
+ listener: Listener? = null,
+ ) : this(
+ controller = controller,
+ controllerFactory = null,
+ callback = callback,
+ transitionAnimator = transitionAnimator,
+ listener = listener,
+ )
+
+ constructor(
+ callback: Callback,
+ transitionAnimator: TransitionAnimator,
+ listener: Listener? = null,
+ controllerFactory: () -> Controller,
+ ) : this(
+ controller = null,
+ controllerFactory = controllerFactory,
+ callback = callback,
+ transitionAnimator = transitionAnimator,
+ listener = listener,
+ )
+
// This is being passed across IPC boundaries and cycles (through PendingIntentRecords,
// etc.) are possible. So we need to make sure we drop any references that might
// transitively cause leaks when we're done with animation.
@VisibleForTesting var delegate: AnimationDelegate?
init {
+ assert((controller != null).xor(controllerFactory != null))
+
delegate = null
- if (!longLived) {
+ if (controller != null) {
// Ephemeral launches bundle the runner with the launch request (instead of being
// registered ahead of time for later use). This means that there could be a timeout
// between creation and invocation, so the delegate needs to exist from the
@@ -1021,17 +1070,21 @@ constructor(
@AnyThread
private fun maybeSetUp() {
- if (!longLived || delegate != null) return
+ if (controllerFactory == null || delegate != null) return
createDelegate()
}
@AnyThread
private fun createDelegate() {
- if (controller == null) return
+ var controller = controller
+ val factory = controllerFactory
+ if (controller == null && factory == null) return
+
+ controller = controller ?: factory!!.invoke()
delegate =
AnimationDelegate(
mainExecutor,
- controller!!,
+ controller,
callback,
DelegatingAnimationCompletionListener(listener, this::dispose),
transitionAnimator,
@@ -1041,13 +1094,12 @@ constructor(
@AnyThread
fun dispose() {
- // Drop references to animation controller once we're done with the animation
- // to avoid leaking.
+ // Drop references to animation controller once we're done with the animation to avoid
+ // leaking in case of ephemeral launches. When long-lived, [controllerFactory] will
+ // still be around to create new controllers.
mainExecutor.execute {
delegate = null
- // When long lived, the same Runner can be used more than once. In this case we need
- // to keep the controller around so we can rebuild the delegate on demand.
- if (!longLived) controller = null
+ controller = null
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index a17a1d46554f..0a0003ee9a8a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -19,17 +19,20 @@ package com.android.systemui.communal.ui.compose
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
@@ -39,8 +42,11 @@ import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import javax.inject.Inject
+import kotlin.math.min
+import kotlin.math.roundToInt
/** Renders the content of the glanceable hub. */
class CommunalContent
@@ -48,6 +54,7 @@ class CommunalContent
constructor(
private val viewModel: CommunalViewModel,
private val interactionHandler: SmartspaceInteractionHandler,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
private val dialogFactory: SystemUIDialogFactory,
private val lockSection: LockSection,
private val bottomAreaSection: BottomAreaSection,
@@ -77,11 +84,20 @@ constructor(
sceneScope = this@Content,
)
}
- with(lockSection) {
- LockIcon(
- overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_lock),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier = Modifier.element(Communal.Elements.LockIcon),
)
+ } else {
+ with(lockSection) {
+ LockIcon(
+ overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ modifier = Modifier.element(Communal.Elements.LockIcon),
+ )
+ }
}
with(bottomAreaSection) {
IndicationArea(
@@ -98,14 +114,42 @@ constructor(
val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconPlaceable =
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ val lockIconSizeInt = lockIconSize.roundToPx()
+ lockIconMeasurable.measure(
+ Constraints.fixed(width = lockIconSizeInt, height = lockIconSizeInt)
+ )
+ } else {
+ lockIconMeasurable.measure(noMinConstraints)
+ }
val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ val lockIconDistanceFromBottom =
+ min(
+ (constraints.maxHeight * lockIconPercentDistanceFromBottom)
+ .roundToInt(),
+ lockIconMinDistanceFromBottom.roundToPx(),
+ )
+ val x = constraints.maxWidth / 2 - lockIconPlaceable.width / 2
+ val y =
+ constraints.maxHeight -
+ lockIconDistanceFromBottom -
+ lockIconPlaceable.height
+ IntRect(
+ left = x,
+ top = y,
+ right = x + lockIconPlaceable.width,
+ bottom = y + lockIconPlaceable.height,
+ )
+ } else {
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+ }
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
@@ -129,12 +173,17 @@ constructor(
val bottomAreaTop = constraints.maxHeight - bottomAreaPlaceable.height
bottomAreaPlaceable.place(x = 0, y = bottomAreaTop)
+
+ val screensaverButtonPaddingInt = screensaverButtonPadding.roundToPx()
screensaverButtonPlaceable?.place(
x =
constraints.maxWidth -
screensaverButtonSizeInt -
- Dimensions.ItemSpacing.roundToPx(),
- y = lockIconBounds.top,
+ screensaverButtonPaddingInt,
+ y =
+ constraints.maxHeight -
+ screensaverButtonSizeInt -
+ screensaverButtonPaddingInt,
)
}
}
@@ -142,6 +191,12 @@ constructor(
}
companion object {
- val screensaverButtonSize: Dp = 64.dp
+ private val screensaverButtonSize: Dp = 64.dp
+ private val screensaverButtonPadding: Dp = 24.dp
+ // TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
+ // position are sorted.
+ private val lockIconSize: Dp = 54.dp
+ private val lockIconPercentDistanceFromBottom = 0.1f
+ private val lockIconMinDistanceFromBottom = 70.dp
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 5dbedc7045e4..bf3360f0ea14 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -931,7 +931,9 @@ private fun BoxScope.CommunalHubLazyGrid(
Modifier.requiredSize(dpSize)
.thenIf(!isItemDragging) {
Modifier.animateItem(
- placementSpec = spring(stiffness = Spring.StiffnessMediumLow)
+ placementSpec = spring(stiffness = Spring.StiffnessMediumLow),
+ // See b/376495198 - not supported with AndroidView
+ fadeOutSpec = null,
)
}
.thenIf(isItemDragging) { Modifier.zIndex(1f) },
@@ -980,11 +982,14 @@ private fun BoxScope.CommunalHubLazyGrid(
size = size,
selected = false,
modifier =
- Modifier.requiredSize(dpSize).animateItem().thenIf(
- communalResponsiveGrid()
- ) {
- Modifier.graphicsLayer { alpha = itemAlpha?.value ?: 1f }
- },
+ Modifier.requiredSize(dpSize)
+ .animateItem(
+ // See b/376495198 - not supported with AndroidView
+ fadeOutSpec = null
+ )
+ .thenIf(communalResponsiveGrid()) {
+ Modifier.graphicsLayer { alpha = itemAlpha?.value ?: 1f }
+ },
index = index,
contentListState = contentListState,
interactionHandler = interactionHandler,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 5c7ca97474b7..0344ab8e0196 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -38,7 +38,6 @@ import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.thenIf
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
@@ -90,14 +89,10 @@ constructor(
) {
init {
- if (!MigrateClocksToBlueprint.isEnabled) {
- throw IllegalStateException("this requires MigrateClocksToBlueprint.isEnabled")
- }
// This scene container section moves the NSSL to the SharedNotificationContainer.
// This also requires that SharedNotificationContainer gets moved to the
// SceneWindowRootView by the SceneWindowRootViewBinder. Prior to Scene Container,
- // but when the KeyguardShadeMigrationNssl flag is enabled, NSSL is moved into this
- // container by the NotificationStackScrollLayoutSection.
+ // NSSL is moved into this container by the NotificationStackScrollLayoutSection.
// Ensure stackScrollLayout is a child of sharedNotificationContainer.
if (stackScrollLayout.parent != sharedNotificationContainer) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index db33e7c628d7..79cf24b9c547 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -35,9 +35,8 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
-import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.modifiers.thenIf
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
@@ -61,7 +60,7 @@ constructor(
private val clockInteractor: KeyguardClockInteractor,
) {
@Composable
- fun SceneScope.DefaultClockLayout(
+ fun ContentScope.DefaultClockLayout(
smartSpacePaddingTop: (Resources) -> Int,
isShadeLayoutWide: Boolean,
modifier: Modifier = Modifier,
@@ -95,7 +94,7 @@ constructor(
}
Column(modifier) {
- SceneTransitionLayout(state) {
+ NestedSceneTransitionLayout(state, Modifier) {
scene(splitShadeLargeClockScene) {
LargeClockWithSmartSpace(
smartSpacePaddingTop = smartSpacePaddingTop,
@@ -134,7 +133,7 @@ constructor(
}
@Composable
- private fun SceneScope.SmallClockWithSmartSpace(
+ private fun ContentScope.SmallClockWithSmartSpace(
smartSpacePaddingTop: (Resources) -> Int,
modifier: Modifier = Modifier,
) {
@@ -159,7 +158,7 @@ constructor(
}
@Composable
- private fun SceneScope.LargeClockWithSmartSpace(
+ private fun ContentScope.LargeClockWithSmartSpace(
smartSpacePaddingTop: (Resources) -> Int,
shouldOffSetClockToOneHalf: Boolean = false,
) {
@@ -200,7 +199,7 @@ constructor(
}
@Composable
- private fun SceneScope.WeatherLargeClockWithSmartSpace(
+ private fun ContentScope.WeatherLargeClockWithSmartSpace(
smartSpacePaddingTop: (Resources) -> Int,
modifier: Modifier = Modifier,
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 2af5ffaee7ed..5790c4af0d77 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -19,6 +19,7 @@ package com.android.systemui.notifications.ui.composable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layoutId
import com.android.compose.animation.scene.ContentScope
@@ -84,7 +85,11 @@ constructor(
viewModel.notificationsPlaceholderViewModelFactory.create()
}
- OverlayShade(modifier = modifier, onScrimClicked = viewModel::onScrimClicked) {
+ OverlayShade(
+ panelAlignment = Alignment.TopStart,
+ modifier = modifier,
+ onScrimClicked = viewModel::onScrimClicked,
+ ) {
Column {
if (viewModel.showHeader) {
val burnIn = rememberBurnIn(clockInteractor)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index b1a19456ab7d..f6c5f588aa95 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -99,7 +99,11 @@ constructor(
val viewModel =
rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() }
- OverlayShade(modifier = modifier, onScrimClicked = viewModel::onScrimClicked) {
+ OverlayShade(
+ panelAlignment = Alignment.TopEnd,
+ modifier = modifier,
+ onScrimClicked = viewModel::onScrimClicked,
+ ) {
Column {
ExpandedShadeHeader(
viewModelFactory = viewModel.shadeHeaderViewModelFactory,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 55fafd5cfeca..8907aec7fd48 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -5,7 +5,6 @@ import androidx.compose.foundation.gestures.Orientation
import com.android.compose.animation.scene.ProgressConverter
import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.transitions
-import com.android.systemui.bouncer.ui.composable.Bouncer
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
@@ -110,17 +109,13 @@ val SceneContainerTransitions = transitions {
// Overlay transitions
- // TODO(b/376659778): Remove this transition once nested STLs are supported.
- from(Scenes.Gone, to = Overlays.NotificationsShade) {
- toNotificationsShadeTransition(translateClock = true)
- }
to(Overlays.NotificationsShade) { toNotificationsShadeTransition() }
to(Overlays.QuickSettingsShade) { toQuickSettingsShadeTransition() }
from(Overlays.NotificationsShade, to = Overlays.QuickSettingsShade) {
notificationsShadeToQuickSettingsShadeTransition()
}
from(Scenes.Gone, to = Overlays.NotificationsShade, key = SlightlyFasterShadeCollapse) {
- toNotificationsShadeTransition(translateClock = true, durationScale = 0.9)
+ toNotificationsShadeTransition(durationScale = 0.9)
}
from(Scenes.Gone, to = Overlays.QuickSettingsShade, key = SlightlyFasterShadeCollapse) {
toQuickSettingsShadeTransition(durationScale = 0.9)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 6bdb36331709..3d62151baf2f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -29,10 +29,7 @@ import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.composable.Shade
import kotlin.time.Duration.Companion.milliseconds
-fun TransitionBuilder.toNotificationsShadeTransition(
- translateClock: Boolean = false,
- durationScale: Double = 1.0,
-) {
+fun TransitionBuilder.toNotificationsShadeTransition(durationScale: Double = 1.0) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
swipeSpec =
spring(
@@ -45,11 +42,6 @@ fun TransitionBuilder.toNotificationsShadeTransition(
elevateInContent = Overlays.NotificationsShade,
)
scaleSize(OverlayShade.Elements.Panel, height = 0f)
- // TODO(b/376659778): This is a temporary hack to have a shared element transition with the
- // lockscreen clock. Remove once nested STLs are supported.
- if (!translateClock) {
- translate(ClockElementKeys.smallClockElementKey)
- }
// Avoid translating the status bar with the shade panel.
translate(NotificationsShade.Elements.StatusBar)
// Slide in the shade panel from the top edge.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 8a5c96da5ac6..cfbe6671db02 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -20,6 +20,9 @@ package com.android.systemui.shade.ui.composable
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.PaddingValues
@@ -41,30 +44,51 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.effect.rememberOffsetOverscrollEffect
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.res.R
/** Renders a lightweight shade UI container, as an overlay. */
@Composable
-fun SceneScope.OverlayShade(
+fun ContentScope.OverlayShade(
+ panelAlignment: Alignment,
onScrimClicked: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
- Box(modifier) {
+ // TODO(b/384653288) This should be removed when b/378470603 is done.
+ val idleEffect = rememberOffsetOverscrollEffect(Orientation.Vertical)
+ Box(
+ modifier
+ .overscroll(idleEffect)
+ .nestedScroll(
+ remember {
+ object : NestedScrollConnection {
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ return available
+ }
+ }
+ }
+ )
+ .scrollable(rememberScrollableState { 0f }, Orientation.Vertical, idleEffect)
+ ) {
Scrim(onClicked = onScrimClicked)
- Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = Alignment.TopEnd) {
+ Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = panelAlignment) {
Panel(
modifier =
Modifier.element(OverlayShade.Elements.Panel)
@@ -77,7 +101,7 @@ fun SceneScope.OverlayShade(
}
@Composable
-private fun SceneScope.Scrim(onClicked: () -> Unit, modifier: Modifier = Modifier) {
+private fun ContentScope.Scrim(onClicked: () -> Unit, modifier: Modifier = Modifier) {
Spacer(
modifier =
modifier
@@ -89,7 +113,7 @@ private fun SceneScope.Scrim(onClicked: () -> Unit, modifier: Modifier = Modifie
}
@Composable
-private fun SceneScope.Panel(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
+private fun ContentScope.Panel(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Box(modifier = modifier.clip(OverlayShade.Shapes.RoundedCornerPanel)) {
Spacer(
modifier =
@@ -180,7 +204,6 @@ object OverlayShade {
object Dimensions {
val PanelCornerRadius = 46.dp
- val OverscrollLimit = 32.dp
}
object Shapes {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index fa5f72bc0997..1480db9de701 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -37,12 +37,14 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.CustomAccessibilityAction
@@ -66,6 +68,10 @@ import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState
+import kotlin.math.round
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
@Composable
fun VolumeSlider(
@@ -196,9 +202,17 @@ private fun LegacyVolumeSlider(
)
}
}
-
- // Perform haptics due to UI composition
- hapticsViewModel?.onValueChange(value)
+ var lastDiscreteStep by remember { mutableFloatStateOf(round(value)) }
+ LaunchedEffect(value) {
+ snapshotFlow { value }
+ .map { round(it) }
+ .filter { it != lastDiscreteStep }
+ .distinctUntilChanged()
+ .collect { discreteStep ->
+ lastDiscreteStep = discreteStep
+ hapticsViewModel?.onValueChange(discreteStep)
+ }
+ }
PlatformSlider(
modifier =
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 801a2d6170cc..b76656d78cc4 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -71,7 +71,6 @@ constructor(
}
var hasCustomPositionUpdatedAnimation: Boolean = false
- var migratedClocks: Boolean = false
private val time = Calendar.getInstance()
@@ -228,11 +227,7 @@ constructor(
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
logger.d("onMeasure")
- if (
- migratedClocks &&
- !isSingleLineInternal &&
- MeasureSpec.getMode(heightMeasureSpec) == EXACTLY
- ) {
+ if (!isSingleLineInternal && MeasureSpec.getMode(heightMeasureSpec) == EXACTLY) {
// Call straight into TextView.setTextSize to avoid setting lastUnconstrainedTextSize
val size = min(lastUnconstrainedTextSize, MeasureSpec.getSize(heightMeasureSpec) / 2F)
super.setTextSize(COMPLEX_UNIT_PX, size)
@@ -248,7 +243,7 @@ constructor(
}
}
- if (migratedClocks && hasCustomPositionUpdatedAnimation) {
+ if (hasCustomPositionUpdatedAnimation) {
// Expand width to avoid clock being clipped during stepping animation
val targetWidth = measuredWidth + MeasureSpec.getSize(widthMeasureSpec) / 2
@@ -582,12 +577,10 @@ constructor(
}
override fun onRtlPropertiesChanged(layoutDirection: Int) {
- if (migratedClocks) {
- if (layoutDirection == LAYOUT_DIRECTION_RTL) {
- textAlignment = TEXT_ALIGNMENT_TEXT_END
- } else {
- textAlignment = TEXT_ALIGNMENT_TEXT_START
- }
+ if (layoutDirection == LAYOUT_DIRECTION_RTL) {
+ textAlignment = TEXT_ALIGNMENT_TEXT_END
+ } else {
+ textAlignment = TEXT_ALIGNMENT_TEXT_START
}
super.onRtlPropertiesChanged(layoutDirection)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index ad9eba841c86..74d595ce65e6 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -20,7 +20,6 @@ import android.graphics.Rect
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
-import android.view.View
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import com.android.systemui.customization.R
@@ -55,7 +54,6 @@ class DefaultClockController(
private val layoutInflater: LayoutInflater,
private val resources: Resources,
private val settings: ClockSettings?,
- private val migratedClocks: Boolean = false,
messageBuffers: ClockMessageBuffers? = null,
) : ClockController {
override val smallClock: DefaultClockFaceController
@@ -67,7 +65,6 @@ class DefaultClockController(
private val burmeseLineSpacing =
resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
- protected var onSecondaryDisplay: Boolean = false
override val events: DefaultClockEvents
override val config: ClockConfig by lazy {
@@ -175,10 +172,7 @@ class DefaultClockController(
recomputePadding(targetRegion)
}
- override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {
- this@DefaultClockController.onSecondaryDisplay = onSecondaryDisplay
- recomputePadding(null)
- }
+ override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {}
}
open fun recomputePadding(targetRegion: Rect?) {}
@@ -197,32 +191,11 @@ class DefaultClockController(
override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
init {
- view.migratedClocks = migratedClocks
view.hasCustomPositionUpdatedAnimation = true
animations = LargeClockAnimations(view, 0f, 0f)
}
- override fun recomputePadding(targetRegion: Rect?) {
- if (migratedClocks) {
- return
- }
- // We center the view within the targetRegion instead of within the parent
- // view by computing the difference and adding that to the padding.
- val lp = view.getLayoutParams() as FrameLayout.LayoutParams
- lp.topMargin =
- if (onSecondaryDisplay) {
- // On the secondary display we don't want any additional top/bottom margin.
- 0
- } else {
- val parent = view.parent
- val yDiff =
- if (targetRegion != null && parent is View && parent.isLaidOut())
- targetRegion.centerY() - parent.height / 2f
- else 0f
- (-0.5f * view.bottom + yDiff).toInt()
- }
- view.setLayoutParams(lp)
- }
+ override fun recomputePadding(targetRegion: Rect?) {}
/** See documentation at [AnimatableClockView.offsetGlyphsForStepClockAnimation]. */
fun offsetGlyphsForStepClockAnimation(fromLeft: Int, direction: Int, fraction: Float) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index e8987257bb47..c73e1c33f88a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -47,7 +47,6 @@ class DefaultClockProvider(
val ctx: Context,
val layoutInflater: LayoutInflater,
val resources: Resources,
- private val migratedClocks: Boolean = false,
private val isClockReactiveVariantsEnabled: Boolean = false,
) : ClockProvider {
private var messageBuffers: ClockMessageBuffers? = null
@@ -83,14 +82,7 @@ class DefaultClockProvider(
FLEX_DESIGN,
)
} else {
- DefaultClockController(
- ctx,
- layoutInflater,
- resources,
- settings,
- migratedClocks,
- messageBuffers,
- )
+ DefaultClockController(ctx, layoutInflater, resources, settings, messageBuffers)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 21d41ae744a7..4a47f1bc12bf 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -140,8 +140,8 @@ class FlexClockFaceController(
}
/**
- * targetRegion passed to all customized clock applies counter translationY of
- * KeyguardStatusView and keyguard_large_clock_top_margin from default clock
+ * targetRegion passed to all customized clock applies counter translationY of Keyguard and
+ * keyguard_large_clock_top_margin from default clock
*/
override fun onTargetRegionChanged(targetRegion: Rect?) {
// When a clock needs to be aligned with screen, like weather clock
diff --git a/packages/SystemUI/docs/clock-plugins.md b/packages/SystemUI/docs/clock-plugins.md
index fee82dfcf2e3..813038ee81ec 100644
--- a/packages/SystemUI/docs/clock-plugins.md
+++ b/packages/SystemUI/docs/clock-plugins.md
@@ -43,12 +43,6 @@ present in the source tree, although it will likely be removed in a later patch.
SystemUI event dispatchers to the clock controllers. It maintains a set of event listeners, but
otherwise attempts to do as little work as possible. It does maintain some state where necessary.
-[KeyguardClockSwitchController](../src/com/android/keyguard/KeyguardClockSwitchController.java) is
-the primary controller for the [KeyguardClockSwitch](../src/com/android/keyguard/KeyguardClockSwitch.java),
-which serves as the view parent within SystemUI. Together they ensure the correct clock (either
-large or small) is shown, handle animation between clock sizes, and control some sizing/layout
-parameters for the clocks.
-
### Creating a custom clock
In order to create a custom clock, a partner must:
- Write an implementation of ClockProviderPlugin and the subinterfaces relevant to your use-case.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
index 85bdf9264467..cea1e9600741 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
@@ -163,6 +163,16 @@ class KeyguardDisplayManagerTest : SysuiTestCase() {
}
@Test
+ fun testShow_rearDisplayOuterDefaultActive_occluded() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+
+ whenever(deviceStateHelper.isRearDisplayOuterDefaultActive(secondaryDisplay))
+ .thenReturn(true)
+ whenever(keyguardStateController.isOccluded).thenReturn(true)
+ verify(presentationFactory, never()).create(eq(secondaryDisplay))
+ }
+
+ @Test
fun testShow_presentationCreated() {
displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
index fa5af510fec1..77e386963129 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
@@ -127,6 +127,7 @@ class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
@DisableFlags(
Flags.FLAG_COMMUNAL_HUB,
+ Flags.FLAG_GLANCEABLE_HUB_V2,
Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX,
Flags.FLAG_SCENE_CONTAINER,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 41cc6ee182cf..271cd3a4f202 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.back.domain.interactor
+import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
@@ -31,6 +32,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.CommunalBackActionInteractor
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
@@ -93,6 +95,7 @@ class BackActionInteractorTest : SysuiTestCase() {
@Mock private lateinit var onBackInvokedDispatcher: WindowOnBackInvokedDispatcher
@Mock private lateinit var iStatusBarService: IStatusBarService
@Mock private lateinit var headsUpManager: HeadsUpManager
+ @Mock private lateinit var communalBackActionInteractor: CommunalBackActionInteractor
private val keyguardRepository = FakeKeyguardRepository()
private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor by lazy {
@@ -117,6 +120,7 @@ class BackActionInteractorTest : SysuiTestCase() {
windowRootViewVisibilityInteractor,
shadeBackActionInteractor,
qsController,
+ communalBackActionInteractor,
)
}
@@ -306,6 +310,19 @@ class BackActionInteractorTest : SysuiTestCase() {
verify(shadeBackActionInteractor).onBackProgressed(0.4f)
}
+ @Test
+ @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_BACK_ACTION)
+ fun onBackAction_communalCanBeDismissed_communalBackActionInteractorCalled() {
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ powerInteractor.setAwakeForTest()
+ val callback = getBackInvokedCallback()
+ whenever(communalBackActionInteractor.canBeDismissed()).thenReturn(true)
+ callback.onBackInvoked()
+
+ verify(communalBackActionInteractor).onBackPressed()
+ }
+
private fun getBackInvokedCallback(): OnBackInvokedCallback {
testScope.runCurrent()
val captor = argumentCaptor<OnBackInvokedCallback>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
index 08f139c6a3af..9c9d5adcfcc9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
@@ -51,7 +51,7 @@ class BiometricPromptRequestTest : SysuiTestCase() {
title = title,
subtitle = subtitle,
description = description,
- contentView = contentView
+ contentView = contentView,
),
BiometricUserInfo(USER_ID),
BiometricOperationInfo(OPERATION_ID),
@@ -101,9 +101,7 @@ class BiometricPromptRequestTest : SysuiTestCase() {
val fpPros = fingerprintSensorPropertiesInternal().first()
val request =
BiometricPromptRequest.Biometric(
- promptInfo(
- logoBitmap = logoBitmap,
- ),
+ promptInfo(logoBitmap = logoBitmap),
BiometricUserInfo(USER_ID),
BiometricOperationInfo(OPERATION_ID),
BiometricModalities(fingerprintProperties = fpPros),
@@ -162,7 +160,7 @@ class BiometricPromptRequestTest : SysuiTestCase() {
BiometricUserInfo(USER_ID),
BiometricOperationInfo(OPERATION_ID),
stealth,
- )
+ ),
)
for (request in toCheck) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt
index 370adee44a42..03bf79bc7a38 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.communal
import android.app.StatsManager
import android.app.StatsManager.StatsPullAtomCallback
import android.content.pm.UserInfo
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.util.StatsEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -32,6 +33,7 @@ import com.android.systemui.communal.shared.log.CommunalMetricsLogger
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.shared.system.SysUiStatsLog
@@ -75,10 +77,7 @@ class CommunalMetricsStartableTest : SysuiTestCase() {
// Set up an existing user, which is required for widgets to show
val userInfos = listOf(UserInfo(0, "main", UserInfo.FLAG_MAIN))
userRepository.setUserInfos(userInfos)
- userTracker.set(
- userInfos = userInfos,
- selectedUserIndex = 0,
- )
+ userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
underTest =
CommunalMetricsStartable(
@@ -90,14 +89,16 @@ class CommunalMetricsStartableTest : SysuiTestCase() {
)
}
+ @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
@Test
- fun start_communalFlagDisabled_doNotSetPullAtomCallback() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ fun start_communalFlagDisabled_doNotSetPullAtomCallback() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
- underTest.start()
+ underTest.start()
- verify(statsManager, never()).setPullAtomCallback(anyInt(), anyOrNull(), any(), any())
- }
+ verify(statsManager, never()).setPullAtomCallback(anyInt(), anyOrNull(), any(), any())
+ }
@Test
fun onPullAtom_atomTagDoesNotMatch_pullSkip() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index b66727e492cf..038ea9ccaaa9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -26,8 +26,8 @@ import android.content.res.mainResources
import android.os.UserManager.USER_TYPE_PROFILE_MANAGED
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
@@ -35,10 +35,13 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.model.DisabledReason
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_BACKGROUND_SETTING
+import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.nullable
@@ -51,15 +54,21 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos =
testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
private val testScope = kosmos.testScope
private lateinit var underTest: CommunalSettingsRepository
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags!!)
+ }
+
@Before
fun setUp() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -164,17 +173,17 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_INVALID_USER)
}
- @EnableFlags(FLAG_COMMUNAL_HUB)
+ @EnableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
fun classicFlagIsDisabled() =
- testScope.runTest {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ kosmos.runTest {
+ setCommunalV2Enabled(false)
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isFalse()
assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
}
- @DisableFlags(FLAG_COMMUNAL_HUB)
+ @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
fun communalHubFlagIsDisabled() =
testScope.runTest {
@@ -295,6 +304,34 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
}
}
+ @Test
+ fun screensaverDisabledByUser() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
+
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 0,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(enabledState).isFalse()
+ }
+
+ @Test
+ fun screensaverEnabledByUser() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
+
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 1,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(enabledState).isTrue()
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
@@ -310,5 +347,11 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
val WORK_PROFILE =
UserInfo(10, "work", /* iconPath= */ "", /* flags= */ 0, USER_TYPE_PROFILE_MANAGED)
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(FLAG_GLANCEABLE_HUB_V2)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt
new file mode 100644
index 000000000000..c365f1cb3872
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.communal.domain.interactor
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class CommunalBackActionInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ private var Kosmos.underTest by Fixture { communalBackActionInteractor }
+
+ @Test
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ fun communalShowing_canBeDismissed() =
+ kosmos.runTest {
+ setCommunalAvailable(true)
+ assertThat(underTest.canBeDismissed()).isEqualTo(false)
+ communalInteractor.changeScene(CommunalScenes.Communal, "test")
+ runCurrent()
+ assertThat(underTest.canBeDismissed()).isEqualTo(true)
+ }
+
+ @Test
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ fun onBackPressed_invokesSceneChange() =
+ kosmos.runTest {
+ underTest.onBackPressed()
+ runCurrent()
+ assertThat(communalSceneRepository.currentScene.value).isEqualTo(CommunalScenes.Blank)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index b9e646fee98f..7ae0577bd289 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -35,6 +35,7 @@ import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID
import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
@@ -232,7 +233,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun isCommunalAvailable_communalDisabled_false() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB)
+ mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 0bfcd242828d..8a9c42d9b64e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -130,19 +130,6 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
}
@Test
- fun tutorialState_startedAndCommunalSceneShowing_stateWillNotUpdate() =
- testScope.runTest {
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
-
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
- goToCommunal()
-
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
- }
-
- @Test
fun tutorialState_completedAndCommunalSceneShowing_stateWillNotUpdate() =
testScope.runTest {
val tutorialSettingState by
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
index 88206850eb60..b78080885b0a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.ui.viewmodel
import android.platform.test.annotations.EnableFlags
+import android.provider.Settings
import android.service.dream.dreamManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -29,12 +30,16 @@ import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.policy.batteryController
import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
@@ -56,10 +61,9 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
}
@Test
- fun shouldShowDreamButtonOnHub_trueWhenCanDream() =
+ fun shouldShowDreamButtonOnHub_trueWhenPluggedIn() =
with(kosmos) {
runTest {
- whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
whenever(batteryController.isPluggedIn()).thenReturn(true)
val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
@@ -68,11 +72,10 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
}
@Test
- fun shouldShowDreamButtonOnHub_falseWhenCannotDream() =
+ fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
with(kosmos) {
runTest {
- whenever(dreamManager.canStartDreaming(any())).thenReturn(false)
- whenever(batteryController.isPluggedIn()).thenReturn(true)
+ whenever(batteryController.isPluggedIn()).thenReturn(false)
val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
assertThat(shouldShowButton).isFalse()
@@ -80,25 +83,40 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
}
@Test
- fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
+ fun onShowDreamButtonTap_dreamsEnabled_startsDream() =
with(kosmos) {
runTest {
- whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
- whenever(batteryController.isPluggedIn()).thenReturn(false)
+ val currentUser = fakeUserRepository.asMainUser()
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 1,
+ currentUser.id,
+ )
+ runCurrent()
- val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
- assertThat(shouldShowButton).isFalse()
+ underTest.onShowDreamButtonTap()
+ runCurrent()
+
+ verify(dreamManager).startDream()
}
}
@Test
- fun onShowDreamButtonTap_startsDream() =
+ fun onShowDreamButtonTap_dreamsDisabled_startsActivity() =
with(kosmos) {
runTest {
+ val currentUser = fakeUserRepository.asMainUser()
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 0,
+ currentUser.id,
+ )
+ runCurrent()
+
underTest.onShowDreamButtonTap()
runCurrent()
- verify(dreamManager).startDream()
+ verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt())
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ControlActionCoordinatorImplTest.kt
index 928514657257..c8661cf59051 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ControlActionCoordinatorImplTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.controls.ui
+import android.view.HapticFeedbackConstants
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.view.HapticFeedbackConstants
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.controls.ControlsMetricsLogger
@@ -29,6 +29,7 @@ import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.wm.shell.taskview.TaskViewFactory
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,31 +46,20 @@ import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.Optional
@SmallTest
@RunWith(AndroidJUnit4::class)
class ControlActionCoordinatorImplTest : SysuiTestCase() {
- @Mock
- private lateinit var vibratorHelper: VibratorHelper
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var bgExecutor: DelayableExecutor
- @Mock
- private lateinit var uiExecutor: DelayableExecutor
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var broadcastSender: BroadcastSender
- @Mock
- private lateinit var taskViewFactory: Optional<TaskViewFactory>
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private lateinit var cvh: ControlViewHolder
- @Mock
- private lateinit var metricsLogger: ControlsMetricsLogger
- @Mock
- private lateinit var controlsSettingsDialogManager: ControlsSettingsDialogManager
+ @Mock private lateinit var vibratorHelper: VibratorHelper
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var bgExecutor: DelayableExecutor
+ @Mock private lateinit var uiExecutor: DelayableExecutor
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var broadcastSender: BroadcastSender
+ @Mock private lateinit var taskViewFactory: Optional<TaskViewFactory>
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var cvh: ControlViewHolder
+ @Mock private lateinit var metricsLogger: ControlsMetricsLogger
+ @Mock private lateinit var controlsSettingsDialogManager: ControlsSettingsDialogManager
companion object {
fun <T> any(): T = Mockito.any<T>()
@@ -89,18 +79,21 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
controlsSettingsRepository.setCanShowControlsInLockscreen(true)
- coordinator = spy(ControlActionCoordinatorImpl(
- mContext,
- bgExecutor,
- uiExecutor,
- activityStarter,
- broadcastSender,
- keyguardStateController,
- taskViewFactory,
- metricsLogger,
- vibratorHelper,
- controlsSettingsRepository,
- ))
+ coordinator =
+ spy(
+ ControlActionCoordinatorImpl(
+ mContext,
+ bgExecutor,
+ uiExecutor,
+ activityStarter,
+ broadcastSender,
+ keyguardStateController,
+ taskViewFactory,
+ metricsLogger,
+ vibratorHelper,
+ controlsSettingsRepository,
+ )
+ )
coordinator.activityContext = mContext
`when`(cvh.cws.ci.controlId).thenReturn(ID)
@@ -198,19 +191,15 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
fun drag_isEdge_performsSegmentTickHaptics() {
coordinator.drag(cvh, true)
- verify(vibratorHelper).performHapticFeedback(
- any(),
- eq(HapticFeedbackConstants.SEGMENT_TICK)
- )
+ verify(vibratorHelper)
+ .performHapticFeedback(any(), eq(HapticFeedbackConstants.SEGMENT_TICK))
}
@Test
fun drag_isNotEdge_performsFrequentTickHaptics() {
coordinator.drag(cvh, false)
- verify(vibratorHelper).performHapticFeedback(
- any(),
- eq(HapticFeedbackConstants.SEGMENT_FREQUENT_TICK)
- )
+ verify(vibratorHelper)
+ .performHapticFeedback(any(), eq(HapticFeedbackConstants.SEGMENT_FREQUENT_TICK))
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index b07097d61b96..5921e9479bd9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -92,10 +92,10 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mockito
import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.isNull
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.firstValue
@@ -768,7 +768,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
runCurrent()
verify(mDreamOverlayCallback).onRedirectWake(true)
client.onWakeRequested()
- verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), any(), isNull())
+ verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), any(), anyOrNull())
verify(mUiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt
index c950523f7854..c950523f7854 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
index 9cfd328a9484..f2a6c11b872e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
@@ -36,6 +36,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@@ -43,6 +44,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
+@Ignore("b/384284415")
class ContextualEducationRepositoryTest : SysuiTestCase() {
private lateinit var underTest: UserContextualEducationRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index ee3e241b5754..56e8185ab580 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -81,7 +81,7 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() {
// Then
verify(cameraGestureHelper)
.launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true), result)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index 50ac26196978..fde9b8ce6a50 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -197,7 +197,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
val dndMode = currentModes!!.single()
assertThat(dndMode.isActive).isFalse()
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
}
@Test
@@ -222,7 +222,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
)
// then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
assertEquals(ZEN_MODE_OFF, spyZenMode.value)
assertNull(spyConditionId.value)
}
@@ -244,7 +244,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
val dndMode = currentModes!!.single()
assertThat(dndMode.isActive).isTrue()
assertThat(zenModeRepository.getModeActiveDuration(dndMode.id)).isNull()
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
}
@Test
@@ -268,7 +268,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
)
// then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
assertNull(spyConditionId.value)
}
@@ -285,7 +285,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
val result = underTest.onTriggered(null)
runCurrent()
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
val dndMode = currentModes!!.single()
assertThat(dndMode.isActive).isTrue()
assertThat(zenModeRepository.getModeActiveDuration(dndMode.id))
@@ -313,7 +313,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
)
// then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
assertEquals(conditionUri, spyConditionId.value)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt
new file mode 100644
index 000000000000..18946f9d7e07
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.data.quickaffordance
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.domain.interactor.keyguardQuickAffordanceHapticViewModelFactory
+import com.android.systemui.keyguard.domain.interactor.keyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceHapticViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class KeyguardQuickAffordanceHapticViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
+ private val configKey = "$slotId::home"
+ private val keyguardQuickAffordanceInteractor = kosmos.keyguardQuickAffordanceInteractor
+ private val viewModelFlow =
+ MutableStateFlow(KeyguardQuickAffordanceViewModel(configKey = configKey, slotId = slotId))
+
+ private val underTest =
+ kosmos.keyguardQuickAffordanceHapticViewModelFactory.create(viewModelFlow)
+
+ @Test
+ fun whenLaunchingFromTriggeredResult_hapticStateIsLaunch() =
+ testScope.runTest {
+ // GIVEN that the result from triggering the affordance launched an activity or dialog
+ val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+ keyguardQuickAffordanceInteractor.setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(true, configKey)
+ )
+ runCurrent()
+
+ // THEN the haptic state indicates that a launch haptics must play
+ assertThat(hapticState)
+ .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.LAUNCH)
+ }
+
+ @Test
+ fun whenNotLaunchFromTriggeredResult_hapticStateDoesNotEmit() =
+ testScope.runTest {
+ // GIVEN that the result from triggering the affordance did not launch an activity or
+ // dialog
+ val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+ keyguardQuickAffordanceInteractor.setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(false, configKey)
+ )
+ runCurrent()
+
+ // THEN there is no haptic state to play any feedback
+ assertThat(hapticState)
+ .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.NO_HAPTICS)
+ }
+
+ @Test
+ fun onQuickAffordanceTogglesToActivated_hapticStateIsToggleOn() =
+ testScope.runTest {
+ // GIVEN that an affordance toggles from deactivated to activated
+ val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+ toggleQuickAffordance(on = true)
+
+ // THEN the haptic state reflects that a toggle on haptics should play
+ assertThat(hapticState)
+ .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.TOGGLE_ON)
+ }
+
+ @Test
+ fun onQuickAffordanceTogglesToDeactivated_hapticStateIsToggleOff() =
+ testScope.runTest {
+ // GIVEN that an affordance toggles from activated to deactivated
+ val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+ toggleQuickAffordance(on = false)
+
+ // THEN the haptic state reflects that a toggle off haptics should play
+ assertThat(hapticState)
+ .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.TOGGLE_OFF)
+ }
+
+ private fun TestScope.toggleQuickAffordance(on: Boolean) {
+ underTest.updateActivatedHistory(!on)
+ runCurrent()
+ underTest.updateActivatedHistory(on)
+ runCurrent()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
index b15352bfe6ab..173b4e56075c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
@@ -49,14 +49,10 @@ import org.mockito.MockitoAnnotations
class MuteQuickAffordanceConfigTest : SysuiTestCase() {
private lateinit var underTest: MuteQuickAffordanceConfig
- @Mock
- private lateinit var ringerModeTracker: RingerModeTracker
- @Mock
- private lateinit var audioManager: AudioManager
- @Mock
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var userFileManager: UserFileManager
+ @Mock private lateinit var ringerModeTracker: RingerModeTracker
+ @Mock private lateinit var audioManager: AudioManager
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var userFileManager: UserFileManager
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@@ -70,9 +66,12 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
whenever(userTracker.userContext).thenReturn(context)
whenever(userFileManager.getSharedPreferences(any(), any(), any()))
- .thenReturn(context.getSharedPreferences("mutequickaffordancetest", Context.MODE_PRIVATE))
+ .thenReturn(
+ context.getSharedPreferences("mutequickaffordancetest", Context.MODE_PRIVATE)
+ )
- underTest = MuteQuickAffordanceConfig(
+ underTest =
+ MuteQuickAffordanceConfig(
context,
userTracker,
userFileManager,
@@ -81,64 +80,71 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
testScope.backgroundScope,
testDispatcher,
testDispatcher,
- )
+ )
}
@Test
- fun pickerState_volumeFixed_notAvailable() = testScope.runTest {
- //given
- whenever(audioManager.isVolumeFixed).thenReturn(true)
+ fun pickerState_volumeFixed_notAvailable() =
+ testScope.runTest {
+ // given
+ whenever(audioManager.isVolumeFixed).thenReturn(true)
- //when
- val result = underTest.getPickerScreenState()
+ // when
+ val result = underTest.getPickerScreenState()
- //then
- assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice, result)
- }
+ // then
+ assertEquals(
+ KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice,
+ result,
+ )
+ }
@Test
- fun pickerState_volumeNotFixed_available() = testScope.runTest {
- //given
- whenever(audioManager.isVolumeFixed).thenReturn(false)
+ fun pickerState_volumeNotFixed_available() =
+ testScope.runTest {
+ // given
+ whenever(audioManager.isVolumeFixed).thenReturn(false)
- //when
- val result = underTest.getPickerScreenState()
+ // when
+ val result = underTest.getPickerScreenState()
- //then
- assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default(), result)
- }
+ // then
+ assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default(), result)
+ }
@Test
- fun triggered_stateWasPreviouslyNORMAL_currentlySILENT_moveToPreviousState() = testScope.runTest {
- //given
- val ringerModeCapture = argumentCaptor<Int>()
- whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
- underTest.onTriggered(null)
- whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
-
- //when
- val result = underTest.onTriggered(null)
- runCurrent()
- verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
-
- //then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
- assertEquals(AudioManager.RINGER_MODE_NORMAL, ringerModeCapture.value)
- }
+ fun triggered_stateWasPreviouslyNORMAL_currentlySILENT_moveToPreviousState() =
+ testScope.runTest {
+ // given
+ val ringerModeCapture = argumentCaptor<Int>()
+ whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+ underTest.onTriggered(null)
+ whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
+
+ // when
+ val result = underTest.onTriggered(null)
+ runCurrent()
+ verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
+
+ // then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
+ assertEquals(AudioManager.RINGER_MODE_NORMAL, ringerModeCapture.value)
+ }
@Test
- fun triggered_stateIsNotSILENT_moveToSILENTringer() = testScope.runTest {
- //given
- val ringerModeCapture = argumentCaptor<Int>()
- whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
-
- //when
- val result = underTest.onTriggered(null)
- runCurrent()
- verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
-
- //then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
- assertEquals(AudioManager.RINGER_MODE_SILENT, ringerModeCapture.value)
- }
-} \ No newline at end of file
+ fun triggered_stateIsNotSILENT_moveToSILENTringer() =
+ testScope.runTest {
+ // given
+ val ringerModeCapture = argumentCaptor<Int>()
+ whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+
+ // when
+ val result = underTest.onTriggered(null)
+ runCurrent()
+ verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
+
+ // then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
+ assertEquals(AudioManager.RINGER_MODE_SILENT, ringerModeCapture.value)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index e9b36b8b3b57..9bdc363b3a38 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -88,9 +88,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
Icon.Loaded(
drawable = ICON,
contentDescription =
- ContentDescription.Resource(
- res = R.string.accessibility_wallet_button,
- ),
+ ContentDescription.Resource(res = R.string.accessibility_wallet_button),
)
)
}
@@ -118,9 +116,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
Icon.Loaded(
drawable = ICON,
contentDescription =
- ContentDescription.Resource(
- res = R.string.accessibility_wallet_button,
- ),
+ ContentDescription.Resource(res = R.string.accessibility_wallet_button),
)
)
}
@@ -163,13 +159,9 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
}
assertThat(underTest.onTriggered(expandable))
- .isEqualTo(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled)
+ .isEqualTo(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true))
verify(walletController)
- .startQuickAccessUiIntent(
- activityStarter,
- animationController,
- /* hasCard= */ true,
- )
+ .startQuickAccessUiIntent(activityStarter, animationController, /* hasCard= */ true)
}
@Test
@@ -184,9 +176,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Test
fun getPickerScreenState_unavailable() =
testScope.runTest {
- setUpState(
- isWalletServiceAvailable = false,
- )
+ setUpState(isWalletServiceAvailable = false)
assertThat(underTest.getPickerScreenState())
.isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
@@ -195,9 +185,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Test
fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() =
testScope.runTest {
- setUpState(
- isWalletFeatureAvailable = false,
- )
+ setUpState(isWalletFeatureAvailable = false)
assertThat(underTest.getPickerScreenState())
.isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
@@ -206,9 +194,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Test
fun getPickerScreenState_disabledWhenThereIsNoCard() =
testScope.runTest {
- setUpState(
- hasSelectedCard = false,
- )
+ setUpState(hasSelectedCard = false)
assertThat(underTest.getPickerScreenState())
.isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
@@ -219,7 +205,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
isWalletServiceAvailable: Boolean = true,
isWalletQuerySuccessful: Boolean = true,
hasSelectedCard: Boolean = true,
- cardType: Int = WalletCard.CARD_TYPE_UNKNOWN
+ cardType: Int = WalletCard.CARD_TYPE_UNKNOWN,
) {
val walletClient: QuickAccessWalletClient = mock()
whenever(walletClient.tileIcon).thenReturn(ICON)
@@ -242,11 +228,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
/*cardType= */ cardType,
/*cardImage= */ mock(),
/*contentDescription= */ CARD_DESCRIPTION,
- /*pendingIntent= */ mock()
+ /*pendingIntent= */ mock(),
)
.build()
),
- 0
+ 0,
)
} else {
GetWalletCardsResponse(emptyList(), 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 46d1ebe75899..9de0215022e9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -764,6 +764,28 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
assertThat(launchingAffordance).isFalse()
}
+ @Test
+ fun onQuickAffordanceTriggered_updatesLaunchingFromTriggeredResult() =
+ testScope.runTest {
+ // WHEN selecting and triggering a quick affordance at a slot
+ val key = homeControls.key
+ val slot = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
+ val encodedKey = "$slot::$key"
+ val actionLaunched = true
+ homeControls.onTriggeredResult =
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(actionLaunched)
+ underTest.select(slot, key)
+ runCurrent()
+ underTest.onQuickAffordanceTriggered(encodedKey, expandable = null, slot)
+
+ // THEN the latest triggered result shows that an action launched for the same key and
+ // slot
+ val launchingFromTriggeredResult by
+ collectLastValue(underTest.launchingFromTriggeredResult)
+ assertThat(launchingFromTriggeredResult?.launched).isEqualTo(actionLaunched)
+ assertThat(launchingFromTriggeredResult?.configKey).isEqualTo(encodedKey)
+ }
+
companion object {
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
private val ICON: Icon =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
index ad5eeabf83d2..26fe379f00bf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -118,6 +119,50 @@ class LockscreenSceneTransitionInteractorTest : SysuiTestCase() {
)
}
+ /** STL: Ls -> overlay, then settle with Idle(overlay). */
+ @Test
+ fun transition_overlay_from_ls_scene_end_in_gone() =
+ testScope.runTest {
+ sceneTransitions.value =
+ ObservableTransitionState.Transition.ShowOrHideOverlay(
+ overlay = Overlays.NotificationsShade,
+ fromContent = Scenes.Lockscreen,
+ toContent = Overlays.NotificationsShade,
+ currentScene = Scenes.Lockscreen,
+ currentOverlays = flowOf(emptySet()),
+ progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ previewProgress = flowOf(0f),
+ isInPreviewStage = flowOf(false),
+ )
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(step = currentStep!!, state = TransitionState.RUNNING, progress = 0.4f)
+
+ sceneTransitions.value =
+ ObservableTransitionState.Idle(
+ Scenes.Lockscreen,
+ setOf(Overlays.NotificationsShade),
+ )
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
/**
* STL: Ls -> Gone, then settle with Idle(Ls). KTF in this scenario needs to invert the
* transition LS -> UNDEFINED to UNDEFINED -> LS as there is no mechanism in KTF to
@@ -259,6 +304,47 @@ class LockscreenSceneTransitionInteractorTest : SysuiTestCase() {
)
}
+ /** STL: Ls with overlay, then settle with Idle(Ls). */
+ @Test
+ fun transition_overlay_to_ls_scene_end_in_ls() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value =
+ ObservableTransitionState.Transition.ShowOrHideOverlay(
+ overlay = Overlays.NotificationsShade,
+ fromContent = Overlays.NotificationsShade,
+ toContent = Scenes.Lockscreen,
+ currentScene = Scenes.Lockscreen,
+ currentOverlays = flowOf(setOf(Overlays.NotificationsShade)),
+ progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ previewProgress = flowOf(0f),
+ isInPreviewStage = flowOf(false),
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(step = currentStep!!, state = TransitionState.RUNNING, progress = 0.4f)
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
/** STL: Gone -> Ls (AOD), will transition to AOD once */
@Test
fun transition_to_ls_scene_with_changed_next_scene_is_respected_just_once() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 0e3b03f74c02..be504cc0f704 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -18,11 +18,8 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.DisableSceneContainer
@@ -75,7 +72,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
private val burnInFlow = MutableStateFlow(BurnInModel())
@Before
- @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@DisableSceneContainer
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -219,50 +215,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
}
@Test
- @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun translationAndScale_whenFullyDozing_MigrationFlagOff_staysOutOfTopInset() =
- testScope.runTest {
- underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
- val movement by collectLastValue(underTest.movement)
- assertThat(movement?.translationX).isEqualTo(0)
-
- // Set to dozing (on AOD)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ),
- validateStep = false,
- )
-
- // Trigger a change to the burn-in model
- burnInFlow.value = BurnInModel(translationX = 20, translationY = -30, scale = 0.5f)
- assertThat(movement?.translationX).isEqualTo(20)
- // -20 instead of -30, due to inset of 80
- assertThat(movement?.translationY).isEqualTo(-20)
- assertThat(movement?.scale).isEqualTo(0.5f)
- assertThat(movement?.scaleClockOnly).isEqualTo(true)
-
- // Set to the beginning of GONE->AOD transition
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ),
- validateStep = false,
- )
- assertThat(movement?.translationX).isEqualTo(0)
- assertThat(movement?.translationY).isEqualTo(0)
- assertThat(movement?.scale).isEqualTo(1f)
- assertThat(movement?.scaleClockOnly).isEqualTo(true)
- }
-
- @Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_whenFullyDozing_MigrationFlagOn_staysOutOfTopInset() =
testScope.runTest {
underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
@@ -334,7 +286,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
@DisableSceneContainer
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_sceneContainerOff_weatherLargeClock() =
testBurnInViewModelForClocks(
isSmallClock = false,
@@ -344,7 +295,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
@DisableSceneContainer
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_sceneContainerOff_weatherSmallClock() =
testBurnInViewModelForClocks(
isSmallClock = true,
@@ -354,7 +304,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
@DisableSceneContainer
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_sceneContainerOff_nonWeatherLargeClock() =
testBurnInViewModelForClocks(
isSmallClock = false,
@@ -364,7 +313,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
@DisableSceneContainer
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_sceneContainerOff_nonWeatherSmallClock() =
testBurnInViewModelForClocks(
isSmallClock = true,
@@ -373,7 +321,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
)
@Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@EnableSceneContainer
fun translationAndScale_sceneContainerOn_weatherLargeClock() =
testBurnInViewModelForClocks(
@@ -383,7 +330,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
)
@Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@EnableSceneContainer
fun translationAndScale_sceneContainerOn_weatherSmallClock() =
testBurnInViewModelForClocks(
@@ -393,7 +339,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
)
@Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@EnableSceneContainer
fun translationAndScale_sceneContainerOn_nonWeatherLargeClock() =
testBurnInViewModelForClocks(
@@ -403,7 +348,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
)
@Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@EnableSceneContainer
@Ignore("b/367659687")
fun translationAndScale_sceneContainerOn_nonWeatherSmallClock() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 95ffc962797d..789477e38b55 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -19,12 +19,10 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.view.View
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -73,7 +71,6 @@ import platform.test.runner.parameterized.Parameters
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index 004aeb069a14..3d9fb0a9be16 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -56,6 +56,7 @@ import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -84,6 +85,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
FakeExecutor mMainExecutor;
FakeExecutor mBackgroundExecutor;
DeviceConfigProxyFake mDeviceConfigProxyFake;
+ FakeShadeDialogContextInteractor mFakeShadeDialogContextInteractor;
@Mock
IActivityManager mIActivityManager;
@@ -118,6 +120,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
+ mFakeShadeDialogContextInteractor = new FakeShadeDialogContextInteractor(mContext);
mDeviceConfigProxyFake = new DeviceConfigProxyFake();
mSystemClock = new FakeSystemClock();
mMainExecutor = new FakeExecutor(mSystemClock);
@@ -346,7 +349,6 @@ public class FgsManagerControllerTest extends SysuiTestCase {
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
"true", false);
FgsManagerController fmc = new FgsManagerControllerImpl(
- mContext,
mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
@@ -359,7 +361,8 @@ public class FgsManagerControllerTest extends SysuiTestCase {
mDialogTransitionAnimator,
mBroadcastDispatcher,
mDumpManager,
- mSystemUIDialogFactory
+ mSystemUIDialogFactory,
+ mFakeShadeDialogContextInteractor
);
fmc.init();
Assert.assertTrue(fmc.getIncludesUserVisibleJobs());
@@ -374,7 +377,6 @@ public class FgsManagerControllerTest extends SysuiTestCase {
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
"false", false);
fmc = new FgsManagerControllerImpl(
- mContext,
mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
@@ -387,7 +389,8 @@ public class FgsManagerControllerTest extends SysuiTestCase {
mDialogTransitionAnimator,
mBroadcastDispatcher,
mDumpManager,
- mSystemUIDialogFactory
+ mSystemUIDialogFactory,
+ mFakeShadeDialogContextInteractor
);
fmc.init();
Assert.assertFalse(fmc.getIncludesUserVisibleJobs());
@@ -487,7 +490,6 @@ public class FgsManagerControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(BroadcastReceiver.class);
FgsManagerController result = new FgsManagerControllerImpl(
- mContext,
mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
@@ -500,7 +502,8 @@ public class FgsManagerControllerTest extends SysuiTestCase {
mDialogTransitionAnimator,
mBroadcastDispatcher,
mDumpManager,
- mSystemUIDialogFactory
+ mSystemUIDialogFactory,
+ mFakeShadeDialogContextInteractor
);
result.init();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt
index a8e390c25a4d..a8e390c25a4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index fbbdc46a7873..0e823ccf1df5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -36,6 +36,7 @@ import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.DataSaverController
import com.android.systemui.util.mockito.whenever
@@ -96,6 +97,7 @@ class DataSaverTileTest(flags: FlagsParameterization) : SysuiTestCase() {
dataSaverController,
mDialogTransitionAnimator,
systemUIDialogFactory,
+ FakeShadeDialogContextInteractor(mContext),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index b88861756889..5c6657b98801 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -8,6 +8,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -231,7 +232,8 @@ public class InternetAdapterTest extends SysuiTestCase {
mViewHolder.onWifiClick(mWifiEntry, mock(View.class));
- verify(mSpyContext).startActivity(any());
+ verify(mInternetDialogController).startActivityForDialog(any());
+ verify(mSpyContext, never()).startActivity(any());
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index 039a1dc05c79..518a0a5a1446 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PseudoGridView
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -84,6 +85,7 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
mDialogTransitionAnimator,
uiEventLogger,
dialogFactory,
+ FakeShadeDialogContextInteractor(mContext),
)
}
@@ -91,32 +93,32 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
fun showDialog_callsDialogShow() {
val launchController = mock<DialogTransitionAnimator.Controller>()
`when`(launchExpandable.dialogTransitionController(any())).thenReturn(launchController)
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(mDialogTransitionAnimator).show(eq(dialog), eq(launchController), anyBoolean())
verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN)
}
@Test
fun dialog_showForAllUsers() {
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog).setShowForAllUsers(true)
}
@Test
fun dialog_cancelOnTouchOutside() {
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog).setCanceledOnTouchOutside(true)
}
@Test
fun adapterAndGridLinked() {
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(userDetailViewAdapter).linkToViewGroup(any<PseudoGridView>())
}
@Test
fun doneButtonLogsCorrectly() {
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog).setPositiveButton(anyInt(), capture(clickCaptor))
@@ -129,7 +131,7 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
fun clickSettingsButton_noFalsing_opensSettings() {
`when`(falsingManager.isFalseTap(anyInt())).thenReturn(false)
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog)
.setNeutralButton(anyInt(), capture(clickCaptor), eq(false) /* dismissOnClick */)
@@ -150,7 +152,7 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
fun clickSettingsButton_Falsing_notOpensSettings() {
`when`(falsingManager.isFalseTap(anyInt())).thenReturn(true)
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog)
.setNeutralButton(anyInt(), capture(clickCaptor), eq(false) /* dismissOnClick */)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index e56b965d9402..3187cca6ca45 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -37,7 +37,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModel
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
@@ -48,9 +47,9 @@ import com.android.systemui.keyguard.ui.viewmodel.lockscreenUserActionsViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.currentValue
-import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.verifyCurrent
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -77,12 +76,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.verify
/**
* Integration test cases for the Scene Framework.
@@ -137,10 +133,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
sceneContainerViewModel.activateIn(testScope)
assertWithMessage("Initial scene key mismatch!")
- .that(sceneContainerViewModel.currentScene.value)
+ .that(currentValue(sceneContainerViewModel.currentScene))
.isEqualTo(sceneContainerConfig.initialSceneKey)
assertWithMessage("Initial scene container visibility mismatch!")
- .that(sceneContainerViewModel.isVisible)
+ .that(currentValue { sceneContainerViewModel.isVisible })
.isTrue()
}
@@ -337,7 +333,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
.that(bouncerActionButton)
.isNotNull()
kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!)
- runCurrent()
// TODO(b/369765704): Assert that an activity was started once we use ActivityStarter.
}
@@ -358,9 +353,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
.that(bouncerActionButton)
.isNotNull()
kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!)
- runCurrent()
- verify(mockTelecomManager).showInCallScreen(any())
+ verifyCurrent(mockTelecomManager).showInCallScreen(any())
}
@Test
@@ -413,7 +407,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
* the UI must gradually transition between scenes.
*/
private fun Kosmos.getCurrentSceneInUi(): SceneKey {
- return when (val state = transitionState.value) {
+ return when (val state = currentValue(transitionState)) {
is ObservableTransitionState.Idle -> state.currentScene
is ObservableTransitionState.Transition.ChangeScene -> state.fromScene
is ObservableTransitionState.Transition.ShowOrHideOverlay -> state.currentScene
@@ -436,7 +430,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
// is not an observable that can trigger a new evaluation.
fakeDeviceEntryRepository.setLockscreenEnabled(enableLockscreen)
fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
- testScope.runCurrent()
}
/** Emulates a phone call in progress. */
@@ -447,7 +440,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
setIsInCall(true)
setCallState(TelephonyManager.CALL_STATE_OFFHOOK)
}
- testScope.runCurrent()
}
/**
@@ -480,24 +472,21 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
- testScope.runCurrent()
// Report progress of transition.
- while (progressFlow.value < 1f) {
+ while (currentValue(progressFlow) < 1f) {
progressFlow.value += 0.2f
- testScope.runCurrent()
}
// End the transition and report the change.
transitionState.value = ObservableTransitionState.Idle(to)
fakeSceneDataSource.unpause(force = true)
- testScope.runCurrent()
assertWithMessage("Visibility mismatch after scene transition from $from to $to!")
- .that(sceneContainerViewModel.isVisible)
+ .that(currentValue { sceneContainerViewModel.isVisible })
.isEqualTo(expectedVisible)
- assertThat(sceneContainerViewModel.currentScene.value).isEqualTo(to)
+ assertThat(currentValue(sceneContainerViewModel.currentScene)).isEqualTo(to)
bouncerSceneJob =
if (to == Scenes.Bouncer) {
@@ -510,7 +499,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
bouncerSceneJob?.cancel()
null
}
- testScope.runCurrent()
}
/**
@@ -556,13 +544,12 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
)
powerInteractor.setAwakeForTest()
- testScope.runCurrent()
}
/** Unlocks the device by entering the correct PIN. Ends up in the Gone scene. */
private fun Kosmos.unlockDevice() {
assertWithMessage("Cannot unlock a device that's already unlocked!")
- .that(deviceEntryInteractor.isUnlocked.value)
+ .that(currentValue(deviceEntryInteractor.isUnlocked))
.isFalse()
emulateUserDrivenTransition(Scenes.Bouncer)
@@ -595,7 +582,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
pinBouncerViewModel.onPinButtonClicked(digit)
}
pinBouncerViewModel.onAuthenticateButtonClicked()
- testScope.runCurrent()
}
/**
@@ -625,26 +611,23 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
}
pinBouncerViewModel.onAuthenticateButtonClicked()
fakeMobileConnectionsRepository.isAnySimSecure.value = false
- testScope.runCurrent()
setAuthMethod(authMethodAfterSimUnlock, enableLockscreen)
- testScope.runCurrent()
}
/** Changes device wakefulness state from asleep to awake, going through intermediary states. */
private fun Kosmos.wakeUpDevice() {
- val wakefulnessModel = powerInteractor.detailedWakefulness.value
+ val wakefulnessModel = currentValue(powerInteractor.detailedWakefulness)
assertWithMessage("Cannot wake up device as it's already awake!")
.that(wakefulnessModel.isAwake())
.isFalse()
powerInteractor.setAwakeForTest()
- testScope.runCurrent()
}
/** Changes device wakefulness state from awake to asleep, going through intermediary states. */
private suspend fun Kosmos.putDeviceToSleep(waitForLock: Boolean = true) {
- val wakefulnessModel = powerInteractor.detailedWakefulness.value
+ val wakefulnessModel = currentValue(powerInteractor.detailedWakefulness)
assertWithMessage("Cannot put device to sleep as it's already asleep!")
.that(wakefulnessModel.isAwake())
.isTrue()
@@ -659,22 +642,18 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
)
.toLong()
)
- } else {
- testScope.runCurrent()
}
}
/** Emulates the dismissal of the IME (soft keyboard). */
private fun Kosmos.dismissIme() {
- (bouncerSceneContentViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
- it.onImeDismissed()
- testScope.runCurrent()
- }
+ (currentValue(bouncerSceneContentViewModel.authMethodViewModel)
+ as? PasswordBouncerViewModel)
+ ?.let { it.onImeDismissed() }
}
private fun Kosmos.introduceLockedSim() {
setAuthMethod(AuthenticationMethodModel.Sim)
fakeMobileConnectionsRepository.isAnySimSecure.value = true
- testScope.runCurrent()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 929537dcf757..0361ffe475a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -212,18 +212,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
}
@Test
- @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testDragDownHelperCalledWhenDraggingDown() =
- testScope.runTest {
- whenever(dragDownHelper.isDraggingDown).thenReturn(true)
- val now = SystemClock.elapsedRealtime()
- val ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0f, 0f, 0 /* meta */)
- underTest.onTouchEvent(ev)
- verify(dragDownHelper).onTouchEvent(ev)
- ev.recycle()
- }
-
- @Test
fun testNoInterceptTouch() =
testScope.runTest {
captureInteractionEventHandler()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index aa8b4f136683..4423426945eb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -117,7 +117,6 @@ class DefaultClockProviderTest : SysuiTestCase() {
verify(mockLargeClockView).onTimeZoneChanged(notNull())
verify(mockSmallClockView).refreshTime()
verify(mockLargeClockView).refreshTime()
- verify(mockLargeClockView).setLayoutParams(any())
}
@Test
@@ -163,7 +162,6 @@ class DefaultClockProviderTest : SysuiTestCase() {
clock.largeClock.events.onFontSettingChanged(200f)
verify(mockLargeClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), eq(200f))
- verify(mockLargeClockView).setLayoutParams(any())
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 165e943a0cc0..40f13bbbf908 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -16,10 +16,12 @@
package com.android.systemui.statusbar.chips.notification.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -71,6 +73,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_noNotifs_empty() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -81,6 +84,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_notifMissingStatusBarChipIconView_empty() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -99,6 +103,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_onePromotedNotif_statusBarIconViewMatches() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -122,6 +127,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_onePromotedNotif_connectedDisplaysFlagEnabled_statusBarIconMatches() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -145,6 +151,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_onePromotedNotif_colorMatches() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -175,6 +182,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_onlyForPromotedNotifs() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -208,6 +216,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_connectedDisplaysFlagEnabled_onlyForPromotedNotifs() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -242,6 +251,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_hasShortCriticalText_usesTextInsteadOfTime() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -272,6 +282,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_noTime_isIconOnly() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -294,6 +305,36 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ fun chips_basicTime_hiddenIfAutomaticallyPromoted() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0])
+ .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_basicTime_isShortTimeDelta() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -322,6 +363,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_countUpTime_isTimer() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -349,6 +391,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_countDownTime_isTimer() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -376,6 +419,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_noHeadsUp_showsTime() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -407,6 +451,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_hasHeadsUpByUser_onlyShowsIcon() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -442,6 +487,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_clickingChipNotifiesInteractor() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
new file mode 100644
index 000000000000..14787e169979
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.featurepods.popups.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarPopupChips
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@EnableFlags(StatusBarPopupChips.FLAG_NAME)
+@RunWith(AndroidJUnit4::class)
+class StatusBarPopupChipsViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.statusBarPopupChipsViewModel
+
+ @Test
+ fun popupChips_allHidden_empty() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.popupChips)
+ assertThat(latest).isEmpty()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index abd0a284ae3d..3359db0a22e6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -43,7 +43,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
-class PromotedNotificationContentExtractorTest : SysuiTestCase() {
+class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val provider =
@@ -54,7 +54,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@Test
@DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun shouldNotExtract_bothFlagsDisabled() {
- val notif = createEntry().also { provider.promotedEntries.add(it) }
+ val notif = createEntry()
val content = extractContent(notif)
assertThat(content).isNull()
}
@@ -63,7 +63,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@EnableFlags(PromotedNotificationUi.FLAG_NAME)
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
fun shouldExtract_promotedNotificationUiFlagEnabled() {
- val entry = createEntry().also { provider.promotedEntries.add(it) }
+ val entry = createEntry()
val content = extractContent(entry)
assertThat(content).isNotNull()
}
@@ -72,7 +72,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
@DisableFlags(PromotedNotificationUi.FLAG_NAME)
fun shouldExtract_statusBarNotifChipsFlagEnabled() {
- val entry = createEntry().also { provider.promotedEntries.add(it) }
+ val entry = createEntry()
val content = extractContent(entry)
assertThat(content).isNotNull()
}
@@ -80,7 +80,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun shouldExtract_bothFlagsEnabled() {
- val entry = createEntry().also { provider.promotedEntries.add(it) }
+ val entry = createEntry()
val content = extractContent(entry)
assertThat(content).isNotNull()
}
@@ -88,22 +88,19 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun shouldNotExtract_providerDidNotPromote() {
- val entry = createEntry().also { provider.promotedEntries.remove(it) }
+ val entry = createEntry(promoted = false)
val content = extractContent(entry)
assertThat(content).isNull()
}
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
- fun extractContent_commonFields() {
- val entry =
- createEntry {
- setSubText(TEST_SUB_TEXT)
- setContentTitle(TEST_CONTENT_TITLE)
- setContentText(TEST_CONTENT_TEXT)
- setShortCriticalText(TEST_SHORT_CRITICAL_TEXT)
- }
- .also { provider.promotedEntries.add(it) }
+ fun extractsContent_commonFields() {
+ val entry = createEntry {
+ setSubText(TEST_SUB_TEXT)
+ setContentTitle(TEST_CONTENT_TITLE)
+ setContentText(TEST_CONTENT_TEXT)
+ }
val content = extractContent(entry)
@@ -117,9 +114,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
@DisableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
fun extractContent_apiFlagOff_shortCriticalTextNotExtracted() {
- val entry =
- createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
- .also { provider.promotedEntries.add(it) }
+ val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
val content = extractContent(entry)
@@ -134,9 +129,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
android.app.Flags.FLAG_API_RICH_ONGOING,
)
fun extractContent_apiFlagOn_shortCriticalTextExtracted() {
- val entry =
- createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
- .also { provider.promotedEntries.add(it) }
+ val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
val content = extractContent(entry)
@@ -151,7 +144,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
android.app.Flags.FLAG_API_RICH_ONGOING,
)
fun extractContent_noShortCriticalTextSet_textIsNull() {
- val entry = createEntry {}.also { provider.promotedEntries.add(it) }
+ val entry = createEntry { setShortCriticalText(null) }
val content = extractContent(entry)
@@ -161,9 +154,8 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
- fun extractContent_fromBigPictureStyle() {
- val entry =
- createEntry { setStyle(BigPictureStyle()) }.also { provider.promotedEntries.add(it) }
+ fun extractsContent_fromBigPictureStyle() {
+ val entry = createEntry { setStyle(BigPictureStyle()) }
val content = extractContent(entry)
@@ -174,8 +166,7 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromBigTextStyle() {
- val entry =
- createEntry { setStyle(BigTextStyle()) }.also { provider.promotedEntries.add(it) }
+ val entry = createEntry { setStyle(BigTextStyle()) }
val content = extractContent(entry)
@@ -187,11 +178,13 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromCallStyle() {
val hangUpIntent =
- PendingIntent.getBroadcast(context, 0, Intent("hangup"), PendingIntent.FLAG_IMMUTABLE)
-
- val entry =
- createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) }
- .also { provider.promotedEntries.add(it) }
+ PendingIntent.getBroadcast(
+ context,
+ 0,
+ Intent("hangup_action"),
+ PendingIntent.FLAG_IMMUTABLE,
+ )
+ val entry = createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) }
val content = extractContent(entry)
@@ -202,11 +195,9 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromProgressStyle() {
- val entry =
- createEntry {
- setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75))
- }
- .also { provider.promotedEntries.add(it) }
+ val entry = createEntry {
+ setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75))
+ }
val content = extractContent(entry)
@@ -220,13 +211,9 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromIneligibleStyle() {
- val entry =
- createEntry {
- setStyle(
- MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON)
- )
- }
- .also { provider.promotedEntries.add(it) }
+ val entry = createEntry {
+ setStyle(MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON))
+ }
val content = extractContent(entry)
@@ -239,9 +226,14 @@ class PromotedNotificationContentExtractorTest : SysuiTestCase() {
return underTest.extractContent(entry, recoveredBuilder)
}
- private fun createEntry(builderBlock: Notification.Builder.() -> Unit = {}): NotificationEntry {
- val notif = Notification.Builder(context, "a").also(builderBlock).build()
- return NotificationEntryBuilder().setNotification(notif).build()
+ private fun createEntry(
+ promoted: Boolean = true,
+ builderBlock: Notification.Builder.() -> Unit = {},
+ ): NotificationEntry {
+ val notif = Notification.Builder(context, "channel").also(builderBlock).build()
+ return NotificationEntryBuilder().setNotification(notif).build().also {
+ provider.shouldPromoteForEntry[it] = promoted
+ }
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 6eb2764165b5..a49a66fe26b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -16,12 +16,18 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -30,14 +36,15 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.app.Person;
import android.content.Context;
+import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.os.Handler;
@@ -61,12 +68,13 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
+import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
@@ -105,7 +113,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Mock private NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
@Mock private HeadsUpStyleProvider mHeadsUpStyleProvider;
@Mock private NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
- @Mock private PromotedNotificationContentExtractor mPromotedNotificationContentExtractor;
+ private final FakePromotedNotificationContentExtractor mPromotedNotificationContentExtractor =
+ new FakePromotedNotificationContentExtractor();
private final SmartReplyStateInflater mSmartReplyStateInflater =
new SmartReplyStateInflater() {
@@ -155,8 +164,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Test
public void testIncreasedHeadsUpBeingUsed() {
- BindParams params = new BindParams();
- params.usesIncreasedHeadsUpHeight = true;
+ BindParams params = new BindParams(false, false, /* usesIncreasedHeadsUpHeight */ true,
+ REDACTION_TYPE_NONE);
Notification.Builder builder = spy(mBuilder);
mNotificationInflater.inflateNotificationViews(
mRow.getEntry(),
@@ -166,14 +175,15 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
+ mContext,
mSmartReplyStateInflater);
verify(builder).createHeadsUpContentView(true);
}
@Test
public void testIncreasedHeightBeingUsed() {
- BindParams params = new BindParams();
- params.usesIncreasedHeight = true;
+ BindParams params = new BindParams(false, /* usesIncreasedHeight */ true, false,
+ REDACTION_TYPE_NONE);
Notification.Builder builder = spy(mBuilder);
mNotificationInflater.inflateNotificationViews(
mRow.getEntry(),
@@ -183,6 +193,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
+ mContext,
mSmartReplyStateInflater);
verify(builder).createContentView(true);
}
@@ -207,7 +218,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mRow.getEntry().getSbn().getNotification().contentView
= new RemoteViews(mContext.getPackageName(), com.android.systemui.res.R.layout.status_bar);
inflateAndWait(true /* expectingException */, mNotificationInflater, FLAG_CONTENT_VIEW_ALL,
- mRow);
+ REDACTION_TYPE_NONE, mRow);
assertTrue(mRow.getPrivateLayout().getChildCount() == 0);
verify(mRow, times(0)).onNotificationUpdated();
}
@@ -227,7 +238,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mRow.getEntry(),
mRow,
FLAG_CONTENT_VIEW_ALL,
- new BindParams(),
+ new BindParams(false, false, false, REDACTION_TYPE_NONE),
false /* forceInflate */,
null /* callback */);
Assert.assertNull(mRow.getEntry().getRunningTask());
@@ -287,7 +298,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mBuilder.setCustomContentView(new RemoteViews(getContext().getPackageName(),
R.layout.custom_view_dark));
RemoteViews decoratedMediaView = mBuilder.createContentView();
- Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
+ assertFalse("The decorated media style doesn't allow a view to be reapplied!",
NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
}
@@ -385,7 +396,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mRow.getPrivateLayout().removeAllViews();
mRow.getEntry().getSbn().getNotification().contentView =
new RemoteViews(mContext.getPackageName(), R.layout.invalid_notification_height);
- inflateAndWait(true, mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+ inflateAndWait(true, mNotificationInflater, FLAG_CONTENT_VIEW_ALL, REDACTION_TYPE_NONE,
+ mRow);
assertEquals(0, mRow.getPrivateLayout().getChildCount());
verify(mRow, times(0)).onNotificationUpdated();
}
@@ -395,12 +407,11 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
public void testExtractsPromotedContent_notWhenBothFlagsDisabled() throws Exception {
final PromotedNotificationContentModel content =
new PromotedNotificationContentModel.Builder("key").build();
- when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, never()).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyZeroExtractCalls();
}
@Test
@@ -410,12 +421,11 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
throws Exception {
final PromotedNotificationContentModel content =
new PromotedNotificationContentModel.Builder("key").build();
- when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyOneExtractCall();
assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
}
@@ -425,12 +435,11 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
public void testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() throws Exception {
final PromotedNotificationContentModel content =
new PromotedNotificationContentModel.Builder("key").build();
- when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyOneExtractCall();
assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
}
@@ -439,36 +448,107 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
public void testExtractsPromotedContent_whenBothFlagsEnabled() throws Exception {
final PromotedNotificationContentModel content =
new PromotedNotificationContentModel.Builder("key").build();
- when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyOneExtractCall();
assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
}
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
public void testExtractsPromotedContent_null() throws Exception {
- when(mPromotedNotificationContentExtractor.extractContent(any(), any())).thenReturn(null);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), null);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyOneExtractCall();
assertNull(mRow.getEntry().getPromotedNotificationContentModel());
}
+ @Test
+ @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+ public void testSensitiveContentPublicView_messageStyle() throws Exception {
+ String displayName = "Display Name";
+ String messageText = "Message Text";
+ String contentText = "Content Text";
+ Icon personIcon = Icon.createWithResource(mContext,
+ com.android.systemui.res.R.drawable.ic_person);
+ Person testPerson = new Person.Builder()
+ .setName(displayName)
+ .setIcon(personIcon)
+ .build();
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(testPerson);
+ messagingStyle.addMessage(new Notification.MessagingStyle.Message(messageText,
+ System.currentTimeMillis(), testPerson));
+ messagingStyle.setConversationType(Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL);
+ messagingStyle.setShortcutIcon(personIcon);
+ Notification messageNotif = new Notification.Builder(mContext).setSmallIcon(
+ com.android.systemui.res.R.drawable.ic_person).setStyle(messagingStyle).build();
+ ExpandableNotificationRow row = mHelper.createRow(messageNotif);
+ inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_SENSITIVE_CONTENT, row);
+ NotificationContentView publicView = row.getPublicLayout();
+ assertNotNull(publicView);
+ // The display name should be included, but not the content or message text
+ assertFalse(hasText(publicView, messageText));
+ assertFalse(hasText(publicView, contentText));
+ assertTrue(hasText(publicView, displayName));
+ }
+
+ @Test
+ @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+ public void testSensitiveContentPublicView_nonMessageStyle() throws Exception {
+ String contentTitle = "Content Title";
+ String contentText = "Content Text";
+ Notification notif = new Notification.Builder(mContext).setSmallIcon(
+ com.android.systemui.res.R.drawable.ic_person)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .build();
+ ExpandableNotificationRow row = mHelper.createRow(notif);
+ inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_SENSITIVE_CONTENT, row);
+ NotificationContentView publicView = row.getPublicLayout();
+ assertNotNull(publicView);
+ assertFalse(hasText(publicView, contentText));
+ assertTrue(hasText(publicView, contentTitle));
+
+ // The standard public view should not use the content title or text
+ inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_PUBLIC, row);
+ publicView = row.getPublicLayout();
+ assertFalse(hasText(publicView, contentText));
+ assertFalse(hasText(publicView, contentTitle));
+ }
+
+ private static boolean hasText(ViewGroup parent, CharSequence text) {
+ for (int i = 0; i < parent.getChildCount(); i++) {
+ View child = parent.getChildAt(i);
+ if (child instanceof ViewGroup) {
+ if (hasText((ViewGroup) child, text)) {
+ return true;
+ }
+ } else if (child instanceof TextView) {
+ return ((TextView) child).getText().toString().contains(text);
+ }
+ }
+ return false;
+ }
+
private static void inflateAndWait(NotificationContentInflater inflater,
@InflationFlag int contentToInflate,
ExpandableNotificationRow row)
throws Exception {
- inflateAndWait(false /* expectingException */, inflater, contentToInflate, row);
+ inflateAndWait(false /* expectingException */, inflater, contentToInflate,
+ REDACTION_TYPE_NONE, row);
}
private static void inflateAndWait(boolean expectingException,
NotificationContentInflater inflater,
@InflationFlag int contentToInflate,
+ @RedactionType int redactionType,
ExpandableNotificationRow row) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
final ExceptionHolder exceptionHolder = new ExceptionHolder();
@@ -496,7 +576,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
row.getEntry(),
row,
contentToInflate,
- new BindParams(),
+ new BindParams(false, false, false, redactionType),
false /* forceInflate */,
callback /* callback */);
assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 18517998096a..f25ba2c93c65 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row
import android.app.Notification
import android.app.Person
import android.content.Context
+import android.graphics.drawable.Icon
import android.os.AsyncTask
import android.os.Build
import android.os.CancellationSignal
@@ -34,10 +35,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
+import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
@@ -45,6 +50,7 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
@@ -67,7 +73,6 @@ import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@@ -110,7 +115,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
return inflatedSmartReplyState
}
}
- private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor = mock()
+ private val promotedNotificationContentExtractor = FakePromotedNotificationContentExtractor()
+ private val conversationNotificationProcessor: ConversationNotificationProcessor = mock()
@Before
fun setUp() {
@@ -127,7 +133,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
NotificationRowContentBinderImpl(
cache,
mock(),
- mock<ConversationNotificationProcessor>(),
+ conversationNotificationProcessor,
mock(),
smartReplyStateInflater,
layoutInflaterFactoryProvider,
@@ -139,14 +145,14 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
fun testIncreasedHeadsUpBeingUsed() {
- val params = BindParams()
- params.usesIncreasedHeadsUpHeight = true
+ val params =
+ BindParams(false, false, /* usesIncreasedHeadsUpHeight */ true, REDACTION_TYPE_NONE)
val builder = spy(builder)
notificationInflater.inflateNotificationViews(
row.entry,
row,
params,
- true /* inflateSynchronously */,
+ true, /* inflateSynchronously */
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
@@ -158,14 +164,13 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
fun testIncreasedHeightBeingUsed() {
- val params = BindParams()
- params.usesIncreasedHeight = true
+ val params = BindParams(false, /* usesIncreasedHeight */ true, false, REDACTION_TYPE_NONE)
val builder = spy(builder)
notificationInflater.inflateNotificationViews(
row.entry,
row,
params,
- true /* inflateSynchronously */,
+ true, /* inflateSynchronously */
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
@@ -194,15 +199,18 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
row.entry.sbn.notification.contentView =
RemoteViews(mContext.packageName, R.layout.status_bar)
inflateAndWait(
- true /* expectingException */,
+ true, /* expectingException */
notificationInflater,
FLAG_CONTENT_VIEW_ALL,
+ REDACTION_TYPE_NONE,
row,
)
Assert.assertTrue(row.privateLayout.childCount == 0)
verify(row, times(0)).onNotificationUpdated()
}
+ @Test fun testInflationOfSensitiveContentPublicView() {}
+
@Test
fun testAsyncTaskRemoved() {
row.entry.abortTask()
@@ -218,8 +226,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
row.entry,
row,
FLAG_CONTENT_VIEW_ALL,
- BindParams(),
- false /* forceInflate */,
+ BindParams(false, false, false, REDACTION_TYPE_NONE),
+ false, /* forceInflate */
null, /* callback */
)
Assert.assertNull(row.entry.runningTask)
@@ -234,7 +242,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
packageContext = mContext,
remoteViews = NewRemoteViews(),
contentModel = NotificationContentModel(headsUpStatusBarModel),
- extractedPromotedNotificationContentModel = null,
+ promotedContent = null,
)
val countDownLatch = CountDownLatch(1)
NotificationRowContentBinderImpl.applyRemoteView(
@@ -432,7 +440,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
mContext.packageName,
com.android.systemui.tests.R.layout.invalid_notification_height,
)
- inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+ inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, REDACTION_TYPE_NONE, row)
Assert.assertEquals(0, row.privateLayout.childCount.toLong())
verify(row, times(0)).onNotificationUpdated()
}
@@ -441,7 +449,13 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
fun testInflatePublicSingleLineView() {
row.publicLayout.removeAllViews()
- inflateAndWait(false, notificationInflater, FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, row)
+ inflateAndWait(
+ false,
+ notificationInflater,
+ FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE,
+ REDACTION_TYPE_NONE,
+ row,
+ )
Assert.assertNotNull(row.publicLayout.mSingleLineView)
Assert.assertTrue(row.publicLayout.mSingleLineView is HybridNotificationView)
}
@@ -449,12 +463,15 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
fun testInflatePublicSingleLineConversationView() {
val testPerson = Person.Builder().setName("Person").build()
+ val style = Notification.MessagingStyle(testPerson)
val messagingBuilder =
Notification.Builder(mContext, "no-id")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text")
- .setStyle(Notification.MessagingStyle(testPerson))
+ .setStyle(style)
+ whenever(conversationNotificationProcessor.processNotification(any(), any(), any()))
+ .thenReturn(style)
val messagingRow = spy(testHelper.createRow(messagingBuilder.build()))
messagingRow.publicLayout.removeAllViews()
@@ -462,6 +479,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
false,
notificationInflater,
FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE,
+ REDACTION_TYPE_NONE,
messagingRow,
)
Assert.assertNotNull(messagingRow.publicLayout.mSingleLineView)
@@ -475,12 +493,11 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_notWhenBothFlagsDisabled() {
val content = PromotedNotificationContentModel.Builder("key").build()
- whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content)
+ promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
- verify(promotedNotificationContentExtractor, never()).extractContent(any(), any())
+ promotedNotificationContentExtractor.verifyZeroExtractCalls()
}
@Test
@@ -488,12 +505,11 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() {
val content = PromotedNotificationContentModel.Builder("key").build()
- whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content)
+ promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
- verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any())
+ promotedNotificationContentExtractor.verifyOneExtractCall()
Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
}
@@ -502,12 +518,11 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@DisableFlags(PromotedNotificationUi.FLAG_NAME)
fun testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() {
val content = PromotedNotificationContentModel.Builder("key").build()
- whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content)
+ promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
- verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any())
+ promotedNotificationContentExtractor.verifyOneExtractCall()
Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
}
@@ -515,15 +530,99 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_whenBothFlagsEnabled() {
val content = PromotedNotificationContentModel.Builder("key").build()
- whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content)
+ promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
- verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any())
+ promotedNotificationContentExtractor.verifyOneExtractCall()
Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
}
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun testExtractsPromotedContent_null() {
+ promotedNotificationContentExtractor.resetForEntry(row.entry, null)
+
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+
+ promotedNotificationContentExtractor.verifyOneExtractCall()
+ Assert.assertNull(row.entry.promotedNotificationContentModel)
+ }
+
+ @Test
+ @Throws(java.lang.Exception::class)
+ @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+ fun testSensitiveContentPublicView_messageStyle() {
+ val displayName = "Display Name"
+ val messageText = "Message Text"
+ val contentText = "Content Text"
+ val personIcon = Icon.createWithResource(mContext, R.drawable.ic_person)
+ val testPerson = Person.Builder().setName(displayName).setIcon(personIcon).build()
+ val messagingStyle = Notification.MessagingStyle(testPerson)
+ messagingStyle.addMessage(
+ Notification.MessagingStyle.Message(messageText, System.currentTimeMillis(), testPerson)
+ )
+ messagingStyle.setConversationType(Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL)
+ messagingStyle.setShortcutIcon(personIcon)
+ val messageNotif =
+ Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_person)
+ .setStyle(messagingStyle)
+ .build()
+ val newRow: ExpandableNotificationRow = testHelper.createRow(messageNotif)
+ inflateAndWait(
+ false,
+ notificationInflater,
+ FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_SENSITIVE_CONTENT,
+ newRow,
+ )
+ // The display name should be included, but not the content or message text
+ val publicView = newRow.publicLayout
+ Assert.assertNotNull(publicView)
+ Assert.assertFalse(hasText(publicView, messageText))
+ Assert.assertFalse(hasText(publicView, contentText))
+ Assert.assertTrue(hasText(publicView, displayName))
+ }
+
+ @Test
+ @Throws(java.lang.Exception::class)
+ @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+ fun testSensitiveContentPublicView_nonMessageStyle() {
+ val contentTitle = "Content Title"
+ val contentText = "Content Text"
+ val notif =
+ Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .build()
+ val newRow: ExpandableNotificationRow = testHelper.createRow(notif)
+ inflateAndWait(
+ false,
+ notificationInflater,
+ FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_SENSITIVE_CONTENT,
+ newRow,
+ )
+ var publicView = newRow.publicLayout
+ Assert.assertNotNull(publicView)
+ Assert.assertFalse(hasText(publicView, contentText))
+ Assert.assertTrue(hasText(publicView, contentTitle))
+
+ // The standard public view should not use the content title or text
+ inflateAndWait(
+ false,
+ notificationInflater,
+ FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_PUBLIC,
+ newRow,
+ )
+ publicView = newRow.publicLayout
+ Assert.assertFalse(hasText(publicView, contentText))
+ Assert.assertFalse(hasText(publicView, contentTitle))
+ }
+
private class ExceptionHolder {
var exception: Exception? = null
}
@@ -562,13 +661,20 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@InflationFlag contentToInflate: Int,
row: ExpandableNotificationRow,
) {
- inflateAndWait(false /* expectingException */, inflater, contentToInflate, row)
+ inflateAndWait(
+ false /* expectingException */,
+ inflater,
+ contentToInflate,
+ REDACTION_TYPE_NONE,
+ row,
+ )
}
private fun inflateAndWait(
expectingException: Boolean,
inflater: NotificationRowContentBinderImpl,
@InflationFlag contentToInflate: Int,
+ @RedactionType redactionType: Int,
row: ExpandableNotificationRow,
) {
val countDownLatch = CountDownLatch(1)
@@ -597,12 +703,26 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
row.entry,
row,
contentToInflate,
- BindParams(),
- false /* forceInflate */,
+ BindParams(false, false, false, redactionType),
+ false, /* forceInflate */
callback, /* callback */
)
Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS))
exceptionHolder.exception?.let { throw it }
}
+
+ fun hasText(parent: ViewGroup, text: CharSequence): Boolean {
+ for (i in 0 until parent.childCount) {
+ val child = parent.getChildAt(i)
+ if (child is ViewGroup) {
+ if (hasText(child, text)) {
+ return true
+ }
+ } else if (child is TextView) {
+ return child.text.toString().contains(text)
+ }
+ }
+ return false
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index b323ef85b370..b8d18757afbb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -81,7 +81,7 @@ import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.icon.IconBuilder;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
+import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -201,7 +201,7 @@ public class NotificationTestHelper {
new MockSmartReplyInflater(),
mock(NotifLayoutInflaterFactory.Provider.class),
mock(HeadsUpStyleProvider.class),
- mock(PromotedNotificationContentExtractor.class),
+ new FakePromotedNotificationContentExtractor(),
mock(NotificationRowContentBinderLogger.class))
: new NotificationContentInflater(
mock(NotifRemoteViewCache.class),
@@ -212,7 +212,7 @@ public class NotificationTestHelper {
new MockSmartReplyInflater(),
mock(NotifLayoutInflaterFactory.Provider.class),
mock(HeadsUpStyleProvider.class),
- mock(PromotedNotificationContentExtractor.class),
+ new FakePromotedNotificationContentExtractor(),
mock(NotificationRowContentBinderLogger.class));
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index de40abb4d9d8..c6cffa9da13b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -952,11 +952,10 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Test
@EnableSceneContainer
public void onTouchEvent_stopExpandingNotification_sceneContainerEnabled() {
- boolean touchHandled = stopExpandingNotification();
+ stopExpandingNotification();
- verify(mNotificationStackScrollLayout).startOverscrollAfterExpanding();
+ verify(mExpandHelper).finishExpanding();
verify(mNotificationStackScrollLayout, never()).dispatchDownEventToScroller(any());
- assertTrue(touchHandled);
}
@Test
@@ -964,11 +963,11 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
public void onTouchEvent_stopExpandingNotification_sceneContainerDisabled() {
stopExpandingNotification();
- verify(mNotificationStackScrollLayout, never()).startOverscrollAfterExpanding();
+ verify(mExpandHelper, never()).finishExpanding();
verify(mNotificationStackScrollLayout).dispatchDownEventToScroller(any());
}
- private boolean stopExpandingNotification() {
+ private void stopExpandingNotification() {
when(mNotificationStackScrollLayout.getExpandHelper()).thenReturn(mExpandHelper);
when(mNotificationStackScrollLayout.getIsExpanded()).thenReturn(true);
when(mNotificationStackScrollLayout.getExpandedInThisMotion()).thenReturn(true);
@@ -983,13 +982,13 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
NotificationStackScrollLayoutController.TouchHandler touchHandler =
mController.getTouchHandler();
- return touchHandler.onTouchEvent(MotionEvent.obtain(
- /* downTime= */ 0,
- /* eventTime= */ 0,
- MotionEvent.ACTION_DOWN,
- 0,
- 0,
- /* metaState= */ 0
+ touchHandler.onTouchEvent(MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ MotionEvent.ACTION_DOWN,
+ 0,
+ 0,
+ /* metaState= */ 0
));
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index f48fd3c998b1..6bdd86efa8c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -241,7 +241,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
shadeTestUtil.setSplitShade(true)
val horizontalPosition = checkNotNull(dimens).horizontalPosition
- assertIs<HorizontalPosition.FloatAtEnd>(horizontalPosition)
+ assertIs<HorizontalPosition.FloatAtStart>(horizontalPosition)
assertThat(horizontalPosition.width).isEqualTo(200)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
new file mode 100644
index 000000000000..41782a123f14
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.InitController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.settings.FakeDisplayTracker;
+import com.android.systemui.shade.NotificationShadeWindowView;
+import com.android.systemui.shade.QuickSettingsController;
+import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.List;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@RunWithLooper()
+public class StatusBarNotificationPresenterTest extends SysuiTestCase {
+ private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
+ private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider =
+ mock(VisualInterruptionDecisionProvider.class);
+ private NotificationInterruptSuppressor mInterruptSuppressor;
+ private VisualInterruptionCondition mAlertsDisabledCondition;
+ private VisualInterruptionCondition mVrModeCondition;
+ private VisualInterruptionFilter mNeedsRedactionFilter;
+ private VisualInterruptionCondition mPanelsDisabledCondition;
+ private CommandQueue mCommandQueue;
+ private final ShadeController mShadeController = mock(ShadeController.class);
+ private final NotificationAlertsInteractor mNotificationAlertsInteractor =
+ mock(NotificationAlertsInteractor.class);
+ private final KeyguardStateController mKeyguardStateController =
+ mock(KeyguardStateController.class);
+
+ @Before
+ public void setup() {
+ mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
+ mDependency.injectTestDependency(StatusBarStateController.class,
+ mock(SysuiStatusBarStateController.class));
+ mDependency.injectTestDependency(ShadeController.class, mShadeController);
+ mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
+ mDependency.injectMockDependency(NotificationShadeWindowController.class);
+
+ when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true);
+
+ createPresenter();
+ if (VisualInterruptionRefactor.isEnabled()) {
+ verifyAndCaptureSuppressors();
+ } else {
+ verifyAndCaptureLegacySuppressor();
+ }
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testInit_refactorDisabled() {
+ assertFalse(VisualInterruptionRefactor.isEnabled());
+ assertNull(mAlertsDisabledCondition);
+ assertNull(mVrModeCondition);
+ assertNull(mNeedsRedactionFilter);
+ assertNull(mPanelsDisabledCondition);
+ assertNotNull(mInterruptSuppressor);
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testInit_refactorEnabled() {
+ assertTrue(VisualInterruptionRefactor.isEnabled());
+ assertNotNull(mAlertsDisabledCondition);
+ assertNotNull(mVrModeCondition);
+ assertNotNull(mNeedsRedactionFilter);
+ assertNotNull(mPanelsDisabledCondition);
+ assertNull(mInterruptSuppressor);
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testNoSuppressHeadsUp_default_refactorDisabled() {
+ assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testNoSuppressHeadsUp_default_refactorEnabled() {
+ assertFalse(mAlertsDisabledCondition.shouldSuppress());
+ assertFalse(mVrModeCondition.shouldSuppress());
+ assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry()));
+ assertFalse(mAlertsDisabledCondition.shouldSuppress());
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
+ mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress heads up while disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
+ mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress heads up while disabled",
+ mPanelsDisabledCondition.shouldSuppress());
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
+ mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress interruptions while notification shade disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
+ mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress interruptions while notification shade disabled",
+ mPanelsDisabledCondition.shouldSuppress());
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testPanelsDisabledConditionSuppressesPeek() {
+ final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertFalse(types.contains(PULSE));
+ assertFalse(types.contains(BUBBLE));
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+ assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createFsiNotificationEntry()));
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+ assertFalse(mNeedsRedactionFilter.shouldSuppress(createFsiNotificationEntry()));
+
+ final Set<VisualInterruptionType> types = mNeedsRedactionFilter.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertFalse(types.contains(PULSE));
+ assertFalse(types.contains(BUBBLE));
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testSuppressInterruptions_vrMode_refactorDisabled() {
+ mStatusBarNotificationPresenter.mVrMode = true;
+
+ assertTrue("Vr mode should suppress interruptions",
+ mInterruptSuppressor.suppressAwakeInterruptions(createNotificationEntry()));
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testSuppressInterruptions_vrMode_refactorEnabled() {
+ mStatusBarNotificationPresenter.mVrMode = true;
+
+ assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress());
+
+ final Set<VisualInterruptionType> types = mVrModeCondition.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertFalse(types.contains(PULSE));
+ assertTrue(types.contains(BUBBLE));
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
+ when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
+
+ assertTrue("When alerts aren't enabled, interruptions are suppressed",
+ mInterruptSuppressor.suppressInterruptions(createNotificationEntry()));
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
+ when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
+
+ assertTrue("When alerts aren't enabled, interruptions are suppressed",
+ mAlertsDisabledCondition.shouldSuppress());
+
+ final Set<VisualInterruptionType> types = mAlertsDisabledCondition.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertTrue(types.contains(PULSE));
+ assertTrue(types.contains(BUBBLE));
+ }
+
+ private void createPresenter() {
+ final ShadeViewController shadeViewController = mock(ShadeViewController.class);
+
+ final NotificationShadeWindowView notificationShadeWindowView =
+ mock(NotificationShadeWindowView.class);
+ when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
+
+ NotificationStackScrollLayoutController stackScrollLayoutController =
+ mock(NotificationStackScrollLayoutController.class);
+ when(stackScrollLayoutController.getView()).thenReturn(
+ mock(NotificationStackScrollLayout.class));
+
+ final InitController initController = new InitController();
+
+ mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
+ mContext,
+ shadeViewController,
+ mock(PanelExpansionInteractor.class),
+ mock(QuickSettingsController.class),
+ mock(HeadsUpManager.class),
+ notificationShadeWindowView,
+ mock(ActivityStarter.class),
+ stackScrollLayoutController,
+ mock(DozeScrimController.class),
+ mock(NotificationShadeWindowController.class),
+ mock(DynamicPrivacyController.class),
+ mKeyguardStateController,
+ mNotificationAlertsInteractor,
+ mock(LockscreenShadeTransitionController.class),
+ mock(PowerInteractor.class),
+ mCommandQueue,
+ mock(NotificationLockscreenUserManager.class),
+ mock(SysuiStatusBarStateController.class),
+ mock(NotifShadeEventSource.class),
+ mock(NotificationMediaManager.class),
+ mock(NotificationGutsManager.class),
+ initController,
+ mVisualInterruptionDecisionProvider,
+ mock(NotificationRemoteInputManager.class),
+ mock(NotificationRemoteInputManager.Callback.class),
+ mock(NotificationListContainer.class));
+
+ initController.executePostInitTasks();
+ }
+
+ private void verifyAndCaptureSuppressors() {
+ mInterruptSuppressor = null;
+
+ final ArgumentCaptor<VisualInterruptionCondition> conditionCaptor =
+ ArgumentCaptor.forClass(VisualInterruptionCondition.class);
+ verify(mVisualInterruptionDecisionProvider, times(3)).addCondition(
+ conditionCaptor.capture());
+ final List<VisualInterruptionCondition> conditions = conditionCaptor.getAllValues();
+ mAlertsDisabledCondition = conditions.get(0);
+ mVrModeCondition = conditions.get(1);
+ mPanelsDisabledCondition = conditions.get(2);
+
+ final ArgumentCaptor<VisualInterruptionFilter> needsRedactionFilterCaptor =
+ ArgumentCaptor.forClass(VisualInterruptionFilter.class);
+ verify(mVisualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture());
+ mNeedsRedactionFilter = needsRedactionFilterCaptor.getValue();
+ }
+
+ private void verifyAndCaptureLegacySuppressor() {
+ mAlertsDisabledCondition = null;
+ mVrModeCondition = null;
+ mNeedsRedactionFilter = null;
+ mPanelsDisabledCondition = null;
+
+ final ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
+ ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
+ verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture());
+ mInterruptSuppressor = suppressorCaptor.getValue();
+ }
+
+ private NotificationEntry createNotificationEntry() {
+ return new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(new Notification.Builder(getContext(), "a").build())
+ .build();
+ }
+
+ private NotificationEntry createFsiNotificationEntry() {
+ final Notification notification = new Notification.Builder(getContext(), "a")
+ .setFullScreenIntent(mock(PendingIntent.class), true)
+ .build();
+
+ return new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(notification)
+ .build();
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
deleted file mode 100644
index c347347eff83..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.phone
-
-import android.app.Notification
-import android.app.Notification.Builder
-import android.app.StatusBarManager
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.testing.TestableLooper
-import android.testing.TestableLooper.RunWithLooper
-import android.view.Display.DEFAULT_DISPLAY
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.InitController
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.runTest
-import com.android.systemui.plugins.activityStarter
-import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.settings.FakeDisplayTracker
-import com.android.systemui.shade.NotificationShadeWindowView
-import com.android.systemui.shade.ShadeViewController
-import com.android.systemui.shade.domain.interactor.panelExpansionInteractor
-import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.lockscreenShadeTransitionController
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.domain.interactor.notificationAlertsInteractor
-import com.android.systemui.statusbar.notification.dynamicPrivacyController
-import com.android.systemui.statusbar.notification.headsup.headsUpManager
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.visualInterruptionDecisionProvider
-import com.android.systemui.statusbar.notificationLockscreenUserManager
-import com.android.systemui.statusbar.notificationRemoteInputManager
-import com.android.systemui.statusbar.notificationShadeWindowController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.keyguardStateController
-import com.android.systemui.statusbar.sysuiStatusBarStateController
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import com.google.common.truth.Truth.assertWithMessage
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.any
-import org.mockito.kotlin.argumentCaptor
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.times
-import org.mockito.kotlin.verify
-import org.mockito.kotlin.whenever
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@RunWithLooper
-class StatusBarNotificationPresenterTest : SysuiTestCase() {
- private lateinit var kosmos: Kosmos
-
- private var interruptSuppressor: NotificationInterruptSuppressor? = null
- private var alertsDisabledCondition: VisualInterruptionCondition? = null
- private var vrModeCondition: VisualInterruptionCondition? = null
- private var needsRedactionFilter: VisualInterruptionFilter? = null
- private var panelsDisabledCondition: VisualInterruptionCondition? = null
-
- private val commandQueue: CommandQueue = CommandQueue(mContext, FakeDisplayTracker(mContext))
- private val keyguardStateController: KeyguardStateController
- get() = kosmos.keyguardStateController
-
- private val notificationAlertsInteractor
- get() = kosmos.notificationAlertsInteractor
-
- private val visualInterruptionDecisionProvider
- get() = kosmos.visualInterruptionDecisionProvider
-
- private lateinit var underTest: StatusBarNotificationPresenter
-
- @Before
- fun setup() {
- kosmos =
- testKosmos().apply {
- whenever(notificationAlertsInteractor.areNotificationAlertsEnabled())
- .thenReturn(true)
- whenever(notificationStackScrollLayoutController.expandHelperCallback)
- .thenReturn(mock())
- lockscreenShadeTransitionController.setStackScroller(
- notificationStackScrollLayoutController
- )
- lockscreenShadeTransitionController.centralSurfaces = mock()
- }
-
- underTest = createPresenter()
- if (VisualInterruptionRefactor.isEnabled) {
- verifyAndCaptureSuppressors()
- } else {
- verifyAndCaptureLegacySuppressor()
- }
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testInit_refactorDisabled() {
- assertThat(VisualInterruptionRefactor.isEnabled).isFalse()
- assertThat(alertsDisabledCondition).isNull()
- assertThat(vrModeCondition).isNull()
- assertThat(needsRedactionFilter).isNull()
- assertThat(panelsDisabledCondition).isNull()
- assertThat(interruptSuppressor).isNotNull()
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testInit_refactorEnabled() {
- assertThat(VisualInterruptionRefactor.isEnabled).isTrue()
- assertThat(alertsDisabledCondition).isNotNull()
- assertThat(vrModeCondition).isNotNull()
- assertThat(needsRedactionFilter).isNotNull()
- assertThat(panelsDisabledCondition).isNotNull()
- assertThat(interruptSuppressor).isNull()
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testNoSuppressHeadsUp_default_refactorDisabled() {
- assertThat(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry())).isFalse()
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testNoSuppressHeadsUp_default_refactorEnabled() {
- assertThat(alertsDisabledCondition!!.shouldSuppress()).isFalse()
- assertThat(vrModeCondition!!.shouldSuppress()).isFalse()
- assertThat(needsRedactionFilter!!.shouldSuppress(createNotificationEntry())).isFalse()
- assertThat(alertsDisabledCondition!!.shouldSuppress()).isFalse()
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
- commandQueue.disable(
- DEFAULT_DISPLAY,
- StatusBarManager.DISABLE_EXPAND,
- 0,
- false, /* animate */
- )
- TestableLooper.get(this).processAllMessages()
-
- assertWithMessage("The panel should suppress heads up while disabled")
- .that(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry()))
- .isTrue()
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
- commandQueue.disable(
- DEFAULT_DISPLAY,
- StatusBarManager.DISABLE_EXPAND,
- 0,
- false, /* animate */
- )
- TestableLooper.get(this).processAllMessages()
-
- assertWithMessage("The panel should suppress heads up while disabled")
- .that(panelsDisabledCondition!!.shouldSuppress())
- .isTrue()
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
- commandQueue.disable(
- DEFAULT_DISPLAY,
- 0,
- StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
- false, /* animate */
- )
- TestableLooper.get(this).processAllMessages()
-
- assertWithMessage(
- "The panel should suppress interruptions while notification shade disabled"
- )
- .that(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry()))
- .isTrue()
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
- commandQueue.disable(
- DEFAULT_DISPLAY,
- 0,
- StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
- false, /* animate */
- )
- TestableLooper.get(this).processAllMessages()
-
- assertWithMessage(
- "The panel should suppress interruptions while notification shade disabled"
- )
- .that(panelsDisabledCondition!!.shouldSuppress())
- .isTrue()
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testPanelsDisabledConditionSuppressesPeek() {
- val types: Set<VisualInterruptionType> = panelsDisabledCondition!!.types
- assertThat(types).contains(VisualInterruptionType.PEEK)
- assertThat(types)
- .containsNoneOf(VisualInterruptionType.BUBBLE, VisualInterruptionType.PULSE)
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
- whenever(keyguardStateController.isShowing()).thenReturn(true)
- whenever(keyguardStateController.isOccluded()).thenReturn(false)
-
- assertThat(interruptSuppressor!!.suppressAwakeHeadsUp(createFsiNotificationEntry()))
- .isFalse()
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
- whenever(keyguardStateController.isShowing()).thenReturn(true)
- whenever(keyguardStateController.isOccluded()).thenReturn(false)
-
- assertThat(needsRedactionFilter!!.shouldSuppress(createFsiNotificationEntry())).isFalse()
-
- val types: Set<VisualInterruptionType> = needsRedactionFilter!!.types
- assertThat(types).contains(VisualInterruptionType.PEEK)
- assertThat(types)
- .containsNoneOf(VisualInterruptionType.BUBBLE, VisualInterruptionType.PULSE)
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testSuppressInterruptions_vrMode_refactorDisabled() {
- underTest.mVrMode = true
-
- assertWithMessage("Vr mode should suppress interruptions")
- .that(interruptSuppressor!!.suppressAwakeInterruptions(createNotificationEntry()))
- .isTrue()
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testSuppressInterruptions_vrMode_refactorEnabled() {
- underTest.mVrMode = true
-
- assertWithMessage("Vr mode should suppress interruptions")
- .that(vrModeCondition!!.shouldSuppress())
- .isTrue()
-
- val types: Set<VisualInterruptionType> = vrModeCondition!!.types
- assertThat(types).contains(VisualInterruptionType.PEEK)
- assertThat(types).doesNotContain(VisualInterruptionType.PULSE)
- assertThat(types).contains(VisualInterruptionType.BUBBLE)
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
- whenever(notificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false)
-
- assertWithMessage("When alerts aren't enabled, interruptions are suppressed")
- .that(interruptSuppressor!!.suppressInterruptions(createNotificationEntry()))
- .isTrue()
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- fun testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
- whenever(notificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false)
-
- assertWithMessage("When alerts aren't enabled, interruptions are suppressed")
- .that(alertsDisabledCondition!!.shouldSuppress())
- .isTrue()
-
- val types: Set<VisualInterruptionType> = alertsDisabledCondition!!.types
- assertThat(types).contains(VisualInterruptionType.PEEK)
- assertThat(types).contains(VisualInterruptionType.PULSE)
- assertThat(types).contains(VisualInterruptionType.BUBBLE)
- }
-
- @Test
- @EnableSceneContainer
- fun testExpandSensitiveNotification_onLockScreen_opensShade() =
- kosmos.runTest {
- // Given we are on the keyguard
- kosmos.sysuiStatusBarStateController.state = StatusBarState.KEYGUARD
- // And the device is locked
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Pin
- )
-
- // When the user expands a sensitive Notification
- val entry =
- createRow().entry.apply {
- setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
- }
- underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
-
- // Then we open the locked shade
- assertThat(kosmos.sysuiStatusBarStateController.state)
- .isEqualTo(StatusBarState.SHADE_LOCKED)
- }
-
- @Test
- @EnableSceneContainer
- fun testExpandSensitiveNotification_onLockedShade_showsBouncer() =
- kosmos.runTest {
- // Given we are on the locked shade
- kosmos.sysuiStatusBarStateController.state = StatusBarState.SHADE_LOCKED
- // And the device is locked
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Pin
- )
-
- // When the user expands a sensitive Notification
- val entry =
- createRow().entry.apply {
- setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
- }
- underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
-
- // Then we show the bouncer
- verify(kosmos.activityStarter).dismissKeyguardThenExecute(any(), eq(null), eq(false))
- // AND we are still on the locked shade
- assertThat(kosmos.sysuiStatusBarStateController.state)
- .isEqualTo(StatusBarState.SHADE_LOCKED)
- }
-
- private fun createPresenter(): StatusBarNotificationPresenter {
- val shadeViewController: ShadeViewController = mock()
-
- val notificationShadeWindowView: NotificationShadeWindowView = mock()
- whenever(notificationShadeWindowView.resources).thenReturn(mContext.resources)
- whenever(kosmos.notificationStackScrollLayoutController.view).thenReturn(mock())
-
- val initController: InitController = InitController()
-
- return StatusBarNotificationPresenter(
- mContext,
- shadeViewController,
- kosmos.panelExpansionInteractor,
- /* quickSettingsController = */ mock(),
- kosmos.headsUpManager,
- notificationShadeWindowView,
- kosmos.activityStarter,
- kosmos.notificationStackScrollLayoutController,
- kosmos.dozeScrimController,
- kosmos.notificationShadeWindowController,
- kosmos.dynamicPrivacyController,
- kosmos.keyguardStateController,
- kosmos.notificationAlertsInteractor,
- kosmos.lockscreenShadeTransitionController,
- kosmos.powerInteractor,
- commandQueue,
- kosmos.notificationLockscreenUserManager,
- kosmos.sysuiStatusBarStateController,
- /* notifShadeEventSource = */ mock(),
- /* notificationMediaManager = */ mock(),
- /* notificationGutsManager = */ mock(),
- initController,
- kosmos.visualInterruptionDecisionProvider,
- kosmos.notificationRemoteInputManager,
- /* remoteInputManagerCallback = */ mock(),
- /* notificationListContainer = */ mock(),
- kosmos.deviceUnlockedInteractor,
- )
- .also { initController.executePostInitTasks() }
- }
-
- private fun verifyAndCaptureSuppressors() {
- interruptSuppressor = null
-
- val conditionCaptor = argumentCaptor<VisualInterruptionCondition>()
- verify(visualInterruptionDecisionProvider, times(3)).addCondition(conditionCaptor.capture())
- val conditions: List<VisualInterruptionCondition> = conditionCaptor.allValues
- alertsDisabledCondition = conditions[0]
- vrModeCondition = conditions[1]
- panelsDisabledCondition = conditions[2]
-
- val needsRedactionFilterCaptor = argumentCaptor<VisualInterruptionFilter>()
- verify(visualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture())
- needsRedactionFilter = needsRedactionFilterCaptor.lastValue
- }
-
- private fun verifyAndCaptureLegacySuppressor() {
- alertsDisabledCondition = null
- vrModeCondition = null
- needsRedactionFilter = null
- panelsDisabledCondition = null
-
- val suppressorCaptor = argumentCaptor<NotificationInterruptSuppressor>()
- verify(visualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture())
- interruptSuppressor = suppressorCaptor.lastValue
- }
-
- private fun createRow(): ExpandableNotificationRow {
- val row: ExpandableNotificationRow = mock()
- val entry: NotificationEntry = createNotificationEntry()
- whenever(row.entry).thenReturn(entry)
- entry.row = row
- return row
- }
-
- private fun createNotificationEntry(): NotificationEntry =
- NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(Builder(mContext, "a").build())
- .build()
-
- private fun createFsiNotificationEntry(): NotificationEntry {
- val notification: Notification =
- Builder(mContext, "a").setFullScreenIntent(mock(), true).build()
-
- return NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(notification)
- .build()
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
index b560c591af1e..1ee8005fb7ab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
@@ -20,7 +20,9 @@ import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
@@ -31,7 +33,6 @@ import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
@@ -43,7 +44,7 @@ import org.mockito.Mockito.verify
@RunWithLooper(setAsMainLooper = true)
class SystemUIBottomSheetDialogTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val configurationController = mock<ConfigurationController>()
private val config = mock<Configuration>()
private val delegate = mock<DialogDelegate<Dialog>>()
@@ -67,21 +68,17 @@ class SystemUIBottomSheetDialogTest : SysuiTestCase() {
@Test
fun onStart_registersConfigCallback() {
- kosmos.testScope.runTest {
+ kosmos.runTest {
dialog.show()
- runCurrent()
-
verify(configurationController).addCallback(any())
}
}
@Test
fun onStop_unregisterConfigCallback() {
- kosmos.testScope.runTest {
+ kosmos.runTest {
dialog.show()
- runCurrent()
dialog.dismiss()
- runCurrent()
verify(configurationController).removeCallback(any())
}
@@ -89,14 +86,12 @@ class SystemUIBottomSheetDialogTest : SysuiTestCase() {
@Test
fun onConfigurationChanged_calledInDelegate() {
- kosmos.testScope.runTest {
+ kosmos.runTest {
dialog.show()
- runCurrent()
val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
verify(configurationController).addCallback(capture(captor))
captor.value.onConfigChanged(config)
- runCurrent()
verify(delegate).onConfigurationChanged(any(), any())
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
index 06b3b57bd133..b2378d2c3aae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.kosmos.mainCoroutineContext
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
import com.android.systemui.runOnMainThreadAndWaitForIdleSync
+import com.android.systemui.shade.data.repository.shadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.systemUIDialogFactory
import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.modesDialogViewModel
@@ -78,6 +79,7 @@ class ModesDialogDelegateTest : SysuiTestCase() {
{ kosmos.modesDialogViewModel },
mockDialogEventLogger,
kosmos.mainCoroutineContext,
+ kosmos.shadeDialogContextInteractor,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 7b52dd836b51..5cd0846ded7e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -501,6 +501,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
public void onWallpaperColorsChanged_changeLockWallpaper() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
diff --git a/packages/SystemUI/res/color/slider_active_track_color.xml b/packages/SystemUI/res/color/slider_active_track_color.xml
deleted file mode 100644
index 8ba5e4901a7a..000000000000
--- a/packages/SystemUI/res/color/slider_active_track_color.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ 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.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="@androidprv:color/materialColorPrimary" android:state_enabled="true" />
- <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" />
-</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/slider_inactive_track_color.xml b/packages/SystemUI/res/color/slider_inactive_track_color.xml
deleted file mode 100644
index 7980f804a516..000000000000
--- a/packages/SystemUI/res/color/slider_inactive_track_color.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ 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.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" android:state_enabled="true" />
- <item android:color="@androidprv:color/materialColorPrimary" />
-</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/slider_thumb_color.xml b/packages/SystemUI/res/color/slider_thumb_color.xml
deleted file mode 100644
index 8a98902426f8..000000000000
--- a/packages/SystemUI/res/color/slider_thumb_color.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ 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.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" android:state_enabled="false" />
- <item android:color="@androidprv:color/materialColorPrimary" />
-</selector>
diff --git a/packages/SystemUI/res/drawable/audio_bars_idle.xml b/packages/SystemUI/res/drawable/audio_bars_idle.xml
new file mode 100644
index 000000000000..92a24755fece
--- /dev/null
+++ b/packages/SystemUI/res/drawable/audio_bars_idle.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="168dp"
+ android:height="168dp"
+ android:viewportWidth="168"
+ android:viewportHeight="168">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_2_G"
+ android:translateX="121.161"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="102.911"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="139.661"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index a3bad8f012ac..5ccedeafcb59 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -56,8 +56,8 @@
android:layout_marginTop="@dimen/volume_dialog_components_spacing"
android:background="@drawable/ripple_drawable_20dp"
android:contentDescription="@string/accessibility_volume_settings"
+ android:scaleType="centerInside"
android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
android:tint="@androidprv:color/materialColorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
index 9ac456c17084..0acf4109bbb5 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -14,7 +14,6 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
@@ -24,11 +23,6 @@
android:layout_width="@dimen/volume_dialog_slider_width"
android:layout_height="@dimen/volume_dialog_slider_height"
android:layout_gravity="center"
- android:theme="@style/Theme.Material3.Light"
android:orientation="vertical"
- app:thumbHeight="52dp"
- app:trackCornerSize="12dp"
- app:trackHeight="40dp"
- app:trackStopIndicatorSize="6dp"
- app:trackInsideCornerSize="2dp" />
+ android:theme="@style/Theme.Material3.DayNight" />
</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_in.json b/packages/SystemUI/res/raw/audio_bars_in.json
new file mode 100644
index 000000000000..c90a59c47d64
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_in.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":18,"w":168,"h":168,"nm":"audio_bars_in","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_out.json b/packages/SystemUI/res/raw/audio_bars_out.json
new file mode 100644
index 000000000000..5eab65e057ab
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_out.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":31,"w":168,"h":168,"nm":"audio_bars_out","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_playing.json b/packages/SystemUI/res/raw/audio_bars_playing.json
new file mode 100644
index 000000000000..6ee8e1915f36
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_playing.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":121,"w":168,"h":168,"nm":"audio_bars_playing","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":38,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[-0.016,-14.1],[5.941,-8.358],[5.961,0],[5.958,8.516],[0,14.274],[-5.958,8.515],[-5.961,0],[-5.972,-8.373]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":70,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":102,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[-0.016,-14.1],[5.941,-8.358],[5.961,0],[5.958,8.516],[0,14.274],[-5.958,8.515],[-5.961,0],[-5.972,-8.373]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":32,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-19.1],[5.957,-13.358],[5.961,0],[5.958,13.641],[0,19.399],[-5.958,13.64],[-5.961,0],[-5.957,-13.373]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":65,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":97,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-19.1],[5.957,-13.358],[5.961,0],[5.958,13.641],[0,19.399],[-5.958,13.64],[-5.961,0],[-5.957,-13.373]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":29,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-15.85],[5.957,-10.108],[5.961,0],[5.958,10.516],[0,16.274],[-5.958,10.515],[-5.961,0],[-5.957,-10.123]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":91,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-15.85],[5.957,-10.108],[5.961,0],[5.958,10.516],[0,16.274],[-5.958,10.515],[-5.961,0],[-5.957,-10.123]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-9.225],[5.957,-3.483],[5.961,0],[5.958,3.766],[0,9.524],[-5.958,3.765],[-5.961,0],[-5.957,-3.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":54,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":86,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-9.225],[5.957,-3.483],[5.961,0],[5.958,3.766],[0,9.524],[-5.958,3.765],[-5.961,0],[-5.957,-3.498]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":19,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":48,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":81,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0}],"markers":[{"tm":60,"cm":"1","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index c1eff5f629b3..a77f5e4629c1 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -108,6 +108,9 @@
<color name="people_tile_background">@color/material_dynamic_secondary20</color>
+ <!-- Dark Theme colors for notification shade/scrim -->
+ <color name="shade_panel">@android:color/system_accent1_900</color>
+
<!-- Keyboard shortcut helper dialog -->
<color name="ksh_key_item_color">@*android:color/system_on_surface_variant_dark</color>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index b4383156dc71..ec24c3df36a8 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -19,10 +19,13 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The maximum number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">4</integer>
+ <!-- The number of columns in the Split Shade QuickSettings -->
+ <integer name="quick_settings_split_shade_num_columns">6</integer>
+
<!-- Use collapsed layout for media player in landscape QQS -->
<bool name="config_quickSettingsMediaLandscapeCollapsed">false</bool>
@@ -51,7 +54,9 @@
ignored. -->
<string-array name="config_keyguardQuickAffordanceDefaults" translatable="false">
<item>bottom_start:home</item>
- <item>bottom_end:create_note</item>
+ <!-- TODO(b/384119565): revisit decision on defaults -->
+ <item android:featureFlag="!com.android.systemui.glanceable_hub_v2_resources">bottom_end:create_note</item>
+ <item android:featureFlag="com.android.systemui.glanceable_hub_v2_resources">bottom_end:glanceable_hub</item>
</string-array>
<!-- Whether volume panel should use the large screen layout or not -->
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 28df2e2a1b8c..d2b7d0b90c43 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -31,6 +31,9 @@
<!-- The dark background color behind the shade -->
<color name="shade_scrim_background_dark">@androidprv:color/system_under_surface_light</color>
+ <!-- Colors for notification shade/scrim -->
+ <color name="shade_panel">@android:color/system_accent1_800</color>
+
<!-- The color of the background in the separated list of the Global Actions menu -->
<color name="global_actions_separated_background">#F5F5F5</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 56aaf4c0c564..d3ee63ba0dd1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2504,14 +2504,14 @@
<!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_remove_tile_action">remove tile</string>
- <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to end" in screen readers [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_tile_add_action">add tile to end</string>
+ <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to the last position" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_add_action">add tile to the last position</string>
<!-- Accessibility action for context menu to move QS tile [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_tile_start_move">Move tile</string>
- <!-- Accessibility action for context menu to add QS tile [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_tile_start_add">Add tile</string>
+ <!-- Accessibility action for context menu to add QS tile to a position [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_start_add">Add tile to desired position</string>
<!-- Accessibility description when QS tile is to be moved, indicating the destination position [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_tile_move_to_position">Move to <xliff:g id="position" example="5">%1$d</xliff:g></string>
@@ -2564,7 +2564,7 @@
<string name="accessibility_quick_settings_open_settings">Open <xliff:g name="page" example="Bluetooth">%s</xliff:g> settings.</string>
<!-- accessibility label for button to edit quick settings [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_edit">Edit order of settings.</string>
+ <string name="accessibility_quick_settings_edit">Edit order of Quick Settings.</string>
<!-- accessibility label for button to open power menu [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_power_menu">Power menu</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3156a50df96f..f6c1ecea2886 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -559,15 +559,18 @@
<style name="SystemUI.Material3.Slider.Volume">
<item name="trackHeight">40dp</item>
<item name="thumbHeight">52dp</item>
+ <item name="trackCornerSize">12dp</item>
+ <item name="trackInsideCornerSize">2dp</item>
+ <item name="trackStopIndicatorSize">6dp</item>
</style>
<style name="SystemUI.Material3.Slider" parent="@style/Widget.Material3.Slider">
<item name="labelStyle">@style/Widget.Material3.Slider.Label</item>
- <item name="thumbColor">@color/slider_thumb_color</item>
- <item name="tickColorActive">@color/slider_inactive_track_color</item>
- <item name="tickColorInactive">@color/slider_active_track_color</item>
- <item name="trackColorActive">@color/slider_active_track_color</item>
- <item name="trackColorInactive">@color/slider_inactive_track_color</item>
+ <item name="thumbColor">@androidprv:color/materialColorPrimary</item>
+ <item name="tickColorActive">@androidprv:color/materialColorSurfaceContainerHighest</item>
+ <item name="tickColorInactive">@androidprv:color/materialColorPrimary</item>
+ <item name="trackColorActive">@androidprv:color/materialColorPrimary</item>
+ <item name="trackColorInactive">@androidprv:color/materialColorSurfaceContainerHighest</item>
</style>
<style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog"/>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index fc536bdb126b..6f13d637d5c5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -20,6 +20,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+import static com.android.systemui.Flags.glanceableHubBackAction;
import static com.android.systemui.shared.Flags.shadeAllowBackGesture;
import android.annotation.LongDef;
@@ -352,6 +353,10 @@ public class QuickStepContract {
}
// Disable back gesture on the hub, but not when the shade is showing.
if ((sysuiStateFlags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) {
+ // Allow back gesture on Glanceable Hub with back action support.
+ if (glanceableHubBackAction()) {
+ return false;
+ }
// Use QS expanded signal as the notification panel is always considered visible
// expanded when on the lock screen and when opening hub over lock screen. This does
// mean that back gesture is disabled when opening shade over hub while in portrait
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 5af80cbd4b29..71b622aa0608 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -43,7 +43,6 @@ import com.android.systemui.dagger.qualifiers.DisplaySpecific
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags.REGION_SAMPLING
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -85,8 +84,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
/**
- * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
- * [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
+ * Controller for a Clock provided by the registry and used on the keyguard. Functionality is forked
+ * from [AnimatableClockController].
*/
open class ClockEventController
@Inject
@@ -348,14 +347,6 @@ constructor(
object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(visible: Boolean) {
isKeyguardVisible = visible
- if (!MigrateClocksToBlueprint.isEnabled) {
- if (!isKeyguardVisible) {
- clock?.run {
- smallClock.animations.doze(if (isDozing) 1f else 0f)
- largeClock.animations.doze(if (isDozing) 1f else 0f)
- }
- }
- }
if (visible) {
refreshTime()
@@ -388,10 +379,6 @@ constructor(
}
private fun refreshTime() {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
clock?.smallClock?.events?.onTimeTick()
clock?.largeClock?.events?.onTimeTick()
}
@@ -483,14 +470,10 @@ constructor(
if (ModesUi.isEnabled) {
listenForDnd(this)
}
- if (MigrateClocksToBlueprint.isEnabled) {
- listenForDozeAmountTransition(this)
- listenForAnyStateToAodTransition(this)
- listenForAnyStateToLockscreenTransition(this)
- listenForAnyStateToDozingTransition(this)
- } else {
- listenForDozeAmount(this)
- }
+ listenForDozeAmountTransition(this)
+ listenForAnyStateToAodTransition(this)
+ listenForAnyStateToLockscreenTransition(this)
+ listenForAnyStateToDozingTransition(this)
}
}
smallTimeListener?.update(shouldTimeListenerRun)
@@ -596,11 +579,6 @@ constructor(
}
@VisibleForTesting
- internal fun listenForDozeAmount(scope: CoroutineScope): Job {
- return scope.launch { keyguardInteractor.dozeAmount.collect { handleDoze(it) } }
- }
-
- @VisibleForTesting
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
return scope.launch {
merge(
@@ -695,8 +673,7 @@ constructor(
isRunning = true
when (clockFace.config.tickRate) {
ClockTickRate.PER_MINUTE -> {
- // Handled by KeyguardClockSwitchController and
- // by KeyguardUpdateMonitorCallback#onTimeChanged.
+ // Handled by KeyguardUpdateMonitorCallback#onTimeChanged.
}
ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable)
ClockTickRate.PER_FRAME -> {
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index df77a58c3b34..3f332f769c6e 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -23,14 +23,11 @@ import android.graphics.Rect
import android.os.Bundle
import android.view.Display
import android.view.Gravity
-import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.FrameLayout.LayoutParams
-import com.android.keyguard.dagger.KeyguardStatusViewComponent
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.res.R
@@ -45,7 +42,6 @@ class ConnectedDisplayKeyguardPresentation
constructor(
@Assisted display: Display,
context: Context,
- private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
private val clockRegistry: ClockRegistry,
private val clockEventController: ClockEventController,
) :
@@ -53,12 +49,11 @@ constructor(
context,
display,
R.style.Theme_SystemUI_KeyguardPresentation,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
) {
private lateinit var rootView: FrameLayout
private var clock: View? = null
- private lateinit var keyguardStatusViewController: KeyguardStatusViewController
private lateinit var faceController: ClockFaceController
private lateinit var clockFrame: FrameLayout
@@ -82,7 +77,7 @@ constructor(
oldLeft: Int,
oldTop: Int,
oldRight: Int,
- oldBottom: Int
+ oldBottom: Int,
) {
clock?.let {
faceController.events.onTargetRegionChanged(
@@ -95,11 +90,7 @@ constructor(
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- if (MigrateClocksToBlueprint.isEnabled) {
- onCreateV2()
- } else {
- onCreate()
- }
+ onCreateV2()
}
fun onCreateV2() {
@@ -112,39 +103,15 @@ constructor(
setClock(clockRegistry.createCurrentClock())
}
- fun onCreate() {
- setContentView(
- LayoutInflater.from(context)
- .inflate(R.layout.keyguard_clock_presentation, /* root= */ null)
- )
-
- setFullscreen()
-
- clock = requireViewById(R.id.clock)
- keyguardStatusViewController =
- keyguardStatusViewComponentFactory
- .build(clock as KeyguardStatusView, display)
- .keyguardStatusViewController
- .apply {
- setDisplayedOnSecondaryDisplay()
- init()
- }
- }
-
override fun onAttachedToWindow() {
- if (MigrateClocksToBlueprint.isEnabled) {
- clockRegistry.registerClockChangeListener(clockChangedListener)
- clockEventController.registerListeners(clock!!)
-
- faceController.animations.enter()
- }
+ clockRegistry.registerClockChangeListener(clockChangedListener)
+ clockEventController.registerListeners(clock!!)
+ faceController.animations.enter()
}
override fun onDetachedFromWindow() {
- if (MigrateClocksToBlueprint.isEnabled) {
- clockEventController.unregisterListeners()
- clockRegistry.unregisterClockChangeListener(clockChangedListener)
- }
+ clockEventController.unregisterListeners()
+ clockRegistry.unregisterClockChangeListener(clockChangedListener)
super.onDetachedFromWindow()
}
@@ -166,7 +133,7 @@ constructor(
context.resources.getDimensionPixelSize(R.dimen.keyguard_presentation_width),
WRAP_CONTENT,
Gravity.CENTER,
- )
+ ),
)
clockEventController.clock = clockController
@@ -190,8 +157,6 @@ constructor(
@AssistedFactory
interface Factory {
/** Creates a new [Presentation] for the given [display]. */
- fun create(
- display: Display,
- ): ConnectedDisplayKeyguardPresentation
+ fun create(display: Display): ConnectedDisplayKeyguardPresentation
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 1083136b570a..acfa08643b63 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -26,11 +26,9 @@ import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.os.Trace;
-import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
-import android.view.DisplayAddress;
import android.view.DisplayInfo;
import android.view.View;
import android.view.WindowManager;
@@ -58,6 +56,9 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Provider;
+/**
+ * Manages Keyguard Presentations for non-primary display(s).
+ */
@SysUISingleton
public class KeyguardDisplayManager {
protected static final String TAG = "KeyguardDisplayManager";
@@ -170,14 +171,17 @@ public class KeyguardDisplayManager {
}
return false;
}
- if (mKeyguardStateController.isOccluded()
- && mDeviceStateHelper.isConcurrentDisplayActive(display)) {
+
+ final boolean deviceStateOccludesKeyguard =
+ mDeviceStateHelper.isConcurrentDisplayActive(display)
+ || mDeviceStateHelper.isRearDisplayOuterDefaultActive(display);
+ if (mKeyguardStateController.isOccluded() && deviceStateOccludesKeyguard) {
if (DEBUG) {
// When activities with FLAG_SHOW_WHEN_LOCKED are shown on top of Keyguard, the
// Keyguard state becomes "occluded". In this case, we should not show the
// KeyguardPresentation, since the activity is presenting content onto the
// non-default display.
- Log.i(TAG, "Do not show KeyguardPresentation when occluded and concurrent"
+ Log.i(TAG, "Do not show KeyguardPresentation when occluded and concurrent or rear"
+ " display is active");
}
return false;
@@ -326,44 +330,45 @@ public class KeyguardDisplayManager {
public static class DeviceStateHelper implements DeviceStateManager.DeviceStateCallback {
@Nullable
- private final DisplayAddress.Physical mRearDisplayPhysicalAddress;
-
- // TODO(b/271317597): These device states should be defined in DeviceStateManager
- private final int mConcurrentState;
- private boolean mIsInConcurrentDisplayState;
+ private DeviceState mDeviceState;
@Inject
DeviceStateHelper(
- @ShadeDisplayAware Context context,
DeviceStateManager deviceStateManager,
@Main Executor mainExecutor) {
-
- final String rearDisplayPhysicalAddress = context.getResources().getString(
- com.android.internal.R.string.config_rearDisplayPhysicalAddress);
- if (TextUtils.isEmpty(rearDisplayPhysicalAddress)) {
- mRearDisplayPhysicalAddress = null;
- } else {
- mRearDisplayPhysicalAddress = DisplayAddress
- .fromPhysicalDisplayId(Long.parseLong(rearDisplayPhysicalAddress));
- }
-
- mConcurrentState = context.getResources().getInteger(
- com.android.internal.R.integer.config_deviceStateConcurrentRearDisplay);
deviceStateManager.registerCallback(mainExecutor, this);
}
@Override
public void onDeviceStateChanged(@NonNull DeviceState state) {
- // When concurrent state ends, the display also turns off. This is enforced in various
- // ExtensionRearDisplayPresentationTest CTS tests. So, we don't need to invoke
- // hide() since that will happen through the onDisplayRemoved callback.
- mIsInConcurrentDisplayState = state.getIdentifier() == mConcurrentState;
+ // When dual display or rear display mode ends, the display also turns off. This is
+ // enforced in various ExtensionRearDisplayPresentationTest CTS tests. So, we don't need
+ // to invoke hide() since that will happen through the onDisplayRemoved callback.
+ mDeviceState = state;
+ }
+
+ /**
+ * @return true if the device is in Dual Display mode, and the specified display is the
+ * rear facing (outer) display.
+ */
+ boolean isConcurrentDisplayActive(@NonNull Display display) {
+ return mDeviceState != null
+ && mDeviceState.hasProperty(
+ DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT)
+ && (display.getFlags() & Display.FLAG_REAR) != 0;
}
- boolean isConcurrentDisplayActive(Display display) {
- return mIsInConcurrentDisplayState
- && mRearDisplayPhysicalAddress != null
- && mRearDisplayPhysicalAddress.equals(display.getAddress());
+ /**
+ * @return true if the device is the updated Rear Display mode, and the specified display is
+ * the inner display. See {@link DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT}.
+ * Note that in this state, the outer display is the default display, while the inner
+ * display is the "rear" display.
+ */
+ boolean isRearDisplayOuterDefaultActive(@NonNull Display display) {
+ return mDeviceState != null
+ && mDeviceState.hasProperty(
+ DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)
+ && (display.getFlags() & Display.FLAG_REAR) != 0;
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 07bd813c2420..40a86dc3713e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -19,13 +19,12 @@ package com.android.keyguard
import android.content.Context
import android.view.View
import com.android.systemui.customization.R as customR
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
-import com.android.systemui.shared.R as sharedR
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shared.R as sharedR
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START
@@ -55,16 +54,17 @@ constructor(
var statusViewCentered = false
private val filterKeyguardAndSplitShadeOnly: () -> Boolean = {
- statusBarStateController.getState() == KEYGUARD && !statusViewCentered }
+ statusBarStateController.getState() == KEYGUARD && !statusViewCentered
+ }
private val filterKeyguard: () -> Boolean = { statusBarStateController.getState() == KEYGUARD }
private val translateAnimator by lazy {
- val smartSpaceViews = if (MigrateClocksToBlueprint.isEnabled) {
- // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer]
- val scrollXTranslation = { view: View, translation: Float ->
- view.scrollX = -translation.toInt()
- }
+ // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer]
+ val scrollXTranslation = { view: View, translation: Float ->
+ view.scrollX = -translation.toInt()
+ }
+ val smartSpaceViews =
setOf(
ViewIdToTranslate(
viewId = sharedR.id.date_smartspace_view,
@@ -83,18 +83,8 @@ constructor(
direction = START,
shouldBeAnimated = filterKeyguard,
translateFunc = scrollXTranslation,
- )
+ ),
)
- } else {
- setOf(ViewIdToTranslate(
- viewId = R.id.keyguard_status_area,
- direction = START,
- shouldBeAnimated = filterKeyguard,
- translateFunc = { view, value ->
- (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value
- }
- ))
- }
UnfoldConstantTranslateAnimator(
viewsIdToTranslate =
@@ -102,39 +92,39 @@ constructor(
ViewIdToTranslate(
viewId = customR.id.lockscreen_clock_view_large,
direction = START,
- shouldBeAnimated = filterKeyguardAndSplitShadeOnly
+ shouldBeAnimated = filterKeyguardAndSplitShadeOnly,
),
ViewIdToTranslate(
viewId = customR.id.lockscreen_clock_view,
direction = START,
- shouldBeAnimated = filterKeyguard
+ shouldBeAnimated = filterKeyguard,
),
ViewIdToTranslate(
viewId = R.id.notification_stack_scroller,
direction = END,
- shouldBeAnimated = filterKeyguardAndSplitShadeOnly
- )
+ shouldBeAnimated = filterKeyguardAndSplitShadeOnly,
+ ),
) + smartSpaceViews,
- progressProvider = unfoldProgressProvider
+ progressProvider = unfoldProgressProvider,
)
}
private val shortcutButtonsAnimator by lazy {
UnfoldConstantTranslateAnimator(
viewsIdToTranslate =
- setOf(
- ViewIdToTranslate(
- viewId = R.id.start_button,
- direction = START,
- shouldBeAnimated = filterKeyguard
+ setOf(
+ ViewIdToTranslate(
+ viewId = R.id.start_button,
+ direction = START,
+ shouldBeAnimated = filterKeyguard,
+ ),
+ ViewIdToTranslate(
+ viewId = R.id.end_button,
+ direction = END,
+ shouldBeAnimated = filterKeyguard,
+ ),
),
- ViewIdToTranslate(
- viewId = R.id.end_button,
- direction = END,
- shouldBeAnimated = filterKeyguard
- )
- ),
- progressProvider = unfoldProgressProvider
+ progressProvider = unfoldProgressProvider,
)
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 0305b5e5ab63..e76f38c8c75c 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -26,7 +26,6 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.clocks.ClockMessageBuffers;
import com.android.systemui.res.R;
@@ -70,7 +69,7 @@ public abstract class ClockRegistryModule {
context,
layoutInflater,
resources,
- MigrateClocksToBlueprint.isEnabled(),
+
com.android.systemui.Flags.clockReactiveVariants()
),
context.getString(R.string.lockscreen_clock_id_fallback),
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 232b62985ad0..47910f3d25bc 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -21,8 +21,11 @@ import android.window.OnBackAnimationCallback
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import android.window.WindowOnBackInvokedDispatcher
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.glanceableHubBackAction
import com.android.systemui.Flags.predictiveBackAnimateShade
+import com.android.systemui.communal.domain.interactor.CommunalBackActionInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -35,7 +38,6 @@ import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Handles requests to go back either from a button or gesture. */
@SysUISingleton
@@ -50,6 +52,7 @@ constructor(
private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
private val shadeBackActionInteractor: ShadeBackActionInteractor,
private val qsController: QuickSettingsController,
+ private val communalBackActionInteractor: CommunalBackActionInteractor,
) : CoreStartable {
private var isCallbackRegistered = false
@@ -114,6 +117,12 @@ constructor(
if (shadeBackActionInteractor.closeUserSwitcherIfOpen()) {
return true
}
+ if (glanceableHubBackAction()) {
+ if (communalBackActionInteractor.canBeDismissed()) {
+ communalBackActionInteractor.onBackPressed()
+ return true
+ }
+ }
if (shouldBackBeHandled()) {
if (shadeBackActionInteractor.canBeCollapsed()) {
// this is the Shade dismiss animation, so make sure QQS closes when it ends.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 4c2dc41fb759..d8c628fd680b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -155,6 +155,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
override fun onAnimationEnd(animation: Animator) {
drawDwell = false
resetDwellAlpha()
+ invalidate()
}
})
start()
@@ -191,6 +192,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
override fun onAnimationEnd(animation: Animator) {
drawDwell = false
resetDwellAlpha()
+ invalidate()
}
})
start()
@@ -248,6 +250,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
override fun onAnimationEnd(animation: Animator) {
drawDwell = false
+ invalidate()
}
})
start()
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index 9cfb5be478ed..b294dd1b0b71 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -41,6 +41,7 @@ import com.android.internal.R as InternalR
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.time.SystemClock
import dagger.assisted.Assisted
@@ -68,6 +69,7 @@ internal constructor(
private val uiEventLogger: UiEventLogger,
private val logger: BluetoothTileDialogLogger,
private val systemuiDialogFactory: SystemUIDialog.Factory,
+ private val shadeDialogContextInteractor: ShadeDialogContextInteractor,
) : SystemUIDialog.Delegate {
private val mutableBluetoothStateToggle: MutableStateFlow<Boolean?> = MutableStateFlow(null)
@@ -105,7 +107,7 @@ internal constructor(
}
override fun createDialog(): SystemUIDialog {
- return systemuiDialogFactory.create(this)
+ return systemuiDialogFactory.create(this, shadeDialogContextInteractor.context)
}
override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
@@ -405,10 +407,11 @@ internal constructor(
}
// updating icon colors
- val tintColor = context.getColor(
- if (item.isActive) InternalR.color.materialColorOnPrimaryContainer
- else InternalR.color.materialColorOnSurface
- )
+ val tintColor =
+ context.getColor(
+ if (item.isActive) InternalR.color.materialColorOnPrimaryContainer
+ else InternalR.color.materialColorOnSurface
+ )
// update icons
iconView.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 26abb48ce7db..73c0179cf8ec 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -55,6 +55,8 @@ interface CommunalSettingsRepository {
/** A [CommunalEnabledState] for the specified user. */
fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+ fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean>
+
/**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
@@ -138,6 +140,20 @@ constructor(
.flowOn(bgDispatcher)
}
+ override fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean> =
+ secureSettings
+ .observerFlow(userId = user.id, names = arrayOf(Settings.Secure.SCREENSAVER_ENABLED))
+ // Force an update
+ .onStart { emit(Unit) }
+ .map {
+ secureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ SCREENSAVER_ENABLED_SETTING_DEFAULT,
+ user.id,
+ ) == 1
+ }
+ .flowOn(bgDispatcher)
+
override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
broadcastDispatcher
.broadcastFlow(
@@ -182,6 +198,7 @@ constructor(
companion object {
const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background"
private const val ENABLED_SETTING_DEFAULT = 1
+ private const val SCREENSAVER_ENABLED_SETTING_DEFAULT = 0
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt
new file mode 100644
index 000000000000..2ccf96abff79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+
+/**
+ * {@link CommunalBackActionInteractor} is responsible for handling back gestures on the glanceable
+ * hub. When invoked SystemUI should navigate back to the lockscreen.
+ */
+@SysUISingleton
+class CommunalBackActionInteractor
+@Inject
+constructor(
+ private val communalInteractor: CommunalInteractor,
+ private val communalSceneInteractor: CommunalSceneInteractor,
+ private val sceneInteractor: SceneInteractor,
+) {
+ fun canBeDismissed(): Boolean {
+ return communalInteractor.isCommunalShowing.value
+ }
+
+ fun onBackPressed() {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/384610333): Properly determine whether to go to dream or lockscreen on back.
+ sceneInteractor.changeScene(
+ toScene = Scenes.Lockscreen,
+ loggingReason = "CommunalBackActionInteractor",
+ )
+ } else {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ loggingReason = "CommunalBackActionInteractor",
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index ea428698e476..947113da0e60 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -285,7 +285,7 @@ constructor(
* use [isIdleOnCommunal].
*/
// TODO(b/323215860): rename to something more appropriate after cleaning up usages
- val isCommunalShowing: Flow<Boolean> =
+ val isCommunalShowing: StateFlow<Boolean> =
flow { emit(SceneContainerFlag.isEnabled) }
.flatMapLatest { sceneContainerEnabled ->
if (sceneContainerEnabled) {
@@ -304,10 +304,10 @@ constructor(
columnName = "isCommunalShowing",
initialValue = false,
)
- .shareIn(
+ .stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- replay = 1,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 862b05bc9b5d..c1f21e4046a3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -69,6 +69,12 @@ constructor(
// Start this eagerly since the value is accessed synchronously in many places.
.stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+ /** Whether or not screensaver (dreams) is enabled for the currently selected user. */
+ val isScreensaverEnabled: Flow<Boolean> =
+ userInteractor.selectedUserInfo.flatMapLatest { user ->
+ repository.getScreensaverEnabledState(user)
+ }
+
/**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
index 7d5b196dfaa8..c6f96e198b91 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
@@ -18,10 +18,15 @@ package com.android.systemui.communal.ui.viewmodel
import android.annotation.SuppressLint
import android.app.DreamManager
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.kotlin.isDevicePluggedIn
+import com.android.systemui.util.kotlin.sample
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlin.coroutines.CoroutineContext
@@ -31,7 +36,6 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -41,6 +45,8 @@ class CommunalToDreamButtonViewModel
constructor(
@Background private val backgroundContext: CoroutineContext,
batteryController: BatteryController,
+ private val settingsInteractor: CommunalSettingsInteractor,
+ private val activityStarter: ActivityStarter,
private val dreamManager: DreamManager,
) : ExclusiveActivatable() {
@@ -49,11 +55,7 @@ constructor(
/** Whether we should show a button on hub to switch to dream. */
@SuppressLint("MissingPermission")
val shouldShowDreamButtonOnHub =
- batteryController
- .isDevicePluggedIn()
- .distinctUntilChanged()
- .map { isPluggedIn -> isPluggedIn && dreamManager.canStartDreaming(true) }
- .flowOn(backgroundContext)
+ batteryController.isDevicePluggedIn().distinctUntilChanged().flowOn(backgroundContext)
/** Handle a tap on the "show dream" button. */
fun onShowDreamButtonTap() {
@@ -63,9 +65,21 @@ constructor(
@SuppressLint("MissingPermission")
override suspend fun onActivated(): Nothing = coroutineScope {
launch {
- _requests.receiveAsFlow().collectLatest {
- withContext(backgroundContext) { dreamManager.startDream() }
- }
+ _requests
+ .receiveAsFlow()
+ .sample(settingsInteractor.isScreensaverEnabled)
+ .collectLatest { enabled ->
+ withContext(backgroundContext) {
+ if (enabled) {
+ dreamManager.startDream()
+ } else {
+ activityStarter.postStartActivityDismissingKeyguard(
+ Intent(Settings.ACTION_DREAM_SETTINGS),
+ 0,
+ )
+ }
+ }
+ }
}
awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 9ae106c3ab39..014c0db618e1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -267,6 +267,7 @@ public class FrameworkServicesModule {
}
@Provides
+ @Nullable
@Singleton
static VirtualDeviceManager provideVirtualDeviceManager(Context context) {
return context.getSystemService(VirtualDeviceManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 571b37f43fd4..b272d65a8a11 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -54,6 +54,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.PhoneWindow;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Flags;
import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.ambient.touch.TouchMonitor;
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
@@ -210,6 +211,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mCommunalVisible = communalVisible;
updateLifecycleStateLocked();
+ updateGestureBlockingLocked();
});
}
};
@@ -585,7 +587,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private void updateGestureBlockingLocked() {
final boolean shouldBlock = mStarted && !mShadeExpanded && !mBouncerShowing
- && !isDreamInPreviewMode();
+ && !isDreamInPreviewMode()
+ && !(Flags.glanceableHubBackAction() && mCommunalVisible);
if (shouldBlock) {
mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index f549e64ca853..d0065c8b06c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -39,7 +39,6 @@ import androidx.annotation.VisibleForTesting
import androidx.core.math.MathUtils
import com.android.app.animation.Interpolators
import com.android.internal.R
-import com.android.keyguard.KeyguardClockSwitchController
import com.android.keyguard.KeyguardViewController
import com.android.systemui.Flags.fasterUnlockTransition
import com.android.systemui.dagger.SysUISingleton
@@ -206,7 +205,7 @@ constructor(
fun onUnlockAnimationFinished() {}
}
- /** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */
+ /** The SmartSpace view on the lockscreen. */
var lockscreenSmartspace: View? = null
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9f131607cb99..63ac5094c400 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -4058,7 +4058,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback)
throws RemoteException {
- mRunner = mActivityTransitionAnimator.get().createRunner(mActivityLaunchController);
+ mRunner = mActivityTransitionAnimator.get()
+ .createEphemeralRunner(mActivityLaunchController);
mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt
index 4bac8f7a1b47..a1fb1a7bb113 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt
@@ -110,7 +110,7 @@ constructor(
apps: Array<RemoteAnimationTarget>,
wallpapers: Array<RemoteAnimationTarget>,
nonApps: Array<RemoteAnimationTarget>,
- finishedCallback: IRemoteAnimationFinishedCallback?
+ finishedCallback: IRemoteAnimationFinishedCallback?,
) {
Log.d(TAG, "occludeAnimationRunner#onAnimationStart")
// Wrap the callback so that it's guaranteed to be nulled out once called.
@@ -126,7 +126,7 @@ constructor(
taskInfo = apps.firstOrNull()?.taskInfo,
)
activityTransitionAnimator
- .createRunner(occludeAnimationController)
+ .createEphemeralRunner(occludeAnimationController)
.onAnimationStart(
transit,
apps,
@@ -161,7 +161,7 @@ constructor(
apps: Array<RemoteAnimationTarget>,
wallpapers: Array<RemoteAnimationTarget>,
nonApps: Array<RemoteAnimationTarget>,
- finishedCallback: IRemoteAnimationFinishedCallback?
+ finishedCallback: IRemoteAnimationFinishedCallback?,
) {
Log.d(TAG, "unoccludeAnimationRunner#onAnimationStart")
// Wrap the callback so that it's guaranteed to be nulled out once called.
@@ -179,14 +179,14 @@ constructor(
interactionJankMonitor.begin(
createInteractionJankMonitorConf(
InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION,
- "UNOCCLUDE"
+ "UNOCCLUDE",
)
)
if (apps.isEmpty()) {
Log.d(
TAG,
"No apps provided to unocclude runner; " +
- "skipping animation and unoccluding."
+ "skipping animation and unoccluding.",
)
unoccludeAnimationFinishedCallback?.onAnimationFinished()
return
@@ -210,7 +210,7 @@ constructor(
0f,
(1f - animatedValue) *
surfaceHeight *
- UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT
+ UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT,
)
SurfaceParams.Builder(target.leash)
@@ -313,12 +313,12 @@ constructor(
private fun createInteractionJankMonitorConf(
cuj: Int,
- tag: String?
+ tag: String?,
): InteractionJankMonitor.Configuration.Builder {
val builder =
InteractionJankMonitor.Configuration.Builder.withView(
cuj,
- keyguardViewController.get().getViewRootImpl().view
+ keyguardViewController.get().getViewRootImpl().view,
)
return if (tag != null) builder.setTag(tag) else builder
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index 74ee052f12b9..57f06fbd3bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -21,13 +21,13 @@ import android.app.StatusBarManager
import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.pm.PackageManager
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.camera.CameraGestureHelper
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeDisplayAware
import dagger.Lazy
@@ -65,7 +65,7 @@ constructor(
icon =
Icon.Resource(
R.drawable.ic_camera,
- ContentDescription.Resource(R.string.accessibility_camera_button)
+ ContentDescription.Resource(R.string.accessibility_camera_button),
)
)
} else {
@@ -88,7 +88,7 @@ constructor(
cameraGestureHelper
.get()
.launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
}
private suspend fun isLaunchable(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index e8d3bfac6361..1b8baf657948 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -210,16 +210,16 @@ constructor(
): KeyguardQuickAffordanceConfig.OnTriggeredResult {
return if (ModesUi.isEnabled) {
if (!isAvailable.value) {
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
} else {
val dnd = interactor.dndMode.value
if (dnd == null) {
Log.wtf(TAG, "Triggered DND but it's null!?")
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
if (dnd.isActive) {
interactor.deactivateMode(dnd)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
} else {
if (interactor.shouldAskForZenDuration(dnd)) {
// NOTE: The dialog handles turning on the mode itself.
@@ -229,16 +229,16 @@ constructor(
)
} else {
interactor.activateMode(dnd)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
}
}
} else {
when {
- !oldIsAvailable -> KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ !oldIsAvailable -> KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
zenMode != ZEN_MODE_OFF -> {
controller.setZen(ZEN_MODE_OFF, null, TAG)
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
settingsValue == ZEN_DURATION_PROMPT ->
@@ -249,12 +249,12 @@ constructor(
settingsValue == ZEN_DURATION_FOREVER -> {
controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG)
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
else -> {
controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, conditionUri, TAG)
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index 480ef5e19d8e..e2642a0964c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -18,15 +18,14 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.content.Context
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.FlashlightController
import javax.inject.Inject
@@ -50,9 +49,9 @@ constructor(
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
R.drawable.qs_flashlight_icon_on,
- ContentDescription.Resource(R.string.quick_settings_flashlight_label)
+ ContentDescription.Resource(R.string.quick_settings_flashlight_label),
),
- ActivationState.Active
+ ActivationState.Active,
)
}
@@ -61,9 +60,9 @@ constructor(
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
R.drawable.qs_flashlight_icon_off,
- ContentDescription.Resource(R.string.quick_settings_flashlight_label)
+ ContentDescription.Resource(R.string.quick_settings_flashlight_label),
),
- ActivationState.Inactive
+ ActivationState.Inactive,
)
}
@@ -92,14 +91,14 @@ constructor(
} else {
FlashlightState.OffAvailable.toLockScreenState()
},
- TAG
+ TAG,
)
}
override fun onFlashlightError() {
trySendWithFailureLogging(
FlashlightState.OffAvailable.toLockScreenState(),
- TAG
+ TAG,
)
}
@@ -114,7 +113,7 @@ constructor(
FlashlightState.OffAvailable.toLockScreenState()
}
},
- TAG
+ TAG,
)
}
}
@@ -130,7 +129,7 @@ constructor(
flashlightController.setFlashlight(
flashlightController.isAvailable && !flashlightController.isEnabled
)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
index d335a1806a6d..06da281648a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -111,7 +111,7 @@ constructor(
transitionKey = CommunalTransitionKeys.SimpleFade,
)
}
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 1cf6183fec6c..ade65c38ff3c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -21,10 +21,10 @@ import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.net.Uri
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.res.R
import kotlinx.coroutines.flow.Flow
/** Defines interface that can act as data source for a single quick affordance model. */
@@ -71,7 +71,7 @@ interface KeyguardQuickAffordanceConfig {
/** The picker shows the item for selecting this affordance as it normally would. */
data class Default(
/** Optional [Intent] to use to start an activity to configure this affordance. */
- val configureIntent: Intent? = null,
+ val configureIntent: Intent? = null
) : PickerScreenState()
/**
@@ -134,34 +134,39 @@ interface KeyguardQuickAffordanceConfig {
) : LockScreenState()
}
- sealed class OnTriggeredResult {
+ sealed class OnTriggeredResult() {
/**
* Returning this as a result from the [onTriggered] method means that the implementation
* has taken care of the action, the system will do nothing.
+ *
+ * @param[actionLaunched] Whether the implementation handled the action by launching a
+ * dialog or an activity.
*/
- object Handled : OnTriggeredResult()
+ data class Handled(val actionLaunched: Boolean) : OnTriggeredResult()
/**
* Returning this as a result from the [onTriggered] method means that the implementation
* has _not_ taken care of the action and the system should start an activity using the
* given [Intent].
*/
- data class StartActivity(
- val intent: Intent,
- val canShowWhileLocked: Boolean,
- ) : OnTriggeredResult()
+ data class StartActivity(val intent: Intent, val canShowWhileLocked: Boolean) :
+ OnTriggeredResult()
/**
* Returning this as a result from the [onTriggered] method means that the implementation
* has _not_ taken care of the action and the system should show a Dialog using the given
* [AlertDialog] and [Expandable].
*/
- data class ShowDialog(
- val dialog: AlertDialog,
- val expandable: Expandable?,
- ) : OnTriggeredResult()
+ data class ShowDialog(val dialog: AlertDialog, val expandable: Expandable?) :
+ OnTriggeredResult()
}
+ /**
+ * Models an [OnTriggeredResult] that did or did not launch a dialog or activity for a given
+ * config key.
+ */
+ data class LaunchingFromTriggeredResult(val launched: Boolean, val configKey: String)
+
companion object {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
index 1358634a55f8..1c9bc9f39663 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.media.AudioManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
@@ -45,7 +46,6 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
@SysUISingleton
@@ -118,7 +118,7 @@ constructor(
audioManager.ringerModeInternal = newRingerMode
}
}
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
@@ -140,11 +140,11 @@ constructor(
.getSharedPreferences(
MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
Context.MODE_PRIVATE,
- userTracker.userId
+ userTracker.userId,
)
.getInt(
LAST_NON_SILENT_RINGER_MODE_KEY,
- ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE
+ ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index eafa1cea59f3..cb7702e090d0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -30,7 +30,6 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
@@ -72,21 +71,15 @@ constructor(
override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
val hasCards =
getPaymentCards(response.walletCards)?.isNotEmpty() == true
- trySendWithFailureLogging(
- hasCards,
- TAG,
- )
+ trySendWithFailureLogging(hasCards, TAG)
}
override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
Log.e(
TAG,
- "Wallet card retrieval error, message: \"${error?.message}\""
- )
- trySendWithFailureLogging(
- null,
- TAG,
+ "Wallet card retrieval error, message: \"${error?.message}\"",
)
+ trySendWithFailureLogging(null, TAG)
}
}
@@ -94,7 +87,7 @@ constructor(
callback,
QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE,
- QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE
+ QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE,
)
withContext(backgroundDispatcher) {
@@ -107,7 +100,7 @@ constructor(
walletController.unregisterWalletChangeObservers(
QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE,
- QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE
+ QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE,
)
}
}
@@ -117,11 +110,7 @@ constructor(
if (hasCards == null) {
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
} else {
- state(
- isWalletAvailable(),
- hasCards,
- walletController.walletClient.tileIcon,
- )
+ state(isWalletAvailable(), hasCards, walletController.walletClient.tileIcon)
}
flowOf(state)
}
@@ -135,28 +124,28 @@ constructor(
explanation =
context.getString(
R.string.wallet_quick_affordance_unavailable_install_the_app
- ),
+ )
)
queryCards().isEmpty() ->
KeyguardQuickAffordanceConfig.PickerScreenState.Disabled(
explanation =
context.getString(
R.string.wallet_quick_affordance_unavailable_configure_the_app
- ),
+ )
)
else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default()
}
}
override fun onTriggered(
- expandable: Expandable?,
+ expandable: Expandable?
): KeyguardQuickAffordanceConfig.OnTriggeredResult {
walletController.startQuickAccessUiIntent(
activityStarter,
expandable?.activityTransitionController(),
/* hasCard= */ true,
)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
}
private suspend fun queryCards(): List<WalletCard> {
@@ -199,10 +188,8 @@ constructor(
Icon.Loaded(
drawable = tileIcon,
contentDescription =
- ContentDescription.Resource(
- res = R.string.accessibility_wallet_button,
- ),
- ),
+ ContentDescription.Resource(res = R.string.accessibility_wallet_button),
+ )
)
} else {
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index ae55825c9842..9c2daf52c5df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -29,7 +29,6 @@ import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
import com.android.systemui.dock.DockManager
@@ -62,6 +61,7 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
@@ -101,6 +101,14 @@ constructor(
val launchingAffordance: StateFlow<Boolean> = repository.get().launchingAffordance.asStateFlow()
/**
+ * Whether a [KeyguardQuickAffordanceConfig.OnTriggeredResult] indicated that the system
+ * launched an activity or showed a dialog.
+ */
+ private val _launchingFromTriggeredResult =
+ MutableStateFlow<KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult?>(null)
+ val launchingFromTriggeredResult = _launchingFromTriggeredResult.asStateFlow()
+
+ /**
* Whether the UI should use the long press gesture to activate quick affordances.
*
* If `false`, the UI goes back to using single taps.
@@ -187,18 +195,45 @@ constructor(
metricsLogger.logOnShortcutTriggered(slotId, configKey)
when (val result = config.onTriggered(expandable)) {
- is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity ->
+ is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity -> {
+ setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(
+ launched = true,
+ configKey,
+ )
+ )
launchQuickAffordance(
intent = result.intent,
canShowWhileLocked = result.canShowWhileLocked,
expandable = expandable,
)
- is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> Unit
- is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog ->
+ }
+ is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> {
+ setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(
+ result.actionLaunched,
+ configKey,
+ )
+ )
+ }
+ is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog -> {
+ setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(
+ launched = true,
+ configKey,
+ )
+ )
showDialog(result.dialog, result.expandable)
+ }
}
}
+ fun setLaunchingFromTriggeredResult(
+ launchingResult: KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult?
+ ) {
+ _launchingFromTriggeredResult.value = launchingResult
+ }
+
/**
* Selects an affordance with the given ID on the slot with the given ID.
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index aa44b6d46289..382436cf9397 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor.scenetransition
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.CoreStartable
@@ -38,7 +39,6 @@ import java.util.UUID
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* This class listens to scene framework scene transitions and manages keyguard transition framework
@@ -111,7 +111,10 @@ constructor(
if (currentTransitionId == null) return
if (prevTransition !is ObservableTransitionState.Transition) return
- if (idle.currentScene == prevTransition.toContent) {
+ if (
+ idle.currentScene == prevTransition.toContent ||
+ idle.currentOverlays.contains(prevTransition.toContent)
+ ) {
finishCurrentTransition()
} else {
val targetState =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
index e7803c5e964c..a4a5ba691965 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
@@ -17,12 +17,23 @@
package com.android.systemui.keyguard.ui.binder
import android.os.VibrationEffect
+import com.android.systemui.Flags
import kotlin.time.Duration.Companion.milliseconds
object KeyguardBottomAreaVibrations {
- val ShakeAnimationDuration = 300.milliseconds
- const val ShakeAnimationCycles = 5f
+ val ShakeAnimationDuration =
+ if (Flags.msdlFeedback()) {
+ 285.milliseconds
+ } else {
+ 300.milliseconds
+ }
+ val ShakeAnimationCycles =
+ if (Flags.msdlFeedback()) {
+ 3f
+ } else {
+ 5f
+ }
private const val SmallVibrationScale = 0.3f
private const val BigVibrationScale = 0.6f
@@ -32,7 +43,7 @@ object KeyguardBottomAreaVibrations {
.apply {
val vibrationDelayMs =
(ShakeAnimationDuration.inWholeMilliseconds / (ShakeAnimationCycles * 2))
- .toInt()
+ .toInt()
val vibrationCount = ShakeAnimationCycles.toInt() * 2
repeat(vibrationCount) {
@@ -47,29 +58,13 @@ object KeyguardBottomAreaVibrations {
val Activated =
VibrationEffect.startComposition()
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_TICK,
- BigVibrationScale,
- 0,
- )
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
- 0.1f,
- 0,
- )
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.1f, 0)
.compose()
val Deactivated =
VibrationEffect.startComposition()
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_TICK,
- BigVibrationScale,
- 0,
- )
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_QUICK_FALL,
- 0.1f,
- 0,
- )
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.1f, 0)
.compose()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 8725cdd273df..8a2e3dd791c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard.ui.binder
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.graphics.drawable.Animatable2
+import android.os.VibrationEffect
import android.util.Size
import android.view.View
import android.view.ViewGroup
@@ -33,25 +34,27 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
+import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceHapticViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.doOnEnd
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.MSDLPlayer
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
/** This is only for a SINGLE Quick affordance */
@SysUISingleton
@@ -60,8 +63,9 @@ class KeyguardQuickAffordanceViewBinder
constructor(
private val falsingManager: FalsingManager?,
private val vibratorHelper: VibratorHelper?,
+ private val msdlPlayer: MSDLPlayer,
private val logger: KeyguardQuickAffordancesLogger,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
+ private val hapticsViewModelFactory: KeyguardQuickAffordanceHapticViewModel.Factory,
) {
private val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
@@ -88,6 +92,12 @@ constructor(
): Binding {
val button = view as ImageView
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
+ val hapticsViewModel =
+ if (Flags.msdlFeedback()) {
+ hapticsViewModelFactory.create(viewModel)
+ } else {
+ null
+ }
val disposableHandle =
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -98,15 +108,12 @@ constructor(
viewModel = buttonModel,
messageDisplayer = messageDisplayer,
)
+ hapticsViewModel?.updateActivatedHistory(buttonModel.isActivated)
}
}
launch {
- updateButtonAlpha(
- view = button,
- viewModel = viewModel,
- alphaFlow = alpha,
- )
+ updateButtonAlpha(view = button, viewModel = viewModel, alphaFlow = alpha)
}
launch {
@@ -117,6 +124,32 @@ constructor(
}
}
}
+
+ if (Flags.msdlFeedback()) {
+ launch {
+ hapticsViewModel
+ ?.quickAffordanceHapticState
+ ?.filter {
+ it !=
+ KeyguardQuickAffordanceHapticViewModel.HapticState
+ .NO_HAPTICS
+ }
+ ?.collect { state ->
+ when (state) {
+ KeyguardQuickAffordanceHapticViewModel.HapticState
+ .TOGGLE_ON -> msdlPlayer.playToken(MSDLToken.SWITCH_ON)
+ KeyguardQuickAffordanceHapticViewModel.HapticState
+ .TOGGLE_OFF ->
+ msdlPlayer.playToken(MSDLToken.SWITCH_OFF)
+ KeyguardQuickAffordanceHapticViewModel.HapticState.LAUNCH ->
+ msdlPlayer.playToken(MSDLToken.LONG_PRESS)
+ KeyguardQuickAffordanceHapticViewModel.HapticState
+ .NO_HAPTICS -> Unit
+ }
+ hapticsViewModel.resetLaunchingFromTriggeredResult()
+ }
+ }
+ }
}
}
@@ -178,7 +211,7 @@ constructor(
com.android.internal.R.color.materialColorOnPrimaryFixed
} else {
com.android.internal.R.color.materialColorOnSurface
- },
+ }
)
)
@@ -221,12 +254,7 @@ constructor(
.getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
.toFloat()
val shakeAnimator =
- ObjectAnimator.ofFloat(
- view,
- "translationX",
- -amplitude / 2,
- amplitude / 2,
- )
+ ObjectAnimator.ofFloat(view, "translationX", -amplitude / 2, amplitude / 2)
shakeAnimator.duration =
KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
@@ -234,11 +262,17 @@ constructor(
shakeAnimator.doOnEnd { view.translationX = 0f }
shakeAnimator.start()
- vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+ vibratorHelper?.playFeedback(KeyguardBottomAreaVibrations.Shake, msdlPlayer)
logger.logQuickAffordanceTapped(viewModel.configKey)
}
view.onLongClickListener =
- OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
+ OnLongClickListener(
+ falsingManager,
+ viewModel,
+ vibratorHelper,
+ onTouchListener,
+ msdlPlayer,
+ )
} else {
view.setOnClickListener(OnClickListener(viewModel, checkNotNull(falsingManager)))
}
@@ -268,7 +302,7 @@ constructor(
Size(
view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
- ),
+ )
)
}
@@ -297,7 +331,8 @@ constructor(
private val falsingManager: FalsingManager?,
private val viewModel: KeyguardQuickAffordanceViewModel,
private val vibratorHelper: VibratorHelper?,
- private val onTouchListener: KeyguardQuickAffordanceOnTouchListener
+ private val onTouchListener: KeyguardQuickAffordanceOnTouchListener,
+ private val msdlPlayer: MSDLPlayer,
) : View.OnLongClickListener {
override fun onLongClick(view: View): Boolean {
if (falsingManager?.isFalseLongTap(FalsingManager.MODERATE_PENALTY) == true) {
@@ -312,12 +347,13 @@ constructor(
slotId = viewModel.slotId,
)
)
- vibratorHelper?.vibrate(
+ vibratorHelper?.playFeedback(
if (viewModel.isActivated) {
KeyguardBottomAreaVibrations.Activated
} else {
KeyguardBottomAreaVibrations.Deactivated
- }
+ },
+ msdlPlayer,
)
}
@@ -328,7 +364,15 @@ constructor(
override fun onLongClickUseDefaultHapticFeedback(view: View) = false
}
- private data class ConfigurationBasedDimensions(
- val buttonSizePx: Size,
- )
+ private data class ConfigurationBasedDimensions(val buttonSizePx: Size)
+}
+
+private fun VibratorHelper.playFeedback(effect: VibrationEffect, msdlPlayer: MSDLPlayer) {
+ if (!Flags.msdlFeedback()) {
+ vibrate(effect)
+ } else {
+ if (effect == KeyguardBottomAreaVibrations.Shake) {
+ msdlPlayer.playToken(MSDLToken.FAILURE)
+ }
+ }
}
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 a2ce4ec5ce9b..6d270b219c81 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
@@ -127,21 +127,18 @@ object KeyguardRootViewBinder {
if (Flags.nonTouchscreenDevicesBypassFalsing()) {
if (
event.action == MotionEvent.ACTION_DOWN &&
- event.buttonState == MotionEvent.BUTTON_PRIMARY &&
- !event.isTouchscreenSource()
+ event.buttonState == MotionEvent.BUTTON_PRIMARY &&
+ !event.isTouchscreenSource()
) {
consumed = true
} else if (
- event.action == MotionEvent.ACTION_UP &&
- !event.isTouchscreenSource()
+ event.action == MotionEvent.ACTION_UP && !event.isTouchscreenSource()
) {
statusBarKeyguardViewManager?.showBouncer(true)
consumed = true
}
}
- viewModel.setRootViewLastTapPosition(
- Point(event.x.toInt(), event.y.toInt())
- )
+ viewModel.setRootViewLastTapPosition(Point(event.x.toInt(), event.y.toInt()))
}
consumed
}
@@ -172,7 +169,6 @@ object KeyguardRootViewBinder {
launch("$TAG#alpha") {
viewModel.alpha(viewState).collect { alpha ->
view.alpha = alpha
- childViews[statusViewId]?.alpha = alpha
childViews[burnInLayerId]?.alpha = alpha
}
}
@@ -253,18 +249,6 @@ object KeyguardRootViewBinder {
}
launch {
- viewModel.burnInLayerAlpha.collect { alpha ->
- childViews[statusViewId]?.alpha = alpha
- }
- }
-
- launch {
- viewModel.lockscreenStateAlpha(viewState).collect { alpha ->
- childViews[statusViewId]?.alpha = alpha
- }
- }
-
- launch {
viewModel.scale.collect { scaleViewModel ->
if (scaleViewModel.scaleClockOnly) {
// For clocks except weather clock, we have scale transition besides
@@ -553,7 +537,6 @@ object KeyguardRootViewBinder {
return device?.supportsSource(InputDevice.SOURCE_TOUCHSCREEN) == true
}
- private val statusViewId = R.id.keyguard_status_view
private val burnInLayerId = R.id.burn_in_layer
private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
private val largeClockId = customR.id.lockscreen_clock_view_large
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 090b65922d2d..6fb31c0e4191 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -48,19 +48,16 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.core.view.isInvisible
-import com.android.internal.policy.SystemBarUtils
import com.android.keyguard.ClockEventController
-import com.android.keyguard.KeyguardClockSwitch
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder
import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel
-import com.android.systemui.coroutines.newTracingContext
+import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
@@ -80,7 +77,6 @@ import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.systemui.shared.clocks.DefaultClockController
import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
@@ -91,18 +87,13 @@ import com.android.systemui.util.settings.SecureSettings
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.json.JSONException
import org.json.JSONObject
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.customization.R as customR
/** Renders the preview of the lock screen. */
class KeyguardPreviewRenderer
@@ -110,7 +101,6 @@ class KeyguardPreviewRenderer
@AssistedInject
constructor(
@Application private val context: Context,
- @Application applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
@Main private val mainHandler: Handler,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@@ -157,8 +147,6 @@ constructor(
val surfacePackage: SurfaceControlViewHost.SurfacePackage
get() = checkNotNull(host.surfacePackage)
- private lateinit var largeClockHostView: FrameLayout
- private lateinit var smallClockHostView: FrameLayout
private var smartSpaceView: View? = null
private val disposables = DisposableHandles()
@@ -166,29 +154,18 @@ constructor(
private val shortcutsBindings = mutableSetOf<KeyguardQuickAffordanceViewBinder.Binding>()
- private val coroutineScope: CoroutineScope
-
@Style.Type private var themeStyle: Int? = null
init {
- coroutineScope =
- CoroutineScope(
- applicationScope.coroutineContext +
- Job() +
- newTracingContext("KeyguardPreviewRenderer")
- )
- disposables += DisposableHandle { coroutineScope.cancel() }
clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
-
quickAffordancesCombinedViewModel.enablePreviewMode(
initiallySelectedSlotId =
- bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
- ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
+ ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
- if (MigrateClocksToBlueprint.isEnabled) {
- clockViewModel.shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance
- }
+
+ clockViewModel.shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance
runBlocking(mainDispatcher) {
host =
SurfaceControlViewHost(
@@ -348,6 +325,7 @@ constructor(
smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
}
+ @OptIn(ExperimentalCoroutinesApi::class)
private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
val keyguardRootView = KeyguardRootView(previewContext, null)
rootView.addView(
@@ -358,34 +336,23 @@ constructor(
),
)
- setUpUdfps(
- previewContext,
- if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView,
- )
+ setUpUdfps(previewContext, keyguardRootView)
setupShortcuts(keyguardRootView)
if (!shouldHideClock) {
setUpClock(previewContext, rootView)
- if (MigrateClocksToBlueprint.isEnabled) {
- KeyguardPreviewClockViewBinder.bind(
- keyguardRootView,
- clockViewModel,
- clockRegistry,
- ::updateClockAppearance,
- ClockPreviewConfig(
- previewContext,
- getPreviewShadeLayoutWide(display!!),
- SceneContainerFlag.isEnabled,
- ),
- )
- } else {
- KeyguardPreviewClockViewBinder.bind(
- largeClockHostView,
- smallClockHostView,
- clockViewModel,
- )
- }
+ KeyguardPreviewClockViewBinder.bind(
+ keyguardRootView,
+ clockViewModel,
+ clockRegistry,
+ ::updateClockAppearance,
+ ClockPreviewConfig(
+ previewContext,
+ getPreviewShadeLayoutWide(display!!),
+ SceneContainerFlag.isEnabled,
+ ),
+ )
}
setUpSmartspace(previewContext, rootView)
@@ -451,82 +418,22 @@ constructor(
.inflate(R.layout.udfps_keyguard_preview, parentView, false) as View
// Place the UDFPS view in the proper sensor location
- if (MigrateClocksToBlueprint.isEnabled) {
- val lockId = KeyguardPreviewClockViewBinder.lockId
- finger.id = lockId
- parentView.addView(finger)
- val cs = ConstraintSet()
- cs.clone(parentView as ConstraintLayout)
- cs.apply {
- constrainWidth(lockId, sensorBounds.width())
- constrainHeight(lockId, sensorBounds.height())
- connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top)
- connect(lockId, START, PARENT_ID, START, sensorBounds.left)
- }
- cs.applyTo(parentView)
- } else {
- val fingerprintLayoutParams =
- FrameLayout.LayoutParams(sensorBounds.width(), sensorBounds.height())
- fingerprintLayoutParams.setMarginsRelative(
- sensorBounds.left,
- sensorBounds.top,
- sensorBounds.right,
- sensorBounds.bottom,
- )
- parentView.addView(finger, fingerprintLayoutParams)
+ val lockId = KeyguardPreviewClockViewBinder.lockId
+ finger.id = lockId
+ parentView.addView(finger)
+ val cs = ConstraintSet()
+ cs.clone(parentView as ConstraintLayout)
+ cs.apply {
+ constrainWidth(lockId, sensorBounds.width())
+ constrainHeight(lockId, sensorBounds.height())
+ connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top)
+ connect(lockId, START, PARENT_ID, START, sensorBounds.left)
}
+ cs.applyTo(parentView)
}
private fun setUpClock(previewContext: Context, parentView: ViewGroup) {
val resources = parentView.resources
- if (!MigrateClocksToBlueprint.isEnabled) {
- largeClockHostView = FrameLayout(previewContext)
- largeClockHostView.layoutParams =
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT,
- )
- largeClockHostView.isInvisible = true
- parentView.addView(largeClockHostView)
-
- smallClockHostView = FrameLayout(previewContext)
- val layoutParams =
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- resources.getDimensionPixelSize(customR.dimen.small_clock_height),
- )
- layoutParams.topMargin =
- SystemBarUtils.getStatusBarHeight(previewContext) +
- resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top)
- smallClockHostView.layoutParams = layoutParams
- smallClockHostView.setPaddingRelative(
- /* start = */ resources.getDimensionPixelSize(customR.dimen.clock_padding_start),
- /* top = */ 0,
- /* end = */ 0,
- /* bottom = */ 0,
- )
- smallClockHostView.clipChildren = false
- parentView.addView(smallClockHostView)
- smallClockHostView.isInvisible = true
- }
-
- // TODO (b/283465254): Move the listeners to KeyguardClockRepository
- if (!MigrateClocksToBlueprint.isEnabled) {
- val clockChangeListener =
- object : ClockRegistry.ClockChangeListener {
- override fun onCurrentClockChanged() {
- onClockChanged()
- }
- }
- clockRegistry.registerClockChangeListener(clockChangeListener)
- disposables += DisposableHandle {
- clockRegistry.unregisterClockChangeListener(clockChangeListener)
- }
-
- clockController.registerListeners(parentView)
- disposables += DisposableHandle { clockController.unregisterListeners() }
- }
-
val receiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
@@ -544,38 +451,9 @@ constructor(
},
)
disposables += DisposableHandle { broadcastDispatcher.unregisterReceiver(receiver) }
-
- if (!MigrateClocksToBlueprint.isEnabled) {
- val layoutChangeListener =
- View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
- if (clockController.clock !is DefaultClockController) {
- clockController.clock
- ?.largeClock
- ?.events
- ?.onTargetRegionChanged(
- KeyguardClockSwitch.getLargeClockRegion(parentView)
- )
- clockController.clock
- ?.smallClock
- ?.events
- ?.onTargetRegionChanged(
- KeyguardClockSwitch.getSmallClockRegion(parentView)
- )
- }
- }
- parentView.addOnLayoutChangeListener(layoutChangeListener)
- disposables += DisposableHandle {
- parentView.removeOnLayoutChangeListener(layoutChangeListener)
- }
- }
-
- onClockChanged()
}
private suspend fun updateClockAppearance(clock: ClockController, resources: Resources) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- clockController.clock = clock
- }
val colors = wallpaperColors
if (clockRegistry.seedColor == null && colors != null) {
// Seed color null means users do not override any color on the clock. The default
@@ -601,9 +479,7 @@ constructor(
// In clock preview, we should have a seed color for clock
// before setting clock to clockEventController to avoid updateColor with seedColor == null
// So in update colors, it should already have the correct theme in clockFaceController
- if (MigrateClocksToBlueprint.isEnabled) {
- clockController.clock = clock
- }
+ clockController.clock = clock
// When set clock to clockController,it will reset fontsize based on context.resources
// We need to override it with overlaid resources
clock.largeClock.events.onFontSettingChanged(
@@ -611,19 +487,6 @@ constructor(
)
}
- private fun onClockChanged() {
- if (MigrateClocksToBlueprint.isEnabled) {
- return
- }
- coroutineScope.launch {
- val clock = clockRegistry.createCurrentClock()
- clockController.clock = clock
- updateClockAppearance(clock, context.resources)
- updateLargeClock(clock)
- updateSmallClock(clock)
- }
- }
-
private fun setupCommunalTutorialIndicator(keyguardRootView: ConstraintLayout) {
keyguardRootView.findViewById<TextView>(R.id.communal_tutorial_indicator)?.let {
indicatorView ->
@@ -657,34 +520,6 @@ constructor(
}
}
- private fun updateLargeClock(clock: ClockController) {
- if (MigrateClocksToBlueprint.isEnabled) {
- return
- }
- clock.largeClock.events.onTargetRegionChanged(
- KeyguardClockSwitch.getLargeClockRegion(largeClockHostView)
- )
- if (shouldHighlightSelectedAffordance) {
- clock.largeClock.view.alpha = DIM_ALPHA
- }
- largeClockHostView.removeAllViews()
- largeClockHostView.addView(clock.largeClock.view)
- }
-
- private fun updateSmallClock(clock: ClockController) {
- if (MigrateClocksToBlueprint.isEnabled) {
- return
- }
- clock.smallClock.events.onTargetRegionChanged(
- KeyguardClockSwitch.getSmallClockRegion(smallClockHostView)
- )
- if (shouldHighlightSelectedAffordance) {
- clock.smallClock.view.alpha = DIM_ALPHA
- }
- smallClockHostView.removeAllViews()
- smallClockHostView.addView(clock.smallClock.view)
- }
-
private fun getPreviewShadeLayoutWide(display: Display): Boolean {
return if (display.displayId == 0) {
shadeInteractor.isShadeLayoutWide.value
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index 160380bb09bc..57fe15d4f52c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -24,7 +24,6 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
@@ -50,9 +49,6 @@ constructor(
}
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
if (emptyView.parent != null) {
// As emptyView is lazy, it might be already attached.
(emptyView.parent as? ViewGroup)?.removeView(emptyView)
@@ -68,17 +64,10 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
clockViewModel.burnInLayer = burnInLayer
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
constraintSet.apply {
// The empty view should not occupy any space
constrainHeight(R.id.burn_in_layer_empty_view, 1)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 70bf8bca55b9..738fb73a4918 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -32,7 +32,6 @@ import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -82,9 +81,6 @@ constructor(
override fun addViews(constraintLayout: ConstraintLayout) {}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
disposableHandle?.dispose()
disposableHandle =
KeyguardClockViewBinder.bind(
@@ -99,20 +95,12 @@ constructor(
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
keyguardClockViewModel.currentClock.value?.let { clock ->
constraintSet.applyDeltaFrom(buildConstraints(clock, constraintSet))
}
}
override fun removeViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
disposableHandle?.dispose()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 3a791fd45528..4bfe5f0458c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -24,7 +24,6 @@ import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.NotificationPanelView
@@ -54,32 +53,25 @@ constructor(
sharedNotificationContainerBinder,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
constraintSet.apply {
val bottomMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
- if (MigrateClocksToBlueprint.isEnabled) {
- val useLargeScreenHeader =
- context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)
- val marginTopLargeScreen =
- largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
- connect(
- R.id.nssl_placeholder,
- TOP,
- R.id.smart_space_barrier_bottom,
- BOTTOM,
- bottomMargin +
- if (useLargeScreenHeader) {
- marginTopLargeScreen
- } else {
- 0
- }
- )
- } else {
- connect(R.id.nssl_placeholder, TOP, R.id.keyguard_status_view, BOTTOM, bottomMargin)
- }
+ val useLargeScreenHeader =
+ context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)
+ val marginTopLargeScreen =
+ largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+ connect(
+ R.id.nssl_placeholder,
+ TOP,
+ R.id.smart_space_barrier_bottom,
+ BOTTOM,
+ bottomMargin +
+ if (useLargeScreenHeader) {
+ marginTopLargeScreen
+ } else {
+ 0
+ },
+ )
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
index 620cc13a0c3a..fc26d18fde6b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -25,7 +25,6 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
@@ -62,9 +61,6 @@ constructor(
}
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
// This moves the existing NSSL view to a different parent, as the controller is a
// singleton and recreating it has other bad side effects.
// In the SceneContainer, this is done by the NotificationSection composable.
@@ -78,10 +74,6 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
disposableHandle?.dispose()
disposableHandle =
sharedNotificationContainerBinder.bind(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 73e14b1524f3..cd038d799f42 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -26,7 +26,6 @@ import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -70,7 +69,6 @@ constructor(
}
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
smartspaceView = smartspaceController.buildAndConnectView(constraintLayout)
weatherView = smartspaceController.buildAndConnectWeatherView(constraintLayout)
@@ -98,7 +96,6 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
disposableHandle?.dispose()
disposableHandle =
@@ -111,13 +108,11 @@ constructor(
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
val dateWeatherPaddingStart = KeyguardSmartspaceViewModel.getDateWeatherStartMargin(context)
val smartspaceHorizontalPadding =
KeyguardSmartspaceViewModel.getSmartspaceHorizontalMargin(context)
constraintSet.apply {
- // migrate addDateWeatherView, addWeatherView from KeyguardClockSwitchController
constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
constrainWidth(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
connect(
@@ -128,7 +123,6 @@ constructor(
dateWeatherPaddingStart,
)
- // migrate addSmartspaceView from KeyguardClockSwitchController
constrainHeight(sharedR.id.bc_smartspace_view, ConstraintSet.WRAP_CONTENT)
constrainWidth(sharedR.id.bc_smartspace_view, ConstraintSet.MATCH_CONSTRAINT)
connect(
@@ -182,7 +176,6 @@ constructor(
}
override fun removeViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
listOf(smartspaceView, dateWeatherView).forEach {
it?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index 729759a9ad00..5d463f72d8b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -23,7 +23,6 @@ import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.shade.ShadeDisplayAware
@@ -50,16 +49,13 @@ constructor(
sharedNotificationContainerBinder,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
constraintSet.apply {
connect(
R.id.nssl_placeholder,
TOP,
PARENT_ID,
TOP,
- context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin),
)
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 1c897237fe89..fb311a533aa2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -24,7 +24,6 @@ import com.android.app.animation.Interpolators
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -194,12 +193,7 @@ constructor(
(!useAltAod) && keyguardClockViewModel.clockSize.value == ClockSize.LARGE
val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt()
- val translationY =
- if (MigrateClocksToBlueprint.isEnabled) {
- max(params.topInset - params.minViewY, burnInY)
- } else {
- max(params.topInset, params.minViewY + burnInY) - params.minViewY
- }
+ val translationY = max(params.topInset - params.minViewY, burnInY)
BurnInModel(
translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
translationY = translationY,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt
new file mode 100644
index 000000000000..890628c31c55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+class KeyguardQuickAffordanceHapticViewModel
+@AssistedInject
+constructor(
+ @Assisted quickAffordanceViewModel: Flow<KeyguardQuickAffordanceViewModel>,
+ private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
+) {
+
+ private val activatedHistory = MutableStateFlow(ActivatedHistory(false))
+
+ private val launchingHapticState: Flow<HapticState> =
+ combine(
+ quickAffordanceViewModel.map { it.configKey },
+ quickAffordanceInteractor.launchingFromTriggeredResult,
+ ) { key, launchingResult ->
+ val validKey = key != null && key == launchingResult?.configKey
+ if (validKey && launchingResult?.launched == true) {
+ HapticState.LAUNCH
+ } else {
+ HapticState.NO_HAPTICS
+ }
+ }
+ .distinctUntilChanged()
+
+ private val toggleHapticState: Flow<HapticState> =
+ activatedHistory
+ .map { history ->
+ when {
+ history.previousValue == false && history.currentValue -> HapticState.TOGGLE_ON
+ history.previousValue == true && !history.currentValue -> HapticState.TOGGLE_OFF
+ else -> HapticState.NO_HAPTICS
+ }
+ }
+ .distinctUntilChanged()
+
+ val quickAffordanceHapticState =
+ merge(launchingHapticState, toggleHapticState).distinctUntilChanged()
+
+ fun resetLaunchingFromTriggeredResult() =
+ quickAffordanceInteractor.setLaunchingFromTriggeredResult(null)
+
+ fun updateActivatedHistory(isActivated: Boolean) {
+ activatedHistory.value =
+ ActivatedHistory(
+ currentValue = isActivated,
+ previousValue = activatedHistory.value.currentValue,
+ )
+ }
+
+ enum class HapticState {
+ TOGGLE_ON,
+ TOGGLE_OFF,
+ LAUNCH,
+ NO_HAPTICS,
+ }
+
+ private data class ActivatedHistory(
+ val currentValue: Boolean,
+ val previousValue: Boolean? = null,
+ )
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ quickAffordanceViewModel: Flow<KeyguardQuickAffordanceViewModel>
+ ): KeyguardQuickAffordanceHapticViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 9066d466ceca..eaba5d5a149c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -29,7 +29,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor
import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -130,7 +129,6 @@ constructor(
PrimaryBouncerToLockscreenTransitionViewModel,
private val screenOffAnimationController: ScreenOffAnimationController,
private val aodBurnInViewModel: AodBurnInViewModel,
- private val aodAlphaViewModel: AodAlphaViewModel,
private val shadeInteractor: ShadeInteractor,
) {
val burnInLayerVisibility: Flow<Int> =
@@ -284,15 +282,6 @@ constructor(
.distinctUntilChanged()
}
- /** Specific alpha value for elements visible during [KeyguardState.LOCKSCREEN] */
- @Deprecated("only used for legacy status view")
- fun lockscreenStateAlpha(viewState: ViewStateAccessor): Flow<Float> {
- return aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState)
- }
-
- /** For elements that appear and move during the animation -> AOD */
- val burnInLayerAlpha: Flow<Float> = aodAlphaViewModel.alpha
-
val translationY: Flow<Float> = aodBurnInViewModel.movement.map { it.translationY.toFloat() }
val translationX: Flow<StateToValue> =
diff --git a/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt b/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt
new file mode 100644
index 000000000000..dd2525f5ca45
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.lottie
+
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieListener
+import com.airbnb.lottie.LottieTask
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * Suspends until [LottieTask] is finished with a result or a failure.
+ *
+ * @return result of the [LottieTask] when it's successful
+ */
+suspend fun LottieTask<LottieComposition>.await() =
+ suspendCancellableCoroutine<LottieComposition> { continuation ->
+ val resultListener =
+ LottieListener<LottieComposition> { result ->
+ with(continuation) { if (!isCancelled && !isCompleted) resume(result) }
+ }
+ val failureListener =
+ LottieListener<Throwable> { throwable ->
+ with(continuation) {
+ if (!isCancelled && !isCompleted) resumeWithException(throwable)
+ }
+ }
+ addListener(resultListener)
+ addFailureListener(failureListener)
+ continuation.invokeOnCancellation {
+ removeListener(resultListener)
+ removeFailureListener(failureListener)
+ }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index c32bd403d2e8..b4dabbe036e9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -34,13 +34,14 @@ import android.view.ViewGroup
import android.view.ViewGroupOverlay
import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.keyguard.KeyguardViewController
import com.android.systemui.Flags.mediaControlsLockscreenShadeBugFix
import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -68,7 +69,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapLatest
-import com.android.app.tracing.coroutines.launchTraced as launch
private val TAG: String = MediaHierarchyManager::class.java.simpleName
@@ -115,7 +115,7 @@ constructor(
wakefulnessLifecycle: WakefulnessLifecycle,
shadeInteractor: ShadeInteractor,
private val secureSettings: SecureSettings,
- @Main private val handler: Handler,
+ @Background private val handler: Handler,
@Application private val coroutineScope: CoroutineScope,
private val splitShadeStateController: SplitShadeStateController,
private val logger: MediaViewLogger,
@@ -631,7 +631,7 @@ constructor(
}
}
}
- secureSettings.registerContentObserverForUserSync(
+ secureSettings.registerContentObserverForUserAsync(
Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
settingsObserver,
UserHandle.USER_ALL,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 574ccee28faa..ab998d10287f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -366,7 +366,6 @@ public abstract class MediaOutputBaseAdapter extends
/ (double) seekBar.getMax());
mVolumeValueText.setText(mContext.getResources().getString(
R.string.media_output_dialog_volume_percentage, percentage));
- mVolumeValueText.setVisibility(View.VISIBLE);
if (mStartFromMute) {
updateUnmutedVolumeIcon(device);
mStartFromMute = false;
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 311cbfb7e632..b2696aeaabfc 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -132,7 +132,7 @@ constructor(
val isDefaultNotesAppSet =
noteTaskInfoResolver.resolveInfo(
QUICK_AFFORDANCE,
- user = controller.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+ user = controller.getUserForHandlingNotesTaking(QUICK_AFFORDANCE),
) != null
return when {
isEnabled && isDefaultNotesAppSet -> PickerScreenState.Default()
@@ -158,7 +158,7 @@ constructor(
override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
controller.showNoteTask(entryPoint = QUICK_AFFORDANCE)
- return OnTriggeredResult.Handled
+ return OnTriggeredResult.Handled(true)
}
}
@@ -194,7 +194,7 @@ private fun RoleManager.createNotesRoleFlow(
fun isDefaultNotesAppSetForUser() =
noteTaskInfoResolver.resolveInfo(
QUICK_AFFORDANCE,
- user = noteTaskController.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+ user = noteTaskController.getUserForHandlingNotesTaking(QUICK_AFFORDANCE),
) != null
trySendBlocking(isDefaultNotesAppSetForUser())
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 91a3120ec770..1e608af14568 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -67,6 +67,7 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.DeviceConfigProxy
@@ -141,7 +142,6 @@ interface FgsManagerController {
class FgsManagerControllerImpl
@Inject
constructor(
- @ShadeDisplayAware private val context: Context,
@ShadeDisplayAware private val resources: Resources,
@Main private val mainExecutor: Executor,
@Background private val backgroundExecutor: Executor,
@@ -155,6 +155,7 @@ constructor(
private val broadcastDispatcher: BroadcastDispatcher,
private val dumpManager: DumpManager,
private val systemUIDialogFactory: SystemUIDialog.Factory,
+ private val shadeDialogContextRepository: ShadeDialogContextInteractor,
) : Dumpable, FgsManagerController {
companion object {
@@ -388,7 +389,7 @@ constructor(
override fun showDialog(expandable: Expandable?) {
synchronized(lock) {
if (dialog == null) {
- val dialog = systemUIDialogFactory.create(context)
+ val dialog = systemUIDialogFactory.create(shadeDialogContextRepository.context)
dialog.setTitle(R.string.fgs_manager_dialog_title)
dialog.setMessage(R.string.fgs_manager_dialog_message)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
index 790793eab258..3049a40f18c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
@@ -17,16 +17,16 @@
package com.android.systemui.qs.composefragment.ui
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.ClipOp
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.drawscope.clipRect
-import androidx.compose.ui.graphics.layer.CompositingStrategy
-import androidx.compose.ui.graphics.layer.drawLayer
+import androidx.compose.ui.graphics.graphicsLayer
/**
* Clipping modifier for clipping out the notification scrim as it slides over QS. It will clip out
@@ -34,16 +34,16 @@ import androidx.compose.ui.graphics.layer.drawLayer
* from the QS container.
*/
fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier {
- return this.drawWithCache {
+ return this.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
+ .drawWithContent {
+ drawContent()
val params = clipParams()
val left = -params.leftInset.toFloat()
val right = size.width + params.rightInset.toFloat()
val top = params.top.toFloat()
val bottom = params.bottom.toFloat()
- val graphicsLayer = obtainGraphicsLayer()
- graphicsLayer.compositingStrategy = CompositingStrategy.Offscreen
- graphicsLayer.record {
- drawContent()
+ val clipSize = Size(right - left, bottom - top)
+ if (!clipSize.isEmpty()) {
clipRect {
drawRoundRect(
color = Color.Black,
@@ -54,9 +54,6 @@ fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams
)
}
}
- onDrawWithContent {
- drawLayer(graphicsLayer)
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 873059ee08db..e7fa27159e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -675,17 +675,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
}
private void add() {
- if (addFromPosition(getLayoutPosition())) {
- itemView.announceForAccessibility(
- itemView.getContext().getText(R.string.accessibility_qs_edit_tile_added));
- }
+ addFromPosition(getLayoutPosition());
}
private void remove() {
- if (removeFromPosition(getLayoutPosition())) {
- itemView.announceForAccessibility(
- itemView.getContext().getText(R.string.accessibility_qs_edit_tile_removed));
- }
+ removeFromPosition(getLayoutPosition());
}
boolean isCurrentTile() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
index 85db95203b45..f3c06a481fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
@@ -54,7 +54,7 @@ fun EditModeButton(
) {
Icon(
imageVector = Icons.Default.Edit,
- contentDescription = stringResource(id = R.string.qs_edit),
+ contentDescription = stringResource(id = R.string.accessibility_quick_settings_edit),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 42a0cb1004f4..b7ff63cdc1fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -41,6 +41,7 @@ import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -56,6 +57,7 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
private final DataSaverController mDataSaverController;
private final DialogTransitionAnimator mDialogTransitionAnimator;
private final SystemUIDialog.Factory mSystemUIDialogFactory;
+ private final ShadeDialogContextInteractor mShadeDialogContextInteractor;
@Inject
public DataSaverTile(
@@ -70,13 +72,15 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
QSLogger qsLogger,
DataSaverController dataSaverController,
DialogTransitionAnimator dialogTransitionAnimator,
- SystemUIDialog.Factory systemUIDialogFactory
+ SystemUIDialog.Factory systemUIDialogFactory,
+ ShadeDialogContextInteractor shadeDialogContextInteractor
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mDataSaverController = dataSaverController;
mDialogTransitionAnimator = dialogTransitionAnimator;
mSystemUIDialogFactory = systemUIDialogFactory;
+ mShadeDialogContextInteractor = shadeDialogContextInteractor;
mDataSaverController.observe(getLifecycle(), this);
}
@@ -102,7 +106,8 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
// Show a dialog to confirm first. Dialogs shown by the DialogTransitionAnimator must be
// created and shown on the main thread, so we post it to the UI handler.
mUiHandler.post(() -> {
- SystemUIDialog dialog = mSystemUIDialogFactory.create(mContext);
+ SystemUIDialog dialog = mSystemUIDialogFactory.create(
+ mShadeDialogContextInteractor.getContext());
dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
dialog.setMessage(com.android.internal.R.string.data_saver_description);
dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 19b45d50c594..7516ca030d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -193,7 +193,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
if (mJob == null) {
mJob = WifiUtils.checkWepAllowed(mContext, mCoroutineScope, wifiEntry.getSsid(),
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, intent -> {
- mInternetDialogController.startActivity(intent, view);
+ mInternetDialogController.startActivityForDialog(intent);
return null;
}, () -> {
wifiConnect(wifiEntry, view);
@@ -211,7 +211,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
true /* connectForCaller */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- mContext.startActivity(intent);
+ mInternetDialogController.startActivityForDialog(intent);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index dbe1ae90b3f6..7036ef914a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -781,6 +781,10 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0, controller);
}
+ void startActivityForDialog(Intent intent) {
+ mActivityStarter.startActivity(intent, false /* dismissShade */);
+ }
+
void launchNetworkSetting(View view) {
startActivity(getSettingsIntent(), view);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 70c2a2a0d55a..5e9deec58c58 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -72,6 +72,7 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeDisplayAware;
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wifitrackerlib.WifiEntry;
@@ -104,9 +105,9 @@ public class InternetDialogDelegate implements
private final Handler mHandler;
private final Executor mBackgroundExecutor;
private final DialogTransitionAnimator mDialogTransitionAnimator;
- private final Context mContext;
private final boolean mAboveStatusBar;
private final SystemUIDialog.Factory mSystemUIDialogFactory;
+ private final ShadeDialogContextInteractor mShadeDialogContextInteractor;
@VisibleForTesting
protected InternetAdapter mAdapter;
@@ -204,10 +205,11 @@ public class InternetDialogDelegate implements
@Main Handler handler,
@Background Executor executor,
KeyguardStateController keyguardStateController,
- SystemUIDialog.Factory systemUIDialogFactory) {
- mContext = context;
+ SystemUIDialog.Factory systemUIDialogFactory,
+ ShadeDialogContextInteractor shadeDialogContextInteractor) {
mAboveStatusBar = aboveStatusBar;
mSystemUIDialogFactory = systemUIDialogFactory;
+ mShadeDialogContextInteractor = shadeDialogContextInteractor;
if (DEBUG) {
Log.d(TAG, "Init InternetDialog");
}
@@ -230,7 +232,8 @@ public class InternetDialogDelegate implements
@Override
public SystemUIDialog createDialog() {
- SystemUIDialog dialog = mSystemUIDialogFactory.create(this, mContext);
+ SystemUIDialog dialog = mSystemUIDialogFactory.create(this,
+ mShadeDialogContextInteractor.getContext());
if (!mAboveStatusBar) {
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 8c54ab40c680..862dba1e7294 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.user
import android.app.Dialog
-import android.content.Context
import android.content.DialogInterface
import android.content.DialogInterface.BUTTON_NEUTRAL
import android.content.Intent
@@ -34,6 +33,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.user.ui.dialog.DialogShowerImpl
import javax.inject.Inject
@@ -50,6 +50,7 @@ constructor(
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val uiEventLogger: UiEventLogger,
private val dialogFactory: SystemUIDialog.Factory,
+ private val shadeDialogContextInteractor: ShadeDialogContextInteractor,
) {
companion object {
@@ -63,7 +64,8 @@ constructor(
* Populate the dialog with information from and adapter obtained from
* [userDetailViewAdapterProvider] and show it as launched from [expandable].
*/
- fun showDialog(context: Context, expandable: Expandable) {
+ fun showDialog(expandable: Expandable) {
+ val context = shadeDialogContextInteractor.context
with(dialogFactory.create(context)) {
setShowForAllUsers(true)
setCanceledOnTouchOutside(true)
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index a7b51faaed57..10ac2cf76763 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -16,6 +16,8 @@
package com.android.systemui.scrim;
+import static com.android.systemui.Flags.notificationShadeBlur;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -214,8 +216,7 @@ public class ScrimDrawable extends Drawable {
public void draw(@NonNull Canvas canvas) {
mPaint.setColor(mMainColor);
mPaint.setAlpha(mAlpha);
- if (WindowBlurFlag.isEnabled()) {
- // TODO(b/370555223): Match the alpha to the visual spec when it is finalized.
+ if (notificationShadeBlur() || WindowBlurFlag.isEnabled()) {
// TODO (b/381263600), wire this at ScrimController, move it to PrimaryBouncerTransition
mPaint.setAlpha((int) (0.5f * mAlpha));
}
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 4bfa61e9dcd4..0f80e7432a54 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -16,6 +16,8 @@
package com.android.systemui.scrim;
+import static com.android.systemui.Flags.notificationShadeBlur;
+
import static java.lang.Float.isNaN;
import android.annotation.NonNull;
@@ -39,13 +41,12 @@ import androidx.core.graphics.ColorUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.res.R;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.util.LargeScreenUtils;
import java.util.concurrent.Executor;
-import static com.android.systemui.Flags.notificationShadeBlur;
-
/**
* A view which can draw a scrim. This view maybe be used in multiple windows running on different
* threads, but is controlled by {@link com.android.systemui.statusbar.phone.ScrimController} so we
@@ -253,8 +254,11 @@ public class ScrimView extends View {
mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount);
}
if (notificationShadeBlur()) {
- // TODO(b/370555223): Fix color and transparency to match visual spec exactly
- mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), Color.GRAY, 0.5f);
+ int layerAbove = ColorUtils.setAlphaComponent(
+ getResources().getColor(R.color.shade_panel, null),
+ (int) (0.4f * 255));
+ int layerBelow = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.1f * 255));
+ mainTinted = ColorUtils.compositeColors(layerAbove, layerBelow);
}
drawable.setColor(mainTinted, animated);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index e1631ccdcb06..bbb13d5c1dfe 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -61,9 +61,18 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
/** Callback for notifying of changes. */
@WeaklyReferencedCallback
interface Callback {
- /** Notifies that the current user will be changed. */
+ /**
+ * Same as {@link onBeforeUserSwitching(Int, Runnable)} but the callback will be called
+ * automatically after the completion of this method.
+ */
fun onBeforeUserSwitching(newUser: Int) {}
+ /** Notifies that the current user will be changed. */
+ fun onBeforeUserSwitching(newUser: Int, resultCallback: Runnable) {
+ onBeforeUserSwitching(newUser)
+ resultCallback.run()
+ }
+
/**
* Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be called
* automatically after the completion of this method.
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index b7a3aedc565e..42d83637ec1a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -196,8 +196,9 @@ internal constructor(
private fun registerUserSwitchObserver() {
iActivityManager.registerUserSwitchObserver(
object : UserSwitchObserver() {
- override fun onBeforeUserSwitching(newUserId: Int) {
+ override fun onBeforeUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
handleBeforeUserSwitching(newUserId)
+ reply?.sendResult(null)
}
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
@@ -236,8 +237,7 @@ internal constructor(
setUserIdInternal(newUserId)
notifySubscribers { callback, resultCallback ->
- callback.onBeforeUserSwitching(newUserId)
- resultCallback.run()
+ callback.onBeforeUserSwitching(newUserId, resultCallback)
}
.await()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index f2c39063c867..839d4596bb7c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -48,7 +48,6 @@ import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -367,9 +366,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
mTouchActive = true;
mTouchCancelled = false;
mDownEvent = ev;
- if (MigrateClocksToBlueprint.isEnabled()) {
- mService.userActivity();
- }
+ mService.userActivity();
} else if (ev.getActionMasked() == MotionEvent.ACTION_UP
|| ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
mTouchActive = false;
@@ -443,8 +440,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
float x = ev.getRawX();
float y = ev.getRawY();
if (mStatusBarViewController.touchIsWithinView(x, y)) {
- if (!(MigrateClocksToBlueprint.isEnabled()
- && mPrimaryBouncerInteractor.isBouncerShowing())) {
+ if (!mPrimaryBouncerInteractor.isBouncerShowing()) {
if (mStatusBarWindowStateController.windowIsShowing()) {
mIsTrackingBarGesture = true;
return logDownDispatch(ev, "sending touch to status bar",
@@ -453,7 +449,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
return logDownDispatch(ev, "hidden or hiding", true);
}
} else {
- mShadeLogger.d("NSWVC: bouncer not showing");
+ mShadeLogger.d("NSWVC: bouncer showing");
}
} else {
mShadeLogger.d("NSWVC: touch not within view");
@@ -511,34 +507,24 @@ public class NotificationShadeWindowViewController implements Dumpable {
&& !bouncerShowing
&& !mStatusBarStateController.isDozing()) {
if (mDragDownHelper.isDragDownEnabled()) {
- if (MigrateClocksToBlueprint.isEnabled()) {
- // When on lockscreen, if the touch originates at the top of the screen
- // go directly to QS and not the shade
- if (mStatusBarStateController.getState() == KEYGUARD
- && mQuickSettingsController.shouldQuickSettingsIntercept(
- ev.getX(), ev.getY(), 0)) {
- mShadeLogger.d("NSWVC: QS intercepted");
- return true;
- }
+ // When on lockscreen, if the touch originates at the top of the screen go
+ // directly to QS and not the shade
+ if (mStatusBarStateController.getState() == KEYGUARD
+ && mQuickSettingsController.shouldQuickSettingsIntercept(
+ ev.getX(), ev.getY(), 0)) {
+ mShadeLogger.d("NSWVC: QS intercepted");
+ return true;
}
// This handles drag down over lockscreen
boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
- if (MigrateClocksToBlueprint.isEnabled()) {
- if (result) {
- mLastInterceptWasDragDownHelper = true;
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mShadeLogger.d("NSWVC: drag down helper intercepted");
- }
- } else if (didNotificationPanelInterceptEvent(ev)) {
- return true;
- }
- } else {
- if (result) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mShadeLogger.d("NSWVC: drag down helper intercepted");
- }
+ if (result) {
+ mLastInterceptWasDragDownHelper = true;
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mShadeLogger.d("NSWVC: drag down helper intercepted");
}
+ } else if (didNotificationPanelInterceptEvent(ev)) {
+ return true;
}
return result;
} else {
@@ -547,12 +533,10 @@ public class NotificationShadeWindowViewController implements Dumpable {
return true;
}
}
- } else if (MigrateClocksToBlueprint.isEnabled()) {
+ } else if (!bouncerShowing && didNotificationPanelInterceptEvent(ev)) {
// This final check handles swipes on HUNs and when Pulsing
- if (!bouncerShowing && didNotificationPanelInterceptEvent(ev)) {
- mShadeLogger.d("NSWVC: intercepted for HUN/PULSING");
- return true;
- }
+ mShadeLogger.d("NSWVC: intercepted for HUN/PULSING");
+ return true;
}
return false;
}
@@ -562,9 +546,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- if (!MigrateClocksToBlueprint.isEnabled()) {
- mShadeViewController.handleExternalInterceptTouch(cancellation);
- }
cancellation.recycle();
}
@@ -574,22 +555,12 @@ public class NotificationShadeWindowViewController implements Dumpable {
if (mStatusBarStateController.isDozing()) {
handled = !mDozeServiceHost.isPulsing();
}
- if (MigrateClocksToBlueprint.isEnabled()) {
- if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
- // we still want to finish our drag down gesture when locking the screen
- handled |= mDragDownHelper.onTouchEvent(ev) || handled;
- }
- if (!handled && mShadeViewController.handleExternalTouch(ev)) {
- return true;
- }
- } else {
- if (mDragDownHelper.isDragDownEnabled()
- || mDragDownHelper.isDraggingDown()) {
- // we still want to finish our drag down gesture when locking the screen
- return mDragDownHelper.onTouchEvent(ev) || handled;
- } else {
- return handled;
- }
+ if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
+ // we still want to finish our drag down gesture when locking the screen
+ handled |= mDragDownHelper.onTouchEvent(ev) || handled;
+ }
+ if (!handled && mShadeViewController.handleExternalTouch(ev)) {
+ return true;
}
return handled;
}
@@ -673,14 +644,12 @@ public class NotificationShadeWindowViewController implements Dumpable {
}
private boolean didNotificationPanelInterceptEvent(MotionEvent ev) {
- if (MigrateClocksToBlueprint.isEnabled()) {
- // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need
- // to also ask NotificationPanelViewController directly, in order to process swipe up
- // events originating from notifications
- if (mShadeViewController.handleExternalInterceptTouch(ev)) {
- mShadeLogger.d("NSWVC: NPVC intercepted");
- return true;
- }
+ // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need to
+ // also ask NotificationPanelViewController directly, in order to process swipe up events
+ // originating from notifications
+ if (mShadeViewController.handleExternalInterceptTouch(ev)) {
+ mShadeLogger.d("NSWVC: NPVC intercepted");
+ return true;
}
return false;
@@ -707,9 +676,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
if (!SceneContainerFlag.isEnabled()) {
mAmbientState.setSwipingUp(false);
}
- if (MigrateClocksToBlueprint.isEnabled()) {
- mDragDownHelper.stopDragging();
- }
+ mDragDownHelper.stopDragging();
}
private void setBrightnessMirrorShowingForDepth(boolean showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 207439e1f374..58111576574e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -21,7 +21,6 @@ import android.view.ViewGroup
import android.view.WindowInsets
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
@@ -32,7 +31,6 @@ import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.fragments.FragmentService
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.plugins.qs.QS
@@ -275,7 +273,6 @@ constructor(
constraintSet.clone(mView)
setKeyguardStatusViewConstraints(constraintSet)
setQsConstraints(constraintSet)
- setNotificationsConstraints(constraintSet)
setLargeScreenShadeHeaderConstraints(constraintSet)
mView.applyConstraints(constraintSet)
}
@@ -288,21 +285,6 @@ constructor(
}
}
- private fun setNotificationsConstraints(constraintSet: ConstraintSet) {
- if (MigrateClocksToBlueprint.isEnabled) {
- return
- }
- val startConstraintId = if (splitShadeEnabled) R.id.qs_edge_guideline else PARENT_ID
- val nsslId = R.id.notification_stack_scroller
- constraintSet.apply {
- connect(nsslId, START, startConstraintId, START)
- setMargin(nsslId, START, if (splitShadeEnabled) 0 else panelMarginHorizontal)
- setMargin(nsslId, END, panelMarginHorizontal)
- setMargin(nsslId, TOP, topMargin)
- setMargin(nsslId, BOTTOM, notificationsBottomMargin)
- }
- }
-
private fun setQsConstraints(constraintSet: ConstraintSet) {
val endConstraintId = if (splitShadeEnabled) R.id.qs_edge_guideline else PARENT_ID
constraintSet.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 13330553b2de..000a666bac0d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -21,7 +21,6 @@ import static androidx.constraintlayout.core.widgets.Optimizer.OPTIMIZATION_GRAP
import android.app.Fragment;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -33,13 +32,10 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import java.util.ArrayList;
-import java.util.Comparator;
import java.util.function.Consumer;
/**
@@ -50,11 +46,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private View mQsFrame;
private View mStackScroller;
- private View mKeyguardStatusBar;
- private final ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
- private final ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
- private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
private Consumer<WindowInsets> mInsetsChangedListener = insets -> {};
private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
private QS mQs;
@@ -80,7 +72,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
protected void onFinishInflate() {
super.onFinishInflate();
mQsFrame = findViewById(R.id.qs_frame);
- mKeyguardStatusBar = findViewById(R.id.keyguard_header);
}
void setStackScroller(View stackScroller) {
@@ -160,46 +151,11 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
}
@Override
- protected void dispatchDraw(Canvas canvas) {
- mDrawingOrderedChildren.clear();
- mLayoutDrawingOrder.clear();
- if (mKeyguardStatusBar.getVisibility() == View.VISIBLE) {
- mDrawingOrderedChildren.add(mKeyguardStatusBar);
- mLayoutDrawingOrder.add(mKeyguardStatusBar);
- }
- if (mQsFrame.getVisibility() == View.VISIBLE) {
- mDrawingOrderedChildren.add(mQsFrame);
- mLayoutDrawingOrder.add(mQsFrame);
- }
- if (mStackScroller.getVisibility() == View.VISIBLE) {
- mDrawingOrderedChildren.add(mStackScroller);
- mLayoutDrawingOrder.add(mStackScroller);
- }
-
- // Let's now find the order that the view has when drawing regularly by sorting
- mLayoutDrawingOrder.sort(mIndexComparator);
- super.dispatchDraw(canvas);
- }
-
- @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return TouchLogger.logDispatchTouch("NotificationsQuickSettingsContainer", ev,
super.dispatchTouchEvent(ev));
}
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- if (MigrateClocksToBlueprint.isEnabled()) {
- return super.drawChild(canvas, child, drawingTime);
- }
- int layoutIndex = mLayoutDrawingOrder.indexOf(child);
- if (layoutIndex >= 0) {
- return super.drawChild(canvas, mDrawingOrderedChildren.get(layoutIndex), drawingTime);
- } else {
- return super.drawChild(canvas, child, drawingTime);
- }
- }
-
public void applyConstraints(ConstraintSet constraintSet) {
constraintSet.applyTo(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 0df2299eb8dd..4fb43fdcfdae 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -68,7 +68,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
@@ -1828,16 +1827,6 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
"onQsIntercept: down action, QS partially expanded/collapsed");
return true;
}
- // TODO (b/265193930): remove dependency on NPVC
- if (mPanelViewControllerLazy.get().isKeyguardShowing()
- && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
- // Dragging down on the lockscreen statusbar should prohibit other interactions
- // immediately, otherwise we'll wait on the touchslop. This is to allow
- // dragging down to expanded quick settings directly on the lockscreen.
- if (!MigrateClocksToBlueprint.isEnabled()) {
- mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
- }
- }
if (mExpansionAnimator != null) {
mInitialHeightOnTouch = mExpansionHeight;
mShadeLog.logMotionEvent(event,
@@ -1879,9 +1868,6 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
&& Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(
mInitialTouchX, mInitialTouchY, h)) {
- if (!MigrateClocksToBlueprint.isEnabled()) {
- mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
- }
mShadeLog.onQsInterceptMoveQsTrackingEnabled(h);
setTracking(true);
traceQsJank(true, false);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index d31868ca0217..61b9f0819f56 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -35,6 +35,8 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractorImpl
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl
import com.android.systemui.shade.display.ShadeDisplayPolicyModule
@@ -216,6 +218,25 @@ object ShadeDisplayAwareModule {
}
@Provides
+ @SysUISingleton
+ fun provideShadeDialogContextInteractor(
+ impl: ShadeDialogContextInteractorImpl
+ ): ShadeDialogContextInteractor = impl
+
+ @Provides
+ @IntoMap
+ @ClassKey(ShadeDialogContextInteractor::class)
+ fun provideShadeDialogContextInteractorCoreStartable(
+ impl: Provider<ShadeDialogContextInteractorImpl>
+ ): CoreStartable {
+ return if (ShadeWindowGoesAround.isEnabled) {
+ impl.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
+
+ @Provides
@IntoMap
@ClassKey(ShadePrimaryDisplayCommand::class)
fun provideShadePrimaryDisplayCommand(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/FakeShadeDialogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/FakeShadeDialogContextInteractor.kt
new file mode 100644
index 000000000000..455370c726a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/FakeShadeDialogContextInteractor.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import android.content.Context
+
+/** Fake context repository that always returns the same context. */
+class FakeShadeDialogContextInteractor(override val context: Context) :
+ ShadeDialogContextInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt
new file mode 100644
index 000000000000..201dc0339a0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import android.content.Context
+import android.util.Log
+import android.view.Display
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL
+import com.android.app.tracing.coroutines.launchTraced
+import com.android.app.tracing.traceSection
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.filter
+
+/** Provides the correct context to show dialogs on the shade window, whenever it moves. */
+interface ShadeDialogContextInteractor {
+ /** Context usable to create dialogs on the notification shade display. */
+ val context: Context
+}
+
+@SysUISingleton
+class ShadeDialogContextInteractorImpl
+@Inject
+constructor(
+ @Main private val defaultContext: Context,
+ private val displayWindowPropertyRepository: Provider<DisplayWindowPropertiesRepository>,
+ private val shadeDisplaysRepository: ShadeDisplaysRepository,
+ @Background private val bgScope: CoroutineScope,
+) : CoreStartable, ShadeDialogContextInteractor {
+
+ override fun start() {
+ if (ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()) return
+ bgScope.launchTraced(TAG) {
+ shadeDisplaysRepository.displayId
+ // No need for default display pre-warming.
+ .filter { it != Display.DEFAULT_DISPLAY }
+ .collectLatest { displayId ->
+ // Prewarms the context in the background every time the display changes.
+ // In this way, there will be no main thread delays when a dialog is shown.
+ getContextOrDefault(displayId)
+ }
+ }
+ }
+
+ override val context: Context
+ get() {
+ if (!ShadeWindowGoesAround.isEnabled) {
+ return defaultContext
+ }
+ val displayId = shadeDisplaysRepository.displayId.value
+ return getContextOrDefault(displayId)
+ }
+
+ private fun getContextOrDefault(displayId: Int): Context {
+ return try {
+ traceSection({ "Getting dialog context for displayId=$displayId" }) {
+ displayWindowPropertyRepository.get().get(displayId, DIALOG_WINDOW_TYPE).context
+ }
+ } catch (e: Exception) {
+ // This can happen if the display was disconnected in the meantime.
+ Log.e(
+ TAG,
+ "Couldn't get dialog context for displayId=$displayId. Returning default one",
+ e,
+ )
+ defaultContext
+ }
+ }
+
+ private companion object {
+ const val TAG = "ShadeDialogContextRepo"
+ const val DIALOG_WINDOW_TYPE = TYPE_STATUS_BAR_SUB_PANEL
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index b2ca33a4aecf..a7ad46296e08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -44,13 +44,11 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.logging.UiEventLogger;
-import com.android.keyguard.KeyguardClockSwitch;
import com.android.systemui.DejankUtils;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -136,7 +134,6 @@ public class StatusBarStateControllerImpl implements
private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
// These views are used by InteractionJankMonitor to get callback from HWUI.
private View mView;
- private KeyguardClockSwitch mClockSwitchView;
/**
* If any of the system bars is hidden.
@@ -426,7 +423,6 @@ public class StatusBarStateControllerImpl implements
if ((mView == null || !mView.isAttachedToWindow())
&& (view != null && view.isAttachedToWindow())) {
mView = view;
- mClockSwitchView = view.findViewById(R.id.keyguard_clock_container);
}
mDozeAmountTarget = dozeAmount;
if (animated) {
@@ -511,16 +507,7 @@ public class StatusBarStateControllerImpl implements
/** Returns the id of the currently rendering clock */
public String getClockId() {
- if (MigrateClocksToBlueprint.isEnabled()) {
- return mKeyguardClockInteractorLazy.get().getRenderedClockId();
- }
-
- if (mClockSwitchView == null) {
- Log.e(TAG, "Clock container was missing");
- return KeyguardClockSwitch.MISSING_CLOCK_ID;
- }
-
- return mClockSwitchView.getClockId();
+ return mKeyguardClockInteractorLazy.get().getRenderedClockId();
}
private void beginInteractionJankMonitor() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 66af275bc702..a7dbb47bc609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.chips.notification.ui.viewmodel
import android.view.View
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
@@ -99,6 +100,17 @@ constructor(
)
}
+ if (Flags.promoteNotificationsAutomatically()) {
+ // When we're promoting notifications automatically, the `when` time set on the
+ // notification will likely just be set to the current time, which would cause the chip
+ // to always show "now". We don't want early testers to get that experience since it's
+ // not what will happen at launch, so just don't show any time.
+ // TODO(b/364653005): Only ignore the `when` time if the notification was
+ // *automatically* promoted (as opposed to being legitimately promoted by the
+ // criteria). We'll need to track that status somehow.
+ return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+ }
+
if (this.promotedContent.time == null) {
return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
new file mode 100644
index 000000000000..9f523fc845ab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.chips.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the status bar popup chips flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarPopupChips {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_POPUP_CHIPS
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.statusBarPopupChips()
+
+ /**
+ * 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/featurepods/popups/shared/model/PopupChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
new file mode 100644
index 000000000000..1663aebd7287
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.featurepods.popups.shared.model
+
+import com.android.systemui.common.shared.model.Icon
+
+/**
+ * Ids used to track different types of popup chips. Will be used to ensure only one chip is
+ * displaying its popup at a time.
+ */
+sealed class PopupChipId(val value: String) {
+ data object MediaControls : PopupChipId("MediaControls")
+}
+
+/** Model for individual status bar popup chips. */
+sealed class PopupChipModel {
+ abstract val logName: String
+ abstract val chipId: PopupChipId
+
+ data class Hidden(override val chipId: PopupChipId, val shouldAnimate: Boolean = true) :
+ PopupChipModel() {
+ override val logName = "Hidden(id=$chipId, anim=$shouldAnimate)"
+ }
+
+ data class Shown(
+ override val chipId: PopupChipId,
+ val icon: Icon,
+ val chipText: String,
+ val isToggled: Boolean = false,
+ val onToggle: () -> Unit,
+ val onIconPressed: () -> Unit,
+ ) : PopupChipModel() {
+ override val logName = "Shown(id=$chipId, toggled=$isToggled)"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt
new file mode 100644
index 000000000000..5712be30ccd6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.featurepods.popups.ui.viewmodel
+
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Interface for a view model that knows the display requirements for a single type of status bar
+ * popup chip.
+ */
+interface StatusBarPopupChipViewModel {
+ /** A flow modeling the popup chip that should be shown (or not shown). */
+ val chip: StateFlow<PopupChipModel>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
new file mode 100644
index 000000000000..b390f29b166c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.featurepods.popups.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * View model deciding which system process chips to show in the status bar. Emits a list of
+ * PopupChipModels.
+ */
+@SysUISingleton
+class StatusBarPopupChipsViewModel @Inject constructor(@Background scope: CoroutineScope) {
+ private data class PopupChipBundle(
+ val media: PopupChipModel = PopupChipModel.Hidden(chipId = PopupChipId.MediaControls)
+ )
+
+ private val incomingPopupChipBundle: Flow<PopupChipBundle?> =
+ flowOf(null).stateIn(scope, SharingStarted.Lazily, PopupChipBundle())
+
+ val popupChips: Flow<List<PopupChipModel>> =
+ incomingPopupChipBundle
+ .map { _ -> listOf(null).filterIsInstance<PopupChipModel.Shown>() }
+ .stateIn(scope, SharingStarted.Lazily, emptyList())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 80e8f55b897a..d83acf34ca99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.inflation;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -186,6 +187,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
if (AsyncHybridViewInflation.isEnabled()) {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_SINGLE_LINE);
+ if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
+ params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
+ }
}
mRowContentBindStage.requestRebind(entry, null);
}
@@ -256,10 +260,10 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
params.setUseMinimized(isMinimized);
- // TODO b/358403414: use the different types of redaction
- boolean needsRedaction = inflaterParams.getRedactionType() != REDACTION_TYPE_NONE;
+ int redactionType = inflaterParams.getRedactionType();
- if (needsRedaction) {
+ params.setRedactionType(redactionType);
+ if (redactionType != REDACTION_TYPE_NONE) {
params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
} else {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
@@ -276,8 +280,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
}
if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
-
- if (inflaterParams.isChildInGroup() && needsRedaction) {
+ if (inflaterParams.isChildInGroup()
+ && redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
} else {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 8a1371f1c415..aa010cf63d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -62,6 +62,7 @@ import com.android.systemui.statusbar.notification.data.NotificationDataLayerMod
import com.android.systemui.statusbar.notification.domain.NotificationDomainLayerModule;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor;
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModelModule;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.icon.ConversationIconManager;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
@@ -78,8 +79,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationPanelLogg
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.logging.dagger.NotificationsLogModule;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger;
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractorImpl;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl;
@@ -92,7 +92,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModesCleanupStartable;
import dagger.Binds;
@@ -105,8 +104,6 @@ import kotlin.coroutines.CoroutineContext;
import kotlinx.coroutines.CoroutineScope;
-import java.util.Optional;
-
import javax.inject.Provider;
/**
@@ -315,21 +312,17 @@ public interface NotificationsModule {
@ClassKey(ZenModesCleanupStartable.class)
CoreStartable bindsZenModesCleanup(ZenModesCleanupStartable zenModesCleanup);
- /**
- * Provides {@link
- * com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor} if
- * one of the relevant feature flags is enabled.
- */
+ /** Provides the default implementation of {@link PromotedNotificationContentExtractor} if at
+ * least one of the relevant feature flags is enabled, or an implementation that always returns
+ * null if none are enabled. */
@Provides
@SysUISingleton
- static Optional<PromotedNotificationContentExtractor>
- providePromotedNotificationContentExtractor(
- PromotedNotificationsProvider provider, Context context,
- PromotedNotificationLogger logger) {
+ static PromotedNotificationContentExtractor providesPromotedNotificationContentExtractor(
+ Provider<PromotedNotificationContentExtractorImpl> implProvider) {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- return Optional.of(new PromotedNotificationContentExtractor(provider, context, logger));
+ return implProvider.get();
} else {
- return Optional.empty();
+ return (entry, recoveredBuilder) -> null;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
index 6756077e5444..d02e17cab534 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
@@ -1299,7 +1299,6 @@ public class HeadsUpManagerImpl
}
private NotificationEntry requireEntry() {
- /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode();
return Objects.requireNonNull(mEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index 863c665eb4f5..4e9e3336b86f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -34,15 +34,22 @@ import com.android.systemui.statusbar.notification.promoted.shared.model.Promote
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
import javax.inject.Inject
+interface PromotedNotificationContentExtractor {
+ fun extractContent(
+ entry: NotificationEntry,
+ recoveredBuilder: Notification.Builder,
+ ): PromotedNotificationContentModel?
+}
+
@SysUISingleton
-class PromotedNotificationContentExtractor
+class PromotedNotificationContentExtractorImpl
@Inject
constructor(
private val promotedNotificationsProvider: PromotedNotificationsProvider,
@ShadeDisplayAware private val context: Context,
private val logger: PromotedNotificationLogger,
-) {
- fun extractContent(
+) : PromotedNotificationContentExtractor {
+ override fun extractContent(
entry: NotificationEntry,
recoveredBuilder: Notification.Builder,
): PromotedNotificationContentModel? {
@@ -169,5 +176,5 @@ private fun CallStyle.extractContent(contentBuilder: PromotedNotificationContent
private fun ProgressStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) {
// TODO: Create NotificationProgressModel.toSkeleton, or something similar.
- contentBuilder.progress = createProgressModel(0xffffffff.toInt(), 0x00000000)
+ contentBuilder.progress = createProgressModel(0xffffffff.toInt(), 0xff000000.toInt())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
index 13ad1413e89d..a43f8dbc1b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.promoted
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.ERROR
import com.android.systemui.log.core.LogLevel.INFO
-import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
@@ -27,7 +26,7 @@ import javax.inject.Inject
class PromotedNotificationLogger
@Inject
-constructor(@NotificationLog private val buffer: LogBuffer) {
+constructor(@PromotedNotificationLog private val buffer: LogBuffer) {
fun logExtractionSkipped(entry: NotificationEntry, reason: String) {
buffer.log(
EXTRACTION_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
new file mode 100644
index 000000000000..0f21514fcc94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.notification.promoted.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class AODPromotedNotificationInteractor
+@Inject
+constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) {
+ val content: Flow<PromotedNotificationContentModel?> =
+ activeNotificationsInteractor.topLevelRepresentativeNotifications.map { notifs ->
+ notifs.firstNotNullOfOrNull { it.promotedContent }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index fe2dabe1ba8a..74809fd8622f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -28,7 +28,7 @@ import com.android.systemui.statusbar.notification.promoted.PromotedNotification
* like the skeleton view on AOD or the status bar chip.
*/
data class PromotedNotificationContentModel(
- val key: String,
+ val identity: Identity,
// for all styles:
val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
@@ -82,7 +82,7 @@ data class PromotedNotificationContentModel(
fun build() =
PromotedNotificationContentModel(
- key = key,
+ identity = Identity(key, style),
skeletonSmallIcon = skeletonSmallIcon,
appName = appName,
subText = subText,
@@ -103,6 +103,8 @@ data class PromotedNotificationContentModel(
)
}
+ data class Identity(val key: String, val style: Style)
+
/** The timestamp associated with a notification, along with the mode used to display it. */
data class When(val time: Long, val mode: Mode) {
/** The mode used to display a notification's `when` value. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt
new file mode 100644
index 000000000000..adfa6a10814d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.notification.promoted.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.AODPromotedNotificationInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Identity
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class AODPromotedNotificationViewModel
+@Inject
+constructor(interactor: AODPromotedNotificationInteractor) {
+ private val content: Flow<PromotedNotificationContentModel?> = interactor.content
+ private val identity: Flow<Identity?> = content.mapNonNullsKeepingNulls { it.identity }
+
+ val notification: Flow<PromotedNotificationViewModel?> =
+ identity.distinctUntilChanged().mapNonNullsKeepingNulls { identity ->
+ val updates = interactor.content.filterNotNull().filter { it.identity == identity }
+ PromotedNotificationViewModel(identity, updates)
+ }
+}
+
+private fun <T, R> Flow<T?>.mapNonNullsKeepingNulls(block: (T) -> R): Flow<R?> = map {
+ it?.let(block)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt
new file mode 100644
index 000000000000..f265e0ff33f8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.notification.promoted.ui.viewmodel
+
+import android.graphics.drawable.Icon
+import com.android.internal.widget.NotificationProgressModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class PromotedNotificationViewModel(
+ identity: PromotedNotificationContentModel.Identity,
+ content: Flow<PromotedNotificationContentModel>,
+) {
+ // for all styles:
+
+ val key: String = identity.key
+ val style: Style = identity.style
+
+ val skeletonSmallIcon: Flow<Icon?> = content.map { it.skeletonSmallIcon }
+ val appName: Flow<CharSequence?> = content.map { it.appName }
+ val subText: Flow<CharSequence?> = content.map { it.subText }
+
+ private val time: Flow<When?> = content.map { it.time }
+ val whenTime: Flow<Long?> = time.map { it?.time }
+ val whenMode: Flow<When.Mode?> = time.map { it?.mode }
+
+ val lastAudiblyAlertedMs: Flow<Long> = content.map { it.lastAudiblyAlertedMs }
+ val profileBadgeResId: Flow<Int?> = content.map { it.profileBadgeResId }
+ val title: Flow<CharSequence?> = content.map { it.title }
+ val text: Flow<CharSequence?> = content.map { it.text }
+ val skeletonLargeIcon: Flow<Icon?> = content.map { it.skeletonLargeIcon }
+
+ // for CallStyle:
+ val personIcon: Flow<Icon?> = content.map { it.personIcon }
+ val personName: Flow<CharSequence?> = content.map { it.personName }
+ val verificationIcon: Flow<Icon?> = content.map { it.verificationIcon }
+ val verificationText: Flow<CharSequence?> = content.map { it.verificationText }
+
+ // for ProgressStyle:
+ val progress: Flow<NotificationProgressModel?> = content.map { it.progress }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 6e05e8e8b80e..70e27a981b49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
@@ -25,6 +26,7 @@ import static com.android.systemui.statusbar.notification.row.NotificationConten
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.app.Notification.MessagingStyle;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
@@ -161,9 +163,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
entry,
mConversationProcessor,
row,
- bindParams.isMinimized,
- bindParams.usesIncreasedHeight,
- bindParams.usesIncreasedHeadsUpHeight,
+ bindParams,
callback,
mRemoteInputManager.getRemoteViewsOnClickHandler(),
/* isMediaFlagEnabled = */ mIsMediaInQS,
@@ -187,13 +187,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
boolean inflateSynchronously,
@InflationFlag int reInflateFlags,
Notification.Builder builder,
+ Context systemUiContext,
Context packageContext,
SmartReplyStateInflater smartRepliesInflater) {
InflationProgress result = createRemoteViews(reInflateFlags,
builder,
- bindParams.isMinimized,
- bindParams.usesIncreasedHeight,
- bindParams.usesIncreasedHeadsUpHeight,
+ bindParams,
+ systemUiContext,
packageContext,
row,
mNotifLayoutInflaterFactoryProvider,
@@ -203,18 +203,20 @@ public class NotificationContentInflater implements NotificationRowContentBinder
result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(),
packageContext, row.getExistingSmartReplyState(), smartRepliesInflater, mLogger);
boolean isConversation = entry.getRanking().isConversation();
+ Notification.MessagingStyle messagingStyle = null;
+ if (isConversation && (AsyncHybridViewInflation.isEnabled()
+ || LockscreenOtpRedaction.isSingleLineViewEnabled())) {
+ messagingStyle = mConversationProcessor
+ .processNotification(entry, builder, mLogger);
+ }
if (AsyncHybridViewInflation.isEnabled()) {
- Notification.MessagingStyle messagingStyle = null;
- if (isConversation) {
- messagingStyle = mConversationProcessor
- .processNotification(entry, builder, mLogger);
- }
SingleLineViewModel viewModel = SingleLineViewInflater
.inflateSingleLineViewModel(
entry.getSbn().getNotification(),
messagingStyle,
builder,
- row.getContext()
+ row.getContext(),
+ false
);
// If the messagingStyle is null, we want to inflate the normal view
isConversation = viewModel.isConversation();
@@ -228,11 +230,22 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mLogger
);
}
-
if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
- result.mPublicInflatedSingleLineViewModel =
- SingleLineViewInflater.inflateRedactedSingleLineViewModel(row.getContext(),
- isConversation);
+ if (bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ entry.getSbn().getNotification(),
+ messagingStyle,
+ builder,
+ row.getContext(),
+ true);
+ } else {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateRedactedSingleLineViewModel(
+ row.getContext(),
+ isConversation
+ );
+ }
result.mPublicInflatedSingleLineView =
SingleLineViewInflater.inflatePublicSingleLineView(
isConversation,
@@ -411,8 +424,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
- Notification.Builder builder, boolean isMinimized, boolean usesIncreasedHeight,
- boolean usesIncreasedHeadsUpHeight, Context packageContext,
+ Notification.Builder builder, BindParams bindParams, Context systemUiContext,
+ Context packageContext,
ExpandableNotificationRow row,
NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
HeadsUpStyleProvider headsUpStyleProvider,
@@ -423,13 +436,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
logger.logAsyncTaskProgress(entryForLogging, "creating contracted remote view");
- result.newContentView = createContentView(builder, isMinimized,
- usesIncreasedHeight);
+ result.newContentView = createContentView(builder, bindParams.isMinimized,
+ bindParams.usesIncreasedHeight);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
logger.logAsyncTaskProgress(entryForLogging, "creating expanded remote view");
- result.newExpandedView = createExpandedView(builder, isMinimized);
+ result.newExpandedView = createExpandedView(builder, bindParams.isMinimized);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
@@ -439,13 +452,20 @@ public class NotificationContentInflater implements NotificationRowContentBinder
result.newHeadsUpView = builder.createCompactHeadsUpContentView();
} else {
result.newHeadsUpView = builder.createHeadsUpContentView(
- usesIncreasedHeadsUpHeight);
+ bindParams.usesIncreasedHeadsUpHeight);
}
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
logger.logAsyncTaskProgress(entryForLogging, "creating public remote view");
- result.newPublicView = builder.makePublicContentView(isMinimized);
+ if (LockscreenOtpRedaction.isEnabled()
+ && bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ result.newPublicView = createSensitiveContentMessageNotification(
+ row.getEntry().getSbn().getNotification(), builder.getStyle(),
+ systemUiContext, packageContext).createContentView(true);
+ } else {
+ result.newPublicView = builder.makePublicContentView(bindParams.isMinimized);
+ }
}
if (AsyncGroupHeaderViewInflation.isEnabled()) {
@@ -473,6 +493,42 @@ public class NotificationContentInflater implements NotificationRowContentBinder
});
}
+ private static Notification.Builder createSensitiveContentMessageNotification(
+ Notification original,
+ Notification.Style originalStyle,
+ Context systemUiContext,
+ Context packageContext) {
+ Notification.Builder redacted =
+ new Notification.Builder(packageContext, original.getChannelId());
+ redacted.setContentTitle(original.extras.getCharSequence(Notification.EXTRA_TITLE));
+ CharSequence redactedMessage = systemUiContext.getString(
+ R.string.redacted_notification_single_line_text
+ );
+
+ if (originalStyle instanceof MessagingStyle oldStyle) {
+ MessagingStyle newStyle = new MessagingStyle(oldStyle.getUser());
+ newStyle.setConversationTitle(oldStyle.getConversationTitle());
+ newStyle.setGroupConversation(false);
+ newStyle.setConversationType(oldStyle.getConversationType());
+ newStyle.setShortcutIcon(oldStyle.getShortcutIcon());
+ newStyle.setBuilder(redacted);
+ MessagingStyle.Message latestMessage =
+ MessagingStyle.findLatestIncomingMessage(oldStyle.getMessages());
+ if (latestMessage != null) {
+ MessagingStyle.Message newMessage = new MessagingStyle.Message(redactedMessage,
+ latestMessage.getTimestamp(), latestMessage.getSenderPerson());
+ newStyle.addMessage(newMessage);
+ }
+ redacted.setStyle(newStyle);
+ } else {
+ redacted.setContentText(redactedMessage);
+ }
+ redacted.setLargeIcon(original.getLargeIcon());
+ redacted.setSmallIcon(original.getSmallIcon());
+ return redacted;
+ }
+
+
private static void setNotifsViewsInflaterFactory(InflationProgress result,
ExpandableNotificationRow row,
NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
@@ -921,7 +977,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
logger.logAsyncTaskProgress(entry, "finishing");
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- entry.setPromotedNotificationContentModel(result.mExtractedPromotedNotificationContent);
+ entry.setPromotedNotificationContentModel(result.mPromotedContent);
}
boolean setRepliesAndActions = true;
@@ -1118,10 +1174,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final NotificationEntry mEntry;
private final Context mContext;
private final boolean mInflateSynchronously;
- private final boolean mIsMinimized;
- private final boolean mUsesIncreasedHeight;
+ private final BindParams mBindParams;
private final InflationCallback mCallback;
- private final boolean mUsesIncreasedHeadsUpHeight;
private final @InflationFlag int mReInflateFlags;
private final NotifRemoteViewCache mRemoteViewCache;
private final Executor mInflationExecutor;
@@ -1145,9 +1199,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationEntry entry,
ConversationNotificationProcessor conversationProcessor,
ExpandableNotificationRow row,
- boolean isMinimized,
- boolean usesIncreasedHeight,
- boolean usesIncreasedHeadsUpHeight,
+ BindParams bindParams,
InflationCallback callback,
RemoteViews.InteractionHandler remoteViewClickHandler,
boolean isMediaFlagEnabled,
@@ -1164,9 +1216,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRemoteViewCache = cache;
mSmartRepliesInflater = smartRepliesInflater;
mContext = mRow.getContext();
- mIsMinimized = isMinimized;
- mUsesIncreasedHeight = usesIncreasedHeight;
- mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
+ mBindParams = bindParams;
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
mConversationProcessor = conversationProcessor;
@@ -1236,8 +1286,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mEntry, recoveredBuilder, mLogger);
}
InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
- recoveredBuilder, mIsMinimized, mUsesIncreasedHeight,
- mUsesIncreasedHeadsUpHeight, packageContext, mRow,
+ recoveredBuilder, mBindParams, mContext, packageContext, mRow,
mNotifLayoutInflaterFactoryProvider, mHeadsUpStyleProvider, mLogger);
mLogger.logAsyncTaskProgress(mEntry,
@@ -1264,7 +1313,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mEntry.getSbn().getNotification(),
messagingStyle,
recoveredBuilder,
- mContext
+ mContext,
+ false
);
result.mInflatedSingleLineView =
SingleLineViewInflater.inflatePrivateSingleLineView(
@@ -1277,9 +1327,22 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
- result.mPublicInflatedSingleLineViewModel =
- SingleLineViewInflater.inflateRedactedSingleLineViewModel(mContext,
- isConversation);
+ if (mBindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ mEntry.getSbn().getNotification(),
+ messagingStyle,
+ recoveredBuilder,
+ mContext,
+ true
+ );
+ } else {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateRedactedSingleLineViewModel(
+ mContext,
+ isConversation
+ );
+ }
result.mPublicInflatedSingleLineView =
SingleLineViewInflater.inflatePublicSingleLineView(
isConversation,
@@ -1292,10 +1355,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if (PromotedNotificationContentModel.featureFlagEnabled()) {
mLogger.logAsyncTaskProgress(mEntry, "extracting promoted notification content");
- result.mExtractedPromotedNotificationContent = mPromotedNotificationContentExtractor
- .extractContent(mEntry, recoveredBuilder);
+ final PromotedNotificationContentModel promotedContent =
+ mPromotedNotificationContentExtractor.extractContent(mEntry,
+ recoveredBuilder);
mLogger.logAsyncTaskProgress(mEntry, "extracted promoted notification content: "
- + result.mExtractedPromotedNotificationContent);
+ + promotedContent);
+
+ result.mPromotedContent = promotedContent;
}
mLogger.logAsyncTaskProgress(mEntry,
@@ -1317,7 +1383,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mCancellationSignal = apply(
mInflationExecutor,
mInflateSynchronously,
- mIsMinimized,
+ mBindParams.isMinimized,
result,
mReInflateFlags,
mRemoteViewCache,
@@ -1399,7 +1465,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@VisibleForTesting
static class InflationProgress {
- PromotedNotificationContentModel mExtractedPromotedNotificationContent;
+ PromotedNotificationContentModel mPromotedContent;
private RemoteViews newContentView;
private RemoteViews newHeadsUpView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
index 07384afe2d2e..1cef8791e0ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -141,20 +143,33 @@ public interface NotificationRowContentBinder {
*/
class BindParams {
+ public BindParams(boolean minimized, boolean increasedHeight,
+ boolean increasedHeadsUpHeight, int redaction) {
+ isMinimized = minimized;
+ usesIncreasedHeight = increasedHeight;
+ usesIncreasedHeadsUpHeight = increasedHeadsUpHeight;
+ redactionType = redaction;
+ }
+
/**
* Bind a minimized version of the content views.
*/
- public boolean isMinimized;
+ public final boolean isMinimized;
/**
* Use increased height when binding contracted view.
*/
- public boolean usesIncreasedHeight;
+ public final boolean usesIncreasedHeight;
/**
* Use increased height when binding heads up views.
*/
- public boolean usesIncreasedHeadsUpHeight;
+ public final boolean usesIncreasedHeadsUpHeight;
+
+ /**
+ * Controls the type of public view to show, if a public view is requested
+ */
+ public final @RedactionType int redactionType;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index c7d80e9d03ce..c619b17f1ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.notification.row
import android.annotation.SuppressLint
-import android.app.Flags
import android.app.Notification
+import android.app.Notification.MessagingStyle
import android.content.Context
import android.content.ContextWrapper
import android.content.pm.ApplicationInfo
@@ -43,6 +43,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.NotifInflation
import com.android.systemui.res.R
import com.android.systemui.statusbar.InflationTask
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.InflationException
@@ -143,9 +144,7 @@ constructor(
entry,
conversationProcessor,
row,
- bindParams.isMinimized,
- bindParams.usesIncreasedHeight,
- bindParams.usesIncreasedHeadsUpHeight,
+ bindParams,
callback,
remoteInputManager.remoteViewsOnClickHandler,
/* isMediaFlagEnabled = */ smartReplyStateInflater,
@@ -179,10 +178,8 @@ constructor(
reInflateFlags = reInflateFlags,
entry = entry,
builder = builder,
- isMinimized = bindParams.isMinimized,
- usesIncreasedHeight = bindParams.usesIncreasedHeight,
- usesIncreasedHeadsUpHeight = bindParams.usesIncreasedHeadsUpHeight,
- systemUIContext = systemUIContext,
+ bindParams,
+ systemUiContext = systemUIContext,
packageContext = packageContext,
row = row,
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
@@ -371,9 +368,7 @@ constructor(
private val entry: NotificationEntry,
private val conversationProcessor: ConversationNotificationProcessor,
private val row: ExpandableNotificationRow,
- private val isMinimized: Boolean,
- private val usesIncreasedHeight: Boolean,
- private val usesIncreasedHeadsUpHeight: Boolean,
+ private val bindParams: BindParams,
private val callback: InflationCallback?,
private val remoteViewClickHandler: InteractionHandler?,
private val smartRepliesInflater: SmartReplyStateInflater,
@@ -441,10 +436,8 @@ constructor(
reInflateFlags = reInflateFlags,
entry = entry,
builder = recoveredBuilder,
- isMinimized = isMinimized,
- usesIncreasedHeight = usesIncreasedHeight,
- usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight,
- systemUIContext = context,
+ bindParams = bindParams,
+ systemUiContext = context,
packageContext = packageContext,
row = row,
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
@@ -514,7 +507,7 @@ constructor(
apply(
inflationExecutor,
inflateSynchronously,
- isMinimized,
+ bindParams.isMinimized,
progress,
reInflateFlags,
remoteViewCache,
@@ -591,7 +584,7 @@ constructor(
@VisibleForTesting val packageContext: Context,
val remoteViews: NewRemoteViews,
val contentModel: NotificationContentModel,
- val extractedPromotedNotificationContentModel: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModel?,
) {
var inflatedContentView: View? = null
@@ -671,10 +664,8 @@ constructor(
@InflationFlag reInflateFlags: Int,
entry: NotificationEntry,
builder: Notification.Builder,
- isMinimized: Boolean,
- usesIncreasedHeight: Boolean,
- usesIncreasedHeadsUpHeight: Boolean,
- systemUIContext: Context,
+ bindParams: BindParams,
+ systemUiContext: Context,
packageContext: Context,
row: ExpandableNotificationRow,
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
@@ -683,16 +674,15 @@ constructor(
promotedNotificationContentExtractor: PromotedNotificationContentExtractor,
logger: NotificationRowContentBinderLogger,
): InflationProgress {
- val promoted =
+ val promotedContent =
if (PromotedNotificationContentModel.featureFlagEnabled()) {
logger.logAsyncTaskProgress(entry, "extracting promoted notification content")
- val extracted =
- promotedNotificationContentExtractor.extractContent(entry, builder)
- logger.logAsyncTaskProgress(
- entry,
- "extracted promoted notification content: {extracted}",
- )
- extracted
+ promotedNotificationContentExtractor.extractContent(entry, builder).also {
+ logger.logAsyncTaskProgress(
+ entry,
+ "extracted promoted notification content: $it",
+ )
+ }
} else {
null
}
@@ -707,9 +697,10 @@ constructor(
createRemoteViews(
reInflateFlags = reInflateFlags,
builder = builder,
- isMinimized = isMinimized,
- usesIncreasedHeight = usesIncreasedHeight,
- usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight,
+ bindParams = bindParams,
+ entry = entry,
+ systemUiContext = systemUiContext,
+ packageContext = packageContext,
row = row,
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
headsUpStyleProvider = headsUpStyleProvider,
@@ -726,7 +717,8 @@ constructor(
notification = entry.sbn.notification,
messagingStyle = messagingStyle,
builder = builder,
- systemUiContext = systemUIContext,
+ systemUiContext = systemUiContext,
+ redactText = false,
)
} else null
@@ -736,10 +728,20 @@ constructor(
reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE != 0
) {
logger.logAsyncTaskProgress(entry, "inflating public single line view model")
- SingleLineViewInflater.inflateRedactedSingleLineViewModel(
- systemUIContext,
- entry.ranking.isConversation,
- )
+ if (bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ notification = entry.sbn.notification,
+ messagingStyle = messagingStyle,
+ builder = builder,
+ systemUiContext = systemUiContext,
+ redactText = true,
+ )
+ } else {
+ SingleLineViewInflater.inflateRedactedSingleLineViewModel(
+ systemUiContext,
+ entry.ranking.isConversation,
+ )
+ }
} else null
val headsUpStatusBarModel =
@@ -759,16 +761,54 @@ constructor(
packageContext = packageContext,
remoteViews = remoteViews,
contentModel = contentModel,
- extractedPromotedNotificationContentModel = promoted,
+ promotedContent = promotedContent,
)
}
+ private fun createSensitiveContentMessageNotification(
+ original: Notification,
+ originalStyle: Notification.Style?,
+ sysUiContext: Context,
+ packageContext: Context,
+ ): Notification.Builder {
+ val redacted = Notification.Builder(packageContext, original.channelId)
+ redacted.setContentTitle(original.extras.getCharSequence(Notification.EXTRA_TITLE))
+ val redactedMessage =
+ sysUiContext.getString(R.string.redacted_notification_single_line_text)
+
+ if (originalStyle is MessagingStyle) {
+ val newStyle = MessagingStyle(originalStyle.user)
+ newStyle.conversationTitle = originalStyle.conversationTitle
+ newStyle.isGroupConversation = false
+ newStyle.conversationType = originalStyle.conversationType
+ newStyle.shortcutIcon = originalStyle.shortcutIcon
+ newStyle.setBuilder(redacted)
+ val latestMessage = MessagingStyle.findLatestIncomingMessage(originalStyle.messages)
+ if (latestMessage != null) {
+ val newMessage =
+ MessagingStyle.Message(
+ redactedMessage,
+ latestMessage.timestamp,
+ latestMessage.senderPerson,
+ )
+ newStyle.addMessage(newMessage)
+ }
+ redacted.style = newStyle
+ } else {
+ redacted.setContentText(redactedMessage)
+ }
+ redacted.setLargeIcon(original.getLargeIcon())
+ redacted.setSmallIcon(original.smallIcon)
+ return redacted
+ }
+
private fun createRemoteViews(
@InflationFlag reInflateFlags: Int,
builder: Notification.Builder,
- isMinimized: Boolean,
- usesIncreasedHeight: Boolean,
- usesIncreasedHeadsUpHeight: Boolean,
+ bindParams: BindParams,
+ entry: NotificationEntry,
+ systemUiContext: Context,
+ packageContext: Context,
row: ExpandableNotificationRow,
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
headsUpStyleProvider: HeadsUpStyleProvider,
@@ -782,7 +822,11 @@ constructor(
entryForLogging,
"creating contracted remote view",
)
- createContentView(builder, isMinimized, usesIncreasedHeight)
+ createContentView(
+ builder,
+ bindParams.isMinimized,
+ bindParams.usesIncreasedHeight,
+ )
} else null
val expanded =
if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
@@ -790,7 +834,7 @@ constructor(
entryForLogging,
"creating expanded remote view",
)
- createExpandedView(builder, isMinimized)
+ createExpandedView(builder, bindParams.isMinimized)
} else null
val headsUp =
if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
@@ -802,13 +846,26 @@ constructor(
if (isHeadsUpCompact) {
builder.createCompactHeadsUpContentView()
} else {
- builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight)
+ builder.createHeadsUpContentView(bindParams.usesIncreasedHeadsUpHeight)
}
} else null
val public =
if (reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC != 0) {
logger.logAsyncTaskProgress(entryForLogging, "creating public remote view")
- builder.makePublicContentView(isMinimized)
+ if (
+ LockscreenOtpRedaction.isEnabled &&
+ bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT
+ ) {
+ createSensitiveContentMessageNotification(
+ entry.sbn.notification,
+ builder.style,
+ systemUiContext,
+ packageContext,
+ )
+ .createContentView(bindParams.usesIncreasedHeight)
+ } else {
+ builder.makePublicContentView(bindParams.isMinimized)
+ }
} else null
val normalGroupHeader =
if (
@@ -1420,8 +1477,7 @@ constructor(
entry.setContentModel(result.contentModel)
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- entry.promotedNotificationContentModel =
- result.extractedPromotedNotificationContentModel
+ entry.promotedNotificationContentModel = result.promotedContent
}
result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
index 427fb66ca2d0..bc44cb0e1074 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -31,6 +33,7 @@ public final class RowContentBindParams {
private boolean mUseIncreasedHeadsUpHeight;
private boolean mViewsNeedReinflation;
private @InflationFlag int mContentViews = DEFAULT_INFLATION_FLAGS;
+ private @RedactionType int mRedactionType = REDACTION_TYPE_NONE;
/**
* Content views that are out of date and need to be rebound.
@@ -58,6 +61,20 @@ public final class RowContentBindParams {
}
/**
+ * @return What type of redaction should be used by the public view (if requested)
+ */
+ public @RedactionType int getRedactionType() {
+ return mRedactionType;
+ }
+
+ /**
+ * Set the redaction type, which controls what sort of public view is shown.
+ */
+ public void setRedactionType(@RedactionType int redactionType) {
+ mRedactionType = redactionType;
+ }
+
+ /**
* Set whether content should use an increased height version of its contracted view.
*/
public void setUseIncreasedCollapsedHeight(boolean useIncreasedHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
index 89fcda949b5b..53f74161e7fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
@@ -72,10 +72,8 @@ public class RowContentBindStage extends BindStage<RowContentBindParams> {
// Bind/unbind with parameters
mBinder.unbindContent(entry, row, contentToUnbind);
- BindParams bindParams = new BindParams();
- bindParams.isMinimized = params.useMinimized();
- bindParams.usesIncreasedHeight = params.useIncreasedHeight();
- bindParams.usesIncreasedHeadsUpHeight = params.useIncreasedHeadsUpHeight();
+ BindParams bindParams = new BindParams(params.useMinimized(), params.useIncreasedHeight(),
+ params.useIncreasedHeadsUpHeight(), params.getRedactionType());
boolean forceInflate = params.needsReinflation();
InflationCallback inflationCallback = new InflationCallback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index e702f10d7f50..fe2803bfc5d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -51,6 +51,7 @@ internal object SingleLineViewInflater {
* notification, not for legacy messaging notifications
* @param builder the recovered Notification Builder
* @param systemUiContext the context of Android System UI
+ * @param redactText indicates if the text needs to be redacted
* @return the inflated SingleLineViewModel
*/
@JvmStatic
@@ -59,13 +60,21 @@ internal object SingleLineViewInflater {
messagingStyle: MessagingStyle?,
builder: Notification.Builder,
systemUiContext: Context,
+ redactText: Boolean,
): SingleLineViewModel {
if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) {
return SingleLineViewModel(null, null, null)
}
peopleHelper.init(systemUiContext)
var titleText = HybridGroupManager.resolveTitle(notification)
- var contentText = HybridGroupManager.resolveText(notification)
+ var contentText =
+ if (redactText) {
+ systemUiContext.getString(
+ com.android.systemui.res.R.string.redacted_notification_single_line_text
+ )
+ } else {
+ HybridGroupManager.resolveText(notification)
+ }
if (messagingStyle == null) {
return SingleLineViewModel(
@@ -81,7 +90,7 @@ internal object SingleLineViewInflater {
if (conversationTextData?.conversationTitle?.isNotEmpty() == true) {
titleText = conversationTextData.conversationTitle
}
- if (conversationTextData?.conversationText?.isNotEmpty() == true) {
+ if (!redactText && conversationTextData?.conversationText?.isNotEmpty() == true) {
contentText = conversationTextData.conversationText
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 7c9d850eaf07..38a70359e816 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3729,14 +3729,6 @@ public class NotificationStackScrollLayout
// Only when scene container is enabled, mark that we are being dragged so that we start
// dispatching the rest of the gesture to scene container.
- void startOverscrollAfterExpanding() {
- if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
- getExpandHelper().finishExpanding();
- setIsBeingDragged(true);
- }
-
- // Only when scene container is enabled, mark that we are being dragged so that we start
- // dispatching the rest of the gesture to scene container.
void startDraggingOnHun() {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
setIsBeingDragged(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 245b1d29fb79..a33a9ed2df75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -2203,11 +2203,10 @@ public class NotificationStackScrollLayoutController implements Dumpable {
expandingNotification = mView.isExpandingNotification();
if (mView.getExpandedInThisMotion() && !expandingNotification && wasExpandingBefore
&& !mView.getDisallowScrollingInThisMotion()) {
- // We need to dispatch the overscroll differently when Scene Container is on,
- // since NSSL no longer controls its own scroll.
+ // Finish expansion here, as this gesture will be marked to be sent to
+ // scene container
if (SceneContainerFlag.isEnabled() && !isCancelOrUp) {
- mView.startOverscrollAfterExpanding();
- return true;
+ expandHelper.finishExpanding();
} else {
mView.dispatchDownEventToScroller(ev);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
index 42acd7bcdc8a..705845ff984c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
@@ -75,7 +75,7 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) :
constraintSet.apply {
if (SceneContainerFlag.isEnabled) {
when (horizontalPosition) {
- is HorizontalPosition.FloatAtEnd ->
+ is HorizontalPosition.FloatAtStart ->
constrainWidth(nsslId, horizontalPosition.width)
is HorizontalPosition.MiddleToEdge ->
setGuidelinePercent(R.id.nssl_guideline, horizontalPosition.ratio)
@@ -83,13 +83,13 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) :
}
}
+ connect(nsslId, START, startConstraintId, START, marginStart)
if (
!SceneContainerFlag.isEnabled ||
- horizontalPosition !is HorizontalPosition.FloatAtEnd
+ horizontalPosition !is HorizontalPosition.FloatAtStart
) {
- connect(nsslId, START, startConstraintId, START, marginStart)
+ connect(nsslId, END, PARENT_ID, END, marginEnd)
}
- connect(nsslId, END, PARENT_ID, END, marginEnd)
connect(nsslId, BOTTOM, PARENT_ID, BOTTOM, marginBottom)
connect(nsslId, TOP, PARENT_ID, TOP, marginTop)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index b81c71ebe19b..fc8c70fb8e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -247,7 +247,7 @@ constructor(
Split -> HorizontalPosition.MiddleToEdge(ratio = 0.5f)
Dual ->
if (isShadeLayoutWide) {
- HorizontalPosition.FloatAtEnd(
+ HorizontalPosition.FloatAtStart(
width = getDimensionPixelSize(R.dimen.shade_panel_width)
)
} else {
@@ -830,10 +830,10 @@ constructor(
data class MiddleToEdge(val ratio: Float = 0.5f) : HorizontalPosition
/**
- * The container has a fixed [width] and is aligned to the end of the screen. In this
- * layout, the start edge of the container is floating, i.e. unconstrained.
+ * The container has a fixed [width] and is aligned to the start of the screen. In this
+ * layout, the end edge of the container is floating, i.e. unconstrained.
*/
- data class FloatAtEnd(val width: Int) : HorizontalPosition
+ data class FloatAtStart(val width: Int) : HorizontalPosition
}
/**
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 7bea4800f7fc..6dc25aa5144f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -21,9 +21,10 @@ import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
-import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
@@ -207,6 +208,7 @@ import com.android.systemui.statusbar.data.repository.StatusBarModeRepositorySto
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -221,7 +223,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
@@ -2514,12 +2515,15 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
* should update only the status bar components.
*/
private void setBouncerShowingForStatusBarComponents(boolean bouncerShowing) {
- int importance = bouncerShowing
- ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
if (!StatusBarConnectedDisplays.isEnabled() && mPhoneStatusBarViewController != null) {
+ int importance = bouncerShowing
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
mPhoneStatusBarViewController.setImportantForAccessibility(importance);
}
+ int importance = bouncerShowing
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_NO;
mShadeSurface.setImportantForAccessibility(importance);
mShadeSurface.setBouncerShowing(bouncerShowing);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 0fac6448909c..f37bc6b2d4fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -37,12 +37,10 @@ import androidx.annotation.NonNull;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.InitController;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeViewController;
@@ -61,7 +59,6 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
@@ -72,6 +69,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.Set;
@@ -104,7 +102,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
private final IStatusBarService mBarService;
private final DynamicPrivacyController mDynamicPrivacyController;
private final NotificationListContainer mNotifListContainer;
- private final DeviceUnlockedInteractor mDeviceUnlockedInteractor;
private final QuickSettingsController mQsController;
protected boolean mVrMode;
@@ -136,8 +133,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
NotificationRemoteInputManager remoteInputManager,
NotificationRemoteInputManager.Callback remoteInputManagerCallback,
- NotificationListContainer notificationListContainer,
- DeviceUnlockedInteractor deviceUnlockedInteractor) {
+ NotificationListContainer notificationListContainer) {
mActivityStarter = activityStarter;
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
@@ -164,7 +160,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mNotifListContainer = notificationListContainer;
- mDeviceUnlockedInteractor = deviceUnlockedInteractor;
IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
@@ -253,25 +248,14 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
} else if (clickedEntry.isSensitive().getValue()
- && isInLockedDownShade()) {
+ && mDynamicPrivacyController.isInLockedDownShade()) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
- // launch the bouncer
mActivityStarter.dismissKeyguardThenExecute(() -> false /* dismissAction */
, null /* cancelRunnable */, false /* afterKeyguardGone */);
}
}
}
- /** @return true if the Shade is shown over the Lockscreen, and the device is locked */
- private boolean isInLockedDownShade() {
- if (SceneContainerFlag.isEnabled()) {
- return mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
- && !mDeviceUnlockedInteractor.getDeviceUnlockStatus().getValue().isUnlocked();
- } else {
- return mDynamicPrivacyController.isInLockedDownShade();
- }
- }
-
@Override
public boolean isDeviceInVrMode() {
return mVrMode;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 78954dea27ba..8b60ee56d5f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -192,7 +192,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
mUiEventLogger.log(
LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP);
- mUserSwitchDialogController.showDialog(mUserAvatarViewWithBackground.getContext(),
+ mUserSwitchDialogController.showDialog(
Expandable.fromView(mUserAvatarViewWithBackground));
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
index a1d5cbea62f9..9ff0d18f0e2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
@@ -44,6 +44,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dialog.ui.composable.AlertDialogContent
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
@@ -67,6 +68,7 @@ constructor(
private val viewModel: Provider<ModesDialogViewModel>,
private val dialogEventLogger: ModesDialogEventLogger,
@Main private val mainCoroutineContext: CoroutineContext,
+ private val shadeDisplayContextRepository: ShadeDialogContextInteractor,
) : SystemUIDialog.Delegate {
// NOTE: This should only be accessed/written from the main thread.
@VisibleForTesting var currentDialog: ComponentSystemUIDialog? = null
@@ -78,7 +80,10 @@ constructor(
currentDialog?.dismiss()
}
- currentDialog = sysuiDialogFactory.create { ModesDialogContent(it) }
+ currentDialog =
+ sysuiDialogFactory.create(context = shadeDisplayContextRepository.context) {
+ ModesDialogContent(it)
+ }
currentDialog
?.lifecycle
?.addObserver(
@@ -106,9 +111,8 @@ constructor(
modifier =
Modifier.semantics {
testTagsAsResourceId = true
- paneTitle = dialog.context.getString(
- R.string.accessibility_desc_quick_settings
- )
+ paneTitle =
+ dialog.context.getString(R.string.accessibility_desc_quick_settings)
},
title = {
Text(
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index a382cf921152..e08114f6c3cd 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -21,10 +21,8 @@ import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.os.PowerManager
import android.provider.Settings
-import androidx.core.view.OneShotPreDrawListener
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.ToAodFoldTransitionInteractor
@@ -125,11 +123,7 @@ constructor(
private val shadeFoldAnimator: ShadeFoldAnimator
get() {
- return if (MigrateClocksToBlueprint.isEnabled) {
- foldTransitionInteractor.get().foldAnimator
- } else {
- shadeViewController.shadeFoldAnimator
- }
+ return foldTransitionInteractor.get().foldAnimator
}
private fun setAnimationState(playing: Boolean) {
@@ -164,15 +158,7 @@ constructor(
setAnimationState(playing = true)
shadeFoldAnimator.prepareFoldToAodAnimation()
- // We don't need to wait for the scrim as it is already displayed
- // but we should wait for the initial animation preparations to be drawn
- // (setting initial alpha/translation)
- // TODO(b/254878364): remove this call to NPVC.getView()
- if (!MigrateClocksToBlueprint.isEnabled) {
- shadeFoldAnimator.view?.let { OneShotPreDrawListener.add(it, onReady) }
- } else {
- onReady.run()
- }
+ onReady.run()
} else {
// No animation, call ready callback immediately
onReady.run()
@@ -252,7 +238,7 @@ constructor(
if (isFolded) {
foldToAodLatencyTracker.onFolded()
}
- }
+ },
)
/**
@@ -272,6 +258,7 @@ constructor(
latencyTracker.onActionStart(LatencyTracker.ACTION_FOLD_TO_AOD)
}
}
+
/**
* Called once the Fold -> AOD animation is started.
*
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
index 102fcc0c59f2..e4b2dc25e411 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
@@ -18,7 +18,6 @@
package com.android.systemui.user.ui.dialog
import android.app.Dialog
-import android.content.Context
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.users.UserCreatingDialog
@@ -32,6 +31,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.user.UserSwitchFullscreenDialog
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.domain.model.ShowDialogRequestModel
@@ -48,7 +48,6 @@ import com.android.app.tracing.coroutines.launchTraced as launch
class UserSwitcherDialogCoordinator
@Inject
constructor(
- @Application private val context: Lazy<Context>,
@Application private val applicationScope: Lazy<CoroutineScope>,
private val falsingManager: Lazy<FalsingManager>,
private val broadcastSender: Lazy<BroadcastSender>,
@@ -59,6 +58,7 @@ constructor(
private val activityStarter: Lazy<ActivityStarter>,
private val falsingCollector: Lazy<FalsingCollector>,
private val userSwitcherViewModel: Lazy<UserSwitcherViewModel>,
+ private val shadeDialogContextInteractor: Lazy<ShadeDialogContextInteractor>,
) : CoreStartable {
private var currentDialog: Dialog? = null
@@ -71,12 +71,13 @@ constructor(
private fun startHandlingDialogShowRequests() {
applicationScope.get().launch {
interactor.get().dialogShowRequests.filterNotNull().collect { request ->
+ val context = shadeDialogContextInteractor.get().context
val (dialog, dialogCuj) =
when (request) {
is ShowDialogRequestModel.ShowAddUserDialog ->
Pair(
AddUserDialog(
- context = context.get(),
+ context = context,
userHandle = request.userHandle,
isKeyguardShowing = request.isKeyguardShowing,
showEphemeralMessage = request.showEphemeralMessage,
@@ -92,7 +93,7 @@ constructor(
is ShowDialogRequestModel.ShowUserCreationDialog ->
Pair(
UserCreatingDialog(
- context.get(),
+ context,
request.isGuest,
),
null,
@@ -100,7 +101,7 @@ constructor(
is ShowDialogRequestModel.ShowExitGuestDialog ->
Pair(
ExitGuestDialog(
- context = context.get(),
+ context = context,
guestUserId = request.guestUserId,
isGuestEphemeral = request.isGuestEphemeral,
targetUserId = request.targetUserId,
@@ -117,7 +118,7 @@ constructor(
is ShowDialogRequestModel.ShowUserSwitcherDialog ->
Pair(
UserSwitchDialog(
- context = context.get(),
+ context = context,
adapter = userDetailAdapterProvider.get(),
uiEventLogger = eventLogger.get(),
falsingManager = falsingManager.get(),
@@ -132,7 +133,7 @@ constructor(
is ShowDialogRequestModel.ShowUserSwitcherFullscreenDialog ->
Pair(
UserSwitchFullscreenDialog(
- context = context.get(),
+ context = context,
falsingCollector = falsingCollector.get(),
userSwitcherViewModel = userSwitcherViewModel.get(),
),
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
index 2e1f82d56fc4..70e342f3eefb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
@@ -17,45 +17,28 @@
package com.android.systemui.volume.dialog.settings.ui.binder
import android.view.View
-import com.android.systemui.lifecycle.WindowLifecycleState
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
-import com.android.systemui.lifecycle.viewModel
+import android.widget.ImageButton
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.settings.ui.viewmodel.VolumeDialogSettingsButtonViewModel
import javax.inject.Inject
-import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@VolumeDialogScope
class VolumeDialogSettingsButtonViewBinder
@Inject
-constructor(private val viewModelFactory: VolumeDialogSettingsButtonViewModel.Factory) {
+constructor(private val viewModel: VolumeDialogSettingsButtonViewModel) {
- fun bind(view: View) {
- with(view) {
- val button = requireViewById<View>(R.id.volume_dialog_settings)
- repeatWhenAttached {
- viewModel(
- traceName = "VolumeDialogViewBinder",
- minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelFactory.create() },
- ) { viewModel ->
- setSnapshotBinding {
- viewModel.isVisible
- .onEach { isVisible ->
- visibility = if (isVisible) View.VISIBLE else View.GONE
- }
- .launchIn(this)
+ fun CoroutineScope.bind(view: View) {
+ val button = view.requireViewById<ImageButton>(R.id.volume_dialog_settings)
+ viewModel.isVisible
+ .onEach { isVisible -> button.visibility = if (isVisible) View.VISIBLE else View.GONE }
+ .launchIn(this)
- button.setOnClickListener { viewModel.onButtonClicked() }
- }
+ viewModel.icon.onEach { button.setImageDrawable(it) }.launchIn(this)
- awaitCancellation()
- }
- }
- }
+ button.setOnClickListener { viewModel.onButtonClicked() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
index 015d773b2c02..03442dbcde66 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
@@ -14,27 +14,206 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.volume.dialog.settings.ui.viewmodel
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.ColorFilter
+import android.graphics.drawable.Drawable
+import android.media.session.PlaybackState
+import androidx.annotation.ColorInt
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieCompositionFactory
+import com.airbnb.lottie.LottieDrawable
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.SimpleColorFilter
+import com.airbnb.lottie.model.KeyPath
+import com.airbnb.lottie.value.LottieValueCallback
+import com.android.internal.R as internalR
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.lottie.await
+import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.settings.domain.VolumeDialogSettingsButtonInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.panel.shared.model.filterData
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.resume
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.runningFold
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transform
+import kotlinx.coroutines.suspendCancellableCoroutine
class VolumeDialogSettingsButtonViewModel
-@AssistedInject
-constructor(private val interactor: VolumeDialogSettingsButtonInteractor) {
+@Inject
+constructor(
+ @Application private val context: Context,
+ @UiBackground private val uiBgCoroutineContext: CoroutineContext,
+ @VolumeDialog private val coroutineScope: CoroutineScope,
+ mediaOutputInteractor: MediaOutputInteractor,
+ private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+ private val interactor: VolumeDialogSettingsButtonInteractor,
+) {
+
+ @SuppressLint("UseCompatLoadingForDrawables")
+ private val drawables: Flow<Drawables> =
+ flow {
+ val color = context.getColor(internalR.color.materialColorPrimary)
+ emit(
+ Drawables(
+ start =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_in)
+ .await()
+ .toDrawable { setColor(color) },
+ playing =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_playing)
+ .await()
+ .toDrawable {
+ repeatCount = LottieDrawable.INFINITE
+ repeatMode = LottieDrawable.RESTART
+ setColor(color)
+ },
+ stop =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_out)
+ .await()
+ .toDrawable { setColor(color) },
+ idle = context.getDrawable(R.drawable.audio_bars_idle)!!,
+ )
+ )
+ }
+ .buffer()
+ .flowOn(uiBgCoroutineContext)
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
val isVisible = interactor.isVisible
+ val icon: Flow<Drawable> =
+ mediaOutputInteractor.defaultActiveMediaSession
+ .filterData()
+ .flatMapLatest { session ->
+ if (session == null) {
+ flowOf(null)
+ } else {
+ mediaDeviceSessionInteractor.playbackState(session)
+ }
+ }
+ .runningFold(null) { playbackStates: PlaybackStates?, playbackState: PlaybackState? ->
+ val isCurrentActive = playbackState?.isActive ?: false
+ if (playbackStates != null && isCurrentActive == playbackState?.isActive) {
+ return@runningFold playbackStates
+ }
+ playbackStates?.copy(
+ isPreviousActive = playbackStates.isCurrentActive,
+ isCurrentActive = isCurrentActive,
+ ) ?: PlaybackStates(isPreviousActive = null, isCurrentActive = isCurrentActive)
+ }
+ .filterNotNull()
+ // only apply the most recent state if we wait for the animation.
+ .buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ // distinct again because the changed state might've been dropped by the buffer
+ .distinctUntilChangedBy { it.isCurrentActive }
+ .transform { emitDrawables(it) }
+ .runningFold(null) { previous: Drawable?, current: Drawable ->
+ // wait for the previous animation to finish before starting the new one
+ // this also waits for the current loop of the playing animation to finish
+ (previous as? LottieDrawable)?.awaitFinish()
+ (current as? LottieDrawable)?.start()
+ current
+ }
+ .filterNotNull()
+
+ private suspend fun FlowCollector<Drawable>.emitDrawables(playbackStates: PlaybackStates) {
+ val animations = drawables.first()
+ val stateChanged =
+ playbackStates.isPreviousActive != null &&
+ playbackStates.isPreviousActive != playbackStates.isCurrentActive
+ if (playbackStates.isCurrentActive) {
+ if (stateChanged) {
+ emit(animations.start)
+ }
+ emit(animations.playing)
+ } else {
+ if (stateChanged) {
+ emit(animations.stop)
+ }
+ emit(animations.idle)
+ }
+ }
fun onButtonClicked() {
interactor.onButtonClicked()
}
- @VolumeDialogScope
- @AssistedFactory
- interface Factory {
+ private data class PlaybackStates(val isPreviousActive: Boolean?, val isCurrentActive: Boolean)
+
+ private data class Drawables(
+ val start: LottieDrawable,
+ val playing: LottieDrawable,
+ val stop: LottieDrawable,
+ val idle: Drawable,
+ )
+}
+
+private fun LottieComposition.toDrawable(setup: LottieDrawable.() -> Unit = {}): LottieDrawable =
+ LottieDrawable().also { drawable ->
+ drawable.composition = this
+ drawable.setup()
+ }
- fun create(): VolumeDialogSettingsButtonViewModel
+/** Suspends until current loop of the repeating animation is finished */
+private suspend fun LottieDrawable.awaitFinish() = suspendCancellableCoroutine { continuation ->
+ if (!isRunning) {
+ continuation.resume(Unit)
+ return@suspendCancellableCoroutine
}
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationRepeat(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+ }
+ addAnimatorListener(listener)
+ continuation.invokeOnCancellation { removeAnimatorListener(listener) }
+}
+
+/**
+ * Overrides colors of the [LottieDrawable] to a specified [color]
+ *
+ * @see com.airbnb.lottie.LottieAnimationView
+ */
+private fun LottieDrawable.setColor(@ColorInt color: Int) {
+ val callback = LottieValueCallback<ColorFilter>(SimpleColorFilter(color))
+ addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER, callback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index f30524638150..faf06b942cab 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -26,7 +26,7 @@ import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
-import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
+import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider
import javax.inject.Inject
@@ -84,5 +84,5 @@ private suspend fun Slider.setValueAnimated(
interpolator = DecelerateInterpolator()
addListener(jankListener)
}
- .awaitAnimation<Float> { value = it }
+ .suspendAnimate<Float> { value = it }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
index 10cf615ce0ce..5f124806dac7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator
import android.view.ViewPropertyAnimator
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringAnimation
+import com.airbnb.lottie.LottieDrawable
import kotlin.coroutines.resume
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -66,7 +67,7 @@ suspend fun ViewPropertyAnimator.suspendAnimate(
* is cancelled.
*/
@Suppress("UNCHECKED_CAST")
-suspend fun <T> ValueAnimator.awaitAnimation(onValueChanged: (T) -> Unit) {
+suspend fun <T> ValueAnimator.suspendAnimate(onValueChanged: (T) -> Unit) {
suspendCancellableCoroutine { continuation ->
addListener(
object : AnimatorListenerAdapter() {
@@ -103,6 +104,29 @@ suspend fun SpringAnimation.suspendAnimate(
}
}
+/**
+ * Starts the animation and suspends until it's finished. Cancels the animation if the running
+ * coroutine is cancelled.
+ */
+suspend fun LottieDrawable.suspendAnimate() = suspendCancellableCoroutine { continuation ->
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ continuation.resumeIfCan(Unit)
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ continuation.resumeIfCan(Unit)
+ }
+ }
+ addAnimatorListener(listener)
+ start()
+ continuation.invokeOnCancellation {
+ removeAnimatorListener(listener)
+ stop()
+ }
+}
+
private fun <T> CancellableContinuation<T>.resumeIfCan(value: T) {
if (!isCancelled && !isCompleted) {
resume(value)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
index 6e1ebc820b08..12e624cae4d4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
@@ -19,10 +19,10 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto
import android.media.session.MediaController
import android.media.session.PlaybackState
import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -38,7 +38,7 @@ import kotlinx.coroutines.withContext
/** Allows to observe and change [MediaDeviceSession] state. */
@OptIn(ExperimentalCoroutinesApi::class)
-@VolumePanelScope
+@SysUISingleton
class MediaDeviceSessionInteractor
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index b3848a6d7817..2973e11c365d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -24,12 +24,13 @@ import androidx.annotation.WorkerThread
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.shared.model.Result
import com.android.systemui.volume.panel.shared.model.filterData
import com.android.systemui.volume.panel.shared.model.wrapInResult
@@ -54,13 +55,13 @@ import kotlinx.coroutines.withContext
/** Provides observable models about the current media session state. */
@OptIn(ExperimentalCoroutinesApi::class)
-@VolumePanelScope
+@SysUISingleton
class MediaOutputInteractor
@Inject
constructor(
private val localMediaRepositoryFactory: LocalMediaRepositoryFactory,
private val packageManager: PackageManager,
- @VolumePanelScope private val coroutineScope: CoroutineScope,
+ @Application private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
mediaControllerRepository: MediaControllerRepository,
private val mediaControllerInteractor: MediaControllerInteractor,
@@ -77,7 +78,7 @@ constructor(
.onStart { emit(activeSessions) }
}
.map { getMediaControllers(it) }
- .stateIn(coroutineScope, SharingStarted.Eagerly, MediaControllers(null, null))
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), MediaControllers(null, null))
/** [MediaDeviceSessions] that contains currently active sessions. */
val activeMediaDeviceSessions: Flow<MediaDeviceSessions> =
@@ -89,7 +90,11 @@ constructor(
)
}
.flowOn(backgroundCoroutineContext)
- .stateIn(coroutineScope, SharingStarted.Eagerly, MediaDeviceSessions(null, null))
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(),
+ MediaDeviceSessions(null, null),
+ )
/** Returns the default [MediaDeviceSession] from [activeMediaDeviceSessions] */
val defaultActiveMediaSession: StateFlow<Result<MediaDeviceSession?>> =
@@ -104,7 +109,7 @@ constructor(
}
.wrapInResult()
.flowOn(backgroundCoroutineContext)
- .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), Result.Loading())
private val localMediaRepository: Flow<LocalMediaRepository> =
defaultActiveMediaSession
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index a41725f754df..4abbbacd800b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -25,7 +25,6 @@ import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND_INACTIVE
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.Flags
@@ -299,20 +298,6 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun keyguardCallback_visibilityChanged_clockDozeCalled() =
- runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
-
- captor.value.onKeyguardVisibilityChanged(true)
- verify(animations, never()).doze(0f)
-
- captor.value.onKeyguardVisibilityChanged(false)
- verify(animations, times(2)).doze(0f)
- }
-
- @Test
fun keyguardCallback_timeFormat_clockNotified() =
runBlocking(IMMEDIATE) {
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
@@ -344,19 +329,6 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
- fun keyguardCallback_verifyKeyguardChanged() =
- runBlocking(IMMEDIATE) {
- val job = underTest.listenForDozeAmount(this)
- repository.setDozeAmount(0.4f)
-
- yield()
-
- verify(animations, times(2)).doze(0.4f)
-
- job.cancel()
- }
-
- @Test
fun listenForDozeAmountTransition_updatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 4aaa82e4a16d..37eb148a5ea7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -14,6 +14,8 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.IRemoteAnimationFinishedCallback
import android.view.RemoteAnimationAdapter
import android.view.RemoteAnimationTarget
+import android.view.RemoteAnimationTarget.MODE_CLOSING
+import android.view.RemoteAnimationTarget.MODE_OPENING
import android.view.SurfaceControl
import android.view.ViewGroup
import android.view.WindowManager.TRANSIT_NONE
@@ -36,10 +38,6 @@ import junit.framework.Assert.assertTrue
import junit.framework.AssertionFailedError
import kotlin.concurrent.thread
import kotlin.test.assertEquals
-import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withTimeout
import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Before
@@ -49,6 +47,7 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
+import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
@@ -215,22 +214,12 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun registersLongLivedTransition() {
- activityTransitionAnimator.register(
- object : DelegateTransitionAnimatorController(controller) {
- override val transitionCookie =
- ActivityTransitionAnimator.TransitionCookie("test_cookie_1")
- override val component = ComponentName("com.test.package", "Test1")
- }
- )
+ var factory = controllerFactory()
+ activityTransitionAnimator.register(factory.cookie, factory)
assertEquals(2, testShellTransitions.remotes.size)
- activityTransitionAnimator.register(
- object : DelegateTransitionAnimatorController(controller) {
- override val transitionCookie =
- ActivityTransitionAnimator.TransitionCookie("test_cookie_2")
- override val component = ComponentName("com.test.package", "Test2")
- }
- )
+ factory = controllerFactory()
+ activityTransitionAnimator.register(factory.cookie, factory)
assertEquals(4, testShellTransitions.remotes.size)
}
@@ -241,20 +230,12 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@Test
fun registersLongLivedTransitionOverridingPreviousRegistration() {
val cookie = ActivityTransitionAnimator.TransitionCookie("test_cookie")
- activityTransitionAnimator.register(
- object : DelegateTransitionAnimatorController(controller) {
- override val transitionCookie = cookie
- override val component = ComponentName("com.test.package", "Test1")
- }
- )
+ var factory = controllerFactory(cookie)
+ activityTransitionAnimator.register(cookie, factory)
val transitions = testShellTransitions.remotes.values.toList()
- activityTransitionAnimator.register(
- object : DelegateTransitionAnimatorController(controller) {
- override val transitionCookie = cookie
- override val component = ComponentName("com.test.package", "Test2")
- }
- )
+ factory = controllerFactory(cookie)
+ activityTransitionAnimator.register(cookie, factory)
assertEquals(2, testShellTransitions.remotes.size)
for (transition in transitions) {
assertThat(testShellTransitions.remotes.values).doesNotContain(transition)
@@ -264,38 +245,19 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@DisableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
@Test
fun doesNotRegisterLongLivedTransitionIfFlagIsDisabled() {
- val controller =
- object : DelegateTransitionAnimatorController(controller) {
- override val transitionCookie =
- ActivityTransitionAnimator.TransitionCookie("test_cookie")
- override val component = ComponentName("com.test.package", "Test")
- }
+ val factory = controllerFactory(component = null)
assertThrows(IllegalStateException::class.java) {
- activityTransitionAnimator.register(controller)
+ activityTransitionAnimator.register(factory.cookie, factory)
}
}
@EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
@Test
fun doesNotRegisterLongLivedTransitionIfMissingRequiredProperties() {
- // No TransitionCookie
- val controllerWithoutCookie =
- object : DelegateTransitionAnimatorController(controller) {
- override val transitionCookie = null
- }
- assertThrows(IllegalStateException::class.java) {
- activityTransitionAnimator.register(controllerWithoutCookie)
- }
-
// No ComponentName
- val controllerWithoutComponent =
- object : DelegateTransitionAnimatorController(controller) {
- override val transitionCookie =
- ActivityTransitionAnimator.TransitionCookie("test_cookie")
- override val component = null
- }
+ var factory = controllerFactory(component = null)
assertThrows(IllegalStateException::class.java) {
- activityTransitionAnimator.register(controllerWithoutComponent)
+ activityTransitionAnimator.register(factory.cookie, factory)
}
// No TransitionRegister
@@ -307,14 +269,9 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
testTransitionAnimator,
disableWmTimeout = true,
)
- val validController =
- object : DelegateTransitionAnimatorController(controller) {
- override val transitionCookie =
- ActivityTransitionAnimator.TransitionCookie("test_cookie")
- override val component = ComponentName("com.test.package", "Test")
- }
+ factory = controllerFactory()
assertThrows(IllegalStateException::class.java) {
- activityTransitionAnimator.register(validController)
+ activityTransitionAnimator.register(factory.cookie, factory)
}
}
@@ -324,18 +281,12 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun unregistersLongLivedTransition() {
-
val cookies = arrayOfNulls<ActivityTransitionAnimator.TransitionCookie>(3)
for (index in 0 until 3) {
- cookies[index] = ActivityTransitionAnimator.TransitionCookie("test_cookie_$index")
-
- val controller =
- object : DelegateTransitionAnimatorController(controller) {
- override val transitionCookie = cookies[index]
- override val component = ComponentName("foo.bar", "Foobar")
- }
- activityTransitionAnimator.register(controller)
+ cookies[index] = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val factory = controllerFactory(cookies[index]!!)
+ activityTransitionAnimator.register(factory.cookie, factory)
}
activityTransitionAnimator.unregister(cookies[0]!!)
@@ -350,7 +301,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@Test
fun doesNotStartIfAnimationIsCancelled() {
- val runner = activityTransitionAnimator.createRunner(controller)
+ val runner = activityTransitionAnimator.createEphemeralRunner(controller)
runner.onAnimationCancelled()
runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)
@@ -364,7 +315,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@Test
fun cancelsIfNoOpeningWindowIsFound() {
- val runner = activityTransitionAnimator.createRunner(controller)
+ val runner = activityTransitionAnimator.createEphemeralRunner(controller)
runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
@@ -377,7 +328,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@Test
fun startsAnimationIfWindowIsOpening() {
- val runner = activityTransitionAnimator.createRunner(controller)
+ val runner = activityTransitionAnimator.createEphemeralRunner(controller)
runner.onAnimationStart(
TRANSIT_NONE,
arrayOf(fakeWindow()),
@@ -404,7 +355,8 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@Test
fun creatingRunnerWithLazyInitializationThrows_whenTheFlagsAreDisabled() {
assertThrows(IllegalStateException::class.java) {
- activityTransitionAnimator.createRunner(controller, longLived = true)
+ val factory = controllerFactory()
+ activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = true)
}
}
@@ -414,7 +366,8 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun runnerCreatesDelegateLazily_whenPostingTimeouts() {
- val runner = activityTransitionAnimator.createRunner(controller, longLived = true)
+ val factory = controllerFactory()
+ val runner = activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = true)
assertNull(runner.delegate)
runner.postTimeouts()
assertNotNull(runner.delegate)
@@ -426,29 +379,29 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun runnerCreatesDelegateLazily_onAnimationStart() {
- val runner = activityTransitionAnimator.createRunner(controller, longLived = true)
+ val factory = controllerFactory()
+ val runner = activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = true)
assertNull(runner.delegate)
- // The delegate is cleaned up after execution (which happens in another thread), so what we
- // do instead is check if it becomes non-null at any point with a 1 second timeout. This
- // will tell us that takeOverWithAnimation() triggered the lazy initialization.
var delegateInitialized = false
- runBlocking {
- val initChecker = launch {
- withTimeout(1.seconds) {
- while (runner.delegate == null) continue
+ activityTransitionAnimator.addListener(
+ object : ActivityTransitionAnimator.Listener {
+ override fun onTransitionAnimationStart() {
+ // This is called iff the delegate was initialized, so it's a good proxy for
+ // checking the initialization.
delegateInitialized = true
}
}
- runner.onAnimationStart(
- TRANSIT_NONE,
- arrayOf(fakeWindow()),
- emptyArray(),
- emptyArray(),
- iCallback,
- )
- initChecker.join()
- }
+ )
+ runner.onAnimationStart(
+ TRANSIT_NONE,
+ arrayOf(fakeWindow()),
+ emptyArray(),
+ emptyArray(),
+ iCallback,
+ )
+
+ waitForIdleSync()
assertTrue(delegateInitialized)
}
@@ -458,28 +411,28 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun runnerCreatesDelegateLazily_onAnimationTakeover() {
- val runner = activityTransitionAnimator.createRunner(controller, longLived = true)
+ val factory = controllerFactory()
+ val runner = activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = false)
assertNull(runner.delegate)
- // The delegate is cleaned up after execution (which happens in another thread), so what we
- // do instead is check if it becomes non-null at any point with a 1 second timeout. This
- // will tell us that takeOverWithAnimation() triggered the lazy initialization.
var delegateInitialized = false
- runBlocking {
- val initChecker = launch {
- withTimeout(1.seconds) {
- while (runner.delegate == null) continue
+ activityTransitionAnimator.addListener(
+ object : ActivityTransitionAnimator.Listener {
+ override fun onTransitionAnimationStart() {
+ // This is called iff the delegate was initialized, so it's a good proxy for
+ // checking the initialization.
delegateInitialized = true
}
}
- runner.takeOverAnimation(
- arrayOf(fakeWindow()),
- arrayOf(WindowAnimationState()),
- SurfaceControl.Transaction(),
- iCallback,
- )
- initChecker.join()
- }
+ )
+ runner.takeOverAnimation(
+ arrayOf(fakeWindow(MODE_CLOSING)),
+ arrayOf(WindowAnimationState()),
+ SurfaceControl.Transaction(),
+ iCallback,
+ )
+
+ waitForIdleSync()
assertTrue(delegateInitialized)
}
@@ -489,7 +442,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun animationTakeoverThrows_whenTheFlagsAreDisabled() {
- val runner = activityTransitionAnimator.createRunner(controller, longLived = false)
+ val runner = activityTransitionAnimator.createEphemeralRunner(controller)
assertThrows(IllegalStateException::class.java) {
runner.takeOverAnimation(
arrayOf(fakeWindow()),
@@ -506,14 +459,28 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun disposeRunner_delegateDereferenced() {
- val runner = activityTransitionAnimator.createRunner(controller)
+ val runner = activityTransitionAnimator.createEphemeralRunner(controller)
assertNotNull(runner.delegate)
runner.dispose()
waitForIdleSync()
assertNull(runner.delegate)
}
- private fun fakeWindow(): RemoteAnimationTarget {
+ private fun controllerFactory(
+ cookie: ActivityTransitionAnimator.TransitionCookie =
+ mock(ActivityTransitionAnimator.TransitionCookie::class.java),
+ component: ComponentName? = mock(ComponentName::class.java),
+ ): ActivityTransitionAnimator.ControllerFactory {
+ return object : ActivityTransitionAnimator.ControllerFactory(cookie, component) {
+ override fun createController(forLaunch: Boolean) =
+ object : DelegateTransitionAnimatorController(controller) {
+ override val isLaunching: Boolean
+ get() = forLaunch
+ }
+ }
+ }
+
+ private fun fakeWindow(mode: Int = MODE_OPENING): RemoteAnimationTarget {
val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
val taskInfo = ActivityManager.RunningTaskInfo()
taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity")
@@ -521,7 +488,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
return RemoteAnimationTarget(
0,
- RemoteAnimationTarget.MODE_OPENING,
+ mode,
SurfaceControl(),
false,
Rect(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index fb0fd23fab24..6bfd08025833 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -34,8 +34,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.model.SysUiState
import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.shadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
@@ -82,7 +84,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
private val uiProperties =
BluetoothTileDialogViewModel.UiProperties.build(
isBluetoothEnabled = ENABLED,
- isAutoOnToggleFeatureAvailable = ENABLED
+ isAutoOnToggleFeatureAvailable = ENABLED,
)
@Mock private lateinit var sysuiDialogFactory: SystemUIDialog.Factory
@Mock private lateinit var dialogManager: SystemUIDialogManager
@@ -98,6 +100,8 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
private lateinit var mBluetoothTileDialogDelegate: BluetoothTileDialogDelegate
private lateinit var deviceItem: DeviceItem
+ private val kosmos = testKosmos()
+
@Before
fun setUp() {
scheduler = TestCoroutineScheduler()
@@ -116,10 +120,16 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
fakeSystemClock,
uiEventLogger,
logger,
- sysuiDialogFactory
+ sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
- whenever(sysuiDialogFactory.create(any(SystemUIDialog.Delegate::class.java))).thenAnswer {
+ whenever(
+ sysuiDialogFactory.create(
+ any(SystemUIDialog.Delegate::class.java),
+ any()
+ )
+ ).thenAnswer {
SystemUIDialog(
mContext,
0,
@@ -128,7 +138,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
sysuiState,
fakeBroadcastDispatcher,
dialogTransitionAnimator,
- it.getArgument(0)
+ it.getArgument(0),
)
}
@@ -140,7 +150,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
deviceName = DEVICE_NAME,
connectionSummary = DEVICE_CONNECTION_SUMMARY,
iconWithDescription = icon,
- background = null
+ background = null,
)
`when`(cachedBluetoothDevice.isBusy).thenReturn(false)
}
@@ -169,7 +179,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
dialog,
listOf(deviceItem),
showSeeAll = false,
- showPairNewDevice = false
+ showPairNewDevice = false,
)
val recyclerView = dialog.requireViewById<RecyclerView>(R.id.device_list)
@@ -217,6 +227,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
uiEventLogger,
logger,
sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
.Adapter(bluetoothTileDialogCallback)
.DeviceItemViewHolder(view)
@@ -238,7 +249,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
dialog,
listOf(deviceItem),
showSeeAll = false,
- showPairNewDevice = true
+ showPairNewDevice = true,
)
val seeAllButton = dialog.requireViewById<View>(R.id.see_all_button)
@@ -272,6 +283,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
uiEventLogger,
logger,
sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
.createDialog()
dialog.show()
@@ -295,6 +307,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
uiEventLogger,
logger,
sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
.createDialog()
dialog.show()
@@ -318,6 +331,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
uiEventLogger,
logger,
sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
.createDialog()
dialog.show()
@@ -339,7 +353,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
dialog,
visibility = VISIBLE,
label = null,
- isActive = true
+ isActive = true,
)
val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
@@ -361,7 +375,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
dialog,
visibility = VISIBLE,
label = null,
- isActive = false
+ isActive = false,
)
val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
@@ -383,7 +397,7 @@ class BluetoothTileDialogDelegateTest : SysuiTestCase() {
dialog,
visibility = GONE,
label = null,
- isActive = false
+ isActive = false,
)
val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index c2c94a88603a..1cabf202463e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -77,6 +77,8 @@ public class CommunalTouchHandlerTest extends SysuiTestCase {
INITIATION_WIDTH,
mKosmos.getCommunalInteractor(),
mKosmos.getConfigurationInteractor(),
+ mKosmos.getSceneInteractor(),
+ Optional.of(mKosmos.getMockWindowRootViewProvider()),
mLifecycle
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 32fa160fc29f..21dd5bc068f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -344,7 +344,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
canShowWhileLocked = canShowWhileLocked,
)
} else {
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
underTest.onQuickAffordanceTriggered(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index 1184a76d54ff..1ce128c2403a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -81,7 +81,7 @@ import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
@FlakyTest(
bugId = 292574995,
- detail = "on certain architectures all permutations with startActivity=true is causing failures"
+ detail = "on certain architectures all permutations with startActivity=true is causing failures",
)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@@ -93,11 +93,7 @@ class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
private val DRAWABLE =
mock<Icon> {
whenever(this.contentDescription)
- .thenReturn(
- ContentDescription.Resource(
- res = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- )
+ .thenReturn(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
}
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
@@ -273,13 +269,7 @@ class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
context = context,
userFileManager =
mock<UserFileManager>().apply {
- whenever(
- getSharedPreferences(
- anyString(),
- anyInt(),
- anyInt(),
- )
- )
+ whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
@@ -316,9 +306,7 @@ class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractorFactory.create(
- featureFlags = featureFlags,
- )
+ KeyguardInteractorFactory.create(featureFlags = featureFlags)
.keyguardInteractor,
shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
@@ -350,9 +338,7 @@ class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
homeControls.setState(
lockScreenState =
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- icon = DRAWABLE,
- )
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = DRAWABLE)
)
homeControls.onTriggeredResult =
if (startActivity) {
@@ -361,7 +347,7 @@ class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
canShowWhileLocked = canShowWhileLocked,
)
} else {
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
underTest.onQuickAffordanceTriggered(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index b26f0a6e71a3..782b24825bcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -557,6 +557,13 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
}
@Test
+ public void startActivityForDialog_always_startActivityWithoutDismissShade() {
+ mInternetDialogController.startActivityForDialog(mock(Intent.class));
+
+ verify(mActivityStarter).startActivity(any(Intent.class), eq(false) /* dismissShade */);
+ }
+
+ @Test
public void launchWifiDetailsSetting_withNoWifiEntryKey_doNothing() {
mInternetDialogController.launchWifiDetailsSetting(null /* key */, mDialogLaunchView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index 300c9b843d1c..8560b67dee33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -35,6 +35,7 @@ import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -149,7 +150,8 @@ public class InternetDialogDelegateTest extends SysuiTestCase {
mHandler,
mBgExecutor,
mKeyguard,
- mSystemUIDialogFactory);
+ mSystemUIDialogFactory,
+ new FakeShadeDialogContextInteractor(mContext));
mInternetDialogDelegate.createDialog();
mInternetDialogDelegate.onCreate(mSystemUIDialog, null);
mInternetDialogDelegate.mAdapter = mInternetAdapter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index a0ecb802dd61..f695c13a9e62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -76,6 +76,8 @@ class UserTrackerImplTest : SysuiTestCase() {
@Mock private lateinit var iActivityManager: IActivityManager
+ @Mock private lateinit var beforeUserSwitchingReply: IRemoteCallback
+
@Mock private lateinit var userSwitchingReply: IRemoteCallback
@Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
@@ -199,9 +201,10 @@ class UserTrackerImplTest : SysuiTestCase() {
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
+ captor.value.onBeforeUserSwitching(newID, beforeUserSwitchingReply)
captor.value.onUserSwitching(newID, userSwitchingReply)
runCurrent()
+ verify(beforeUserSwitchingReply).sendResult(any())
verify(userSwitchingReply).sendResult(any())
verify(userManager).getProfiles(newID)
@@ -341,10 +344,11 @@ class UserTrackerImplTest : SysuiTestCase() {
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
+ captor.value.onBeforeUserSwitching(newID, beforeUserSwitchingReply)
captor.value.onUserSwitching(newID, userSwitchingReply)
runCurrent()
+ verify(beforeUserSwitchingReply).sendResult(any())
verify(userSwitchingReply).sendResult(any())
assertThat(callback.calledOnUserChanging).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
@@ -395,7 +399,7 @@ class UserTrackerImplTest : SysuiTestCase() {
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
+ captor.value.onBeforeUserSwitching(newID, any())
captor.value.onUserSwitchComplete(newID)
runCurrent()
@@ -453,8 +457,10 @@ class UserTrackerImplTest : SysuiTestCase() {
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onBeforeUserSwitching(newID, beforeUserSwitchingReply)
captor.value.onUserSwitching(newID, userSwitchingReply)
runCurrent()
+ verify(beforeUserSwitchingReply).sendResult(any())
verify(userSwitchingReply).sendResult(any())
captor.value.onUserSwitchComplete(newID)
@@ -488,6 +494,7 @@ class UserTrackerImplTest : SysuiTestCase() {
}
private class TestCallback : UserTracker.Callback {
+ var calledOnBeforeUserChanging = 0
var calledOnUserChanging = 0
var calledOnUserChanged = 0
var calledOnProfilesChanged = 0
@@ -495,6 +502,11 @@ class UserTrackerImplTest : SysuiTestCase() {
var lastUserContext: Context? = null
var lastUserProfiles = emptyList<UserInfo>()
+ override fun onBeforeUserSwitching(newUser: Int) {
+ calledOnBeforeUserChanging++
+ lastUser = newUser
+ }
+
override fun onUserChanging(newUser: Int, userContext: Context) {
calledOnUserChanging++
lastUser = newUser
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 041d1a611b55..4b11e2c24722 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade
import android.content.Context
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.flag.junit.FlagsParameterization
@@ -32,7 +31,6 @@ import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityContainerController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
@@ -406,18 +404,6 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
}
@Test
- @DisableSceneContainer
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun handleDispatchTouchEvent_nsslMigrationOff_userActivity_not_called() {
- underTest.setStatusBarViewController(phoneStatusBarViewController)
-
- interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
-
- verify(centralSurfaces, times(0)).userActivity()
- }
-
- @Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun handleDispatchTouchEvent_nsslMigrationOn_userActivity() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
@@ -438,7 +424,6 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
}
@Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() {
// GIVEN dozing
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
@@ -451,7 +436,6 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
}
@Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() {
// GIVEN dozing
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
@@ -464,7 +448,6 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
}
@Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun shouldInterceptTouchEvent_dozingAndPulsing_touchIntercepted() {
// GIVEN dozing
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
@@ -609,7 +592,6 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
}
@Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun cancelCurrentTouch_callsDragDownHelper() {
underTest.cancelCurrentTouch()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 13bc82fa2c70..a04ca038021e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade
-import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -27,7 +26,6 @@ import androidx.annotation.IdRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.fragments.FragmentService
@@ -65,10 +63,7 @@ import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-/**
- * Uses Flags.KEYGUARD_STATUS_VIEW_MIGRATE_NSSL set to false. If all goes well, this set of tests
- * will be deleted.
- */
+/** NotificationsQSContainerController tests */
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
@@ -122,7 +117,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
delayableExecutor,
notificationStackScrollLayoutController,
ResourcesSplitShadeStateController(),
- largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
+ largeScreenHeaderHelperLazy = { largeScreenHeaderHelper },
)
overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN)
@@ -209,23 +204,23 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
given(
taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = 0, // taskbar should disappear when shade is expanded
expectedNotificationsMargin = NOTIFICATIONS_MARGIN,
- expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
given(
taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = STABLE_INSET_BOTTOM,
expectedNotificationsMargin = NOTIFICATIONS_MARGIN,
- expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
}
@@ -237,22 +232,22 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = 0,
- expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
given(
taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
- expectedQsPadding = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
}
@@ -263,22 +258,22 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withCutout()
+ insets = windowInsets().withCutout(),
)
then(
expectedContainerPadding = CUTOUT_HEIGHT,
- expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
given(
taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withCutout().withStableBottom()
+ insets = windowInsets().withCutout().withStableBottom(),
)
then(
expectedContainerPadding = 0,
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
- expectedQsPadding = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
}
@@ -289,18 +284,18 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
given(
taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(expectedContainerPadding = 0, expectedQsPadding = STABLE_INSET_BOTTOM)
given(
taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = STABLE_INSET_BOTTOM,
- expectedQsPadding = STABLE_INSET_BOTTOM
+ expectedQsPadding = STABLE_INSET_BOTTOM,
)
}
@@ -314,19 +309,19 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withCutout().withStableBottom()
+ insets = windowInsets().withCutout().withStableBottom(),
)
then(expectedContainerPadding = CUTOUT_HEIGHT, expectedQsPadding = STABLE_INSET_BOTTOM)
given(
taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = 0,
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
- expectedQsPadding = STABLE_INSET_BOTTOM
+ expectedQsPadding = STABLE_INSET_BOTTOM,
)
}
@@ -339,7 +334,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(expectedContainerPadding = 0, expectedNotificationsMargin = 0)
@@ -355,7 +350,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(expectedContainerPadding = 0)
@@ -376,43 +371,6 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSplitShadeLayout_isAlignedToGuideline() {
- enableSplitShade()
- underTest.updateResources()
- assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd).isEqualTo(R.id.qs_edge_guideline)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart)
- .isEqualTo(R.id.qs_edge_guideline)
- }
-
- @Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSinglePaneLayout_childrenHaveEqualMargins() {
- disableSplitShade()
- underTest.updateResources()
- val qsStartMargin = getConstraintSetLayout(R.id.qs_frame).startMargin
- val qsEndMargin = getConstraintSetLayout(R.id.qs_frame).endMargin
- val notifStartMargin = getConstraintSetLayout(R.id.notification_stack_scroller).startMargin
- val notifEndMargin = getConstraintSetLayout(R.id.notification_stack_scroller).endMargin
- assertThat(
- qsStartMargin == qsEndMargin &&
- notifStartMargin == notifEndMargin &&
- qsStartMargin == notifStartMargin
- )
- .isTrue()
- }
-
- @Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSplitShadeLayout_childrenHaveInsideMarginsOfZero() {
- enableSplitShade()
- underTest.updateResources()
- assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(0)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startMargin)
- .isEqualTo(0)
- }
-
- @Test
fun testSplitShadeLayout_qsFrameHasHorizontalMarginsOfZero() {
enableSplitShade()
underTest.updateResources()
@@ -421,37 +379,6 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testLargeScreenLayout_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
- setLargeScreen()
- val largeScreenHeaderResourceHeight = 100
- val largeScreenHeaderHelperHeight = 200
- whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
- .thenReturn(largeScreenHeaderHelperHeight)
- overrideResource(R.dimen.large_screen_shade_header_height, largeScreenHeaderResourceHeight)
-
- // ensure the estimated height (would be 30 here) wouldn't impact this test case
- overrideResource(R.dimen.large_screen_shade_header_min_height, 10)
- overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 10)
-
- underTest.updateResources()
-
- assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin)
- .isEqualTo(largeScreenHeaderHelperHeight)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).topMargin)
- .isEqualTo(largeScreenHeaderHelperHeight)
- }
-
- @Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSmallScreenLayout_qsAndNotifsTopMarginIsZero() {
- setSmallScreen()
- underTest.updateResources()
- assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin).isEqualTo(0)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).topMargin).isEqualTo(0)
- }
-
- @Test
fun testSinglePaneShadeLayout_qsFrameHasHorizontalMarginsSetToCorrectValue() {
disableSplitShade()
underTest.updateResources()
@@ -464,17 +391,6 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSinglePaneShadeLayout_isAlignedToParent() {
- disableSplitShade()
- underTest.updateResources()
- assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd)
- .isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart)
- .isEqualTo(ConstraintSet.PARENT_ID)
- }
-
- @Test
fun testAllChildrenOfNotificationContainer_haveIds() {
// set dimen to 0 to avoid triggering updating bottom spacing
overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, 0)
@@ -493,7 +409,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
delayableExecutor,
notificationStackScrollLayoutController,
ResourcesSplitShadeStateController(),
- largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
+ largeScreenHeaderHelperLazy = { largeScreenHeaderHelper },
)
controller.updateConstraints()
@@ -509,7 +425,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = emptyInsets(),
- applyImmediately = false
+ applyImmediately = false,
)
fakeSystemClock.advanceTime(INSET_DEBOUNCE_MILLIS / 2)
windowInsetsCallback.accept(windowInsets().withStableBottom())
@@ -576,7 +492,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
taskbarVisible: Boolean,
navigationMode: Int,
insets: WindowInsets,
- applyImmediately: Boolean = true
+ applyImmediately: Boolean = true,
) {
Mockito.clearInvocations(view)
taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false)
@@ -591,7 +507,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
fun then(
expectedContainerPadding: Int,
expectedNotificationsMargin: Int = NOTIFICATIONS_MARGIN,
- expectedQsPadding: Int = 0
+ expectedQsPadding: Int = 0,
) {
verify(view).setPadding(anyInt(), anyInt(), anyInt(), eq(expectedContainerPadding))
verify(view).setNotificationsMarginBottom(expectedNotificationsMargin)
@@ -623,7 +539,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
val layoutParams =
ConstraintLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
+ ViewGroup.LayoutParams.WRAP_CONTENT,
)
// required as cloning ConstraintSet fails if view doesn't have layout params
view.layoutParams = layoutParams
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
index 503fa789cb80..1eb88c5a5616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
@@ -89,6 +89,7 @@ class SingleLineViewBinderTest : SysuiTestCase() {
messagingStyle = null,
builder = notificationBuilder,
systemUiContext = context,
+ redactText = false,
)
// WHEN: binds the viewHolder
@@ -149,6 +150,7 @@ class SingleLineViewBinderTest : SysuiTestCase() {
messagingStyle = style,
builder = notificationBuilder,
systemUiContext = context,
+ redactText = false,
)
// WHEN: binds the view
SingleLineViewBinder.bind(viewModel, view)
@@ -197,6 +199,7 @@ class SingleLineViewBinderTest : SysuiTestCase() {
messagingStyle = null,
builder = notificationBuilder,
systemUiContext = context,
+ redactText = false,
)
// WHEN: binds the view with the view model
SingleLineViewBinder.bind(viewModel, view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
index d3666321c8e4..ef70e277832e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
@@ -379,7 +379,8 @@ class SingleLineViewInflaterTest : SysuiTestCase() {
this,
if (isConversation) messagingStyle else null,
builder,
- context
+ context,
+ false
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index a91fb45c9c80..e1a891662889 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -1544,14 +1544,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
assertFalse(mStackScroller.mHeadsUpAnimatingAway);
}
- @Test
- @EnableSceneContainer
- public void finishExpanding_sceneContainerEnabled() {
- mStackScroller.startOverscrollAfterExpanding();
- verify(mStackScroller.getExpandHelper()).finishExpanding();
- assertTrue(mStackScroller.getIsBeingDragged());
- }
-
private MotionEvent captureTouchSentToSceneFramework() {
ArgumentCaptor<MotionEvent> captor = ArgumentCaptor.forClass(MotionEvent.class);
verify(mStackScrollLayoutController).sendTouchToSceneFramework(captor.capture());
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
index 76fc61185b49..25d1c377ecbd 100644
--- a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
@@ -433,6 +433,8 @@ class FakeStatusBarService : IStatusBarService.Stub() {
override fun showRearDisplayDialog(currentBaseState: Int) {}
+ override fun unbundleNotification(key: String) {}
+
companion object {
const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
const val SECONDARY_DISPLAY_ID = 2
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt
new file mode 100644
index 000000000000..57c8fd066ea8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.communal.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+
+val Kosmos.communalBackActionInteractor by
+ Kosmos.Fixture {
+ CommunalBackActionInteractor(
+ communalInteractor = communalInteractor,
+ communalSceneInteractor = communalSceneInteractor,
+ sceneInteractor = sceneInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index ad92b318b0b9..bfc424848900 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -86,12 +86,8 @@ suspend fun Kosmos.setCommunalEnabled(enabled: Boolean) {
}
suspend fun Kosmos.setCommunalV2Enabled(enabled: Boolean) {
- setCommunalV2ConfigEnabled(true)
- if (enabled) {
- fakeUserRepository.asMainUser()
- } else {
- fakeUserRepository.asDefaultUser()
- }
+ setCommunalV2ConfigEnabled(enabled)
+ setCommunalEnabled(enabled)
}
suspend fun Kosmos.setCommunalAvailable(available: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
index b407b1ba227a..e3cfb80489e3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.communal.ui.viewmodel
import android.service.dream.dreamManager
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.policy.batteryController
val Kosmos.communalToDreamButtonViewModel by
@@ -26,6 +28,8 @@ val Kosmos.communalToDreamButtonViewModel by
CommunalToDreamButtonViewModel(
backgroundContext = testDispatcher,
batteryController = batteryController,
+ settingsInteractor = communalSettingsInteractor,
+ activityStarter = activityStarter,
dreamManager = dreamManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
index 534ded57eb85..9012393badd6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
@@ -58,4 +58,26 @@ class FakeDisplayWindowPropertiesRepository(private val context: Context) :
fun insert(instance: DisplayWindowProperties) {
properties.put(instance.displayId, instance.windowType, instance)
}
+
+ /** inserts an entry, mocking everything except the context. */
+ fun insertForContext(displayId: Int, windowType: Int, context: Context) {
+ properties.put(
+ displayId,
+ windowType,
+ DisplayWindowProperties(
+ displayId = displayId,
+ windowType = windowType,
+ context = context,
+ windowManager = mock(),
+ layoutInflater = mock(),
+ ),
+ )
+ }
+
+ /** Whether the repository contains an entry already. */
+ fun contains(displayId: Int, windowType: Int): Boolean =
+ properties.contains(displayId, windowType)
+
+ /** Removes all the entries. */
+ fun clear() = properties.clear()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index 548b5646f5d4..5d206691b520 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
@@ -30,7 +30,7 @@ class FakeKeyguardQuickAffordanceConfig(
override val pickerIconResourceId: Int = 0,
) : KeyguardQuickAffordanceConfig {
- var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled
+ var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled(false)
private val _lockScreenState =
MutableStateFlow<KeyguardQuickAffordanceConfig.LockScreenState>(
@@ -41,9 +41,7 @@ class FakeKeyguardQuickAffordanceConfig(
override fun pickerName(): String = pickerName
- override fun onTriggered(
- expandable: Expandable?,
- ): OnTriggeredResult {
+ override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
return onTriggeredResult
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt
new file mode 100644
index 000000000000..d857157137b6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceHapticViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.flow.Flow
+
+val Kosmos.keyguardQuickAffordanceHapticViewModelFactory by
+ Kosmos.Fixture {
+ object : KeyguardQuickAffordanceHapticViewModel.Factory {
+ override fun create(
+ quickAffordanceViewModel: Flow<KeyguardQuickAffordanceViewModel>
+ ): KeyguardQuickAffordanceHapticViewModel =
+ KeyguardQuickAffordanceHapticViewModel(
+ quickAffordanceViewModel,
+ keyguardQuickAffordanceInteractor,
+ )
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index e47310727905..abbfa93edd17 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -87,7 +87,6 @@ val Kosmos.keyguardRootViewModel by Fixture {
primaryBouncerToLockscreenTransitionViewModel,
screenOffAnimationController = screenOffAnimationController,
aodBurnInViewModel = aodBurnInViewModel,
- aodAlphaViewModel = aodAlphaViewModel,
shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index d941fb049835..43835607c77d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -17,6 +17,7 @@ import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.mockito.kotlin.verify
var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
@@ -82,6 +83,32 @@ fun <T> TestScope.currentValue(stateFlow: StateFlow<T>): T {
}
/** Retrieve the current value of this [StateFlow] safely. See `currentValue(TestScope)`. */
+fun <T> Kosmos.currentValue(fn: () -> T) = testScope.currentValue(fn)
+
+/**
+ * Retrieve the result of [fn] after running all pending tasks. Do not use to retrieve the value of
+ * a flow directly; for that, use either `currentValue(StateFlow)` or [collectLastValue]
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+fun <T> TestScope.currentValue(fn: () -> T): T {
+ runCurrent()
+ return fn()
+}
+
+/** Retrieve the result of [fn] after running all pending tasks. See `TestScope.currentValue(fn)` */
fun <T> Kosmos.currentValue(stateFlow: StateFlow<T>): T {
return testScope.currentValue(stateFlow)
}
+
+/** Safely verify that a mock has been called after the test scope has caught up */
+@OptIn(ExperimentalCoroutinesApi::class)
+fun <T> TestScope.verifyCurrent(mock: T): T {
+ runCurrent()
+ return verify(mock)
+}
+
+/**
+ * Safely verify that a mock has been called after the test scope has caught up. See
+ * `TestScope.verifyCurrent`
+ */
+fun <T> Kosmos.verifyCurrent(mock: T) = testScope.verifyCurrent(mock)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 41cfceaa5e38..39f1ad42797b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -63,6 +63,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.scrimStartable
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.shared.model.sceneDataSource
+import com.android.systemui.scene.ui.view.mockWindowRootViewProvider
import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -191,4 +192,5 @@ class KosmosJavaAdapter() {
}
val disableFlagsInteractor by lazy { kosmos.disableFlagsInteractor }
val fakeDisableFlagsRepository by lazy { kosmos.fakeDisableFlagsRepository }
+ val mockWindowRootViewProvider by lazy { kosmos.mockWindowRootViewProvider }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
index 49bbbef6ccc0..0ec8d49ec29b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
@@ -17,6 +17,6 @@
package com.android.systemui.plugins
import com.android.systemui.kosmos.Kosmos
-import org.mockito.kotlin.mock
+import com.android.systemui.util.mockito.mock
var Kosmos.activityStarter by Kosmos.Fixture { mock<ActivityStarter>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
index f52572a9e42d..f5eebb46c2ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
@@ -19,13 +19,13 @@ package com.android.systemui.scene.shared.model
import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
+import com.android.systemui.kosmos.currentValue
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.TestScope
-class FakeSceneDataSource(
- initialSceneKey: SceneKey,
-) : SceneDataSource {
+class FakeSceneDataSource(initialSceneKey: SceneKey, val testScope: TestScope) : SceneDataSource {
private val _currentScene = MutableStateFlow(initialSceneKey)
override val currentScene: StateFlow<SceneKey> = _currentScene.asStateFlow()
@@ -33,18 +33,20 @@ class FakeSceneDataSource(
private val _currentOverlays = MutableStateFlow<Set<OverlayKey>>(emptySet())
override val currentOverlays: StateFlow<Set<OverlayKey>> = _currentOverlays.asStateFlow()
- var isPaused = false
- private set
+ private var _isPaused = false
+ val isPaused
+ get() = testScope.currentValue { _isPaused }
- var pendingScene: SceneKey? = null
- private set
+ private var _pendingScene: SceneKey? = null
+ val pendingScene
+ get() = testScope.currentValue { _pendingScene }
var pendingOverlays: Set<OverlayKey>? = null
private set
override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) {
- if (isPaused) {
- pendingScene = toScene
+ if (_isPaused) {
+ _pendingScene = toScene
} else {
_currentScene.value = toScene
}
@@ -55,7 +57,7 @@ class FakeSceneDataSource(
}
override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
- if (isPaused) {
+ if (_isPaused) {
pendingOverlays = (pendingOverlays ?: currentOverlays.value) + overlay
} else {
_currentOverlays.value += overlay
@@ -63,7 +65,7 @@ class FakeSceneDataSource(
}
override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
- if (isPaused) {
+ if (_isPaused) {
pendingOverlays = (pendingOverlays ?: currentOverlays.value) - overlay
} else {
_currentOverlays.value -= overlay
@@ -82,9 +84,9 @@ class FakeSceneDataSource(
* last one will be remembered.
*/
fun pause() {
- check(!isPaused) { "Can't pause what's already paused!" }
+ check(!_isPaused) { "Can't pause what's already paused!" }
- isPaused = true
+ _isPaused = true
}
/**
@@ -100,15 +102,12 @@ class FakeSceneDataSource(
*
* If [expectedScene] is provided, will assert that it's indeed the latest called.
*/
- fun unpause(
- force: Boolean = false,
- expectedScene: SceneKey? = null,
- ) {
- check(force || isPaused) { "Can't unpause what's already not paused!" }
-
- isPaused = false
- pendingScene?.let { _currentScene.value = it }
- pendingScene = null
+ fun unpause(force: Boolean = false, expectedScene: SceneKey? = null) {
+ check(force || _isPaused) { "Can't unpause what's already not paused!" }
+
+ _isPaused = false
+ _pendingScene?.let { _currentScene.value = it }
+ _pendingScene = null
pendingOverlays?.let { _currentOverlays.value = it }
pendingOverlays = null
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneDataSourceKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneDataSourceKosmos.kt
index f5196866ae6f..7eebfc305682 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneDataSourceKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneDataSourceKosmos.kt
@@ -19,13 +19,12 @@ package com.android.systemui.scene.shared.model
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.initialSceneKey
import com.android.systemui.scene.sceneContainerConfig
val Kosmos.fakeSceneDataSource by Fixture {
- FakeSceneDataSource(
- initialSceneKey = initialSceneKey,
- )
+ FakeSceneDataSource(initialSceneKey = initialSceneKey, testScope = testScope)
}
val Kosmos.sceneDataSourceDelegator by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/WindowRootViewKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/WindowRootViewKosmos.kt
index 5c91dc80b3d4..e6ba9a581836 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/WindowRootViewKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/WindowRootViewKosmos.kt
@@ -17,6 +17,9 @@
package com.android.systemui.scene.ui.view
import com.android.systemui.kosmos.Kosmos
+import javax.inject.Provider
import org.mockito.kotlin.mock
val Kosmos.mockShadeRootView by Kosmos.Fixture { mock<WindowRootView>() }
+val Kosmos.mockWindowRootViewProvider by
+ Kosmos.Fixture { Provider<WindowRootView> { mock<WindowRootView>() } }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeAnimationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeAnimationRepositoryKosmos.kt
index 4dcd2208b152..3ed730271bc3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeAnimationRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeAnimationRepositoryKosmos.kt
@@ -16,6 +16,14 @@
package com.android.systemui.shade.data.repository
+import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
val Kosmos.shadeAnimationRepository by Kosmos.Fixture { ShadeAnimationRepository() }
+val Kosmos.shadeDialogContextInteractor by
+ Kosmos.Fixture {
+ mock<ShadeDialogContextInteractor> { on { context } doReturn applicationContext }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
index 8865573c4030..e5a75d59468d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,14 +18,8 @@ package com.android.systemui.statusbar
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import org.mockito.kotlin.mock
-
-var Kosmos.lockscreenShadeKeyguardTransitionController by Fixture {
- mock<LockscreenShadeKeyguardTransitionController>()
-}
+import com.android.systemui.util.mockito.mock
var Kosmos.lockscreenShadeKeyguardTransitionControllerFactory by Fixture {
- LockscreenShadeKeyguardTransitionController.Factory {
- lockscreenShadeKeyguardTransitionController
- }
+ mock<LockscreenShadeKeyguardTransitionController.Factory>()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
index fc52e454a1c6..27679804d11f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
@@ -18,12 +18,8 @@ package com.android.systemui.statusbar
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import org.mockito.kotlin.mock
-
-var Kosmos.lockscreenShadeQsTransitionController by Fixture {
- mock<LockscreenShadeQsTransitionController>()
-}
+import com.android.systemui.util.mockito.mock
var Kosmos.lockscreenShadeQsTransitionControllerFactory by Fixture {
- LockscreenShadeQsTransitionController.Factory { lockscreenShadeQsTransitionController }
+ mock<LockscreenShadeQsTransitionController.Factory>()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
index 5523bd68f692..43e39c05f6e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
@@ -18,12 +18,8 @@ package com.android.systemui.statusbar
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import org.mockito.kotlin.mock
-
-var Kosmos.singleShadeLockScreenOverScroller by Fixture {
- mock<SingleShadeLockScreenOverScroller>()
-}
+import com.android.systemui.util.mockito.mock
var Kosmos.singleShadeLockScreenOverScrollerFactory by Fixture {
- SingleShadeLockScreenOverScroller.Factory { _ -> singleShadeLockScreenOverScroller }
+ mock<SingleShadeLockScreenOverScroller.Factory>()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
index e491dffb0ed5..017371a6cba8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
@@ -18,10 +18,8 @@ package com.android.systemui.statusbar
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import org.mockito.kotlin.mock
-
-var Kosmos.splitShadeLockScreenOverScroller by Fixture { mock<SplitShadeLockScreenOverScroller>() }
+import com.android.systemui.util.mockito.mock
var Kosmos.splitShadeLockScreenOverScrollerFactory by Fixture {
- SplitShadeLockScreenOverScroller.Factory { _, _ -> splitShadeLockScreenOverScroller }
+ mock<SplitShadeLockScreenOverScroller.Factory>()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
index 768952d1ee77..62cdc87f980f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.domain.interactor
+package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import org.mockito.kotlin.mock
+import com.android.systemui.kosmos.testScope
-val Kosmos.notificationAlertsInteractor by Kosmos.Fixture { mock<NotificationAlertsInteractor>() }
+val Kosmos.statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel by
+ Kosmos.Fixture { StatusBarPopupChipsViewModel(testScope.backgroundScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
new file mode 100644
index 000000000000..680e0de22794
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.notification.promoted
+
+import android.app.Notification
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import org.junit.Assert
+
+class FakePromotedNotificationContentExtractor : PromotedNotificationContentExtractor {
+ @JvmField
+ val contentForEntry = mutableMapOf<NotificationEntry, PromotedNotificationContentModel?>()
+ @JvmField val extractCalls = mutableListOf<Pair<NotificationEntry, Notification.Builder>>()
+
+ override fun extractContent(
+ entry: NotificationEntry,
+ recoveredBuilder: Notification.Builder,
+ ): PromotedNotificationContentModel? {
+ extractCalls.add(entry to recoveredBuilder)
+
+ if (contentForEntry.isEmpty()) {
+ // If *no* entries are set, just return null for everything.
+ return null
+ } else {
+ // If entries *are* set, fail on unexpected ones.
+ Assert.assertTrue(contentForEntry.containsKey(entry))
+ return contentForEntry.get(entry)
+ }
+ }
+
+ fun resetForEntry(entry: NotificationEntry, content: PromotedNotificationContentModel?) {
+ contentForEntry.clear()
+ contentForEntry.put(entry, content)
+ extractCalls.clear()
+ }
+
+ fun verifyZeroExtractCalls() {
+ Assert.assertTrue(extractCalls.isEmpty())
+ }
+
+ fun verifyOneExtractCall() {
+ Assert.assertEquals(1, extractCalls.size)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt
index 88caf6e2ba30..ea7b41d43871 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt
@@ -17,11 +17,20 @@
package com.android.systemui.statusbar.notification.promoted
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import org.junit.Assert
class FakePromotedNotificationsProvider : PromotedNotificationsProvider {
val promotedEntries = mutableSetOf<NotificationEntry>()
+ val shouldPromoteForEntry = mutableMapOf<NotificationEntry, Boolean>()
override fun shouldPromote(entry: NotificationEntry): Boolean {
- return promotedEntries.contains(entry)
+ if (shouldPromoteForEntry.isEmpty()) {
+ // If *no* entries are set, just return false for everything.
+ return false
+ } else {
+ // If entries *are* set, fail on unexpected ones.
+ Assert.assertTrue(shouldPromoteForEntry.containsKey(entry))
+ return shouldPromoteForEntry[entry] ?: false
+ }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
index 5e9f12b4b1cc..52c17c82fb12 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
@@ -21,7 +21,7 @@ import com.android.systemui.kosmos.Kosmos
var Kosmos.promotedNotificationContentExtractor by
Kosmos.Fixture {
- PromotedNotificationContentExtractor(
+ PromotedNotificationContentExtractorImpl(
promotedNotificationsProvider,
applicationContext,
promotedNotificationLogger,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 7126933154df..e739e82aa8a8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -65,7 +65,7 @@ import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.icon.IconBuilder
import com.android.systemui.statusbar.notification.icon.IconManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractorImpl
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProviderImpl
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.CoordinateOnClickListener
@@ -222,16 +222,11 @@ class ExpandableNotificationRowBuilder(
Mockito.mock(LauncherApps::class.java, STUB_ONLY),
Mockito.mock(ConversationNotificationManager::class.java, STUB_ONLY),
)
-
- val promotedNotificationsProvider = PromotedNotificationsProviderImpl()
- val promotedNotificationLog = logcatLogBuffer("PromotedNotifLog")
- val promotedNotificationLogger = PromotedNotificationLogger(promotedNotificationLog)
-
val promotedNotificationContentExtractor =
- PromotedNotificationContentExtractor(
- promotedNotificationsProvider,
+ PromotedNotificationContentExtractorImpl(
+ PromotedNotificationsProviderImpl(),
context,
- promotedNotificationLogger,
+ PromotedNotificationLogger(logcatLogBuffer("PromotedNotifLog")),
)
mContentBinder =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
index 26642d4f7534..f19ac1e5a58d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.policy
import com.android.systemui.kosmos.Kosmos
-import org.mockito.kotlin.mock
+import org.mockito.Mockito.mock
var Kosmos.keyguardStateController: KeyguardStateController by
- Kosmos.Fixture { mock<KeyguardStateController>() }
+ Kosmos.Fixture { mock(KeyguardStateController::class.java) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
index 932e768676cb..6c98d19db5d7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
@@ -20,6 +20,7 @@ import com.android.systemui.animation.dialogTransitionAnimator
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.mainCoroutineContext
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.shade.data.repository.shadeDialogContextInteractor
import com.android.systemui.statusbar.phone.systemUIDialogFactory
import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.modesDialogViewModel
import com.android.systemui.util.mockito.mock
@@ -35,5 +36,6 @@ var Kosmos.modesDialogDelegate: ModesDialogDelegate by
{ modesDialogViewModel },
modesDialogEventLogger,
mainCoroutineContext,
+ shadeDialogContextInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
index a3572754ab19..f31697e82a45 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
@@ -70,6 +70,14 @@ class FakeSettings : SecureSettings, SystemSettings, UserSettingsProxy {
}
}
+ fun getContentObservers(uri: Uri, userHandle: Int): List<ContentObserver> {
+ if (userHandle == UserHandle.USER_ALL) {
+ return contentObserversAllUsers[uri.toString()] ?: listOf()
+ } else {
+ return contentObservers[SettingsKey(userHandle, uri.toString())] ?: listOf()
+ }
+ }
+
override fun getContentResolver(): ContentResolver {
throw UnsupportedOperationException("FakeSettings.getContentResolver is not implemented")
}
diff --git a/packages/Vcn/framework-b/src/android/net/vcn/VcnTransportInfo.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnTransportInfo.java
index 3638429f33fb..a760b12ac8a1 100644
--- a/packages/Vcn/framework-b/src/android/net/vcn/VcnTransportInfo.java
+++ b/packages/Vcn/framework-b/src/android/net/vcn/VcnTransportInfo.java
@@ -161,7 +161,14 @@ public final class VcnTransportInfo implements TransportInfo, Parcelable {
return 0;
}
- /** @hide */
+ /**
+ * Create a copy of a {@link VcnTransportInfo} with some fields redacted based on the
+ * permissions held by the receiving app.
+ *
+ * @param redactions bitmask of redactions that needs to be performed on this instance.
+ * @return the copy of this instance with the necessary redactions.
+ * @hide
+ */
@FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 3441d94facda..9ceca5d1dbfe 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1589,7 +1589,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
* lock because this calls out to WindowManagerService.
*/
void addWindowTokensForAllDisplays() {
- final Display[] displays = mDisplayManager.getDisplays();
+ Display[] displays = {};
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ displays = mDisplayManager.getDisplays();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
for (int i = 0; i < displays.length; i++) {
final int displayId = displays[i].getDisplayId();
addWindowTokenForDisplay(displayId);
@@ -1625,7 +1631,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
public void onRemoved() {
- final Display[] displays = mDisplayManager.getDisplays();
+ Display[] displays = {};
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ displays = mDisplayManager.getDisplays();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
for (int i = 0; i < displays.length; i++) {
final int displayId = displays[i].getDisplayId();
onDisplayRemoved(displayId);
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 3025e2eaede0..549f8fa77b53 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -217,6 +217,13 @@ public class UserBackupManagerService {
+ mPowerManagerWakeLock.getTag()));
return;
}
+
+ if (!mPowerManagerWakeLock.isHeld()) {
+ Slog.w(TAG, addUserIdToLogMessage(mUserId,
+ "Wakelock not held: " + mPowerManagerWakeLock.getTag()));
+ return;
+ }
+
mPowerManagerWakeLock.release();
Slog.v(
TAG,
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index fd18fa856916..abfb8268bd9a 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -55,6 +55,7 @@ import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
+import android.media.AudioManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -102,6 +103,7 @@ public class ContextualSearchManagerService extends SystemService {
private final PackageManagerInternal mPackageManager;
private final WindowManagerInternal mWmInternal;
private final DevicePolicyManagerInternal mDpmInternal;
+ private final AudioManager mAudioManager;
private final Object mLock = new Object();
private final AssistDataRequester mAssistDataRequester;
@@ -163,6 +165,8 @@ public class ContextualSearchManagerService extends SystemService {
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
mPackageManager = LocalServices.getService(PackageManagerInternal.class);
+ mAudioManager = context.getSystemService(AudioManager.class);
+
mWmInternal = Objects.requireNonNull(LocalServices.getService(WindowManagerInternal.class));
mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mAssistDataRequester = new AssistDataRequester(
@@ -306,6 +310,10 @@ public class ContextualSearchManagerService extends SystemService {
SystemClock.uptimeMillis());
launchIntent.putExtra(ContextualSearchManager.EXTRA_ENTRYPOINT, entrypoint);
launchIntent.putExtra(ContextualSearchManager.EXTRA_TOKEN, mToken);
+ if (Flags.includeAudioPlayingStatus()) {
+ launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_AUDIO_PLAYING,
+ mAudioManager.isMusicActive());
+ }
boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed();
final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities();
final List<IBinder> activityTokens = new ArrayList<>(records.size());
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 06f9e2bf55b2..dc830642dcc5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -229,7 +229,6 @@ java_library_static {
"power_hint_flags_lib",
"biometrics_flags_lib",
"am_flags_lib",
- "updates_flags_lib",
"com_android_server_accessibility_flags_lib",
"//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
"com_android_launcher3_flags_lib",
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4944cafeb83d..4b8770b3cd35 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RECONFIGURATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
@@ -31,6 +32,7 @@ import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROA
import static com.android.server.am.BroadcastConstants.getDeviceConfigBoolean;
import android.annotation.NonNull;
+import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.ForegroundServiceTypePolicy;
import android.content.ComponentName;
@@ -55,6 +57,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
import dalvik.annotation.optimization.NeverCompile;
@@ -181,6 +184,12 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION =
"follow_up_oomadj_update_wait_duration";
+ /*
+ * Oom score cutoff beyond which any process that does not have the CPU_TIME capability will be
+ * frozen.
+ */
+ static final String KEY_FREEZER_CUTOFF_ADJ = "freezer_cutoff_adj";
+
private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024;
private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -267,6 +276,9 @@ final class ActivityManagerConstants extends ContentObserver {
*/
private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L;
+ /** The default value to {@link #KEY_FREEZER_CUTOFF_ADJ} */
+ private static final int DEFAULT_FREEZER_CUTOFF_ADJ = ProcessList.CACHED_APP_MIN_ADJ;
+
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
*/
@@ -1171,6 +1183,14 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION;
/**
+ * The cutoff adj for the freezer, app processes with adj greater than this value will be
+ * eligible for the freezer.
+ *
+ * @see #KEY_FREEZER_CUTOFF_ADJ
+ */
+ public int FREEZER_CUTOFF_ADJ = DEFAULT_FREEZER_CUTOFF_ADJ;
+
+ /**
* Indicates whether PSS profiling in AppProfiler is disabled or not.
*/
static final String KEY_DISABLE_APP_PROFILER_PSS_PROFILING =
@@ -1194,6 +1214,7 @@ final class ActivityManagerConstants extends ContentObserver {
new OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(Properties properties) {
+ boolean oomAdjusterConfigUpdated = false;
for (String name : properties.getKeyset()) {
if (name == null) {
return;
@@ -1372,6 +1393,11 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_TIERED_CACHED_ADJ_UI_TIER_SIZE:
updateUseTieredCachedAdj();
break;
+ case KEY_FREEZER_CUTOFF_ADJ:
+ FREEZER_CUTOFF_ADJ = properties.getInt(KEY_FREEZER_CUTOFF_ADJ,
+ DEFAULT_FREEZER_CUTOFF_ADJ);
+ oomAdjusterConfigUpdated = true;
+ break;
case KEY_DISABLE_APP_PROFILER_PSS_PROFILING:
updateDisableAppProfilerPssProfiling();
break;
@@ -1389,6 +1415,13 @@ final class ActivityManagerConstants extends ContentObserver {
break;
}
}
+ if (oomAdjusterConfigUpdated) {
+ final ActivityManagerInternal ami = LocalServices.getService(
+ ActivityManagerInternal.class);
+ if (ami != null) {
+ ami.updateOomAdj(OOM_ADJ_REASON_RECONFIGURATION);
+ }
+ }
}
};
@@ -2534,6 +2567,9 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print(" "); pw.print(KEY_ENABLE_NEW_OOMADJ);
pw.print("="); pw.println(ENABLE_NEW_OOMADJ);
+ pw.print(" "); pw.print(KEY_FREEZER_CUTOFF_ADJ);
+ pw.print("="); pw.println(FREEZER_CUTOFF_ADJ);
+
pw.print(" "); pw.print(KEY_DISABLE_APP_PROFILER_PSS_PROFILING);
pw.print("="); pw.println(APP_PROFILER_PSS_PROFILING_DISABLED);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cd929c1883d0..50b6990c0c1c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4799,9 +4799,6 @@ public class ActivityManagerService extends IActivityManager.Stub
updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
- updateOomAdjLocked(app, OOM_ADJ_REASON_PROCESS_BEGIN);
- checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
-
final long now = SystemClock.uptimeMillis();
synchronized (mAppProfiler.mProfilerLock) {
app.mProfile.setLastRequestedGc(now);
@@ -4815,6 +4812,15 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mProcessesOnHold.remove(app);
+ // See if the top visible activity is waiting to run in this process...
+ if (com.android.server.am.Flags.expediteActivityLaunchOnColdStart()) {
+ if (normalMode) {
+ mAtmInternal.attachApplication(app.getWindowProcessController());
+ }
+ }
+ updateOomAdjLocked(app, OOM_ADJ_REASON_PROCESS_BEGIN);
+ checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
+
if (!mConstants.mEnableWaitForFinishAttachApplication) {
finishAttachApplicationInner(startSeq, callingUid, pid);
}
@@ -4880,18 +4886,21 @@ public class ActivityManagerService extends IActivityManager.Stub
// Mark the finish attach application phase as completed
mProcessStateController.setPendingFinishAttach(app, false);
- final boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
final String processName = app.processName;
boolean badApp = false;
boolean didSomething = false;
- // See if the top visible activity is waiting to run in this process...
- if (normalMode) {
- try {
- didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
- } catch (Exception e) {
- Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
- badApp = true;
+ if (!com.android.server.am.Flags.expediteActivityLaunchOnColdStart()) {
+ final boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
+
+ if (normalMode) {
+ try {
+ didSomething |= mAtmInternal.attachApplication(
+ app.getWindowProcessController());
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
+ badApp = true;
+ }
}
}
@@ -19387,9 +19396,6 @@ public class ActivityManagerService extends IActivityManager.Stub
creatorPackage);
if (creatorToken != null) {
extraIntent.setCreatorToken(creatorToken);
- // TODO remove Slog.wtf once proven FrameworkStatsLog works. b/375396329
- Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: "
- + creatorPackage + "; intent: " + extraIntent);
FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED, creatorUid, false);
}
});
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 2f5362f53361..d335529a006a 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -25,9 +25,11 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BIND_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FOLLOW_UP;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RECONFIGURATION;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_TASK;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RESTRICTION_CHANGE;
@@ -203,6 +205,10 @@ public class CachedAppOptimizer {
FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_RESTRICTION_CHANGE;
static final int UNFREEZE_REASON_COMPONENT_DISABLED =
FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_COMPONENT_DISABLED;
+ static final int UNFREEZE_REASON_OOM_ADJ_FOLLOW_UP =
+ FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_OOM_ADJ_FOLLOW_UP;
+ static final int UNFREEZE_REASON_OOM_ADJ_RECONFIGURATION =
+ FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_OOM_ADJ_RECONFIGURATION;
@IntDef(prefix = {"UNFREEZE_REASON_"}, value = {
UNFREEZE_REASON_NONE,
@@ -234,6 +240,8 @@ public class CachedAppOptimizer {
UNFREEZE_REASON_EXECUTING_SERVICE,
UNFREEZE_REASON_RESTRICTION_CHANGE,
UNFREEZE_REASON_COMPONENT_DISABLED,
+ UNFREEZE_REASON_OOM_ADJ_FOLLOW_UP,
+ UNFREEZE_REASON_OOM_ADJ_RECONFIGURATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UnfreezeReason {}
@@ -2451,8 +2459,8 @@ public class CachedAppOptimizer {
synchronized (mAm.mPidsSelfLocked) {
pr = mAm.mPidsSelfLocked.get(blocked);
}
- if (pr != null
- && pr.mState.getCurAdj() < ProcessList.FREEZER_CUTOFF_ADJ) {
+ if (pr != null && pr.mState.getCurAdj()
+ < mAm.mConstants.FREEZER_CUTOFF_ADJ) {
Slog.d(TAG_AM, app.processName + " (" + pid + ") blocks "
+ pr.processName + " (" + blocked + ")");
// Found at least one blocked non-cached process
@@ -2539,6 +2547,10 @@ public class CachedAppOptimizer {
return UNFREEZE_REASON_RESTRICTION_CHANGE;
case OOM_ADJ_REASON_COMPONENT_DISABLED:
return UNFREEZE_REASON_COMPONENT_DISABLED;
+ case OOM_ADJ_REASON_FOLLOW_UP:
+ return UNFREEZE_REASON_OOM_ADJ_FOLLOW_UP;
+ case OOM_ADJ_REASON_RECONFIGURATION:
+ return UNFREEZE_REASON_OOM_ADJ_RECONFIGURATION;
default:
return UNFREEZE_REASON_NONE;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index aadf6f61956c..9c569db99797 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -55,6 +55,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RECONFIGURATION;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_TASK;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RESTRICTION_CHANGE;
@@ -105,7 +106,6 @@ import static com.android.server.am.ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ;
-import static com.android.server.am.ProcessList.FREEZER_CUTOFF_ADJ;
import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ;
import static com.android.server.am.ProcessList.HOME_APP_ADJ;
import static com.android.server.am.ProcessList.INVALID_ADJ;
@@ -232,6 +232,8 @@ public class OomAdjuster {
return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED;
case OOM_ADJ_REASON_FOLLOW_UP:
return AppProtoEnums.OOM_ADJ_REASON_FOLLOW_UP;
+ case OOM_ADJ_REASON_RECONFIGURATION:
+ return AppProtoEnums.OOM_ADJ_REASON_RECONFIGURATION;
default:
return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
}
@@ -288,6 +290,8 @@ public class OomAdjuster {
return OOM_ADJ_REASON_METHOD + "_componentDisabled";
case OOM_ADJ_REASON_FOLLOW_UP:
return OOM_ADJ_REASON_METHOD + "_followUp";
+ case OOM_ADJ_REASON_RECONFIGURATION:
+ return OOM_ADJ_REASON_METHOD + "_reconfiguration";
default:
return "_unknown";
}
@@ -4079,7 +4083,7 @@ public class OomAdjuster {
}
// Reasons to freeze:
- if (proc.mState.getCurAdj() >= FREEZER_CUTOFF_ADJ) {
+ if (proc.mState.getCurAdj() >= mConstants.FREEZER_CUTOFF_ADJ) {
// Oomscore is in a high enough state, it is safe to freeze.
return true;
}
@@ -4098,9 +4102,8 @@ public class OomAdjuster {
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
final ProcessStateRecord state = app.mState;
if (Flags.traceUpdateAppFreezeStateLsp()) {
- final boolean oomAdjChanged =
- (state.getCurAdj() >= FREEZER_CUTOFF_ADJ ^ oldOomAdj >= FREEZER_CUTOFF_ADJ)
- || oldOomAdj == UNKNOWN_ADJ;
+ final boolean oomAdjChanged = (state.getCurAdj() >= mConstants.FREEZER_CUTOFF_ADJ
+ ^ oldOomAdj >= mConstants.FREEZER_CUTOFF_ADJ) || oldOomAdj == UNKNOWN_ADJ;
final boolean shouldNotFreezeChanged = opt.shouldNotFreezeAdjSeq() == mAdjSeq;
final boolean hasCpuCapability =
(PROCESS_CAPABILITY_CPU_TIME & app.mState.getCurCapability())
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index 123780fb7567..99bdd10ff71b 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -112,23 +112,10 @@ public final class PhantomProcessList {
private final ActivityManagerService mService;
private final Handler mKillHandler;
- private static final int CGROUP_V1 = 0;
- private static final int CGROUP_V2 = 1;
- private static final String[] CGROUP_PATH_PREFIXES = {
- "/acct/uid_" /* cgroup v1 */,
- "/sys/fs/cgroup/uid_" /* cgroup v2 */
- };
- private static final String CGROUP_PID_PREFIX = "/pid_";
- private static final String CGROUP_PROCS = "/cgroup.procs";
-
- @VisibleForTesting
- int mCgroupVersion = CGROUP_V1;
-
PhantomProcessList(final ActivityManagerService service) {
mService = service;
mKillHandler = service.mProcessList.sKillHandler;
mInjector = new Injector();
- probeCgroupVersion();
}
@VisibleForTesting
@@ -157,9 +144,15 @@ public final class PhantomProcessList {
final int appPid = app.getPid();
InputStream input = mCgroupProcsFds.get(appPid);
if (input == null) {
- final String path = getCgroupFilePath(app.info.uid, appPid);
+ String path = null;
try {
+ path = getCgroupFilePath(app.info.uid, appPid);
input = mInjector.openCgroupProcs(path);
+ } catch (IllegalArgumentException e) {
+ if (DEBUG_PROCESSES) {
+ Slog.w(TAG, "Unable to obtain cgroup.procs path ", e);
+ }
+ return;
} catch (FileNotFoundException | SecurityException e) {
if (DEBUG_PROCESSES) {
Slog.w(TAG, "Unable to open " + path, e);
@@ -207,18 +200,9 @@ public final class PhantomProcessList {
}
}
- private void probeCgroupVersion() {
- for (int i = CGROUP_PATH_PREFIXES.length - 1; i >= 0; i--) {
- if ((new File(CGROUP_PATH_PREFIXES[i] + Process.SYSTEM_UID)).exists()) {
- mCgroupVersion = i;
- break;
- }
- }
- }
-
@VisibleForTesting
String getCgroupFilePath(int uid, int pid) {
- return CGROUP_PATH_PREFIXES[mCgroupVersion] + uid + CGROUP_PID_PREFIX + pid + CGROUP_PROCS;
+ return nativeGetCgroupProcsPath(uid, pid);
}
static String getProcessName(int pid) {
@@ -630,4 +614,7 @@ public final class PhantomProcessList {
return PhantomProcessList.getProcessName(pid);
}
}
+
+ private static native String nativeGetCgroupProcsPath(int uid, int pid)
+ throws IllegalArgumentException;
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 70febcd63455..bddde9d589f3 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -383,12 +383,6 @@ public final class ProcessList {
private static final long LMKD_RECONNECT_DELAY_MS = 1000;
/**
- * The cuttoff adj for the freezer, app processes with adj greater than this value will be
- * eligible for the freezer.
- */
- static final int FREEZER_CUTOFF_ADJ = CACHED_APP_MIN_ADJ;
-
- /**
* Apps have no access to the private data directories of any other app, even if the other
* app has made them world-readable.
*/
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 98f738c38d63..49149e1fa415 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1699,7 +1699,7 @@ class ProcessRecord implements WindowProcessListener {
return mService.mOomAdjuster.mCachedAppOptimizer.useFreezer()
&& !mOptRecord.isFreezeExempt()
&& !mOptRecord.shouldNotFreeze()
- && mState.getCurAdj() >= ProcessList.FREEZER_CUTOFF_ADJ;
+ && mState.getCurAdj() >= mService.mConstants.FREEZER_CUTOFF_ADJ;
}
public void forEachConnectionHost(Consumer<ProcessRecord> consumer) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 87f87c76725e..c82933c5069e 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -158,6 +158,7 @@ public class SettingsToPropertiesMapper {
"aoc",
"app_widgets",
"arc_next",
+ "art_cloud",
"art_mainline",
"art_performance",
"attack_tools",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c31b9ef60bd2..70f2a8e1dd1b 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -160,6 +160,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -176,6 +177,9 @@ import java.util.function.Consumer;
class UserController implements Handler.Callback {
private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM;
+ // Amount of time we wait for observers to handle onBeforeUserSwitching, before crashing system.
+ static final int DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS = 20 * 1000;
+
// Amount of time we wait for observers to handle a user switch before
// giving up on them and dismissing the user switching dialog.
static final int DEFAULT_USER_SWITCH_TIMEOUT_MS = 3 * 1000;
@@ -1920,8 +1924,14 @@ class UserController implements Handler.Callback {
return false;
}
- mHandler.post(() -> startUserInternalOnHandler(userId, oldUserId, userStartMode,
- unlockListener, callingUid, callingPid));
+ final Runnable continueStartUserInternal = () -> continueStartUserInternal(userInfo,
+ oldUserId, userStartMode, unlockListener, callingUid, callingPid);
+ if (foreground) {
+ mHandler.post(() -> dispatchOnBeforeUserSwitching(userId, () ->
+ mHandler.post(continueStartUserInternal)));
+ } else {
+ continueStartUserInternal.run();
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1929,11 +1939,11 @@ class UserController implements Handler.Callback {
return true;
}
- private void startUserInternalOnHandler(int userId, int oldUserId, int userStartMode,
+ private void continueStartUserInternal(UserInfo userInfo, int oldUserId, int userStartMode,
IProgressListener unlockListener, int callingUid, int callingPid) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
final boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
- final UserInfo userInfo = getUserInfo(userId);
+ final int userId = userInfo.id;
boolean needStart = false;
boolean updateUmState = false;
@@ -1995,7 +2005,6 @@ class UserController implements Handler.Callback {
// it should be moved outside, but for now it's not as there are many calls to
// external components here afterwards
updateProfileRelatedCaches();
- dispatchOnBeforeUserSwitching(userId);
mInjector.getWindowManager().setCurrentUser(userId);
mInjector.reportCurWakefulnessUsageEvent();
// Once the internal notion of the active user has switched, we lock the device
@@ -2296,25 +2305,40 @@ class UserController implements Handler.Callback {
mUserSwitchObservers.finishBroadcast();
}
- private void dispatchOnBeforeUserSwitching(@UserIdInt int newUserId) {
+ private void dispatchOnBeforeUserSwitching(@UserIdInt int newUserId, Runnable onComplete) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("dispatchOnBeforeUserSwitching-" + newUserId);
- final int observerCount = mUserSwitchObservers.beginBroadcast();
- for (int i = 0; i < observerCount; i++) {
- final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
- t.traceBegin("onBeforeUserSwitching-" + name);
+ final AtomicBoolean isSuccessful = new AtomicBoolean(false);
+ startTimeoutForOnBeforeUserSwitching(isSuccessful);
+ informUserSwitchObservers((observer, callback) -> {
try {
- mUserSwitchObservers.getBroadcastItem(i).onBeforeUserSwitching(newUserId);
+ observer.onBeforeUserSwitching(newUserId, callback);
} catch (RemoteException e) {
- // Ignore
- } finally {
- t.traceEnd();
+ // ignore
}
- }
- mUserSwitchObservers.finishBroadcast();
+ }, () -> {
+ isSuccessful.set(true);
+ onComplete.run();
+ }, "onBeforeUserSwitching");
t.traceEnd();
}
+ private void startTimeoutForOnBeforeUserSwitching(AtomicBoolean isSuccessful) {
+ mHandler.postDelayed(() -> {
+ if (isSuccessful.get()) {
+ return;
+ }
+ String unresponsiveObservers;
+ synchronized (mLock) {
+ unresponsiveObservers = String.join(", ", mCurWaitingUserSwitchCallbacks);
+ }
+ throw new RuntimeException("Timeout on dispatchOnBeforeUserSwitching. "
+ + "These UserSwitchObservers did not respond in "
+ + DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS + "ms: " + unresponsiveObservers + ".");
+ }, DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS);
+ }
+
+
/** Called on handler thread */
@VisibleForTesting
void dispatchUserSwitchComplete(@UserIdInt int oldUserId, @UserIdInt int newUserId) {
@@ -2527,70 +2551,76 @@ class UserController implements Handler.Callback {
t.traceBegin("dispatchUserSwitch-" + oldUserId + "-to-" + newUserId);
EventLog.writeEvent(EventLogTags.UC_DISPATCH_USER_SWITCH, oldUserId, newUserId);
+ uss.switching = true;
+ informUserSwitchObservers((observer, callback) -> {
+ try {
+ observer.onUserSwitching(newUserId, callback);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }, () -> {
+ synchronized (mLock) {
+ sendContinueUserSwitchLU(uss, oldUserId, newUserId);
+ }
+ }, "onUserSwitching");
+ t.traceEnd();
+ }
+ void informUserSwitchObservers(BiConsumer<IUserSwitchObserver, IRemoteCallback> consumer,
+ final Runnable onComplete, String trace) {
final int observerCount = mUserSwitchObservers.beginBroadcast();
- if (observerCount > 0) {
- final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
+ if (observerCount == 0) {
+ onComplete.run();
+ mUserSwitchObservers.finishBroadcast();
+ return;
+ }
+ final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
+ synchronized (mLock) {
+ mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
+ }
+ final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
+ final long userSwitchTimeoutMs = getUserSwitchTimeoutMs();
+ final long dispatchStartedTime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < observerCount; i++) {
+ final long dispatchStartedTimeForObserver = SystemClock.elapsedRealtime();
+ // Prepend with unique prefix to guarantee that keys are unique
+ final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
synchronized (mLock) {
- uss.switching = true;
- mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
- }
- final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
- final long userSwitchTimeoutMs = getUserSwitchTimeoutMs();
- final long dispatchStartedTime = SystemClock.elapsedRealtime();
- for (int i = 0; i < observerCount; i++) {
- final long dispatchStartedTimeForObserver = SystemClock.elapsedRealtime();
- try {
- // Prepend with unique prefix to guarantee that keys are unique
- final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
+ curWaitingUserSwitchCallbacks.add(name);
+ }
+ final IRemoteCallback callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ asyncTraceEnd(trace + "-" + name, 0);
synchronized (mLock) {
- curWaitingUserSwitchCallbacks.add(name);
- }
- final IRemoteCallback callback = new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- asyncTraceEnd("onUserSwitching-" + name, newUserId);
- synchronized (mLock) {
- long delayForObserver = SystemClock.elapsedRealtime()
- - dispatchStartedTimeForObserver;
- if (delayForObserver > LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS) {
- Slogf.w(TAG, "User switch slowed down by observer " + name
- + ": result took " + delayForObserver
- + " ms to process.");
- }
-
- long totalDelay = SystemClock.elapsedRealtime()
- - dispatchStartedTime;
- if (totalDelay > userSwitchTimeoutMs) {
- Slogf.e(TAG, "User switch timeout: observer " + name
- + "'s result was received " + totalDelay
- + " ms after dispatchUserSwitch.");
- }
-
- curWaitingUserSwitchCallbacks.remove(name);
- // Continue switching if all callbacks have been notified and
- // user switching session is still valid
- if (waitingCallbacksCount.decrementAndGet() == 0
- && (curWaitingUserSwitchCallbacks
- == mCurWaitingUserSwitchCallbacks)) {
- sendContinueUserSwitchLU(uss, oldUserId, newUserId);
- }
- }
+ long delayForObserver = SystemClock.elapsedRealtime()
+ - dispatchStartedTimeForObserver;
+ if (delayForObserver > LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS) {
+ Slogf.w(TAG, "User switch slowed down by observer " + name
+ + ": result took " + delayForObserver
+ + " ms to process. " + trace);
}
- };
- asyncTraceBegin("onUserSwitching-" + name, newUserId);
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(newUserId, callback);
- } catch (RemoteException e) {
- // Ignore
+ long totalDelay = SystemClock.elapsedRealtime() - dispatchStartedTime;
+ if (totalDelay > userSwitchTimeoutMs) {
+ Slogf.e(TAG, "User switch timeout: observer " + name
+ + "'s result was received " + totalDelay
+ + " ms after dispatchUserSwitch. " + trace);
+ }
+ curWaitingUserSwitchCallbacks.remove(name);
+ // Continue switching if all callbacks have been notified and
+ // user switching session is still valid
+ if (waitingCallbacksCount.decrementAndGet() == 0
+ && (curWaitingUserSwitchCallbacks
+ == mCurWaitingUserSwitchCallbacks)) {
+ onComplete.run();
+ }
+ }
}
- }
- } else {
- synchronized (mLock) {
- sendContinueUserSwitchLU(uss, oldUserId, newUserId);
- }
+ };
+ asyncTraceBegin(trace + "-" + name, 0);
+ consumer.accept(mUserSwitchObservers.getBroadcastItem(i), callback);
}
mUserSwitchObservers.finishBroadcast();
- t.traceEnd(); // end dispatchUserSwitch-
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 89e4a8d82676..cfcede8ee40d 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -288,3 +288,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "expedite_activity_launch_on_cold_start"
+ namespace: "system_performance"
+ description: "Notify ActivityTaskManager of cold starts early to fix app launch behavior."
+ bug: "319519089"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index bad5b8b9567a..5740e16dc886 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2397,9 +2397,13 @@ public final class DisplayManagerService extends SystemService {
// We don't bother invalidating the display info caches here because any changes to the
// display info will trigger a cache invalidation inside of LogicalDisplay before we hit
// this point.
- sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
applyDisplayChangedLocked(display);
+
+ if (mDisplayTopologyCoordinator != null) {
+ mDisplayTopologyCoordinator.onDisplayChanged(display.getDisplayInfoLocked());
+ }
}
private void applyDisplayChangedLocked(@NonNull LogicalDisplay display) {
@@ -2643,7 +2647,8 @@ public final class DisplayManagerService extends SystemService {
private void updateCanHostTasksIfNeededLocked(LogicalDisplay display) {
if (display.setCanHostTasksLocked(!mMirrorBuiltInDisplay)) {
- sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ sendDisplayEventIfEnabledLocked(display,
+ DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
}
}
@@ -3474,7 +3479,7 @@ public final class DisplayManagerService extends SystemService {
private void sendDisplayEventFrameRateOverrideLocked(int displayId) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
- displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
mHandler.sendMessage(msg);
}
@@ -4061,7 +4066,7 @@ public final class DisplayManagerService extends SystemService {
handleLogicalDisplayAddedLocked(display);
break;
- case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED:
+ case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_BASIC_CHANGED:
handleLogicalDisplayChangedLocked(display);
break;
@@ -4286,8 +4291,9 @@ public final class DisplayManagerService extends SystemService {
switch (event) {
case DisplayManagerGlobal.EVENT_DISPLAY_ADDED:
return (mask & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED) != 0;
- case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
- return (mask & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED) != 0;
+ case DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED:
+ return (mask & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED)
+ != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED:
return (mask
& DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED)
@@ -4542,7 +4548,8 @@ public final class DisplayManagerService extends SystemService {
public void registerCallback(IDisplayManagerCallback callback) {
registerCallbackWithEventMask(callback,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED);
}
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 5b78726cc421..461a9f3f2a0d 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -85,13 +85,26 @@ class DisplayTopologyCoordinator {
}
/**
+ * Update the topology with display changes.
+ * @param info The new display info
+ */
+ void onDisplayChanged(DisplayInfo info) {
+ synchronized (mSyncRoot) {
+ if (mTopology.updateDisplay(info.displayId, getWidth(info), getHeight(info))) {
+ sendTopologyUpdateLocked();
+ }
+ }
+ }
+
+ /**
* Remove a display from the topology.
* @param displayId The logical display ID
*/
void onDisplayRemoved(int displayId) {
synchronized (mSyncRoot) {
- mTopology.removeDisplay(displayId);
- sendTopologyUpdateLocked();
+ if (mTopology.removeDisplay(displayId)) {
+ sendTopologyUpdateLocked();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 79592a656409..006921572977 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -81,7 +81,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
public static final int LOGICAL_DISPLAY_EVENT_BASE = 0;
public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1 << 0;
- public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 1 << 1;
+ public static final int LOGICAL_DISPLAY_EVENT_BASIC_CHANGED = 1 << 1;
public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 1 << 2;
public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 1 << 3;
public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 1 << 4;
@@ -172,9 +172,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
/**
* Has an entry for every logical display that the rest of the system has been notified about.
- * Any entry in here requires us to send a {@link LOGICAL_DISPLAY_EVENT_REMOVED} event when it
- * is deleted or {@link LOGICAL_DISPLAY_EVENT_CHANGED} when it is changed. The values are any
- * of the {@code UPDATE_STATE_*} constant types.
+ * The values are any of the {@code UPDATE_STATE_*} constant types.
*/
private final SparseIntArray mUpdatedLogicalDisplays = new SparseIntArray();
@@ -811,7 +809,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
final boolean isCurrentlyEnabled = display.isEnabledLocked();
int logicalDisplayEventMask = mLogicalDisplaysToUpdate
.get(displayId, LOGICAL_DISPLAY_EVENT_BASE);
-
+ boolean hasBasicInfoChanged =
+ !mTempDisplayInfo.equals(newDisplayInfo, /* compareRefreshRate */ false);
// The display is no longer valid and needs to be removed.
if (!display.isValidLocked()) {
// Remove from group
@@ -863,19 +862,28 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
int event = isCurrentlyEnabled ? LOGICAL_DISPLAY_EVENT_ADDED :
LOGICAL_DISPLAY_EVENT_REMOVED;
logicalDisplayEventMask |= event;
- } else if (wasDirty || !mTempDisplayInfo.equals(newDisplayInfo)) {
+ } else if (wasDirty) {
// If only the hdr/sdr ratio changed, then send just the event for that case
if ((diff == DisplayDeviceInfo.DIFF_HDR_SDR_RATIO)) {
logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED;
} else {
- logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CHANGED;
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED
+ | LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED
+ | LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
}
+ } else if (hasBasicInfoChanged
+ || mTempDisplayInfo.getRefreshRate() != newDisplayInfo.getRefreshRate()) {
+ // If only the hdr/sdr ratio changed, then send just the event for that case
+ if ((diff == DisplayDeviceInfo.DIFF_HDR_SDR_RATIO)) {
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED;
+ } else {
- if (mFlags.isDisplayListenerPerformanceImprovementsEnabled()) {
+ if (hasBasicInfoChanged) {
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED;
+ }
logicalDisplayEventMask
|= updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo);
}
-
// The display is involved in a display layout transition
} else if (updateState == UPDATE_STATE_TRANSITION) {
logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION;
@@ -891,7 +899,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// things like display cutouts.
display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
- logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CHANGED;
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED
+ | LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
}
}
mLogicalDisplaysToUpdate.put(displayId, logicalDisplayEventMask);
@@ -930,7 +939,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
if (mFlags.isConnectedDisplayManagementEnabled()) {
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DISCONNECTED);
}
- sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_BASIC_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_STATE_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
@@ -962,7 +971,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mask |= LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
}
- if (mTempDisplayInfo.state != newDisplayInfo.state) {
+ if (mFlags.isDisplayListenerPerformanceImprovementsEnabled()
+ && mTempDisplayInfo.state != newDisplayInfo.state) {
mask |= LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
}
return mask;
@@ -1357,8 +1367,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return "added";
case LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION:
return "transition";
- case LOGICAL_DISPLAY_EVENT_CHANGED:
- return "changed";
case LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED:
return "framerate_override";
case LOGICAL_DISPLAY_EVENT_SWAPPED:
@@ -1375,6 +1383,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return "state_changed";
case LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED:
return "refresh_rate_changed";
+ case LOGICAL_DISPLAY_EVENT_BASIC_CHANGED:
+ return "basic_changed";
}
return null;
}
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java
new file mode 100644
index 000000000000..6cab60c05b8e
--- /dev/null
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.provider.proxy;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.provider.GnssAssistanceProviderBase;
+import android.location.provider.IGnssAssistanceCallback;
+import android.location.provider.IGnssAssistanceProvider;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.servicewatcher.CurrentUserServiceSupplier;
+import com.android.server.servicewatcher.ServiceWatcher;
+
+/**
+ * Proxy for IGnssAssitanceProvider implementations.
+ */
+public class ProxyGnssAssistanceProvider {
+
+ private static final String TAG = "GnssAssistanceProxy";
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static ProxyGnssAssistanceProvider createAndRegister(Context context) {
+ ProxyGnssAssistanceProvider proxy = new ProxyGnssAssistanceProvider(context);
+ if (proxy.register()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private final ServiceWatcher mServiceWatcher;
+
+ private ProxyGnssAssistanceProvider(Context context) {
+ mServiceWatcher =
+ ServiceWatcher.create(
+ context,
+ TAG,
+ CurrentUserServiceSupplier.createFromConfig(
+ context,
+ GnssAssistanceProviderBase.ACTION_GNSS_ASSISTANCE_PROVIDER,
+ com.android.internal.R.bool.config_enableGnssAssistanceOverlay,
+ com.android.internal.R.string
+ .config_gnssAssistanceProviderPackageName),
+ /* serviceListener= */ null);
+ }
+
+ private boolean register() {
+ boolean resolves = mServiceWatcher.checkServiceResolves();
+ if (resolves) {
+ mServiceWatcher.register();
+ }
+ return resolves;
+ }
+
+ /**
+ * Request GNSS assistance.
+ */
+ public void request(IGnssAssistanceCallback callback) {
+ mServiceWatcher.runOnBinder(
+ new ServiceWatcher.BinderOperation() {
+ @Override
+ public void run(IBinder binder) throws RemoteException {
+ IGnssAssistanceProvider.Stub.asInterface(binder).request(callback);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ try {
+ Log.w(TAG, "Error on requesting GnssAssistance: " + t);
+ callback.onError();
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 58c8450d714d..0d6e502cf965 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.media.MediaRoute2ProviderService.Reason;
import android.media.MediaRouter2;
import android.media.MediaRouter2Utils;
import android.media.RouteDiscoveryPreference;
@@ -123,6 +124,13 @@ abstract class MediaRoute2Provider {
}
}
+ /** Calls {@link Callback#onRequestFailed} with the given id and reason. */
+ protected void notifyRequestFailed(long requestId, @Reason int reason) {
+ if (mCallback != null) {
+ mCallback.onRequestFailed(/* provider= */ this, requestId, reason);
+ }
+ }
+
void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo) {
setProviderState(providerInfo);
notifyProviderState();
@@ -171,11 +179,34 @@ abstract class MediaRoute2Provider {
void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
void onSessionCreated(@NonNull MediaRoute2Provider provider,
long requestId, @Nullable RoutingSessionInfo sessionInfo);
- void onSessionUpdated(@NonNull MediaRoute2Provider provider,
- @NonNull RoutingSessionInfo sessionInfo);
+
+ /**
+ * Called when there's a session info change.
+ *
+ * <p>If the provided {@code sessionInfo} has a null {@link
+ * RoutingSessionInfo#getClientPackageName()}, that means that it's applicable to all
+ * packages. We call this type of routing session "global". This is typically used for
+ * system provided {@link RoutingSessionInfo}. However, some applications may be exempted
+ * from the global routing sessions, because their media is being routed using a session
+ * different from the global routing session.
+ *
+ * @param provider The provider that owns the session that changed.
+ * @param sessionInfo The new {@link RoutingSessionInfo}.
+ * @param packageNamesWithRoutingSessionOverrides The names of packages that are not
+ * affected by global session changes. This set may only be non-empty when the {@code
+ * sessionInfo} is for the global session, and therefore has no {@link
+ * RoutingSessionInfo#getClientPackageName()}.
+ */
+ void onSessionUpdated(
+ @NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo,
+ Set<String> packageNamesWithRoutingSessionOverrides);
+
void onSessionReleased(@NonNull MediaRoute2Provider provider,
@NonNull RoutingSessionInfo sessionInfo);
- void onRequestFailed(@NonNull MediaRoute2Provider provider, long requestId, int reason);
+
+ void onRequestFailed(
+ @NonNull MediaRoute2Provider provider, long requestId, @Reason int reason);
}
/**
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index f09be2c15ee0..d6f7d3bdd4a4 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -31,6 +32,7 @@ import android.media.IMediaRoute2ProviderServiceCallback;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.MediaRoute2ProviderService.Reason;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
@@ -41,6 +43,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Slog;
@@ -89,6 +92,12 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
mRequestIdToSessionCreationRequest;
@GuardedBy("mLock")
+ private final Map<String, SystemMediaSessionCallback> mSystemSessionCallbacks;
+
+ @GuardedBy("mLock")
+ private final LongSparseArray<SystemMediaSessionCallback> mRequestIdToSystemSessionRequest;
+
+ @GuardedBy("mLock")
private final Map<String, SessionCreationOrTransferRequest> mSessionOriginalIdToTransferRequest;
MediaRoute2ProviderServiceProxy(
@@ -102,6 +111,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
mContext = Objects.requireNonNull(context, "Context must not be null.");
mRequestIdToSessionCreationRequest = new LongSparseArray<>();
mSessionOriginalIdToTransferRequest = new HashMap<>();
+ mRequestIdToSystemSessionRequest = new LongSparseArray<>();
+ mSystemSessionCallbacks = new ArrayMap<>();
mIsSelfScanOnlyProvider = isSelfScanOnlyProvider;
mSupportsSystemMediaRouting = supportsSystemMediaRouting;
mUserId = userId;
@@ -236,6 +247,48 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
}
}
+ /**
+ * Requests the creation of a system media routing session.
+ *
+ * @param requestId The id of the request.
+ * @param uid The uid of the package whose media to route, or {@link
+ * android.os.Process#INVALID_UID} if not applicable (for example, if all the system's media
+ * must be routed).
+ * @param packageName The package name to populate {@link
+ * RoutingSessionInfo#getClientPackageName()}.
+ * @param routeId The id of the route to be initially {@link
+ * RoutingSessionInfo#getSelectedRoutes()}.
+ * @param sessionHints An optional bundle with paramets.
+ * @param callback A {@link SystemMediaSessionCallback} to notify of session events.
+ * @see MediaRoute2ProviderService#onCreateSystemRoutingSession
+ */
+ public void requestCreateSystemMediaSession(
+ long requestId,
+ int uid,
+ String packageName,
+ String routeId,
+ @Nullable Bundle sessionHints,
+ @NonNull SystemMediaSessionCallback callback) {
+ if (!Flags.enableMirroringInMediaRouter2()) {
+ throw new IllegalStateException(
+ "Unexpected call to requestCreateSystemMediaSession. Governing flag is"
+ + " disabled.");
+ }
+ if (mConnectionReady) {
+ boolean binderRequestSucceeded =
+ mActiveConnection.requestCreateSystemMediaSession(
+ requestId, uid, packageName, routeId, sessionHints);
+ if (!binderRequestSucceeded) {
+ // notify failure.
+ return;
+ }
+ updateBinding();
+ synchronized (mLock) {
+ mRequestIdToSystemSessionRequest.put(requestId, callback);
+ }
+ }
+ }
+
public boolean hasComponentName(String packageName, String className) {
return mComponentName.getPackageName().equals(packageName)
&& mComponentName.getClassName().equals(className);
@@ -292,7 +345,14 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
mLastDiscoveryPreference != null
&& mLastDiscoveryPreference.shouldPerformActiveScan()
&& mSupportsSystemMediaRouting;
+ boolean bindDueToOngoingSystemMediaRoutingSessions = false;
+ if (Flags.enableMirroringInMediaRouter2()) {
+ synchronized (mLock) {
+ bindDueToOngoingSystemMediaRoutingSessions = !mSystemSessionCallbacks.isEmpty();
+ }
+ }
if (!getSessionInfos().isEmpty()
+ || bindDueToOngoingSystemMediaRoutingSessions
|| bindDueToManagerScan
|| bindDueToSystemMediaRoutingSupport) {
return true;
@@ -438,6 +498,14 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
String newSessionId = newSession.getId();
synchronized (mLock) {
+ var systemMediaSessionCallback = mRequestIdToSystemSessionRequest.get(requestId);
+ if (systemMediaSessionCallback != null) {
+ mRequestIdToSystemSessionRequest.remove(requestId);
+ mSystemSessionCallbacks.put(newSession.getOriginalId(), systemMediaSessionCallback);
+ systemMediaSessionCallback.onSessionUpdate(newSession);
+ return;
+ }
+
if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
newSession =
createSessionWithPopulatedTransferInitiationDataLocked(
@@ -569,6 +637,12 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
boolean found = false;
synchronized (mLock) {
+ var sessionCallback = mSystemSessionCallbacks.get(releasedSession.getOriginalId());
+ if (sessionCallback != null) {
+ sessionCallback.onSessionReleased();
+ return;
+ }
+
mSessionOriginalIdToTransferRequest.remove(releasedSession.getId());
for (RoutingSessionInfo session : mSessionInfos) {
if (TextUtils.equals(session.getId(), releasedSession.getId())) {
@@ -602,7 +676,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
private void dispatchSessionUpdated(RoutingSessionInfo session) {
mHandler.sendMessage(
- obtainMessage(mCallback::onSessionUpdated, this, session));
+ obtainMessage(
+ mCallback::onSessionUpdated,
+ this,
+ session,
+ /* packageNamesWithRoutingSessionOverrides= */ Set.of()));
}
private void dispatchSessionReleased(RoutingSessionInfo session) {
@@ -645,6 +723,19 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
for (RoutingSessionInfo sessionInfo : mSessionInfos) {
mCallback.onSessionReleased(this, sessionInfo);
}
+ if (Flags.enableMirroringInMediaRouter2()) {
+ for (var callback : mSystemSessionCallbacks.values()) {
+ callback.onSessionReleased();
+ }
+ mSystemSessionCallbacks.clear();
+ int requestsSize = mRequestIdToSystemSessionRequest.size();
+ for (int i = 0; i < requestsSize; i++) {
+ var callback = mRequestIdToSystemSessionRequest.valueAt(i);
+ var requestId = mRequestIdToSystemSessionRequest.keyAt(i);
+ callback.onRequestFailed(requestId, REASON_REJECTED);
+ }
+ mSystemSessionCallbacks.clear();
+ }
mSessionInfos.clear();
mReleasingSessions.clear();
mRequestIdToSessionCreationRequest.clear();
@@ -673,6 +764,26 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
pendingTransferCount);
}
+ /**
+ * Callback for events related to system media sessions.
+ *
+ * @see MediaRoute2ProviderService#onCreateSystemRoutingSession
+ */
+ public interface SystemMediaSessionCallback {
+
+ /**
+ * Called when the corresponding session's {@link RoutingSessionInfo}, or upon the creation
+ * of the given session info.
+ */
+ void onSessionUpdate(@NonNull RoutingSessionInfo sessionInfo);
+
+ /** Called when the request with the given id fails for the given reason. */
+ void onRequestFailed(long requestId, @Reason int reason);
+
+ /** Called when the corresponding session is released. */
+ void onSessionReleased();
+ }
+
// All methods in this class are called on the main thread.
private final class ServiceConnectionImpl implements ServiceConnection {
@@ -739,6 +850,28 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
}
}
+ /**
+ * Sends a system media session creation request to the provider service, and returns
+ * whether the request transaction succeeded.
+ *
+ * <p>The transaction might fail, for example, if the recipient process has died.
+ */
+ public boolean requestCreateSystemMediaSession(
+ long requestId,
+ int uid,
+ String packageName,
+ String routeId,
+ @Nullable Bundle sessionHints) {
+ try {
+ mService.requestCreateSystemMediaSession(
+ requestId, uid, packageName, routeId, sessionHints);
+ return true;
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "requestCreateSystemMediaSession: Failed to deliver request.");
+ }
+ return false;
+ }
+
public void releaseSession(long requestId, String sessionId) {
try {
mService.releaseSession(requestId, sessionId);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 58deffcbd4ba..5e6737a485af 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -846,33 +846,29 @@ class MediaRouter2ServiceImpl {
try {
synchronized (mLock) {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- List<RoutingSessionInfo> sessionInfos;
+ SystemMediaRoute2Provider systemProvider = userRecord.mHandler.getSystemProvider();
if (hasSystemRoutingPermissions) {
- if (setDeviceRouteSelected && !Flags.enableMirroringInMediaRouter2()) {
+ if (!Flags.enableMirroringInMediaRouter2() && setDeviceRouteSelected) {
// Return a fake system session that shows the device route as selected and
// available bluetooth routes as transferable.
- return userRecord.mHandler.getSystemProvider()
- .generateDeviceRouteSelectedSessionInfo(targetPackageName);
+ return systemProvider.generateDeviceRouteSelectedSessionInfo(
+ targetPackageName);
} else {
- sessionInfos = userRecord.mHandler.getSystemProvider().getSessionInfos();
- if (!sessionInfos.isEmpty()) {
- // Return a copy of the current system session with no modification,
- // except setting the client package name.
- return new RoutingSessionInfo.Builder(sessionInfos.get(0))
- .setClientPackageName(targetPackageName)
- .build();
+ RoutingSessionInfo session =
+ systemProvider.getSessionForPackage(targetPackageName);
+ if (session != null) {
+ return session;
} else {
Slog.w(TAG, "System provider does not have any session info.");
+ return null;
}
}
} else {
- return new RoutingSessionInfo.Builder(
- userRecord.mHandler.getSystemProvider().getDefaultSessionInfo())
+ return new RoutingSessionInfo.Builder(systemProvider.getDefaultSessionInfo())
.setClientPackageName(targetPackageName)
.build();
}
}
- return null;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2638,10 +2634,17 @@ class MediaRouter2ServiceImpl {
}
@Override
- public void onSessionUpdated(@NonNull MediaRoute2Provider provider,
- @NonNull RoutingSessionInfo sessionInfo) {
- sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler,
- this, provider, sessionInfo));
+ public void onSessionUpdated(
+ @NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo,
+ Set<String> packageNamesWithRoutingSessionOverrides) {
+ sendMessage(
+ PooledLambda.obtainMessage(
+ UserHandler::onSessionInfoChangedOnHandler,
+ this,
+ provider,
+ sessionInfo,
+ packageNamesWithRoutingSessionOverrides));
}
@Override
@@ -3152,10 +3155,31 @@ class MediaRouter2ServiceImpl {
toOriginalRequestId(uniqueRequestId), sessionInfo);
}
- private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
- @NonNull RoutingSessionInfo sessionInfo) {
+ /**
+ * Implementation of {@link MediaRoute2Provider.Callback#onSessionUpdated}.
+ *
+ * <p>Must run on the thread that corresponds to this {@link UserHandler}.
+ */
+ private void onSessionInfoChangedOnHandler(
+ @NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo,
+ Set<String> packageNamesWithRoutingSessionOverrides) {
List<ManagerRecord> managers = getManagerRecords();
for (ManagerRecord manager : managers) {
+ if (Flags.enableMirroringInMediaRouter2()) {
+ String targetPackageName = manager.mTargetPackageName;
+ boolean skipDueToOverride =
+ targetPackageName != null
+ && packageNamesWithRoutingSessionOverrides.contains(
+ targetPackageName);
+ boolean sessionIsForTargetPackage =
+ TextUtils.isEmpty(sessionInfo.getClientPackageName()) // is global.
+ || TextUtils.equals(
+ targetPackageName, sessionInfo.getClientPackageName());
+ if (skipDueToOverride || !sessionIsForTargetPackage) {
+ continue;
+ }
+ }
manager.notifySessionUpdated(sessionInfo);
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index b93846bf9ee7..60fced1e0c51 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -62,7 +62,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
private final AudioManager mAudioManager;
- private final Handler mHandler;
+ protected final Handler mHandler;
private final Context mContext;
private final UserHandle mUser;
@@ -116,7 +116,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
() -> {
publishProviderState();
if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
});
@@ -129,7 +129,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
() -> {
publishProviderState();
if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
}));
}
@@ -161,7 +161,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
public void setCallback(Callback callback) {
super.setCallback(callback);
notifyProviderState();
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
@Override
@@ -296,7 +296,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
&& updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
}
@@ -327,6 +327,23 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
/**
+ * Returns the {@link RoutingSessionInfo} that corresponds to the package with the given name.
+ */
+ public RoutingSessionInfo getSessionForPackage(String targetPackageName) {
+ synchronized (mLock) {
+ if (!mSessionInfos.isEmpty()) {
+ // Return a copy of the current system session with no modification,
+ // except setting the client package name.
+ return new RoutingSessionInfo.Builder(mSessionInfos.get(0))
+ .setClientPackageName(targetPackageName)
+ .build();
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /**
* Builds a system {@link RoutingSessionInfo} with the selected route set to the currently
* selected <b>device</b> route (wired or built-in, but not bluetooth) and transferable routes
* set to the currently available (connected) bluetooth routes.
@@ -626,20 +643,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
notifyProviderState();
}
- void notifySessionInfoUpdated() {
+ void notifyGlobalSessionInfoUpdated() {
if (mCallback == null) {
return;
}
RoutingSessionInfo sessionInfo;
synchronized (mLock) {
- sessionInfo = mSessionInfos.get(0);
- if (sessionInfo == null) {
+ if (mSessionInfos.isEmpty()) {
return;
}
+ sessionInfo = mSessionInfos.get(0);
}
- mCallback.onSessionUpdated(this, sessionInfo);
+ mCallback.onSessionUpdated(
+ this, sessionInfo, /* packageNamesWithRoutingSessionOverrides= */ Set.of());
}
@Override
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
index 7dc30ab66fd2..8931e3a1426e 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
@@ -18,23 +18,33 @@ package com.android.server.media;
import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.MediaRoute2ProviderService.Reason;
+import android.media.MediaRouter2Utils;
import android.media.RoutingSessionInfo;
+import android.os.Binder;
import android.os.Looper;
+import android.os.Process;
import android.os.UserHandle;
-import android.util.ArraySet;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.LongSparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.media.MediaRoute2ProviderServiceProxy.SystemMediaSessionCallback;
-import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.stream.Stream;
/**
@@ -48,11 +58,33 @@ import java.util.stream.Stream;
private static final String ROUTE_ID_PREFIX_SYSTEM = "SYSTEM";
private static final String ROUTE_ID_SYSTEM_SEPARATOR = ".";
+ private final PackageManager mPackageManager;
+
@GuardedBy("mLock")
private MediaRoute2ProviderInfo mLastSystemProviderInfo;
@GuardedBy("mLock")
- private final Map<String, ProviderProxyRecord> mProxyRecords = new HashMap<>();
+ private final Map<String, ProviderProxyRecord> mProxyRecords = new ArrayMap<>();
+
+ /**
+ * Maps package names to corresponding sessions maintained by {@link MediaRoute2ProviderService
+ * provider services}.
+ */
+ @GuardedBy("mLock")
+ private final Map<String, SystemMediaSessionRecord> mPackageNameToSessionRecord =
+ new ArrayMap<>();
+
+ /**
+ * Maps route {@link MediaRoute2Info#getOriginalId original ids} to the id of the {@link
+ * MediaRoute2ProviderService provider service} that manages the corresponding route.
+ */
+ @GuardedBy("mLock")
+ private final Map<String, String> mOriginalRouteIdToProviderId = new ArrayMap<>();
+
+ /** Maps request ids to pending session creation callbacks. */
+ @GuardedBy("mLock")
+ private final LongSparseArray<SystemMediaSessionCallbackImpl> mPendingSessionCreations =
+ new LongSparseArray<>();
private static final ComponentName COMPONENT_NAME =
new ComponentName(
@@ -69,6 +101,128 @@ import java.util.stream.Stream;
private SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) {
super(context, COMPONENT_NAME, user, looper);
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public void transferToRoute(
+ long requestId,
+ @NonNull UserHandle clientUserHandle,
+ @NonNull String clientPackageName,
+ String sessionOriginalId,
+ String routeOriginalId,
+ int transferReason) {
+ synchronized (mLock) {
+ var targetProviderProxyId = mOriginalRouteIdToProviderId.get(routeOriginalId);
+ var targetProviderProxyRecord = mProxyRecords.get(targetProviderProxyId);
+ // Holds the target route, if it's managed by a provider service. Holds null otherwise.
+ var serviceTargetRoute =
+ targetProviderProxyRecord != null
+ ? targetProviderProxyRecord.getRouteByOriginalId(routeOriginalId)
+ : null;
+ var existingSessionRecord = mPackageNameToSessionRecord.get(clientPackageName);
+ if (existingSessionRecord != null) {
+ var existingSession = existingSessionRecord.mSourceSessionInfo;
+ if (targetProviderProxyId != null
+ && TextUtils.equals(
+ targetProviderProxyId, existingSession.getProviderId())) {
+ // The currently selected route and target route both belong to the same
+ // provider. We tell the provider to handle the transfer.
+ targetProviderProxyRecord.requestTransfer(
+ existingSession.getOriginalId(), serviceTargetRoute);
+ } else {
+ // The target route is handled by a provider other than the target one. We need
+ // to release the existing session.
+ var currentProxyRecord = existingSessionRecord.getProxyRecord();
+ if (currentProxyRecord != null) {
+ currentProxyRecord.releaseSession(
+ requestId, existingSession.getOriginalId());
+ existingSessionRecord.removeSelfFromSessionMap();
+ }
+ }
+ }
+
+ if (serviceTargetRoute != null) {
+ boolean isGlobalSession = TextUtils.isEmpty(clientPackageName);
+ int uid;
+ if (isGlobalSession) {
+ uid = Process.INVALID_UID;
+ } else {
+ uid = fetchUid(clientPackageName, clientUserHandle);
+ if (uid == Process.INVALID_UID) {
+ throw new IllegalArgumentException(
+ "Cannot resolve transfer for "
+ + clientPackageName
+ + " and "
+ + clientUserHandle);
+ }
+ }
+ var pendingCreationCallback =
+ new SystemMediaSessionCallbackImpl(
+ targetProviderProxyId, requestId, clientPackageName);
+ mPendingSessionCreations.put(requestId, pendingCreationCallback);
+ targetProviderProxyRecord.requestCreateSystemMediaSession(
+ requestId,
+ uid,
+ clientPackageName,
+ routeOriginalId,
+ pendingCreationCallback);
+ } else {
+ // The target route is not provided by any of the services. Assume it's a system
+ // provided route.
+ super.transferToRoute(
+ requestId,
+ clientUserHandle,
+ clientPackageName,
+ sessionOriginalId,
+ routeOriginalId,
+ transferReason);
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public RoutingSessionInfo getSessionForPackage(String packageName) {
+ synchronized (mLock) {
+ var systemSession = super.getSessionForPackage(packageName);
+ if (systemSession == null) {
+ return null;
+ }
+ var overridingSession = mPackageNameToSessionRecord.get(packageName);
+ if (overridingSession != null) {
+ var builder =
+ new RoutingSessionInfo.Builder(overridingSession.mTranslatedSessionInfo)
+ .setProviderId(mUniqueId)
+ .setSystemSession(true);
+ for (var systemRoute : mLastSystemProviderInfo.getRoutes()) {
+ builder.addTransferableRoute(systemRoute.getOriginalId());
+ }
+ return builder.build();
+ } else {
+ return systemSession;
+ }
+ }
+ }
+
+ /**
+ * Returns the uid that corresponds to the given name and user handle, or {@link
+ * Process#INVALID_UID} if a uid couldn't be found.
+ */
+ @SuppressLint("MissingPermission")
+ // We clear the calling identity before calling the package manager, and we are running on the
+ // system_server.
+ private int fetchUid(String clientPackageName, UserHandle clientUserHandle) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mPackageManager.getApplicationInfoAsUser(
+ clientPackageName, /* flags= */ 0, clientUserHandle)
+ .uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ return Process.INVALID_UID;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
@@ -85,21 +239,21 @@ import java.util.stream.Stream;
} else {
mProxyRecords.put(serviceProxy.mUniqueId, proxyRecord);
}
- setProviderState(buildProviderInfo());
+ updateProviderInfo();
}
updateSessionInfo();
notifyProviderState();
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
@Override
public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) {
synchronized (mLock) {
mLastSystemProviderInfo = providerInfo;
- setProviderState(buildProviderInfo());
+ updateProviderInfo();
}
updateSessionInfo();
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
/**
@@ -116,10 +270,13 @@ import java.util.stream.Stream;
var builder = new RoutingSessionInfo.Builder(systemSessionInfo);
mProxyRecords.values().stream()
.flatMap(ProviderProxyRecord::getRoutesStream)
- .map(MediaRoute2Info::getId)
+ .map(MediaRoute2Info::getOriginalId)
.forEach(builder::addTransferableRoute);
mSessionInfos.clear();
mSessionInfos.add(builder.build());
+ for (var sessionRecords : mPackageNameToSessionRecord.values()) {
+ mSessionInfos.add(sessionRecords.mTranslatedSessionInfo);
+ }
}
}
@@ -129,13 +286,84 @@ import java.util.stream.Stream;
* provider services}.
*/
@GuardedBy("mLock")
- private MediaRoute2ProviderInfo buildProviderInfo() {
+ private void updateProviderInfo() {
MediaRoute2ProviderInfo.Builder builder =
new MediaRoute2ProviderInfo.Builder(mLastSystemProviderInfo);
- mProxyRecords.values().stream()
- .flatMap(ProviderProxyRecord::getRoutesStream)
- .forEach(builder::addRoute);
- return builder.build();
+ mOriginalRouteIdToProviderId.clear();
+ for (var proxyRecord : mProxyRecords.values()) {
+ String proxyId = proxyRecord.mProxy.mUniqueId;
+ proxyRecord
+ .getRoutesStream()
+ .forEach(
+ route -> {
+ builder.addRoute(route);
+ mOriginalRouteIdToProviderId.put(route.getOriginalId(), proxyId);
+ });
+ }
+ setProviderState(builder.build());
+ }
+
+ @Override
+ /* package */ void notifyGlobalSessionInfoUpdated() {
+ if (mCallback == null) {
+ return;
+ }
+
+ RoutingSessionInfo sessionInfo;
+ Set<String> packageNamesWithRoutingSessionOverrides;
+ synchronized (mLock) {
+ if (mSessionInfos.isEmpty()) {
+ return;
+ }
+ packageNamesWithRoutingSessionOverrides = mPackageNameToSessionRecord.keySet();
+ sessionInfo = mSessionInfos.getFirst();
+ }
+
+ mCallback.onSessionUpdated(this, sessionInfo, packageNamesWithRoutingSessionOverrides);
+ }
+
+ private void onSessionOverrideUpdated(RoutingSessionInfo sessionInfo) {
+ // TODO: b/362507305 - Consider adding routes from other provider services. This is not a
+ // trivial change because a provider1-route to provider2-route transfer has seemingly two
+ // possible approachies. Either we first release the current session and then create the new
+ // one, in which case the audio is briefly going to leak through the system route. On the
+ // other hand, if we first create the provider2 session, then there will be a period during
+ // which there will be two overlapping routing policies asking for the exact same media
+ // stream.
+ var builder = new RoutingSessionInfo.Builder(sessionInfo);
+ mLastSystemProviderInfo.getRoutes().stream()
+ .map(MediaRoute2Info::getOriginalId)
+ .forEach(builder::addTransferableRoute);
+ mCallback.onSessionUpdated(
+ /* provider= */ this,
+ builder.build(),
+ /* packageNamesWithRoutingSessionOverrides= */ Set.of());
+ }
+
+ /**
+ * Equivalent to {@link #asSystemRouteId}, except it takes a unique route id instead of a
+ * original id.
+ */
+ private static String uniqueIdAsSystemRouteId(String providerId, String uniqueRouteId) {
+ return asSystemRouteId(providerId, MediaRouter2Utils.getOriginalId(uniqueRouteId));
+ }
+
+ /**
+ * Returns a unique {@link MediaRoute2Info#getOriginalId() original id} for this provider to
+ * publish system media routes from {@link MediaRoute2ProviderService provider services}.
+ *
+ * <p>This provider will publish system media routes as part of the system routing session.
+ * However, said routes may also support {@link MediaRoute2Info#FLAG_ROUTING_TYPE_REMOTE remote
+ * routing}, meaning we cannot use the same id, or there would be an id collision. As a result,
+ * we derive a {@link MediaRoute2Info#getOriginalId original id} that is unique among all
+ * original route ids used by this provider.
+ */
+ private static String asSystemRouteId(String providerId, String originalRouteId) {
+ return ROUTE_ID_PREFIX_SYSTEM
+ + ROUTE_ID_SYSTEM_SEPARATOR
+ + providerId
+ + ROUTE_ID_SYSTEM_SEPARATOR
+ + originalRouteId;
}
/**
@@ -145,14 +373,69 @@ import java.util.stream.Stream;
* @param mProxy The corresponding {@link MediaRoute2ProviderServiceProxy}.
* @param mSystemMediaRoutes The last snapshot of routes from the service that support system
* media routing, as defined by {@link MediaRoute2Info#supportsSystemMediaRouting()}.
+ * @param mNewOriginalIdToSourceOriginalIdMap Maps the {@link #mSystemMediaRoutes} ids to the
+ * original ids of corresponding {@link MediaRoute2ProviderService service} route.
*/
private record ProviderProxyRecord(
MediaRoute2ProviderServiceProxy mProxy,
- Collection<MediaRoute2Info> mSystemMediaRoutes) {
+ Map<String, MediaRoute2Info> mSystemMediaRoutes,
+ Map<String, String> mNewOriginalIdToSourceOriginalIdMap) {
/** Returns a stream representation of the {@link #mSystemMediaRoutes}. */
public Stream<MediaRoute2Info> getRoutesStream() {
- return mSystemMediaRoutes.stream();
+ return mSystemMediaRoutes.values().stream();
+ }
+
+ @Nullable
+ public MediaRoute2Info getRouteByOriginalId(String routeOriginalId) {
+ return mSystemMediaRoutes.get(routeOriginalId);
+ }
+
+ /**
+ * Requests the creation of a system media routing session.
+ *
+ * @param requestId The request id.
+ * @param uid The uid of the package whose media to route, or {@link Process#INVALID_UID} if
+ * not applicable.
+ * @param packageName The name of the package whose media to route.
+ * @param originalRouteId The {@link MediaRoute2Info#getOriginalId() original route id} of
+ * the route that should be initially selected.
+ * @param callback A {@link MediaRoute2ProviderServiceProxy.SystemMediaSessionCallback} for
+ * events.
+ * @see MediaRoute2ProviderService#onCreateSystemRoutingSession
+ */
+ public void requestCreateSystemMediaSession(
+ long requestId,
+ int uid,
+ String packageName,
+ String originalRouteId,
+ SystemMediaSessionCallback callback) {
+ var targetRouteId = mNewOriginalIdToSourceOriginalIdMap.get(originalRouteId);
+ if (targetRouteId == null) {
+ Log.w(
+ TAG,
+ "Failed system media session creation due to lack of mapping for id: "
+ + originalRouteId);
+ callback.onRequestFailed(
+ requestId, MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE);
+ } else {
+ mProxy.requestCreateSystemMediaSession(
+ requestId,
+ uid,
+ packageName,
+ targetRouteId,
+ /* sessionHints= */ null,
+ callback);
+ }
+ }
+
+ public void requestTransfer(String sessionId, MediaRoute2Info targetRoute) {
+ // TODO: Map the target route to the source route original id.
+ throw new UnsupportedOperationException("TODO Implement");
+ }
+
+ public void releaseSession(long requestId, String originalSessionId) {
+ mProxy.releaseSession(requestId, originalSessionId);
}
/**
@@ -165,22 +448,177 @@ import java.util.stream.Stream;
if (providerInfo == null) {
return null;
}
- ArraySet<MediaRoute2Info> routes = new ArraySet<>();
- providerInfo.getRoutes().stream()
- .filter(MediaRoute2Info::supportsSystemMediaRouting)
- .forEach(
- route -> {
- String id =
- ROUTE_ID_PREFIX_SYSTEM
- + route.getProviderId()
- + ROUTE_ID_SYSTEM_SEPARATOR
- + route.getOriginalId();
- routes.add(
- new MediaRoute2Info.Builder(id, route.getName())
- .addFeature(FEATURE_LIVE_AUDIO)
- .build());
- });
- return new ProviderProxyRecord(serviceProxy, Collections.unmodifiableSet(routes));
+ Map<String, MediaRoute2Info> routesMap = new ArrayMap<>();
+ Map<String, String> idMap = new ArrayMap<>();
+ for (MediaRoute2Info sourceRoute : providerInfo.getRoutes()) {
+ if (!sourceRoute.supportsSystemMediaRouting()) {
+ continue;
+ }
+ String id =
+ asSystemRouteId(providerInfo.getUniqueId(), sourceRoute.getOriginalId());
+ var newRoute =
+ new MediaRoute2Info.Builder(id, sourceRoute.getName())
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .build();
+ routesMap.put(id, newRoute);
+ idMap.put(id, sourceRoute.getOriginalId());
+ }
+ return new ProviderProxyRecord(
+ serviceProxy,
+ Collections.unmodifiableMap(routesMap),
+ Collections.unmodifiableMap(idMap));
+ }
+ }
+
+ private class SystemMediaSessionCallbackImpl implements SystemMediaSessionCallback {
+
+ private final String mProviderId;
+ private final long mRequestId;
+ private final String mClientPackageName;
+ // Accessed only on mHandler.
+ @Nullable private SystemMediaSessionRecord mSessionRecord;
+
+ private SystemMediaSessionCallbackImpl(
+ String providerId, long requestId, String clientPackageName) {
+ mProviderId = providerId;
+ mRequestId = requestId;
+ mClientPackageName = clientPackageName;
+ }
+
+ @Override
+ public void onSessionUpdate(@NonNull RoutingSessionInfo sessionInfo) {
+ mHandler.post(
+ () -> {
+ if (mSessionRecord != null) {
+ mSessionRecord.onSessionUpdate(sessionInfo);
+ }
+ SystemMediaSessionRecord systemMediaSessionRecord =
+ new SystemMediaSessionRecord(mProviderId, sessionInfo);
+ RoutingSessionInfo translatedSession;
+ synchronized (mLock) {
+ mSessionRecord = systemMediaSessionRecord;
+ mPackageNameToSessionRecord.put(
+ mClientPackageName, systemMediaSessionRecord);
+ mPendingSessionCreations.remove(mRequestId);
+ translatedSession = systemMediaSessionRecord.mTranslatedSessionInfo;
+ }
+ onSessionOverrideUpdated(translatedSession);
+ });
+ }
+
+ @Override
+ public void onRequestFailed(long requestId, @Reason int reason) {
+ mHandler.post(
+ () -> {
+ if (mSessionRecord != null) {
+ mSessionRecord.onRequestFailed(requestId, reason);
+ }
+ synchronized (mLock) {
+ mPendingSessionCreations.remove(mRequestId);
+ }
+ notifyRequestFailed(requestId, reason);
+ });
+ }
+
+ @Override
+ public void onSessionReleased() {
+ mHandler.post(
+ () -> {
+ if (mSessionRecord != null) {
+ mSessionRecord.onSessionReleased();
+ } else {
+ // Should never happen. The session hasn't yet been created.
+ throw new IllegalStateException();
+ }
+ });
+ }
+ }
+
+ private class SystemMediaSessionRecord implements SystemMediaSessionCallback {
+
+ private final String mProviderId;
+
+ @GuardedBy("SystemMediaRoute2Provider2.this.mLock")
+ @NonNull
+ private RoutingSessionInfo mSourceSessionInfo;
+
+ /**
+ * The same as {@link #mSourceSessionInfo}, except ids are {@link #asSystemRouteId system
+ * provider ids}.
+ */
+ @GuardedBy("SystemMediaRoute2Provider2.this.mLock")
+ @NonNull
+ private RoutingSessionInfo mTranslatedSessionInfo;
+
+ SystemMediaSessionRecord(
+ @NonNull String providerId, @NonNull RoutingSessionInfo sessionInfo) {
+ mProviderId = providerId;
+ mSourceSessionInfo = sessionInfo;
+ mTranslatedSessionInfo = asSystemProviderSession(sessionInfo);
+ }
+
+ @Override
+ public void onSessionUpdate(@NonNull RoutingSessionInfo sessionInfo) {
+ RoutingSessionInfo translatedSessionInfo = mTranslatedSessionInfo;
+ synchronized (mLock) {
+ mSourceSessionInfo = sessionInfo;
+ mTranslatedSessionInfo = asSystemProviderSession(sessionInfo);
+ }
+ onSessionOverrideUpdated(translatedSessionInfo);
+ }
+
+ @Override
+ public void onRequestFailed(long requestId, @Reason int reason) {
+ notifyRequestFailed(requestId, reason);
+ }
+
+ @Override
+ public void onSessionReleased() {
+ synchronized (mLock) {
+ removeSelfFromSessionMap();
+ }
+ notifyGlobalSessionInfoUpdated();
+ }
+
+ @GuardedBy("SystemMediaRoute2Provider2.this.mLock")
+ @Nullable
+ public ProviderProxyRecord getProxyRecord() {
+ ProviderProxyRecord provider = mProxyRecords.get(mProviderId);
+ if (provider == null) {
+ // Unexpected condition where the proxy is no longer available while there's an
+ // ongoing session. Could happen due to a crash in the provider process.
+ removeSelfFromSessionMap();
+ }
+ return provider;
+ }
+
+ @GuardedBy("SystemMediaRoute2Provider2.this.mLock")
+ private void removeSelfFromSessionMap() {
+ mPackageNameToSessionRecord.remove(mSourceSessionInfo.getClientPackageName());
+ }
+
+ private RoutingSessionInfo asSystemProviderSession(RoutingSessionInfo session) {
+ var builder =
+ new RoutingSessionInfo.Builder(session)
+ .setProviderId(mUniqueId)
+ .setSystemSession(true)
+ .clearSelectedRoutes()
+ .clearSelectableRoutes()
+ .clearDeselectableRoutes()
+ .clearTransferableRoutes();
+ session.getSelectedRoutes().stream()
+ .map(it -> uniqueIdAsSystemRouteId(session.getProviderId(), it))
+ .forEach(builder::addSelectedRoute);
+ session.getSelectableRoutes().stream()
+ .map(it -> uniqueIdAsSystemRouteId(session.getProviderId(), it))
+ .forEach(builder::addSelectableRoute);
+ session.getDeselectableRoutes().stream()
+ .map(it -> uniqueIdAsSystemRouteId(session.getProviderId(), it))
+ .forEach(builder::addDeselectableRoute);
+ session.getTransferableRoutes().stream()
+ .map(it -> uniqueIdAsSystemRouteId(session.getProviderId(), it))
+ .forEach(builder::addTransferableRoute);
+ return builder.build();
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 89902f7f8321..7cbbe2938fd5 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -101,4 +101,10 @@ public interface NotificationDelegate {
void onNotificationFeedbackReceived(String key, Bundle feedback);
void prepareForPossibleShutdown();
+
+ /**
+ * Called when the notification should be unbundled.
+ * @param key the notification key
+ */
+ void unbundleNotification(String key);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c6d7fc7508da..7375a68c547b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -112,6 +112,7 @@ import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_
import static android.service.notification.Flags.callstyleCallbackApi;
import static android.service.notification.Flags.notificationClassification;
import static android.service.notification.Flags.notificationForceGrouping;
+import static android.service.notification.Flags.notificationRegroupOnClassification;
import static android.service.notification.Flags.redactSensitiveNotificationsBigTextStyle;
import static android.service.notification.Flags.redactSensitiveNotificationsFromUntrustedListeners;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
@@ -1851,6 +1852,42 @@ public class NotificationManagerService extends SystemService {
}
}
+ @Override
+ public void unbundleNotification(String key) {
+ if (!(notificationClassification() && notificationRegroupOnClassification())) {
+ return;
+ }
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r == null) {
+ return;
+ }
+
+ if (DBG) {
+ Slog.v(TAG, "unbundleNotification: " + r);
+ }
+
+ boolean hasOriginalSummary = false;
+ if (r.getSbn().isAppGroup() && r.getNotification().isGroupChild()) {
+ final String oldGroupKey = GroupHelper.getFullAggregateGroupKey(
+ r.getSbn().getPackageName(), r.getOriginalGroupKey(), r.getUserId());
+ NotificationRecord groupSummary = mSummaryByGroupKey.get(oldGroupKey);
+ // We only care about app-provided valid groups
+ hasOriginalSummary = (groupSummary != null
+ && !GroupHelper.isAggregatedGroup(groupSummary));
+ }
+
+ // Only NotificationRecord's mChannel is updated when bundled, the Notification
+ // mChannelId will always be the original channel.
+ String origChannelId = r.getNotification().getChannelId();
+ NotificationChannel originalChannel = mPreferencesHelper.getNotificationChannel(
+ r.getSbn().getPackageName(), r.getUid(), origChannelId, false);
+ if (originalChannel != null && !origChannelId.equals(r.getChannel().getId())) {
+ r.updateNotificationChannel(originalChannel);
+ mGroupHelper.onNotificationUnbundled(r, hasOriginalSummary);
+ }
+ }
+ }
};
NotificationManagerPrivate mNotificationManagerPrivate = new NotificationManagerPrivate() {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 749952e3336f..15377d6b269a 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -559,7 +559,7 @@ public class PreferencesHelper implements RankingConfig {
if (r.uid == UNKNOWN_UID) {
if (Flags.persistIncompleteRestoreData()) {
- r.userId = userId;
+ r.userIdWhenUidUnknown = userId;
}
mRestoredWithoutUids.put(unrestoredPackageKey(pkg, userId), r);
} else {
@@ -756,7 +756,7 @@ public class PreferencesHelper implements RankingConfig {
if (Flags.persistIncompleteRestoreData() && r.uid == UNKNOWN_UID) {
out.attributeLong(null, ATT_CREATION_TIME, r.creationTime);
- out.attributeInt(null, ATT_USERID, r.userId);
+ out.attributeInt(null, ATT_USERID, r.userIdWhenUidUnknown);
}
if (!forBackup) {
@@ -1959,7 +1959,7 @@ public class PreferencesHelper implements RankingConfig {
ArrayList<ZenBypassingApp> bypassing = new ArrayList<>();
synchronized (mLock) {
for (PackagePreferences p : mPackagePreferences.values()) {
- if (p.userId != userId) {
+ if (UserHandle.getUserId(p.uid) != userId) {
continue;
}
int totalChannelCount = p.channels.size();
@@ -3189,7 +3189,7 @@ public class PreferencesHelper implements RankingConfig {
// Until we enable the UI, we should return false.
boolean canHavePromotedNotifs = android.app.Flags.uiRichOngoing();
- @UserIdInt int userId;
+ @UserIdInt int userIdWhenUidUnknown;
Delegate delegate = null;
ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c4d1cc723804..ec0f25169d75 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4068,7 +4068,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Nullable IBinder focusedToken) {
boolean handled = PhoneWindowManager.this.handleKeyGestureEvent(event,
focusedToken);
- if (handled && Arrays.stream(event.getKeycodes()).anyMatch(
+ if (handled && !event.isCancelled() && Arrays.stream(event.getKeycodes()).anyMatch(
(keycode) -> keycode == KeyEvent.KEYCODE_POWER)) {
mPowerKeyHandled = true;
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 0c3c46c75eee..7f88e7463208 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -479,6 +479,7 @@ public class Notifier {
case PowerManager.PARTIAL_WAKE_LOCK:
return BatteryStats.WAKE_TYPE_PARTIAL;
+ case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
return BatteryStats.WAKE_TYPE_FULL;
@@ -503,6 +504,31 @@ public class Notifier {
}
}
+ @VisibleForTesting
+ int getWakelockMonitorTypeForLogging(int flags) {
+ switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ case PowerManager.FULL_WAKE_LOCK, PowerManager.SCREEN_DIM_WAKE_LOCK,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ return PowerManager.FULL_WAKE_LOCK;
+ case PowerManager.DRAW_WAKE_LOCK:
+ return PowerManager.DRAW_WAKE_LOCK;
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ if (mSuspendWhenScreenOffDueToProximityConfig) {
+ return -1;
+ }
+ return PowerManager.PARTIAL_WAKE_LOCK;
+ case PowerManager.PARTIAL_WAKE_LOCK:
+ return PowerManager.PARTIAL_WAKE_LOCK;
+ case PowerManager.DOZE_WAKE_LOCK:
+ // Doze wake locks are an internal implementation detail of the
+ // communication between dream manager service and power manager
+ // service. They have no additive battery impact.
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
/**
* Notifies that the device is changing wakefulness.
* This function may be called even if the previous change hasn't finished in
@@ -1288,7 +1314,7 @@ public class Notifier {
if (mBatteryStatsInternal == null) {
return;
}
- final int type = flags & PowerManager.WAKE_LOCK_LEVEL_MASK;
+ final int type = getWakelockMonitorTypeForLogging(flags);
if (workSource == null || workSource.isEmpty()) {
final int mappedUid = mBatteryStatsInternal.getOwnerUid(ownerUid);
mFrameworkStatsLogger.wakelockStateChanged(mappedUid, tag, type, eventType);
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index aae7417970eb..83461125b404 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -20,6 +20,7 @@ import static android.os.Flags.adpfUseFmqChannel;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.power.hint.Flags.adpfSessionTag;
+import static com.android.server.power.hint.Flags.cpuHeadroomAffinityCheck;
import static com.android.server.power.hint.Flags.powerhintThreadCleanup;
import static com.android.server.power.hint.Flags.resetOnForkEnabled;
@@ -191,7 +192,7 @@ public final class HintManagerService extends SystemService {
private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms";
private static final String PROPERTY_CHECK_HEADROOM_TID = "persist.hms.check_headroom_tid";
-
+ private static final String PROPERTY_CHECK_HEADROOM_AFFINITY = "persist.hms.check_affinity";
private Boolean mFMQUsesIntegratedEventFlag = false;
private final Object mCpuHeadroomLock = new Object();
@@ -1501,6 +1502,10 @@ public final class HintManagerService extends SystemService {
}
}
}
+ if (cpuHeadroomAffinityCheck() && params.tids.length > 1
+ && SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_AFFINITY, true)) {
+ checkThreadAffinityForTids(params.tids);
+ }
halParams.tids = params.tids;
}
if (halParams.calculationWindowMillis
@@ -1529,6 +1534,27 @@ public final class HintManagerService extends SystemService {
return null;
}
}
+ private void checkThreadAffinityForTids(int[] tids) {
+ long[] reference = null;
+ for (int tid : tids) {
+ long[] affinity;
+ try {
+ affinity = Process.getSchedAffinity(tid);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to get affinity " + tid, e);
+ throw new IllegalStateException("Could not check affinity for tid " + tid);
+ }
+ if (reference == null) {
+ reference = affinity;
+ } else if (!Arrays.equals(reference, affinity)) {
+ Slog.d(TAG, "Thread affinity is different: tid "
+ + tids[0] + "->" + Arrays.toString(reference) + ", tid "
+ + tid + "->" + Arrays.toString(affinity));
+ throw new IllegalStateException("Thread affinity is not the same for tids "
+ + Arrays.toString(tids));
+ }
+ }
+ }
private void checkCpuHeadroomParams(CpuHeadroomParamsInternal params) {
boolean calculationTypeMatched = false;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f8877ad912b5..c18918fd9f8b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -2175,6 +2175,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
}
+ /**
+ * Called when the notification should be unbundled.
+ * @param key the notification key
+ */
+ @Override
+ public void unbundleNotification(@Nullable String key) {
+ enforceStatusBarService();
+ enforceValidCallingUser();
+ Binder.withCleanCallingIdentity(() -> {
+ mNotificationDelegate.unbundleNotification(key);
+ });
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
index dad3a784cfb9..bb163ef4c1d5 100644
--- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -369,10 +369,9 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener {
tagName = parser.getName();
if (TAG_QUOTA.equals(tagName)) {
CacheQuotaHint request = getRequestFromXml(parser);
- if (request == null) {
- continue;
+ if (request != null) {
+ quotas.add(request);
}
- quotas.add(request);
}
}
eventType = parser.next();
diff --git a/services/core/java/com/android/server/updates/Android.bp b/services/core/java/com/android/server/updates/Android.bp
deleted file mode 100644
index 10beebb82711..000000000000
--- a/services/core/java/com/android/server/updates/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-aconfig_declarations {
- name: "updates_flags",
- package: "com.android.server.updates",
- container: "system",
- srcs: ["*.aconfig"],
-}
-
-java_aconfig_library {
- name: "updates_flags_lib",
- aconfig_declarations: "updates_flags",
-}
diff --git a/services/core/java/com/android/server/updates/CertificateTransparencyLogInstallReceiver.java b/services/core/java/com/android/server/updates/CertificateTransparencyLogInstallReceiver.java
deleted file mode 100644
index af4025e1db7c..000000000000
--- a/services/core/java/com/android/server/updates/CertificateTransparencyLogInstallReceiver.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.updates;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.FileUtils;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.Slog;
-
-import libcore.io.Streams;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
-public class CertificateTransparencyLogInstallReceiver extends ConfigUpdateInstallReceiver {
-
- private static final String TAG = "CTLogInstallReceiver";
- private static final String LOGDIR_PREFIX = "logs-";
-
- public CertificateTransparencyLogInstallReceiver() {
- super("/data/misc/keychain/ct/", "ct_logs", "metadata/", "version");
- }
-
- @Override
- protected void install(InputStream inputStream, int version) throws IOException {
- if (!Flags.certificateTransparencyInstaller()) {
- return;
- }
- // To support atomically replacing the old configuration directory with the new there's a
- // bunch of steps. We create a new directory with the logs and then do an atomic update of
- // the current symlink to point to the new directory.
- // 1. Ensure that the update dir exists and is readable
- updateDir.mkdir();
- if (!updateDir.isDirectory()) {
- throw new IOException("Unable to make directory " + updateDir.getCanonicalPath());
- }
- if (!updateDir.setReadable(true, false)) {
- throw new IOException("Unable to set permissions on " + updateDir.getCanonicalPath());
- }
- File currentSymlink = new File(updateDir, "current");
- File newVersion = new File(updateDir, LOGDIR_PREFIX + String.valueOf(version));
- // 2. Handle the corner case where the new directory already exists.
- if (newVersion.exists()) {
- // If the symlink has already been updated then the update died between steps 7 and 8
- // and so we cannot delete the directory since its in use. Instead just bump the version
- // and return.
- if (newVersion.getCanonicalPath().equals(currentSymlink.getCanonicalPath())) {
- writeUpdate(
- updateDir,
- updateVersion,
- new ByteArrayInputStream(Long.toString(version).getBytes()));
- deleteOldLogDirectories();
- return;
- } else {
- FileUtils.deleteContentsAndDir(newVersion);
- }
- }
- try {
- // 3. Create /data/misc/keychain/ct/<new_version>/ .
- newVersion.mkdir();
- if (!newVersion.isDirectory()) {
- throw new IOException("Unable to make directory " + newVersion.getCanonicalPath());
- }
- if (!newVersion.setReadable(true, false)) {
- throw new IOException(
- "Failed to set " + newVersion.getCanonicalPath() + " readable");
- }
-
- // 4. Validate the log list json and move the file in <new_version>/ .
- installLogList(newVersion, inputStream);
-
- // 5. Create the temp symlink. We'll rename this to the target symlink to get an atomic
- // update.
- File tempSymlink = new File(updateDir, "new_symlink");
- try {
- Os.symlink(newVersion.getCanonicalPath(), tempSymlink.getCanonicalPath());
- } catch (ErrnoException e) {
- throw new IOException("Failed to create symlink", e);
- }
-
- // 6. Update the symlink target, this is the actual update step.
- tempSymlink.renameTo(currentSymlink.getAbsoluteFile());
- } catch (IOException | RuntimeException e) {
- FileUtils.deleteContentsAndDir(newVersion);
- throw e;
- }
- Slog.i(TAG, "CT log directory updated to " + newVersion.getAbsolutePath());
- // 7. Update the current version information
- writeUpdate(
- updateDir,
- updateVersion,
- new ByteArrayInputStream(Long.toString(version).getBytes()));
- // 8. Cleanup
- deleteOldLogDirectories();
- }
-
- @Override
- protected void postInstall(Context context, Intent intent) {
- if (!Flags.certificateTransparencyInstaller()) {
- return;
- }
- }
-
- private void installLogList(File directory, InputStream inputStream) throws IOException {
- try {
- byte[] content = Streams.readFullyNoClose(inputStream);
- if (new JSONObject(new String(content, StandardCharsets.UTF_8)).length() == 0) {
- throw new IOException("Log list data not valid");
- }
-
- File file = new File(directory, "log_list.json");
- try (FileOutputStream outputStream = new FileOutputStream(file)) {
- outputStream.write(content);
- }
- if (!file.setReadable(true, false)) {
- throw new IOException("Failed to set permissions on " + file.getCanonicalPath());
- }
- } catch (JSONException e) {
- throw new IOException("Malformed json in log list", e);
- }
- }
-
- private void deleteOldLogDirectories() throws IOException {
- if (!updateDir.exists()) {
- return;
- }
- File currentTarget = new File(updateDir, "current").getCanonicalFile();
- FileFilter filter =
- new FileFilter() {
- @Override
- public boolean accept(File file) {
- return !currentTarget.equals(file)
- && file.getName().startsWith(LOGDIR_PREFIX);
- }
- };
- for (File f : updateDir.listFiles(filter)) {
- FileUtils.deleteContentsAndDir(f);
- }
- }
-}
diff --git a/services/core/java/com/android/server/updates/flags.aconfig b/services/core/java/com/android/server/updates/flags.aconfig
deleted file mode 100644
index 476cb3723c97..000000000000
--- a/services/core/java/com/android/server/updates/flags.aconfig
+++ /dev/null
@@ -1,10 +0,0 @@
-package: "com.android.server.updates"
-container: "system"
-
-flag {
- name: "certificate_transparency_installer"
- is_exported: true
- namespace: "network_security"
- description: "Enable certificate transparency installer for log list data"
- bug: "319829948"
-}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 1a9d21187ddb..6709e3a72db0 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -25,6 +25,9 @@ import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES;
import static android.content.Context.KEYGUARD_SERVICE;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_HOME;
+import static android.content.Intent.CATEGORY_SECONDARY_HOME;
import static android.content.Intent.EXTRA_INTENT;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_TASK_ID;
@@ -40,6 +43,7 @@ import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.TaskInfo;
import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
@@ -67,6 +71,7 @@ import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
+import com.android.window.flags.Flags;
/**
* A class that contains activity intercepting logic for {@link ActivityStarter#execute()}
@@ -119,6 +124,11 @@ class ActivityStartInterceptor {
*/
TaskDisplayArea mPresumableLaunchDisplayArea;
+ /**
+ * Whether the component is specified originally in the given Intent.
+ */
+ boolean mComponentSpecified;
+
ActivityStartInterceptor(
ActivityTaskManagerService service, ActivityTaskSupervisor supervisor) {
this(service, supervisor, service.mContext);
@@ -185,6 +195,14 @@ class ActivityStartInterceptor {
return TaskFragment.fromTaskFragmentToken(taskFragToken, mService);
}
+ // TODO: consolidate this method with the one below since this is used for test only.
+ boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
+ Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid,
+ ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea) {
+ return intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, callingPid,
+ callingUid, activityOptions, presumableLaunchDisplayArea, false);
+ }
+
/**
* Intercept the launch intent based on various signals. If an interception happened the
* internal variables get assigned and need to be read explicitly by the caller.
@@ -193,7 +211,8 @@ class ActivityStartInterceptor {
*/
boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid,
- ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea) {
+ ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea,
+ boolean componentSpecified) {
mUserManager = UserManager.get(mServiceContext);
mIntent = intent;
@@ -206,6 +225,7 @@ class ActivityStartInterceptor {
mInTaskFragment = inTaskFragment;
mActivityOptions = activityOptions;
mPresumableLaunchDisplayArea = presumableLaunchDisplayArea;
+ mComponentSpecified = componentSpecified;
if (interceptQuietProfileIfNeeded()) {
// If work profile is turned off, skip the work challenge since the profile can only
@@ -230,7 +250,8 @@ class ActivityStartInterceptor {
}
if (interceptHomeIfNeeded()) {
// Replace primary home intents directed at displays that do not support primary home
- // but support secondary home with the relevant secondary home activity.
+ // but support secondary home with the relevant secondary home activity. Or the home
+ // intent is not in the correct format.
return true;
}
@@ -479,9 +500,78 @@ class ActivityStartInterceptor {
if (mPresumableLaunchDisplayArea == null || mService.mRootWindowContainer == null) {
return false;
}
- if (!ActivityRecord.isHomeIntent(mIntent)) {
- return false;
+
+ boolean intercepted = false;
+ if (Flags.normalizeHomeIntent()) {
+ if (!ACTION_MAIN.equals(mIntent.getAction()) || (!mIntent.hasCategory(CATEGORY_HOME)
+ && !mIntent.hasCategory(CATEGORY_SECONDARY_HOME))) {
+ // not a home intent
+ return false;
+ }
+
+ if (mComponentSpecified) {
+ final ComponentName homeComponent = mIntent.getComponent();
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfo = mService.mRootWindowContainer.resolveHomeActivity(
+ mUserId, homeIntent);
+ if (!aInfo.getComponentName().equals(homeComponent)) {
+ // Do nothing if the intent is not for the default home component.
+ return false;
+ }
+ }
+
+ if (!ActivityRecord.isHomeIntent(mIntent) || mComponentSpecified) {
+ // This is not a standard home intent, make it so if possible.
+ normalizeHomeIntent();
+ intercepted = true;
+ }
+ } else {
+ if (!ActivityRecord.isHomeIntent(mIntent)) {
+ return false;
+ }
+ }
+
+ intercepted |= replaceToSecondaryHomeIntentIfNeeded();
+ if (intercepted) {
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mResolvedType = null;
+
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, /* flags= */ 0,
+ mRealCallingUid, mRealCallingPid);
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, /*profilerInfo=*/
+ null);
+ }
+ return intercepted;
+ }
+
+ private void normalizeHomeIntent() {
+ Slog.w(TAG, "The home Intent is not correctly formatted");
+ if (mIntent.getCategories().size() > 1) {
+ Slog.d(TAG, "Purge home intent categories");
+ boolean isSecondaryHome = false;
+ final Object[] categories = mIntent.getCategories().toArray();
+ for (int i = categories.length - 1; i >= 0; i--) {
+ final String category = (String) categories[i];
+ if (CATEGORY_SECONDARY_HOME.equals(category)) {
+ isSecondaryHome = true;
+ }
+ mIntent.removeCategory(category);
+ }
+ mIntent.addCategory(isSecondaryHome ? CATEGORY_SECONDARY_HOME : CATEGORY_HOME);
+ }
+ if (mIntent.getType() != null || mIntent.getData() != null) {
+ Slog.d(TAG, "Purge home intent data/type");
+ mIntent.setType(null);
}
+ if (mComponentSpecified) {
+ Slog.d(TAG, "Purge home intent component, " + mIntent.getComponent());
+ mIntent.setComponent(null);
+ }
+ mIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ private boolean replaceToSecondaryHomeIntentIfNeeded() {
if (!mIntent.hasCategory(Intent.CATEGORY_HOME)) {
// Already a secondary home intent, leave it alone.
return false;
@@ -506,13 +596,6 @@ class ActivityStartInterceptor {
// and should not be moved to the caller's task. Also, activities cannot change their type,
// e.g. a standard activity cannot become a home activity.
mIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
- mCallingPid = mRealCallingPid;
- mCallingUid = mRealCallingUid;
- mResolvedType = null;
-
- mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, /* flags= */ 0,
- mRealCallingUid, mRealCallingPid);
- mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, /*profilerInfo=*/ null);
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index acb93844c945..0ab2ffe3e298 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1341,7 +1341,8 @@ class ActivityStarter {
callingPackage,
callingFeatureId);
if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
- callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) {
+ callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea,
+ request.componentSpecified)) {
// activity start was intercepted, e.g. because the target user is currently in quiet
// mode (turn off work) or the target application is suspended
intent = mInterceptor.mIntent;
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 0369a0ff4c76..9f88bc952351 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -164,15 +164,13 @@ final class AppCompatUtils {
appCompatTaskInfo.setIsFromLetterboxDoubleTap(reachabilityOverrides.isFromDoubleTap());
+ appCompatTaskInfo.topActivityAppBounds.set(getAppBounds(top));
final boolean isTopActivityLetterboxed = top.areBoundsLetterboxed();
appCompatTaskInfo.setTopActivityLetterboxed(isTopActivityLetterboxed);
if (isTopActivityLetterboxed) {
final Rect bounds = top.getBounds();
- final Rect appBounds = getAppBounds(top);
appCompatTaskInfo.topActivityLetterboxWidth = bounds.width();
appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
- appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
- appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
// TODO(b/379824541) Remove duplicate information.
appCompatTaskInfo.topActivityLetterboxBounds = bounds;
// We need to consider if letterboxed or pillarboxed.
@@ -281,8 +279,7 @@ final class AppCompatUtils {
info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
- info.topActivityLetterboxAppHeight = TaskInfo.PROPERTY_VALUE_UNSET;
- info.topActivityLetterboxAppWidth = TaskInfo.PROPERTY_VALUE_UNSET;
+ info.topActivityAppBounds.setEmpty();
info.topActivityLetterboxBounds = null;
info.cameraCompatTaskInfo.freeformCameraCompatMode =
CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_UNSPECIFIED;
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index dd1af0a497ca..6b6f0111305c 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -29,9 +29,6 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-
-import com.android.internal.R;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -687,11 +684,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume
@Override
public Animation getFadeInAnimation() {
+ final Animation anim = super.getFadeInAnimation();
if (mHasScreenRotationAnimation) {
// Use a shorter animation so it is easier to align with screen rotation animation.
- return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
+ anim.setDuration(getScaledDuration(SHORT_DURATION_MS));
}
- return super.getFadeInAnimation();
+ return anim;
}
@Override
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 8eccffd8fe3b..a4e58ef923b8 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -368,6 +368,15 @@ final class ContentRecorder implements WindowContainerListener {
return;
}
+ final SurfaceControl sourceSurface = mRecordedWindowContainer.getSurfaceControl();
+ if (sourceSurface == null || !sourceSurface.isValid()) {
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Unable to start recording for display %d since the "
+ + "surface is null or have been released.",
+ mDisplayContent.getDisplayId());
+ return;
+ }
+
final int contentToRecord = mContentRecordingSession.getContentToRecord();
// TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are inaccurate.
@@ -395,8 +404,7 @@ final class ContentRecorder implements WindowContainerListener {
mDisplayContent.getDisplayId(), mDisplayContent.getDisplayInfo().state);
// Create a mirrored hierarchy for the SurfaceControl of the DisplayArea to capture.
- mRecordedSurface = SurfaceControl.mirrorSurface(
- mRecordedWindowContainer.getSurfaceControl());
+ mRecordedSurface = SurfaceControl.mirrorSurface(sourceSurface);
SurfaceControl.Transaction transaction =
mDisplayContent.mWmService.mTransactionFactory.get()
// Set the mMirroredSurface's parent to the root SurfaceControl for this
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f8086615b7d1..4dd950ba6ee9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2908,6 +2908,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
&& !mDisplayRotation.isRotatingSeamlessly()) {
clearFixedRotationLaunchingApp();
}
+ // If there won't be a transition to notify the launch is done, then it should be ready to
+ // update with display orientation. E.g. a translucent activity enters pip from a task which
+ // contains another opaque activity.
+ if (mFixedRotationLaunchingApp != null && mFixedRotationLaunchingApp.isVisible()
+ && !mTransitionController.isCollecting()
+ && !mAtmService.mBackNavigationController.isMonitoringFinishTransition()) {
+ final Transition finishTransition = mTransitionController.mFinishingTransition;
+ if (finishTransition == null || !finishTransition.mParticipants.contains(
+ mFixedRotationLaunchingApp)) {
+ continueUpdateOrientationForDiffOrienLaunchingApp();
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/FadeAnimationController.java b/services/core/java/com/android/server/wm/FadeAnimationController.java
index 7af67e63f469..c60d3677319a 100644
--- a/services/core/java/com/android/server/wm/FadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeAnimationController.java
@@ -20,41 +20,51 @@ import static com.android.server.wm.AnimationSpecProto.WINDOW;
import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
import android.annotation.NonNull;
-import android.content.Context;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
import android.view.animation.Transformation;
-import com.android.internal.R;
-
import java.io.PrintWriter;
/**
* An animation controller to fade-in/out for a window token.
*/
public class FadeAnimationController {
+ static final int SHORT_DURATION_MS = 200;
+ static final int MEDIUM_DURATION_MS = 350;
+
protected final DisplayContent mDisplayContent;
- protected final Context mContext;
public FadeAnimationController(DisplayContent displayContent) {
mDisplayContent = displayContent;
- mContext = displayContent.mWmService.mContext;
}
/**
* @return a fade-in Animation.
*/
public Animation getFadeInAnimation() {
- return AnimationUtils.loadAnimation(mContext, R.anim.fade_in);
+ final AlphaAnimation anim = new AlphaAnimation(0f, 1f);
+ anim.setDuration(getScaledDuration(MEDIUM_DURATION_MS));
+ anim.setInterpolator(new DecelerateInterpolator());
+ return anim;
}
/**
* @return a fade-out Animation.
*/
public Animation getFadeOutAnimation() {
- return AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
+ final AlphaAnimation anim = new AlphaAnimation(1f, 0f);
+ anim.setDuration(getScaledDuration(SHORT_DURATION_MS));
+ anim.setInterpolator(new AccelerateInterpolator());
+ return anim;
+ }
+
+ long getScaledDuration(int durationMs) {
+ return (long) (durationMs * mDisplayContent.mWmService.getWindowAnimationScaleLocked());
}
/** Run the fade in/out animation for a window token. */
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 98521d36ad44..dede7676a4b6 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -21,6 +21,7 @@ jiamingliu@google.com
pdwilliams@google.com
charlesccchen@google.com
marziana@google.com
+mcarli@google.com
# Files related to background activity launches
per-file Background*Start* = set noparent
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 7fdc2c67b5ce..44f000da3d73 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1515,9 +1515,9 @@ class RecentTasks {
boolean skipExcludedCheck) {
if (!skipExcludedCheck) {
// Keep the most recent task of home display even if it is excluded from recents.
- final boolean isExcludeFromRecents =
- (task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
- == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+ final boolean isExcludeFromRecents = task.getBaseIntent() != null
+ && (task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
if (isExcludeFromRecents) {
if (DEBUG_RECENTS_TRIM_TASKS) {
Slog.d(TAG,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 76d8861022bb..9062afb50acb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3652,14 +3652,6 @@ class Task extends TaskFragment {
// If the developer has persist a different configuration, we need to override it to the
// starting window because persisted configuration does not effect to Task.
info.taskInfo.configuration.setTo(activity.getConfiguration());
- if (!Flags.drawSnapshotAspectRatioMatch()) {
- final WindowState mainWindow = getTopFullscreenMainWindow();
- if (mainWindow != null) {
- info.topOpaqueWindowInsetsState =
- mainWindow.getInsetsStateWithVisibilityOverride();
- info.topOpaqueWindowLayoutParams = mainWindow.getAttrs();
- }
- }
return info;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9d9c53dfe0f4..db62cebf7603 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2945,7 +2945,7 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
if (callingPid != MY_PID) {
- throw new WindowManager.InvalidDisplayException(
+ throw new IllegalArgumentException(
"attachWindowContextToDisplayContent: trying to attach to a"
+ " non-existing display:" + displayId);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 01639cc3b516..d26539c377a9 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -80,6 +80,7 @@ cc_library_static {
":lib_oomConnection_native",
":lib_anrTimer_native",
":lib_lazilyRegisteredServices_native",
+ ":lib_phantomProcessList_native",
],
include_dirs: [
@@ -265,3 +266,10 @@ filegroup {
"com_android_server_vr_VrManagerService.cpp",
],
}
+
+filegroup {
+ name: "lib_phantomProcessList_native",
+ srcs: [
+ "com_android_server_am_PhantomProcessList.cpp",
+ ],
+}
diff --git a/services/core/jni/com_android_server_am_PhantomProcessList.cpp b/services/core/jni/com_android_server_am_PhantomProcessList.cpp
new file mode 100644
index 000000000000..0c5e6d85919a
--- /dev/null
+++ b/services/core/jni/com_android_server_am_PhantomProcessList.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <processgroup/processgroup.h>
+
+namespace android {
+namespace {
+
+jstring getCgroupProcsPath(JNIEnv* env, jobject clazz, jint uid, jint pid) {
+ if (uid < 0) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "uid is negative: %d", uid);
+ return nullptr;
+ }
+
+ std::string path;
+ if (!CgroupGetAttributePathForProcess("CgroupProcs", uid, pid, path)) {
+ path.clear();
+ }
+
+ return env->NewStringUTF(path.c_str());
+}
+
+const JNINativeMethod sMethods[] = {
+ {"nativeGetCgroupProcsPath", "(II)Ljava/lang/String;", (void*)getCgroupProcsPath},
+};
+
+} // anonymous namespace
+
+int register_android_server_am_PhantomProcessList(JNIEnv* env) {
+ const char* className = "com/android/server/am/PhantomProcessList";
+ return jniRegisterNativeMethods(env, className, sMethods, NELEM(sMethods));
+}
+
+} // namespace android \ No newline at end of file
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 04642302ce45..2403934cbeb8 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -553,6 +553,9 @@ private:
PointerIcon loadPointerIcon(JNIEnv* env, ui::LogicalDisplayId displayId, PointerIconStyle type);
bool isDisplayInteractive(ui::LogicalDisplayId displayId);
+ // TODO(b/362719483) remove when the real topology is available
+ void populateFakeDisplayTopology(const std::vector<DisplayViewport>& viewports);
+
static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); }
};
@@ -641,6 +644,49 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
mInputManager->getChoreographer().setDisplayViewports(viewports);
mInputManager->getReader().requestRefreshConfiguration(
InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // TODO(b/362719483) remove when the real topology is available
+ populateFakeDisplayTopology(viewports);
+}
+
+void NativeInputManager::populateFakeDisplayTopology(
+ const std::vector<DisplayViewport>& viewports) {
+ if (!com::android::input::flags::connected_displays_cursor()) {
+ return;
+ }
+
+ // create a fake topology assuming following order
+ // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ...
+ // This also adds a 100px offset on corresponding edge for better manual testing
+ // ┌────────┐
+ // │ next ├─────────┐
+ // ┌─└───────┐┤ next 2 │ ...
+ // │ default │└─────────┘
+ // └─────────┘
+ DisplayTopologyGraph displaytopology;
+ displaytopology.primaryDisplayId = ui::LogicalDisplayId::DEFAULT;
+
+ // treat default display as base, in real topology it should be the primary-display
+ ui::LogicalDisplayId previousDisplay = ui::LogicalDisplayId::DEFAULT;
+ for (const auto& viewport : viewports) {
+ if (viewport.displayId == ui::LogicalDisplayId::DEFAULT) {
+ continue;
+ }
+ if (previousDisplay == ui::LogicalDisplayId::DEFAULT) {
+ displaytopology.graph[previousDisplay].push_back(
+ {viewport.displayId, DisplayTopologyPosition::TOP, 100});
+ displaytopology.graph[viewport.displayId].push_back(
+ {previousDisplay, DisplayTopologyPosition::BOTTOM, -100});
+ } else {
+ displaytopology.graph[previousDisplay].push_back(
+ {viewport.displayId, DisplayTopologyPosition::RIGHT, 100});
+ displaytopology.graph[viewport.displayId].push_back(
+ {previousDisplay, DisplayTopologyPosition::LEFT, -100});
+ }
+ previousDisplay = viewport.displayId;
+ }
+
+ mInputManager->getChoreographer().setDisplayTopology(displaytopology);
}
void NativeInputManager::setDisplayTopology(JNIEnv* env, jobject topologyGraph) {
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index e3bd69c30de7..569383e71a06 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -72,6 +72,7 @@ int register_com_android_server_display_DisplayControl(JNIEnv* env);
int register_com_android_server_SystemClockTime(JNIEnv* env);
int register_android_server_display_smallAreaDetectionController(JNIEnv* env);
int register_com_android_server_accessibility_BrailleDisplayConnection(JNIEnv* env);
+int register_android_server_am_PhantomProcessList(JNIEnv* env);
// Note: Consider adding new JNI entrypoints for optional services to
// LazyJniRegistrar instead, and relying on lazy registration.
@@ -139,5 +140,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_com_android_server_SystemClockTime(env);
register_android_server_display_smallAreaDetectionController(env);
register_com_android_server_accessibility_BrailleDisplayConnection(env);
+ register_android_server_am_PhantomProcessList(env);
return JNI_VERSION_1_4;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 60130d1f97be..f10b7b9a95a4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -91,6 +91,7 @@ import android.server.ServerProtoEnums;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
+import android.tracing.perfetto.InitArguments;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Dumpable;
@@ -792,6 +793,12 @@ public final class SystemServer implements Dumpable {
private void run() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
try {
+ if (android.tracing.Flags.systemServerLargePerfettoShmemBuffer()) {
+ // Explicitly initialize a 4 MB shmem buffer for Perfetto producers (b/382369925)
+ android.tracing.perfetto.Producer.init(new InitArguments(
+ InitArguments.PERFETTO_BACKEND_SYSTEM, 4 * 1024));
+ }
+
t.traceBegin("InitBeforeStartServices");
// Record the process start information in sys props.
@@ -3114,10 +3121,10 @@ public final class SystemServer implements Dumpable {
if (com.android.ranging.flags.Flags.rangingStackEnabled()) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)
|| context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)
+ PackageManager.FEATURE_WIFI_AWARE)
|| (com.android.ranging.flags.Flags.rangingCsEnabled()
&& context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING))) {
+ PackageManager.FEATURE_BLUETOOTH_LE))) {
t.traceBegin("RangingService");
// TODO: b/375264320 - Remove after RELEASE_RANGING_STACK is ramped to next.
try {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index c31594a4bf47..fc585c9e0f96 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -16,6 +16,8 @@
package com.android.server.profcollect;
+import static android.content.Intent.ACTION_BATTERY_LOW;
+import static android.content.Intent.ACTION_BATTERY_OKAY;
import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_SCREEN_ON;
@@ -77,6 +79,7 @@ public final class ProfcollectForwardingService extends SystemService {
static boolean sVerityEnforced;
static boolean sIsInteractive;
static boolean sAdbActive;
+ static boolean sIsBatteryLow;
private static IProfCollectd sIProfcollect;
private static ProfcollectForwardingService sSelfService;
@@ -91,7 +94,11 @@ public final class ProfcollectForwardingService extends SystemService {
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (ACTION_SCREEN_ON.equals(intent.getAction())) {
+ if (ACTION_BATTERY_LOW.equals(intent.getAction())) {
+ sIsBatteryLow = true;
+ } else if (ACTION_BATTERY_OKAY.equals(intent.getAction())) {
+ sIsBatteryLow = false;
+ } else if (ACTION_SCREEN_ON.equals(intent.getAction())) {
Log.d(LOG_TAG, "Received broadcast that the device became interactive, was "
+ sIsInteractive);
sIsInteractive = true;
@@ -141,6 +148,8 @@ public final class ProfcollectForwardingService extends SystemService {
context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled);
final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_BATTERY_LOW);
+ filter.addAction(ACTION_BATTERY_OKAY);
filter.addAction(ACTION_SCREEN_ON);
filter.addAction(ACTION_SCREEN_OFF);
filter.addAction(INTENT_UPLOAD_PROFILES);
diff --git a/services/profcollect/src/com/android/server/profcollect/Utils.java b/services/profcollect/src/com/android/server/profcollect/Utils.java
index b754ca1875b6..c109f5cf05b6 100644
--- a/services/profcollect/src/com/android/server/profcollect/Utils.java
+++ b/services/profcollect/src/com/android/server/profcollect/Utils.java
@@ -118,6 +118,7 @@ final class Utils {
}
return ProfcollectForwardingService.sVerityEnforced
&& !ProfcollectForwardingService.sAdbActive
- && ProfcollectForwardingService.sIsInteractive;
+ && ProfcollectForwardingService.sIsInteractive
+ && !ProfcollectForwardingService.sIsBatteryLow;
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index f96294ed4ca8..b7b4f0424165 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -214,7 +214,8 @@ public class DisplayManagerServiceTest {
private static final String PACKAGE_NAME = "com.android.frameworks.displayservicetests";
private static final long STANDARD_DISPLAY_EVENTS =
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
private static final long STANDARD_AND_CONNECTION_DISPLAY_EVENTS =
STANDARD_DISPLAY_EVENTS
@@ -222,7 +223,7 @@ public class DisplayManagerServiceTest {
private static final String EVENT_DISPLAY_ADDED = "EVENT_DISPLAY_ADDED";
private static final String EVENT_DISPLAY_REMOVED = "EVENT_DISPLAY_REMOVED";
- private static final String EVENT_DISPLAY_CHANGED = "EVENT_DISPLAY_CHANGED";
+ private static final String EVENT_DISPLAY_BASIC_CHANGED = "EVENT_DISPLAY_BASIC_CHANGED";
private static final String EVENT_DISPLAY_BRIGHTNESS_CHANGED =
"EVENT_DISPLAY_BRIGHTNESS_CHANGED";
private static final String EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED =
@@ -889,7 +890,6 @@ public class DisplayManagerServiceTest {
FakeDisplayManagerCallback callback = registerDisplayListenerCallback(
displayManager, bs, displayDevice);
-
// Simulate DisplayDevice change
DisplayDeviceInfo displayDeviceInfo2 = new DisplayDeviceInfo();
displayDeviceInfo2.copyFrom(displayDeviceInfo);
@@ -900,7 +900,8 @@ public class DisplayManagerServiceTest {
Handler handler = displayManager.getDisplayHandler();
waitForIdleHandler(handler);
- assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_BASIC_CHANGED,
+ EVENT_DISPLAY_REFRESH_RATE_CHANGED);
}
/**
@@ -2145,7 +2146,7 @@ public class DisplayManagerServiceTest {
new DisplayEventReceiver.FrameRateOverride(myUid, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_BASIC_CHANGED);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -2154,7 +2155,7 @@ public class DisplayManagerServiceTest {
new DisplayEventReceiver.FrameRateOverride(1234, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_BASIC_CHANGED);
updateFrameRateOverride(displayManager, displayDevice,
new DisplayEventReceiver.FrameRateOverride[]{
@@ -2163,7 +2164,7 @@ public class DisplayManagerServiceTest {
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_BASIC_CHANGED);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -2172,7 +2173,7 @@ public class DisplayManagerServiceTest {
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_BASIC_CHANGED);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -2180,7 +2181,7 @@ public class DisplayManagerServiceTest {
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_BASIC_CHANGED);
}
/**
@@ -2303,16 +2304,16 @@ public class DisplayManagerServiceTest {
updateRenderFrameRate(displayManager, displayDevice, 30f);
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_REFRESH_RATE_CHANGED);
callback.clear();
updateRenderFrameRate(displayManager, displayDevice, 30f);
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_REFRESH_RATE_CHANGED);
updateRenderFrameRate(displayManager, displayDevice, 20f);
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_REFRESH_RATE_CHANGED);
callback.clear();
}
@@ -3888,7 +3889,7 @@ public class DisplayManagerServiceTest {
observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
waitForIdleHandler(handler);
- assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_BASIC_CHANGED);
}
@Test
@@ -3919,7 +3920,7 @@ public class DisplayManagerServiceTest {
observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
waitForIdleHandler(handler);
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_BASIC_CHANGED);
}
private void initDisplayPowerController(DisplayManagerInternal localService) {
@@ -4389,8 +4390,8 @@ public class DisplayManagerServiceTest {
return EVENT_DISPLAY_ADDED;
case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED:
return EVENT_DISPLAY_REMOVED;
- case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
- return EVENT_DISPLAY_CHANGED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED:
+ return EVENT_DISPLAY_BASIC_CHANGED;
case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED:
return EVENT_DISPLAY_BRIGHTNESS_CHANGED;
case DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 5d427139a857..c65024f8f9d5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -26,6 +26,7 @@ import org.junit.Test
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -43,7 +44,7 @@ class DisplayTopologyCoordinatorTest {
@Before
fun setUp() {
- displayInfo.displayId = 2
+ displayInfo.displayId = Display.DEFAULT_DISPLAY
displayInfo.logicalWidth = 300
displayInfo.logicalHeight = 200
displayInfo.logicalDensityDpi = 100
@@ -90,6 +91,44 @@ class DisplayTopologyCoordinatorTest {
}
@Test
+ fun updateDisplay() {
+ whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
+ .thenReturn(true)
+
+ coordinator.onDisplayChanged(displayInfo)
+
+ verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ }
+
+ @Test
+ fun updateDisplay_notChanged() {
+ whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
+ .thenReturn(false)
+
+ coordinator.onDisplayChanged(displayInfo)
+
+ verify(mockTopologyChangedCallback, never()).invoke(any())
+ }
+
+ @Test
+ fun removeDisplay() {
+ whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(true)
+
+ coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
+
+ verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ }
+
+ @Test
+ fun removeDisplay_notChanged() {
+ whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(false)
+
+ coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
+
+ verify(mockTopologyChangedCallback, never()).invoke(any())
+ }
+
+ @Test
fun getTopology_copy() {
assertThat(coordinator.topology).isEqualTo(mockTopologyCopy)
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index ad30f22fe060..0dbb6ba58b3c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -36,9 +36,9 @@ import static com.android.server.display.DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DE
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CONNECTED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_DISCONNECTED;
-import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
+import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN;
import static com.android.server.utils.FoldSettingProvider.SETTING_VALUE_SELECTIVE_STAY_AWAKE;
@@ -1170,18 +1170,20 @@ public class LogicalDisplayMapperTest {
@Test
public void updateAndGetMaskForDisplayPropertyChanges_getsPropertyChangedFlags() {
- // Change the display state
+ // Change the refresh rate override
DisplayInfo newDisplayInfo = new DisplayInfo();
- newDisplayInfo.state = STATE_OFF;
- assertEquals(LOGICAL_DISPLAY_EVENT_STATE_CHANGED,
+ newDisplayInfo.refreshRateOverride = 30;
+ assertEquals(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED,
mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
- // Change the refresh rate override
+ // Change the display state
+ when(mFlagsMock.isDisplayListenerPerformanceImprovementsEnabled()).thenReturn(true);
newDisplayInfo = new DisplayInfo();
- newDisplayInfo.refreshRateOverride = 30;
- assertEquals(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED,
+ newDisplayInfo.state = STATE_OFF;
+ assertEquals(LOGICAL_DISPLAY_EVENT_STATE_CHANGED,
mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
+
// Change multiple properties
newDisplayInfo = new DisplayInfo();
newDisplayInfo.refreshRateOverride = 30;
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index 94d4b9522d60..03bd73c52083 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -24,6 +24,7 @@ cc_library_shared {
":lib_freezer_native",
":lib_oomConnection_native",
":lib_lazilyRegisteredServices_native",
+ ":lib_phantomProcessList_native",
"onload.cpp",
],
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
index 9b4c8178b092..30fa7de94af1 100644
--- a/services/tests/mockingservicestests/jni/onload.cpp
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -28,6 +28,7 @@ int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_Freezer(JNIEnv* env);
int register_android_server_am_OomConnection(JNIEnv* env);
int register_android_server_utils_LazyJniRegistrar(JNIEnv* env);
+int register_android_server_am_PhantomProcessList(JNIEnv* env);
};
using namespace android;
@@ -46,5 +47,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_am_Freezer(env);
register_android_server_am_OomConnection(env);
register_android_server_utils_LazyJniRegistrar(env);
+ register_android_server_am_PhantomProcessList(env);
return JNI_VERSION_1_4;
}
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 4b2e850d08e7..35f421e582d8 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -70,9 +70,12 @@ import android.os.PerformanceHintManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SessionCreationConfig;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
import com.android.server.FgThread;
@@ -89,6 +92,8 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -160,6 +165,8 @@ public class HintManagerServiceTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private HintManagerService mService;
private ChannelConfig mConfig;
@@ -1322,6 +1329,7 @@ public class HintManagerServiceTest {
@Test
+ @EnableFlags({Flags.FLAG_CPU_HEADROOM_AFFINITY_CHECK})
public void testCpuHeadroomCache() throws Exception {
CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal();
CpuHeadroomParams halParams1 = new CpuHeadroomParams();
@@ -1335,11 +1343,14 @@ public class HintManagerServiceTest {
halParams2.calculationType = CpuHeadroomParams.CalculationType.MIN;
halParams2.tids = new int[]{};
+ CountDownLatch latch = new CountDownLatch(2);
+ int[] tids = createThreads(2, latch);
CpuHeadroomParamsInternal params3 = new CpuHeadroomParamsInternal();
+ params3.tids = tids;
params3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
CpuHeadroomParams halParams3 = new CpuHeadroomParams();
+ halParams3.tids = tids;
halParams3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
- halParams3.tids = new int[]{Process.myPid()};
// this params should not be cached as the window is not default
CpuHeadroomParamsInternal params4 = new CpuHeadroomParamsInternal();
@@ -1411,6 +1422,65 @@ public class HintManagerServiceTest {
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2));
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams3));
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4));
+ latch.countDown();
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_CPU_HEADROOM_AFFINITY_CHECK})
+ public void testGetCpuHeadroomDifferentAffinity_flagOn() throws Exception {
+ CountDownLatch latch = new CountDownLatch(2);
+ int[] tids = createThreads(2, latch);
+ CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal();
+ params.tids = tids;
+ CpuHeadroomParams halParams = new CpuHeadroomParams();
+ halParams.tids = tids;
+ float headroom = 0.1f;
+ CpuHeadroomResult halRet = CpuHeadroomResult.globalHeadroom(headroom);
+ String ret1 = runAndWaitForCommand("taskset -p 1 " + tids[0]);
+ String ret2 = runAndWaitForCommand("taskset -p 3 " + tids[1]);
+
+ HintManagerService service = createService();
+ clearInvocations(mIPowerMock);
+ when(mIPowerMock.getCpuHeadroom(eq(halParams))).thenReturn(halRet);
+ assertThrows("taskset cmd return: " + ret1 + "\n" + ret2, IllegalStateException.class,
+ () -> service.getBinderServiceInstance().getCpuHeadroom(params));
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+ }
+
+ @Test
+ @DisableFlags({Flags.FLAG_CPU_HEADROOM_AFFINITY_CHECK})
+ public void testGetCpuHeadroomDifferentAffinity_flagOff() throws Exception {
+ CountDownLatch latch = new CountDownLatch(2);
+ int[] tids = createThreads(2, latch);
+ CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal();
+ params.tids = tids;
+ CpuHeadroomParams halParams = new CpuHeadroomParams();
+ halParams.tids = tids;
+ float headroom = 0.1f;
+ CpuHeadroomResult halRet = CpuHeadroomResult.globalHeadroom(headroom);
+ String ret1 = runAndWaitForCommand("taskset -p 1 " + tids[0]);
+ String ret2 = runAndWaitForCommand("taskset -p 3 " + tids[1]);
+
+ HintManagerService service = createService();
+ clearInvocations(mIPowerMock);
+ when(mIPowerMock.getCpuHeadroom(eq(halParams))).thenReturn(halRet);
+ assertEquals("taskset cmd return: " + ret1 + "\n" + ret2, halRet,
+ service.getBinderServiceInstance().getCpuHeadroom(params));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(any());
+ }
+
+ private String runAndWaitForCommand(String command) throws Exception {
+ java.lang.Process process = Runtime.getRuntime().exec(command);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String line;
+ StringBuilder res = new StringBuilder();
+ while ((line = reader.readLine()) != null) {
+ res.append(line);
+ }
+ process.waitFor();
+ // somehow the exit code can be 1 for the taskset command though it exits successfully,
+ // thus we just return the output
+ return res.toString();
}
@Test
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 96741e0b1e87..469bd66b7e7b 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -21,6 +21,7 @@ import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -889,6 +890,32 @@ public class NotifierTest {
"my.package.name", false, null, null);
}
+ @Test
+ public void getWakelockMonitorTypeForLogging_evaluatesWakelockLevel() {
+ createNotifier();
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.SCREEN_DIM_WAKE_LOCK),
+ PowerManager.FULL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK), PowerManager.FULL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.DRAW_WAKE_LOCK),
+ PowerManager.DRAW_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.PARTIAL_WAKE_LOCK),
+ PowerManager.PARTIAL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK),
+ PowerManager.PARTIAL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.DOZE_WAKE_LOCK), -1);
+
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
+ .thenReturn(true);
+
+ createNotifier();
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK), -1);
+ }
+
private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 2fe6918630f6..7dbbff22a537 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -33,6 +33,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.server.am.UserController.CLEAR_USER_JOURNEY_SESSION_MSG;
import static com.android.server.am.UserController.COMPLETE_USER_SWITCH_MSG;
import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS;
import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
@@ -94,6 +95,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
@@ -181,14 +183,12 @@ public class UserControllerTest {
Intent.ACTION_USER_STARTING);
private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
- 0, // for startUserInternalOnHandler
REPORT_USER_SWITCH_MSG,
USER_SWITCH_TIMEOUT_MSG,
USER_START_MSG,
USER_CURRENT_MSG);
private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
- 0, // for startUserInternalOnHandler
USER_START_MSG,
REPORT_LOCKED_BOOT_COMPLETE_MSG);
@@ -376,7 +376,7 @@ public class UserControllerTest {
// and the cascade effect goes on...). In fact, a better approach would to not assert the
// binder calls, but their side effects (in this case, that the user is stopped right away)
assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
- .containsExactly(/* for startUserInternalOnHandler */ 0, USER_START_MSG);
+ .containsExactly(USER_START_MSG);
}
private void startUserAssertions(
@@ -419,17 +419,12 @@ public class UserControllerTest {
@Test
public void testDispatchUserSwitch() throws RemoteException {
// Prepare mock observer and register it
- IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
- when(observer.asBinder()).thenReturn(new Binder());
- doAnswer(invocation -> {
- IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
- callback.sendResult(null);
- return null;
- }).when(observer).onUserSwitching(anyInt(), any());
- mUserController.registerUserSwitchObserver(observer, "mock");
+ IUserSwitchObserver observer = registerUserSwitchObserver(
+ /* replyToOnBeforeUserSwitchingCallback= */ true,
+ /* replyToOnUserSwitchingCallback= */ true);
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
- verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID));
+ verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID), any());
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
assertNotNull(reportMsg);
UserState userState = (UserState) reportMsg.obj;
@@ -453,14 +448,26 @@ public class UserControllerTest {
}
@Test
+ public void testShouldCrashWhenOnBeforeUserSwitchingTimeouts() throws RemoteException {
+ IUserSwitchObserver observer = registerUserSwitchObserver(
+ /* replyToOnBeforeUserSwitchingCallback= */ false,
+ /* replyToOnUserSwitchingCallback= */ true);
+ mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
+ verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID), any());
+ assertThrows("Should have crashed when observers don't reply to onBeforeUserSwitching in "
+ + DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS + " ms", RuntimeException.class,
+ mInjector.mHandler::runPendingCallbacks);
+ }
+
+ @Test
public void testDispatchUserSwitchBadReceiver() throws RemoteException {
- // Prepare mock observer which doesn't notify the callback and register it
- IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
- when(observer.asBinder()).thenReturn(new Binder());
- mUserController.registerUserSwitchObserver(observer, "mock");
+ // Prepare mock observer which doesn't notify the onUserSwitching callback and register it
+ IUserSwitchObserver observer = registerUserSwitchObserver(
+ /* replyToOnBeforeUserSwitchingCallback= */ true,
+ /* replyToOnUserSwitchingCallback= */ false);
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
- verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID));
+ verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID), any());
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
assertNotNull(reportMsg);
UserState userState = (UserState) reportMsg.obj;
@@ -551,7 +558,6 @@ public class UserControllerTest {
expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG);
if (backgroundUserStopping) {
expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG);
- expectedCodes.add(0); // this is for directly posting in stopping.
}
if (expectScheduleBackgroundUserStopping) {
expectedCodes.add(SCHEDULED_STOP_BACKGROUND_USER_MSG);
@@ -567,9 +573,9 @@ public class UserControllerTest {
@Test
public void testDispatchUserSwitchComplete() throws RemoteException {
// Prepare mock observer and register it
- IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
- when(observer.asBinder()).thenReturn(new Binder());
- mUserController.registerUserSwitchObserver(observer, "mock");
+ IUserSwitchObserver observer = registerUserSwitchObserver(
+ /* replyToOnBeforeUserSwitchingCallback= */ true,
+ /* replyToOnUserSwitchingCallback= */ true);
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -1752,6 +1758,29 @@ public class UserControllerTest {
verify(mInjector, never()).onSystemUserVisibilityChanged(anyBoolean());
}
+ private IUserSwitchObserver registerUserSwitchObserver(
+ boolean replyToOnBeforeUserSwitchingCallback, boolean replyToOnUserSwitchingCallback)
+ throws RemoteException {
+ IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
+ when(observer.asBinder()).thenReturn(new Binder());
+ if (replyToOnBeforeUserSwitchingCallback) {
+ doAnswer(invocation -> {
+ IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
+ callback.sendResult(null);
+ return null;
+ }).when(observer).onBeforeUserSwitching(anyInt(), any());
+ }
+ if (replyToOnUserSwitchingCallback) {
+ doAnswer(invocation -> {
+ IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
+ callback.sendResult(null);
+ return null;
+ }).when(observer).onUserSwitching(anyInt(), any());
+ }
+ mUserController.registerUserSwitchObserver(observer, "mock");
+ return observer;
+ }
+
// Should be public to allow mocking
private static class TestInjector extends UserController.Injector {
public final TestHandler mHandler;
@@ -1957,6 +1986,7 @@ public class UserControllerTest {
* fix this, but in the meantime, this is your warning.
*/
private final List<Message> mMessages = new ArrayList<>();
+ private final List<Runnable> mPendingCallbacks = new ArrayList<>();
TestHandler(Looper looper) {
super(looper);
@@ -1989,14 +2019,24 @@ public class UserControllerTest {
@Override
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- Message copy = new Message();
- copy.copyFrom(msg);
- mMessages.add(copy);
- if (msg.getCallback() != null) {
- msg.getCallback().run();
+ if (msg.getCallback() == null) {
+ Message copy = new Message();
+ copy.copyFrom(msg);
+ mMessages.add(copy);
+ } else {
+ if (SystemClock.uptimeMillis() >= uptimeMillis) {
+ msg.getCallback().run();
+ } else {
+ mPendingCallbacks.add(msg.getCallback());
+ }
msg.setCallback(null);
}
return super.sendMessageAtTime(msg, uptimeMillis);
}
+
+ private void runPendingCallbacks() {
+ mPendingCallbacks.forEach(Runnable::run);
+ mPendingCallbacks.clear();
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
index 9c61d95bc5e5..9528a05d38a0 100644
--- a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
@@ -23,13 +23,13 @@ import android.test.AndroidTestCase;
import android.util.Pair;
import android.util.Xml;
-import com.android.internal.util.FastXmlSerializer;
import com.android.modules.utils.TypedXmlSerializer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
@@ -123,8 +123,24 @@ public class CacheQuotaStrategyTest extends AndroidTestCase {
buildCacheQuotaHint("uuid2", 10, 250));
}
+ @Test
+ public void testReadInvalidInput() throws Exception {
+ String input = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
+ "<cache-info previousBytes=\"1000\">\n"
+ + "<quota/>\n"
+ + "</cache-info>\n";
+
+ try {
+ CacheQuotaStrategy.readFromXml(new ByteArrayInputStream(
+ input.getBytes("UTF-8")));
+ fail("Expected XML parsing exception");
+ } catch (XmlPullParserException e) {
+ // Expected XmlPullParserException exception
+ }
+ }
+
private CacheQuotaHint buildCacheQuotaHint(String volumeUuid, int uid, long quota) {
return new CacheQuotaHint.Builder()
.setVolumeUuid(volumeUuid).setUid(uid).setQuota(quota).build();
}
-} \ No newline at end of file
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index decbaacdcef9..d1dc8d6e81c8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -274,6 +274,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
assertEquals(new ArraySet<>(), approved.get(true));
}
+ @SuppressWarnings("GuardedBy")
@Test
public void testReadXml_userDisabled_restore() throws Exception {
String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
@@ -289,7 +290,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
mAssistants.readXml(parser, mNm::canUseManagedServices, true,
ActivityManager.getCurrentUser());
- ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0);
+ ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(
+ ActivityManager.getCurrentUser());
// approved should not be null
assertNotNull(approved);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 20f4bb65d27b..601023f89656 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -17683,4 +17683,145 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertThat(mService.mNotificationList).isEmpty();
}
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testUnbundleNotification_ungrouped_restoresOriginalChannel() throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+
+ // Post a single notification
+ final boolean hasOriginalSummary = false;
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ final String keyToUnbundle = r.getKey();
+ mService.addNotification(r);
+
+ // Classify notification into the NEWS bundle
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment = new Adjustment(
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r.applyAdjustments();
+ // Check that the NotificationRecord channel is updated
+ assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
+ // Check that the Notification mChannelId is not updated
+ assertThat(r.getNotification().getChannelId()).isEqualTo(TEST_CHANNEL_ID);
+
+ // Unbundle the notification
+ mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+
+ // Check that the original channel was restored
+ assertThat(r.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+ verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(r), eq(hasOriginalSummary));
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testUnbundleNotification_grouped_restoresOriginalChannel() throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+
+ // Post grouped notifications
+ final String originalGroupName = "originalGroup";
+ final int summaryId = 0;
+ final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 1, originalGroupName, false);
+ mService.addNotification(r1);
+ final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 2, originalGroupName, false);
+ mService.addNotification(r2);
+ final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel,
+ summaryId, originalGroupName, true);
+ mService.addNotification(summary);
+ final String originalGroupKey = summary.getGroupKey();
+ assertThat(mService.mSummaryByGroupKey).containsEntry(originalGroupKey, summary);
+
+ // Classify a child notification into the NEWS bundle
+ final String keyToUnbundle = r1.getKey();
+ final boolean hasOriginalSummary = true;
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment = new Adjustment(r1.getSbn().getPackageName(), r1.getKey(), signals,
+ "", r1.getUser().getIdentifier());
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r1.applyAdjustments();
+ assertThat(r1.getChannel().getId()).isEqualTo(NEWS_ID);
+
+ // Unbundle the notification
+ mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+
+ // Check that the original channel was restored
+ assertThat(r1.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+ verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(r1), eq(hasOriginalSummary));
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testUnbundleNotification_groupedSummaryCanceled_restoresOriginalChannel()
+ throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+
+ // Post grouped notifications
+ final String originalGroupName = "originalGroup";
+ final int summaryId = 0;
+ final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 1, originalGroupName, false);
+ mService.addNotification(r1);
+ final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 2, originalGroupName, false);
+ mService.addNotification(r2);
+ final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel,
+ summaryId, originalGroupName, true);
+ mService.addNotification(summary);
+ final String originalGroupKey = summary.getGroupKey();
+ assertThat(mService.mSummaryByGroupKey).containsEntry(originalGroupKey, summary);
+
+ // Classify a child notification into the NEWS bundle
+ final String keyToUnbundle = r1.getKey();
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment = new Adjustment(r1.getSbn().getPackageName(), r1.getKey(), signals,
+ "", r1.getUser().getIdentifier());
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r1.applyAdjustments();
+ assertThat(r1.getChannel().getId()).isEqualTo(NEWS_ID);
+
+ // Cancel original summary
+ final boolean hasOriginalSummary = false;
+ mService.mSummaryByGroupKey.remove(summary.getGroupKey());
+
+ // Unbundle the notification
+ mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+
+ // Check that the original channel was restored
+ assertThat(r1.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+ verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(r1), eq(hasOriginalSummary));
+ }
+
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index fbd53f714dbf..8e79514c875e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -66,7 +66,6 @@ import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.No
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
-import static com.android.server.notification.Flags.FLAG_ALL_NOTIFS_NEED_TTL;
import static com.android.server.notification.Flags.FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI;
import static com.android.server.notification.Flags.FLAG_PERSIST_INCOMPLETE_RESTORE_DATA;
import static com.android.server.notification.NotificationChannelLogger.NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED_BY_USER;
@@ -155,7 +154,6 @@ import android.util.proto.ProtoOutputStream;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.config.sysui.TestableFlagResolver;
@@ -167,9 +165,6 @@ import com.android.os.AtomsProto.PackageNotificationPreferences;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.PermissionHelper.PackagePermission;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.protobuf.InvalidProtocolBufferException;
@@ -204,6 +199,9 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@EnableFlags(FLAG_PERSIST_INCOMPLETE_RESTORE_DATA)
@@ -2640,6 +2638,35 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void getPackagesBypassingDnd_multipleUsers() {
+ int uidUser1 = UserHandle.getUid(1, UID_P);
+ NotificationChannel channelUser1Bypass = new NotificationChannel("id11", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channelUser1Bypass.setBypassDnd(true);
+ NotificationChannel channelUser1NoBypass = new NotificationChannel("id12", "name2",
+ NotificationManager.IMPORTANCE_MAX);
+ channelUser1NoBypass.setBypassDnd(false);
+
+ int uidUser2 = UserHandle.getUid(2, UID_P);
+ NotificationChannel channelUser2Bypass = new NotificationChannel("id21", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channelUser2Bypass.setBypassDnd(true);
+
+ mHelper.createNotificationChannel(PKG_P, uidUser1, channelUser1Bypass, true,
+ /* hasDndAccess= */ true, uidUser1, false);
+ mHelper.createNotificationChannel(PKG_P, uidUser1, channelUser1NoBypass, true,
+ /* hasDndAccess= */ true, uidUser1, false);
+ mHelper.createNotificationChannel(PKG_P, uidUser2, channelUser2Bypass, true,
+ /* hasDndAccess= */ true, uidUser2, false);
+
+ assertThat(mHelper.getPackagesBypassingDnd(0)).isEmpty();
+ assertThat(mHelper.getPackagesBypassingDnd(1))
+ .containsExactly(new ZenBypassingApp(PKG_P, false));
+ assertThat(mHelper.getPackagesBypassingDnd(2))
+ .containsExactly(new ZenBypassingApp(PKG_P, true));
+ }
+
+ @Test
public void getPackagesBypassingDnd_oneChannelBypassing_groupBlocked() {
int uid = UID_N_MR1;
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 670f9f697a5c..bacf5ed9d81f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -53,7 +53,9 @@ import android.content.pm.UserPackage;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.Pair;
import android.util.SparseArray;
@@ -66,6 +68,7 @@ import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
+import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.Before;
@@ -133,6 +136,8 @@ public class ActivityStartInterceptorTest {
private SparseArray<ActivityInterceptorCallback> mActivityInterceptorCallbacks =
new SparseArray<>();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
@@ -237,6 +242,20 @@ public class ActivityStartInterceptorTest {
}
@Test
+ @EnableFlags(Flags.FLAG_NORMALIZE_HOME_INTENT)
+ public void testInterceptIncorrectHomeIntent() {
+ // Create a non-standard home intent
+ final Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ // Ensure the intent is intercepted and normalized to standard home intent.
+ assertTrue(mInterceptor.intercept(homeIntent, null, mAInfo, null, null, null, 0, 0, null,
+ mTaskDisplayArea, false));
+ assertTrue(ActivityRecord.isHomeIntent(homeIntent));
+ }
+
+ @Test
public void testInterceptLockTaskModeViolationPackage() {
when(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 0a7df5a305bc..0af41ea1f634 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -450,7 +450,7 @@ public class DisplayAreaTest extends WindowTestsBase {
public void testGetOrientation() {
final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test");
mDisplayContent.addChild(area, POSITION_TOP);
- final WindowState win = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay");
+ final WindowState win = newWindowBuilder("overlay", TYPE_APPLICATION_OVERLAY).build();
win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
win.mToken.reparent(area, POSITION_TOP);
spyOn(win);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index db71f2bf039d..57aacd36b16b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -178,8 +178,8 @@ public class DisplayContentTests extends WindowTestsBase {
@SetupWindows(addAllCommonWindows = true)
@Test
public void testForAllWindows() {
- final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
- mDisplayContent, "exiting app");
+ final WindowState exitingAppWindow = newWindowBuilder("exiting app",
+ TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
final ActivityRecord exitingApp = exitingAppWindow.mActivityRecord;
exitingApp.startAnimation(exitingApp.getPendingTransaction(), mock(AnimationAdapter.class),
false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION);
@@ -211,8 +211,8 @@ public class DisplayContentTests extends WindowTestsBase {
@SetupWindows(addAllCommonWindows = true)
@Test
public void testForAllWindows_WithAppImeTarget() {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
+ TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
mDisplayContent.setImeLayeringTarget(imeAppTarget);
@@ -289,8 +289,8 @@ public class DisplayContentTests extends WindowTestsBase {
public void testForAllWindows_WithInBetweenWindowToken() {
// This window is set-up to be z-ordered between some windows that go in the same token like
// the nav bar and status bar.
- final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION,
- mDisplayContent, "voiceInteractionWindow");
+ final WindowState voiceInteractionWindow = newWindowBuilder("voiceInteractionWindow",
+ TYPE_VOICE_INTERACTION).setDisplay(mDisplayContent).build();
assertForAllWindowsOrder(Arrays.asList(
mWallpaperWindow,
@@ -310,7 +310,8 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testComputeImeTarget() {
// Verify that an app window can be an ime target.
- final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
+ final WindowState appWin = newWindowBuilder("appWin", TYPE_APPLICATION).setDisplay(
+ mDisplayContent).build();
appWin.setHasSurface(true);
assertTrue(appWin.canBeImeTarget());
WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
@@ -318,8 +319,8 @@ public class DisplayContentTests extends WindowTestsBase {
appWin.mHidden = false;
// Verify that an child window can be an ime target.
- final WindowState childWin = createWindow(appWin,
- TYPE_APPLICATION_ATTACHED_DIALOG, "childWin");
+ final WindowState childWin = newWindowBuilder("childWin",
+ TYPE_APPLICATION_ATTACHED_DIALOG).setParent(appWin).build();
childWin.setHasSurface(true);
assertTrue(childWin.canBeImeTarget());
imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
@@ -331,8 +332,8 @@ public class DisplayContentTests extends WindowTestsBase {
public void testComputeImeTarget_startingWindow() {
ActivityRecord activity = createActivityRecord(mDisplayContent);
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
- "startingWin");
+ final WindowState startingWin = newWindowBuilder("startingWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(activity).build();
startingWin.setHasSurface(true);
assertTrue(startingWin.canBeImeTarget());
@@ -342,7 +343,8 @@ public class DisplayContentTests extends WindowTestsBase {
// Verify that the starting window still be an ime target even an app window launching
// behind it.
- final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "appWin");
+ final WindowState appWin = newWindowBuilder("appWin", TYPE_BASE_APPLICATION).setWindowToken(
+ activity).build();
appWin.setHasSurface(true);
assertTrue(appWin.canBeImeTarget());
@@ -352,8 +354,8 @@ public class DisplayContentTests extends WindowTestsBase {
// Verify that the starting window still be an ime target even the child window behind a
// launching app window
- final WindowState childWin = createWindow(appWin,
- TYPE_APPLICATION_ATTACHED_DIALOG, "childWin");
+ final WindowState childWin = newWindowBuilder("childWin",
+ TYPE_APPLICATION_ATTACHED_DIALOG).setParent(appWin).build();
childWin.setHasSurface(true);
assertTrue(childWin.canBeImeTarget());
imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
@@ -365,8 +367,8 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer();
final ActivityRecord activity = createActivityRecord(mDisplayContent);
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
- "startingWin");
+ final WindowState startingWin = newWindowBuilder("startingWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(activity).build();
startingWin.setHasSurface(true);
assertTrue(startingWin.canBeImeTarget());
final WindowContainer imeSurfaceParentWindow = mock(WindowContainer.class);
@@ -385,10 +387,10 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testComputeImeTargetReturnsNull_windowDidntRequestIme() {
- final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION,
- new ActivityBuilder(mAtm).setCreateTask(true).build(), "app");
- final WindowState win2 = createWindow(null, TYPE_BASE_APPLICATION,
- new ActivityBuilder(mAtm).setCreateTask(true).build(), "app2");
+ final WindowState win1 = newWindowBuilder("app", TYPE_BASE_APPLICATION).setWindowToken(
+ new ActivityBuilder(mAtm).setCreateTask(true).build()).build();
+ final WindowState win2 = newWindowBuilder("app2", TYPE_BASE_APPLICATION).setWindowToken(
+ new ActivityBuilder(mAtm).setCreateTask(true).build()).build();
mDisplayContent.setImeInputTarget(win1);
mDisplayContent.setImeLayeringTarget(win2);
@@ -404,8 +406,8 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer();
final ActivityRecord activity = createActivityRecord(mDisplayContent);
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
- "startingWin");
+ final WindowState startingWin = newWindowBuilder("startingWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(activity).build();
startingWin.setHasSurface(true);
assertTrue(startingWin.canBeImeTarget());
final WindowContainer imeSurfaceParentWindow = mock(WindowContainer.class);
@@ -433,8 +435,8 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer();
final ActivityRecord activity = createActivityRecord(mDisplayContent);
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
- "startingWin");
+ final WindowState startingWin = newWindowBuilder("startingWin",
+ TYPE_APPLICATION_STARTING).setWindowToken(activity).build();
startingWin.setHasSurface(true);
assertTrue(startingWin.canBeImeTarget());
@@ -532,8 +534,8 @@ public class DisplayContentTests extends WindowTestsBase {
mWm.mPerDisplayFocusEnabled = perDisplayFocusEnabled;
// Create a focusable window and check that focus is calculated correctly
- final WindowState window1 =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
+ final WindowState window1 = newWindowBuilder("window1", TYPE_BASE_APPLICATION).setDisplay(
+ mDisplayContent).build();
window1.mActivityRecord.mTargetSdk = targetSdk;
updateFocusedWindow();
assertTrue(window1.isFocused());
@@ -549,7 +551,8 @@ public class DisplayContentTests extends WindowTestsBase {
final ActivityRecord app2 = new ActivityBuilder(mAtm)
.setTask(new TaskBuilder(mSupervisor).setDisplay(dc).build())
.setUseProcess(window1.getProcess()).setOnTop(true).build();
- final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, app2, "window2");
+ final WindowState window2 = newWindowBuilder("window2",
+ TYPE_BASE_APPLICATION).setWindowToken(app2).build();
window2.mActivityRecord.mTargetSdk = targetSdk;
updateFocusedWindow();
assertTrue(window2.isFocused());
@@ -616,7 +619,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testDisplayHasContent() {
- final WindowState window = createWindow(null, TYPE_APPLICATION_OVERLAY, "window");
+ final WindowState window = newWindowBuilder("window", TYPE_APPLICATION_OVERLAY).build();
setDrawnState(WindowStateAnimator.COMMIT_DRAW_PENDING, window);
assertFalse(mDisplayContent.getLastHasContent());
// The pending draw state should be committed and the has-content state is also updated.
@@ -632,7 +635,8 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testImeIsAttachedToDisplayForLetterboxedApp() {
final DisplayContent dc = mDisplayContent;
- final WindowState ws = createWindow(null, TYPE_APPLICATION, dc, "app window");
+ final WindowState ws = newWindowBuilder("app window", TYPE_APPLICATION).setDisplay(
+ dc).build();
dc.setImeLayeringTarget(ws);
dc.setImeInputTarget(ws);
@@ -655,7 +659,8 @@ public class DisplayContentTests extends WindowTestsBase {
final WindowState[] windows = new WindowState[types.length];
for (int i = 0; i < types.length; i++) {
final int type = types[i];
- windows[i] = createWindow(null /* parent */, type, displayContent, "window-" + type);
+ windows[i] = newWindowBuilder("window-" + type, type).setDisplay(
+ displayContent).build();
windows[i].setHasSurface(true);
windows[i].mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
}
@@ -887,7 +892,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testLayoutSeq_assignedDuringLayout() {
final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+ final WindowState win = newWindowBuilder("w", TYPE_BASE_APPLICATION).setDisplay(dc).build();
performLayout(dc);
@@ -902,10 +907,12 @@ public class DisplayContentTests extends WindowTestsBase {
// Create a window that requests landscape orientation. It will define device orientation
// by default.
- final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+ final WindowState window = newWindowBuilder("w", TYPE_BASE_APPLICATION).setDisplay(
+ dc).build();
window.mActivityRecord.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- final WindowState keyguard = createWindow(null, TYPE_NOTIFICATION_SHADE , dc, "keyguard");
+ final WindowState keyguard = newWindowBuilder("keyguard",
+ TYPE_NOTIFICATION_SHADE).setDisplay(dc).build();
keyguard.mHasSurface = true;
keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
@@ -936,8 +943,8 @@ public class DisplayContentTests extends WindowTestsBase {
// Create a window that requests a fixed orientation. It will define device orientation
// by default.
- final WindowState window = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY, dc,
- "window");
+ final WindowState window = newWindowBuilder("window", TYPE_APPLICATION_OVERLAY).setDisplay(
+ dc).build();
window.mHasSurface = true;
window.mAttrs.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE;
@@ -1003,12 +1010,14 @@ public class DisplayContentTests extends WindowTestsBase {
public void testInputMethodTargetUpdateWhenSwitchingOnDisplays() {
final DisplayContent newDisplay = createNewDisplay();
- final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
+ final WindowState appWin = newWindowBuilder("appWin", TYPE_APPLICATION).setDisplay(
+ mDisplayContent).build();
final Task rootTask = mDisplayContent.getTopRootTask();
final ActivityRecord activity = rootTask.topRunningActivity();
doReturn(true).when(activity).shouldBeVisibleUnchecked();
- final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1");
+ final WindowState appWin1 = newWindowBuilder("appWin1", TYPE_APPLICATION).setDisplay(
+ newDisplay).build();
final Task rootTask1 = newDisplay.getTopRootTask();
final ActivityRecord activity1 = rootTask1.topRunningActivity();
doReturn(true).when(activity1).shouldBeVisibleUnchecked();
@@ -1203,7 +1212,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testComputeImeParent_app() throws Exception {
final DisplayContent dc = createNewDisplay();
- dc.setImeLayeringTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
+ dc.setImeLayeringTarget(newWindowBuilder("app", TYPE_BASE_APPLICATION).build());
dc.setImeInputTarget(dc.getImeTarget(IME_TARGET_LAYERING).getWindow());
assertEquals(dc.getImeTarget(
IME_TARGET_LAYERING).getWindow().mActivityRecord.getSurfaceControl(),
@@ -1213,7 +1222,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testComputeImeParent_app_notFullscreen() throws Exception {
final DisplayContent dc = createNewDisplay();
- dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "app"));
+ dc.setImeLayeringTarget(newWindowBuilder("app", TYPE_STATUS_BAR).build());
dc.getImeTarget(IME_TARGET_LAYERING).getWindow().setWindowingMode(
WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
dc.setImeInputTarget(dc.getImeTarget(IME_TARGET_LAYERING).getWindow());
@@ -1235,7 +1244,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testComputeImeParent_noApp() throws Exception {
final DisplayContent dc = createNewDisplay();
- dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "statusBar"));
+ dc.setImeLayeringTarget(newWindowBuilder("statusBar", TYPE_STATUS_BAR).build());
dc.setImeInputTarget(dc.getImeTarget(IME_TARGET_LAYERING).getWindow());
assertEquals(dc.getImeContainer().getParentSurfaceControl(),
dc.computeImeParent().getSurfaceControl());
@@ -1244,8 +1253,8 @@ public class DisplayContentTests extends WindowTestsBase {
@SetupWindows(addWindows = W_ACTIVITY)
@Test
public void testComputeImeParent_inputTargetNotUpdate() throws Exception {
- WindowState app1 = createWindow(null, TYPE_BASE_APPLICATION, "app1");
- WindowState app2 = createWindow(null, TYPE_BASE_APPLICATION, "app2");
+ WindowState app1 = newWindowBuilder("app1", TYPE_BASE_APPLICATION).build();
+ WindowState app2 = newWindowBuilder("app2", TYPE_BASE_APPLICATION).build();
doReturn(true).when(mDisplayContent).shouldImeAttachedToApp();
mDisplayContent.setImeLayeringTarget(app1);
mDisplayContent.setImeInputTarget(app1);
@@ -1260,10 +1269,10 @@ public class DisplayContentTests extends WindowTestsBase {
@SetupWindows(addWindows = W_ACTIVITY)
@Test
public void testComputeImeParent_updateParentWhenTargetNotUseIme() throws Exception {
- WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay");
+ WindowState overlay = newWindowBuilder("overlay", TYPE_APPLICATION_OVERLAY).build();
overlay.setBounds(100, 100, 200, 200);
overlay.mAttrs.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
- WindowState app = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).build();
mDisplayContent.setImeLayeringTarget(overlay);
mDisplayContent.setImeInputTarget(app);
assertFalse(mDisplayContent.shouldImeAttachedToApp());
@@ -1274,8 +1283,8 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testComputeImeParent_remoteControlTarget() throws Exception {
final DisplayContent dc = mDisplayContent;
- WindowState app1 = createWindow(null, TYPE_BASE_APPLICATION, "app1");
- WindowState app2 = createWindow(null, TYPE_BASE_APPLICATION, "app2");
+ WindowState app1 = newWindowBuilder("app1", TYPE_BASE_APPLICATION).build();
+ WindowState app2 = newWindowBuilder("app2", TYPE_BASE_APPLICATION).build();
dc.setImeLayeringTarget(app1);
dc.setImeInputTarget(app2);
@@ -1301,7 +1310,7 @@ public class DisplayContentTests extends WindowTestsBase {
public void testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved() throws Exception {
final DisplayContent dc = createNewDisplay();
- WindowState app = createWindow(null, TYPE_BASE_APPLICATION, dc, "app");
+ WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).setDisplay(dc).build();
dc.setImeInputTarget(app);
assertEquals(app, dc.computeImeControlTarget());
@@ -1316,7 +1325,7 @@ public class DisplayContentTests extends WindowTestsBase {
public void testComputeImeControlTarget() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.setRemoteInsetsController(createDisplayWindowInsetsController());
- dc.mCurrentFocus = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ dc.mCurrentFocus = newWindowBuilder("app", TYPE_BASE_APPLICATION).build();
// Expect returning null IME control target when the focus window has not yet been the
// IME input target (e.g. IME is restarting) in fullscreen windowing mode.
@@ -1332,7 +1341,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testComputeImeControlTarget_splitscreen() throws Exception {
final DisplayContent dc = createNewDisplay();
- dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
+ dc.setImeInputTarget(newWindowBuilder("app", TYPE_BASE_APPLICATION).build());
dc.getImeInputTarget().getWindowState().setWindowingMode(
WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState());
@@ -1346,7 +1355,7 @@ public class DisplayContentTests extends WindowTestsBase {
public void testImeSecureFlagGetUpdatedAfterImeInputTarget() {
// Verify IME window can get up-to-date secure flag update when the IME input target
// set before setCanScreenshot called.
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
SurfaceControl.Transaction t = mDisplayContent.mInputMethodWindow.getPendingTransaction();
spyOn(t);
mDisplayContent.setImeInputTarget(app);
@@ -1391,7 +1400,8 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testUpdateSystemGestureExclusion() throws Exception {
final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setDisplay(
+ dc).build();
win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
@@ -1423,11 +1433,12 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testCalculateSystemGestureExclusion() throws Exception {
final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setDisplay(
+ dc).build();
win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
- final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "win2");
+ final WindowState win2 = newWindowBuilder("win2", TYPE_APPLICATION).setDisplay(dc).build();
win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50)));
@@ -1451,11 +1462,12 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testCalculateSystemGestureExclusion_modal() throws Exception {
final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "base");
+ final WindowState win = newWindowBuilder("base", TYPE_BASE_APPLICATION).setDisplay(
+ dc).build();
win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
win.setSystemGestureExclusion(Collections.singletonList(new Rect(0, 0, 1000, 1000)));
- final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "modal");
+ final WindowState win2 = newWindowBuilder("modal", TYPE_APPLICATION).setDisplay(dc).build();
win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
win2.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
win2.getAttrs().width = 10;
@@ -1476,7 +1488,8 @@ public class DisplayContentTests extends WindowTestsBase {
mWm.mConstants.mSystemGestureExcludedByPreQStickyImmersive = true;
final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setDisplay(
+ dc).build();
win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -1500,7 +1513,8 @@ public class DisplayContentTests extends WindowTestsBase {
mWm.mConstants.mSystemGestureExcludedByPreQStickyImmersive = true;
final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setDisplay(
+ dc).build();
win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
win.getAttrs().privateFlags |= PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
@@ -1559,9 +1573,9 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testHybridRotationAnimation() {
final DisplayContent displayContent = mDefaultDisplay;
- final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
- final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
- final WindowState app = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
+ final WindowState navBar = newWindowBuilder("navBar", TYPE_NAVIGATION_BAR).build();
+ final WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).build();
final WindowState[] windows = { statusBar, navBar, app };
makeWindowVisible(windows);
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
@@ -1863,6 +1877,11 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals("Display must be portrait after closing the translucent activity",
Configuration.ORIENTATION_PORTRAIT,
mDisplayContent.getConfiguration().orientation);
+
+ mDisplayContent.setFixedRotationLaunchingAppUnchecked(nonTopVisible);
+ mDisplayContent.onTransitionFinished();
+ assertFalse("Complete fixed rotation if not in a transition",
+ mDisplayContent.hasTopFixedRotationLaunchingApp());
}
@Test
@@ -2183,7 +2202,8 @@ public class DisplayContentTests extends WindowTestsBase {
Task rootTask = createTask(display);
Task task = createTaskInRootTask(rootTask, 0 /* userId */);
WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
- WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
+ WindowState behindWindow = newWindowBuilder("Screenshot", TYPE_SCREENSHOT).setDisplay(
+ display).build();
WindowState result = display.findScrollCaptureTargetWindow(behindWindow,
ActivityTaskManager.INVALID_TASK_ID);
@@ -2196,7 +2216,7 @@ public class DisplayContentTests extends WindowTestsBase {
Task rootTask = createTask(display);
Task task = createTaskInRootTask(rootTask, 0 /* userId */);
WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
- WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible");
+ WindowState invisible = newWindowBuilder("invisible", TYPE_APPLICATION).build();
invisible.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false
WindowState result = display.findScrollCaptureTargetWindow(null,
@@ -2209,7 +2229,7 @@ public class DisplayContentTests extends WindowTestsBase {
DisplayContent display = createNewDisplay();
Task rootTask = createTask(display);
Task task = createTaskInRootTask(rootTask, 0 /* userId */);
- WindowState secureWindow = createWindow(null, TYPE_APPLICATION, "Secure Window");
+ WindowState secureWindow = newWindowBuilder("Secure Window", TYPE_APPLICATION).build();
secureWindow.mAttrs.flags |= FLAG_SECURE;
WindowState result = display.findScrollCaptureTargetWindow(null,
@@ -2222,7 +2242,7 @@ public class DisplayContentTests extends WindowTestsBase {
DisplayContent display = createNewDisplay();
Task rootTask = createTask(display);
Task task = createTaskInRootTask(rootTask, 0 /* userId */);
- WindowState secureWindow = createWindow(null, TYPE_APPLICATION, "Secure Window");
+ WindowState secureWindow = newWindowBuilder("Secure window", TYPE_APPLICATION).build();
secureWindow.mAttrs.flags |= FLAG_SECURE;
WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId);
@@ -2235,7 +2255,8 @@ public class DisplayContentTests extends WindowTestsBase {
Task rootTask = createTask(display);
Task task = createTaskInRootTask(rootTask, 0 /* userId */);
WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
- WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
+ WindowState behindWindow = newWindowBuilder("Screenshot", TYPE_SCREENSHOT).setDisplay(
+ display).build();
WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId);
assertEquals(window, result);
@@ -2248,7 +2269,8 @@ public class DisplayContentTests extends WindowTestsBase {
Task task = createTaskInRootTask(rootTask, 0 /* userId */);
WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
window.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false
- WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
+ WindowState behindWindow = newWindowBuilder("Screenshot", TYPE_SCREENSHOT).setDisplay(
+ display).build();
WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId);
assertEquals(window, result);
@@ -2317,9 +2339,10 @@ public class DisplayContentTests extends WindowTestsBase {
@SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
@Test
public void testComputeImeTarget_shouldNotCheckOutdatedImeTargetLayerWhenRemoved() {
- final WindowState child1 = createWindow(mAppWindow, FIRST_SUB_WINDOW, "child1");
- final WindowState nextImeTargetApp = createWindow(null /* parent */,
- TYPE_BASE_APPLICATION, "nextImeTargetApp");
+ final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+ mAppWindow).build();
+ final WindowState nextImeTargetApp = newWindowBuilder("nextImeTargetApp",
+ TYPE_BASE_APPLICATION).build();
spyOn(child1);
doReturn(false).when(mDisplayContent).shouldImeAttachedToApp();
mDisplayContent.setImeLayeringTarget(child1);
@@ -2353,7 +2376,8 @@ public class DisplayContentTests extends WindowTestsBase {
// Preparation: Simulate snapshot Task.
ActivityRecord act1 = createActivityRecord(mDisplayContent);
- final WindowState appWin1 = createWindow(null, TYPE_BASE_APPLICATION, act1, "appWin1");
+ final WindowState appWin1 = newWindowBuilder("appWin1",
+ TYPE_BASE_APPLICATION).setWindowToken(act1).build();
spyOn(appWin1);
spyOn(appWin1.mWinAnimator);
appWin1.setHasSurface(true);
@@ -2372,7 +2396,8 @@ public class DisplayContentTests extends WindowTestsBase {
// Test step 2: Simulate launching appWin2 and appWin1 is in app transition.
ActivityRecord act2 = createActivityRecord(mDisplayContent);
- final WindowState appWin2 = createWindow(null, TYPE_BASE_APPLICATION, act2, "appWin2");
+ final WindowState appWin2 = newWindowBuilder("appWin2",
+ TYPE_BASE_APPLICATION).setWindowToken(act2).build();
appWin2.setHasSurface(true);
assertTrue(appWin2.canBeImeTarget());
doReturn(true).when(appWin1).inTransitionSelfOrParent();
@@ -2394,7 +2419,8 @@ public class DisplayContentTests extends WindowTestsBase {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
+ activity).build();
task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
doReturn(true).when(task).okToAnimate();
ArrayList<WindowContainer> sources = new ArrayList<>();
@@ -2420,7 +2446,8 @@ public class DisplayContentTests extends WindowTestsBase {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
+ activity).build();
mDisplayContent.setImeLayeringTarget(win);
mDisplayContent.setImeInputTarget(win);
@@ -2446,7 +2473,8 @@ public class DisplayContentTests extends WindowTestsBase {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
+ activity).build();
win.onSurfaceShownChanged(true);
makeWindowVisible(win, mDisplayContent.mInputMethodWindow);
task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
@@ -2471,7 +2499,8 @@ public class DisplayContentTests extends WindowTestsBase {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
+ activity).build();
makeWindowVisible(mDisplayContent.mInputMethodWindow);
mDisplayContent.setImeLayeringTarget(win);
@@ -2687,7 +2716,8 @@ public class DisplayContentTests extends WindowTestsBase {
public void testKeyguardGoingAwayWhileAodShown() {
mDisplayContent.getDisplayPolicy().setAwake(true);
- final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
+ final WindowState appWin = newWindowBuilder("appWin", TYPE_APPLICATION).setDisplay(
+ mDisplayContent).build();
final ActivityRecord activity = appWin.mActivityRecord;
mAtm.mKeyguardController.setKeyguardShown(appWin.getDisplayId(), true /* keyguardShowing */,
@@ -2713,15 +2743,15 @@ public class DisplayContentTests extends WindowTestsBase {
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
public void testImeChildWindowFocusWhenImeLayeringTargetChanges() {
- final WindowState imeChildWindow =
- createWindow(mImeWindow, TYPE_APPLICATION_ATTACHED_DIALOG, "imeChildWindow");
+ final WindowState imeChildWindow = newWindowBuilder("imeChildWindow",
+ TYPE_APPLICATION_ATTACHED_DIALOG).setParent(mImeWindow).build();
makeWindowVisibleAndDrawn(imeChildWindow, mImeWindow);
assertTrue(imeChildWindow.canReceiveKeys());
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
// Verify imeChildWindow can be focused window if the next IME target requests IME visible.
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
+ TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
mDisplayContent.setImeLayeringTarget(imeAppTarget);
spyOn(imeAppTarget);
doReturn(true).when(imeAppTarget).isRequestedVisible(ime());
@@ -2729,8 +2759,8 @@ public class DisplayContentTests extends WindowTestsBase {
// Verify imeChildWindow doesn't be focused window if the next IME target does not
// request IME visible.
- final WindowState nextImeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget");
+ final WindowState nextImeAppTarget = newWindowBuilder("nextImeAppTarget",
+ TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
assertNotEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
}
@@ -2738,22 +2768,22 @@ public class DisplayContentTests extends WindowTestsBase {
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() {
- final WindowState imeMenuDialog =
- createWindow(null, TYPE_INPUT_METHOD_DIALOG, "imeMenuDialog");
+ final WindowState imeMenuDialog = newWindowBuilder("imeMenuDialog",
+ TYPE_INPUT_METHOD_DIALOG).build();
makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow);
assertTrue(imeMenuDialog.canReceiveKeys());
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
// Verify imeMenuDialog can be focused window if the next IME target requests IME visible.
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
+ TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
mDisplayContent.setImeLayeringTarget(imeAppTarget);
imeAppTarget.setRequestedVisibleTypes(ime());
assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
// Verify imeMenuDialog doesn't be focused window if the next IME target is closing.
- final WindowState nextImeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget");
+ final WindowState nextImeAppTarget = newWindowBuilder("nextImeAppTarget",
+ TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
makeWindowVisibleAndDrawn(nextImeAppTarget);
// Even if the app still requests IME, the ime dialog should not gain focus if the target
// app is invisible.
@@ -2765,10 +2795,12 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testKeepClearAreasMultipleWindows() {
- final WindowState w1 = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "w1");
+ final WindowState w1 = newWindowBuilder("w1", TYPE_NAVIGATION_BAR).setDisplay(
+ mDisplayContent).build();
final Rect rect1 = new Rect(0, 0, 10, 10);
w1.setKeepClearAreas(Arrays.asList(rect1), Collections.emptyList());
- final WindowState w2 = createWindow(null, TYPE_NOTIFICATION_SHADE, mDisplayContent, "w2");
+ final WindowState w2 = newWindowBuilder("w2", TYPE_NOTIFICATION_SHADE).setDisplay(
+ mDisplayContent).build();
final Rect rect2 = new Rect(10, 10, 20, 20);
w2.setKeepClearAreas(Arrays.asList(rect2), Collections.emptyList());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 1015651438c3..ceb06497adbc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -76,7 +76,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Before
public void setUp() throws Exception {
- mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
+ mWindow = spy(newWindowBuilder("window", TYPE_APPLICATION).build());
spyOn(mStatusBarWindow);
spyOn(mNavBarWindow);
@@ -147,7 +147,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
public void addingWindow_withInsetsTypes() {
mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one.
- final WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "statusBar");
+ final WindowState win = newWindowBuilder("statusBar", TYPE_STATUS_BAR_SUB_PANEL).build();
final Binder owner = new Binder();
win.mAttrs.providedInsets = new InsetsFrameProvider[] {
new InsetsFrameProvider(owner, 0, WindowInsets.Type.statusBars()),
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 27d46fc4e39e..ea925c019b77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -76,7 +76,7 @@ import org.junit.runner.RunWith;
public class DisplayPolicyTests extends WindowTestsBase {
private WindowState createOpaqueFullscreen(boolean hasLightNavBar) {
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "opaqueFullscreen");
+ final WindowState win = newWindowBuilder("opaqueFullscreen", TYPE_BASE_APPLICATION).build();
final WindowManager.LayoutParams attrs = win.mAttrs;
attrs.width = MATCH_PARENT;
attrs.height = MATCH_PARENT;
@@ -99,7 +99,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
}
private WindowState createDimmingDialogWindow(boolean canBeImTarget) {
- final WindowState win = spy(createWindow(null, TYPE_APPLICATION, "dimmingDialog"));
+ final WindowState win = spy(newWindowBuilder("dimmingDialog", TYPE_APPLICATION).build());
final WindowManager.LayoutParams attrs = win.mAttrs;
attrs.width = WRAP_CONTENT;
attrs.height = WRAP_CONTENT;
@@ -111,7 +111,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
private WindowState createInputMethodWindow(boolean visible, boolean drawNavBar,
boolean hasLightNavBar) {
- final WindowState win = createWindow(null, TYPE_INPUT_METHOD, "inputMethod");
+ final WindowState win = newWindowBuilder("inputMethod", TYPE_INPUT_METHOD).build();
final WindowManager.LayoutParams attrs = win.mAttrs;
attrs.width = MATCH_PARENT;
attrs.height = MATCH_PARENT;
@@ -301,7 +301,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
}
private WindowState createApplicationWindow() {
- final WindowState win = createWindow(null, TYPE_APPLICATION, "Application");
+ final WindowState win = newWindowBuilder("Application", TYPE_APPLICATION).build();
final WindowManager.LayoutParams attrs = win.mAttrs;
attrs.width = MATCH_PARENT;
attrs.height = MATCH_PARENT;
@@ -312,7 +312,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
}
private WindowState createBaseApplicationWindow() {
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "Application");
+ final WindowState win = newWindowBuilder("Application", TYPE_BASE_APPLICATION).build();
final WindowManager.LayoutParams attrs = win.mAttrs;
attrs.width = MATCH_PARENT;
attrs.height = MATCH_PARENT;
@@ -450,7 +450,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90, di.logicalHeight, di.logicalWidth);
// Add a window that provides the same insets in current rotation. But it specifies
// different insets in other rotations.
- final WindowState bar2 = createWindow(null, navbar.mAttrs.type, "bar2");
+ final WindowState bar2 = newWindowBuilder("bar2", navbar.mAttrs.type).build();
bar2.mAttrs.providedInsets = new InsetsFrameProvider[] {
new InsetsFrameProvider(bar2, 0, WindowInsets.Type.navigationBars())
.setInsetsSize(Insets.of(0, 0, 0, NAV_BAR_HEIGHT))
diff --git a/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java b/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java
index 34f0c191ecf5..fe9f63615757 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java
@@ -76,6 +76,7 @@ public class IntegrationTests {
private ActivityTestRule<EmptyActivity> mEmptyActivityRule =
new ActivityTestRule<>(EmptyActivity.class, false , true);
+
@Before
public void setUp() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -163,7 +164,7 @@ public class IntegrationTests {
// of that state.
for (int i = 0; i < uiStates.size(); i++) {
StateTracker.StateData stateData = uiStates.get(i);
- if (stateData.mWidgetCategory.equals(AppJankStats.ANIMATION)) {
+ if (stateData.mWidgetCategory.equals(AppJankStats.WIDGET_CATEGORY_ANIMATION)) {
assertNotEquals(Long.MAX_VALUE, stateData.mVsyncIdEnd);
}
}
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
index 30c568be7716..c90595782cd1 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
@@ -215,7 +215,8 @@ public class JankDataProcessorTest {
assertEquals(jankStats.getJankyFrameCount() * 2, pendingStat.getJankyFrames());
assertEquals(jankStats.getTotalFrameCount() * 2, pendingStat.getTotalFrames());
- int[] originalHistogramBuckets = jankStats.getFrameOverrunHistogram().getBucketCounters();
+ int[] originalHistogramBuckets =
+ jankStats.getRelativeFrameTimeHistogram().getBucketCounters();
int[] frameOverrunBuckets = pendingStat.getFrameOverrunBuckets();
for (int i = 0; i < frameOverrunBuckets.length; i++) {
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
index 0b4d97ed20d6..df92898d76b1 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
@@ -17,7 +17,7 @@
package android.app.jank.tests;
import android.app.jank.AppJankStats;
-import android.app.jank.FrameOverrunHistogram;
+import android.app.jank.RelativeFrameTimeHistogram;
public class JankUtils {
private static final int APP_ID = 25;
@@ -29,8 +29,8 @@ public class JankUtils {
AppJankStats jankStats = new AppJankStats(
/*App Uid*/APP_ID,
/*Widget Id*/"test widget id",
- /*Widget Category*/AppJankStats.SCROLL,
- /*Widget State*/AppJankStats.SCROLLING,
+ /*Widget Category*/AppJankStats.WIDGET_CATEGORY_SCROLL,
+ /*Widget State*/AppJankStats.WIDGET_STATE_SCROLLING,
/*Total Frames*/100,
/*Janky Frames*/25,
getOverrunHistogram()
@@ -41,12 +41,12 @@ public class JankUtils {
/**
* Returns a mock histogram to be used with an AppJankStats object.
*/
- public static FrameOverrunHistogram getOverrunHistogram() {
- FrameOverrunHistogram overrunHistogram = new FrameOverrunHistogram();
- overrunHistogram.addFrameOverrunMillis(-2);
- overrunHistogram.addFrameOverrunMillis(1);
- overrunHistogram.addFrameOverrunMillis(5);
- overrunHistogram.addFrameOverrunMillis(25);
+ public static RelativeFrameTimeHistogram getOverrunHistogram() {
+ RelativeFrameTimeHistogram overrunHistogram = new RelativeFrameTimeHistogram();
+ overrunHistogram.addRelativeFrameTimeMillis(-2);
+ overrunHistogram.addRelativeFrameTimeMillis(1);
+ overrunHistogram.addRelativeFrameTimeMillis(5);
+ overrunHistogram.addRelativeFrameTimeMillis(25);
return overrunHistogram;
}
}
diff --git a/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java b/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java
index 5fff46038ead..71796d64ddee 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java
@@ -45,8 +45,8 @@ public class TestWidget extends View {
*/
public void simulateAnimationStarting() {
if (jankTrackerCreated()) {
- mJankTracker.addUiState(AppJankStats.ANIMATION,
- Integer.toString(this.getId()), AppJankStats.ANIMATING);
+ mJankTracker.addUiState(AppJankStats.WIDGET_CATEGORY_ANIMATION,
+ Integer.toString(this.getId()), AppJankStats.WIDGET_STATE_ANIMATING);
}
}
@@ -55,8 +55,8 @@ public class TestWidget extends View {
*/
public void simulateAnimationEnding() {
if (jankTrackerCreated()) {
- mJankTracker.removeUiState(AppJankStats.ANIMATION,
- Integer.toString(this.getId()), AppJankStats.ANIMATING);
+ mJankTracker.removeUiState(AppJankStats.WIDGET_CATEGORY_ANIMATION,
+ Integer.toString(this.getId()), AppJankStats.WIDGET_STATE_ANIMATING);
}
}
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index 700856c50bae..14c8de8db5fc 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -819,7 +819,7 @@ public class GraphicsActivity extends Activity {
private List<Float> getExpectedFrameRateForCompatibility(int compatibility) {
assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED for compatibility "
+ compatibility,
- compatibility == Surface.FRAME_RATE_COMPATIBILITY_GTE);
+ compatibility == Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST);
Display display = getDisplay();
List<Float> expectedFrameRates = getRefreshRates(display.getMode(), display)
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
index 4d4827676c74..f1d4dc6b8faf 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
@@ -85,7 +85,8 @@ public class SurfaceControlTest {
@Test
public void testSurfaceControlFrameRateCompatibilityGte() throws InterruptedException {
GraphicsActivity activity = mActivityRule.getActivity();
- activity.testSurfaceControlFrameRateCompatibility(Surface.FRAME_RATE_COMPATIBILITY_GTE);
+ activity.testSurfaceControlFrameRateCompatibility(
+ Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST);
}
@Test
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 2e7b20763b9e..2db8b1e18ec8 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -29,6 +29,7 @@ import android.tools.helpers.SYSTEMUI_PACKAGE
import android.tools.traces.parsers.WindowManagerStateHelper
import android.tools.traces.wm.WindowingMode
import android.view.KeyEvent.KEYCODE_LEFT_BRACKET
+import android.view.KeyEvent.KEYCODE_MINUS
import android.view.KeyEvent.KEYCODE_RIGHT_BRACKET
import android.view.KeyEvent.META_META_ON
import android.view.WindowInsets
@@ -160,10 +161,21 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) :
?: error("Unable to find resource $MINIMIZE_BUTTON_VIEW\n")
}
- fun minimizeDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice, isPip: Boolean = false) {
- val caption = getCaptionForTheApp(wmHelper, device)
- val minimizeButton = getMinimizeButtonForTheApp(caption)
- minimizeButton.click()
+ fun minimizeDesktopApp(
+ wmHelper: WindowManagerStateHelper,
+ device: UiDevice,
+ isPip: Boolean = false,
+ usingKeyboard: Boolean = false,
+ ) {
+ if (usingKeyboard) {
+ val keyEventHelper = KeyEventHelper(getInstrumentation())
+ keyEventHelper.press(KEYCODE_MINUS, META_META_ON)
+ } else {
+ val caption = getCaptionForTheApp(wmHelper, device)
+ val minimizeButton = getMinimizeButtonForTheApp(caption)
+ minimizeButton.click()
+ }
+
wmHelper
.StateSyncBuilder()
.withAppTransitionIdle()
@@ -226,8 +238,7 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) :
toLeft: Boolean,
) {
val bracketKey = if (toLeft) KEYCODE_LEFT_BRACKET else KEYCODE_RIGHT_BRACKET
- keyEventHelper.actionDown(bracketKey, META_META_ON)
- keyEventHelper.actionUp(bracketKey, META_META_ON)
+ keyEventHelper.press(bracketKey, META_META_ON)
waitAndVerifySnapResize(wmHelper, context, toLeft)
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt
index ebd8cc3ce1b4..55ed09154aee 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt
@@ -29,6 +29,10 @@ import android.view.KeyEvent
class KeyEventHelper(
private val instr: Instrumentation,
) {
+ fun press(keyCode: Int, metaState: Int = 0) {
+ actionDown(keyCode, metaState)
+ actionUp(keyCode, metaState)
+ }
fun actionDown(keyCode: Int, metaState: Int = 0, time: Long = SystemClock.uptimeMillis()) {
injectKeyEvent(ACTION_DOWN, keyCode, metaState, downTime = time, eventTime = time)
diff --git a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
index 281837920548..860d9f680c4c 100644
--- a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
+++ b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
@@ -16,10 +16,17 @@
package com.android.test.input
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+
import android.view.KeyCharacterMap
import android.view.KeyEvent
+import com.android.hardware.input.Flags
+
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Rule
import org.junit.Test
/**
@@ -30,26 +37,38 @@ import org.junit.Test
*
*/
class KeyCharacterMapTest {
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
@Test
+ @EnableFlags(Flags.FLAG_REMOVE_FALLBACK_MODIFIERS)
fun testGetFallback() {
// Based off of VIRTUAL kcm fallbacks.
val keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
// One modifier fallback.
- assertEquals(
- keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
- KeyEvent.META_CTRL_ON).keyCode,
- KeyEvent.KEYCODE_LANGUAGE_SWITCH)
+ val oneModifierFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
+ KeyEvent.META_CTRL_ON)
+ assertEquals(KeyEvent.KEYCODE_LANGUAGE_SWITCH, oneModifierFallback.keyCode)
+ assertEquals(0, oneModifierFallback.metaState)
// Multiple modifier fallback.
- assertEquals(
- keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_DEL,
- KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON).keyCode,
- KeyEvent.KEYCODE_BACK)
+ val twoModifierFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_DEL,
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON)
+ assertEquals(KeyEvent.KEYCODE_BACK, twoModifierFallback.keyCode)
+ assertEquals(0, twoModifierFallback.metaState)
// No default button, fallback only.
- assertEquals(
- keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0).keyCode,
- KeyEvent.KEYCODE_DPAD_CENTER)
+ val keyOnlyFallback =
+ keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0)
+ assertEquals(KeyEvent.KEYCODE_DPAD_CENTER, keyOnlyFallback.keyCode)
+ assertEquals(0, keyOnlyFallback.metaState)
+
+ // A key event that is not an exact match for a fallback. Expect a null return.
+ // E.g. Ctrl + Space -> LanguageSwitch
+ // Ctrl + Alt + Space -> Ctrl + Alt + Space (No fallback).
+ val noMatchFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON)
+ assertNull(noMatchFallback)
}
}
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
index 44aa4028c916..370c0048d9a9 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
@@ -38,6 +38,9 @@ android_test {
],
test_suites: [
"general-tests",
+ // This is an equivalent of general-tests for automotive.
+ // It helps manage the build time on automotive branches.
+ "automotive-general-tests",
],
sdk_version: "test_current",
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 5e0f87f0dcaf..60c4bf5c4131 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -292,13 +292,12 @@ bool processFile(Bundle* bundle, ZipFile* zip,
}
if (!hasData) {
const String8& srcName = file->getSourceFile();
- time_t fileModWhen;
- fileModWhen = getFileModDate(srcName.c_str());
- if (fileModWhen == (time_t) -1) { // file existence tested earlier,
- return false; // not expecting an error here
+ auto fileModWhen = getFileModDate(srcName.c_str());
+ if (fileModWhen == kInvalidModDate) { // file existence tested earlier,
+ return false; // not expecting an error here
}
-
- if (fileModWhen > entry->getModWhen()) {
+
+ if (toTimeT(fileModWhen) > entry->getModWhen()) {
// mark as deleted so add() will succeed
if (bundle->getVerbose()) {
printf(" (removing old '%s')\n", storageName.c_str());
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 661df4d0fe33..e24fe07f959b 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -683,8 +683,6 @@ class ChunkPrinter {
item->PrettyPrint(printer_);
printer_->Print(")");
}
-
- printer_->Print("\n");
}
void PrintQualifiers(uint32_t qualifiers) const {
@@ -763,11 +761,13 @@ class ChunkPrinter {
bool PrintTableType(const ResTable_type* chunk) {
printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id)));
- printer_->Print(StringPrintf(
- " name: %s",
- android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1)
- .c_str()));
+ const auto name =
+ android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1);
+ printer_->Print(StringPrintf(" name: %s", name.c_str()));
printer_->Print(StringPrintf(" flags: 0x%02x", android::util::DeviceToHost32(chunk->flags)));
+ printer_->Print(android::util::DeviceToHost32(chunk->flags) & ResTable_type::FLAG_SPARSE
+ ? " (SPARSE)"
+ : " (DENSE)");
printer_->Print(
StringPrintf(" entryCount: %u", android::util::DeviceToHost32(chunk->entryCount)));
printer_->Print(
@@ -777,8 +777,7 @@ class ChunkPrinter {
config.copyFromDtoH(chunk->config);
printer_->Print(StringPrintf(" config: %s\n", config.to_string().c_str()));
- const ResourceType* type = ParseResourceType(
- android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1));
+ const ResourceType* type = ParseResourceType(name);
printer_->Indent();
@@ -817,11 +816,8 @@ class ChunkPrinter {
for (size_t i = 0; i < map_entry_count; i++) {
PrintResValue(&(maps[i].value), config, type);
- printer_->Print(StringPrintf(
- " name: %s name-id:%d\n",
- android::util::GetString(key_pool_, android::util::DeviceToHost32(maps[i].name.ident))
- .c_str(),
- android::util::DeviceToHost32(maps[i].name.ident)));
+ printer_->Print(StringPrintf(" name-id: 0x%08x\n",
+ android::util::DeviceToHost32(maps[i].name.ident)));
}
} else {
printer_->Print("\n");
@@ -829,6 +825,8 @@ class ChunkPrinter {
// Print the value of the entry
Res_value value = entry->value();
PrintResValue(&value, config, type);
+
+ printer_->Print("\n");
}
printer_->Undent();