summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp15
-rw-r--r--Android.bp2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java185
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java25
-rw-r--r--api/coverage/tools/ExtractFlaggedApis.kt60
-rw-r--r--api/coverage/tools/extract_flagged_apis.proto9
-rw-r--r--core/api/current.txt23
-rw-r--r--core/api/system-current.txt11
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/app/ActivityThread.java11
-rw-r--r--core/java/android/app/IApplicationThread.aidl2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java4
-rw-r--r--core/java/android/companion/virtual/IVirtualDevice.aidl4
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceInternal.java9
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java19
-rw-r--r--core/java/android/content/pm/PackageInstaller.java6
-rw-r--r--core/java/android/content/pm/PackageManager.java52
-rw-r--r--core/java/android/content/pm/SharedLibraryInfo.java6
-rw-r--r--core/java/android/content/pm/UserProperties.java194
-rw-r--r--core/java/android/content/pm/flags.aconfig7
-rw-r--r--core/java/android/credentials/flags.aconfig7
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java21
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java49
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java58
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java25
-rw-r--r--core/java/android/hardware/camera2/extension/RequestProcessor.java2
-rw-r--r--core/java/android/os/CoolingDevice.java16
-rw-r--r--core/java/android/os/Debug.java9
-rw-r--r--core/java/android/os/Temperature.java16
-rw-r--r--core/java/android/os/flags.aconfig8
-rw-r--r--core/java/android/provider/Settings.java17
-rw-r--r--core/java/android/service/dreams/flags.aconfig9
-rw-r--r--core/java/android/service/voice/VisualQueryDetectionService.java14
-rw-r--r--core/java/android/service/voice/VisualQueryDetector.java4
-rw-r--r--core/java/android/view/AttachedSurfaceControl.java39
-rw-r--r--core/java/android/view/IWindowManager.aidl8
-rw-r--r--core/java/android/view/ViewRootImpl.java41
-rw-r--r--core/java/android/view/WindowManager.java69
-rw-r--r--core/java/android/view/WindowManagerGlobal.java95
-rw-r--r--core/java/android/view/WindowManagerImpl.java14
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.java6
-rw-r--r--core/java/android/window/ITrustedPresentationListener.aidl24
-rw-r--r--core/java/android/window/TrustedPresentationListener.java26
-rw-r--r--core/java/android/window/TrustedPresentationThresholds.aidl3
-rw-r--r--core/java/android/window/TrustedPresentationThresholds.java144
-rw-r--r--core/java/android/window/flags/window_surfaces.aconfig8
-rw-r--r--core/java/com/android/internal/jank/Cuj.java503
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java151
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java1112
-rw-r--r--core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java87
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java2
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogGroup.java1
-rw-r--r--core/java/com/android/server/pm/pkg/AndroidPackage.java9
-rw-r--r--core/jni/android_os_Debug.cpp5
-rw-r--r--core/res/res/values/attrs_manifest.xml4
-rw-r--r--core/res/res/values/public-staging.xml2
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java7
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/CujTest.java156
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java128
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java230
-rw-r--r--data/etc/services.core.protolog.json105
-rw-r--r--graphics/java/android/graphics/Paint.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java29
-rw-r--r--location/java/android/location/GnssNavigationMessage.java2
-rw-r--r--media/java/android/media/flags/media_better_together.aconfig7
-rw-r--r--packages/SettingsLib/Spa/build.gradle.kts2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt85
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle.kts2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt4
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt7
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt144
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SuwScaffoldTest.kt90
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt16
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt20
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt3
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt8
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt3
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt11
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt49
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java48
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java4
-rw-r--r--packages/SystemUI/aconfig/Android.bp1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt11
-rw-r--r--packages/SystemUI/docs/executors.md32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt74
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt27
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt133
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt23
-rw-r--r--packages/SystemUI/res/drawable/qs_record_issue_icon_off.xml25
-rw-r--r--packages/SystemUI/res/drawable/qs_record_issue_icon_on.xml25
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml2
-rw-r--r--packages/SystemUI/res/values/ids.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java58
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationLockoutModel.kt (renamed from packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt125
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt42
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt122
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java)49
-rw-r--r--packages/SystemUI/tests/utils/src/android/content/pm/LauncherAppsKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt48
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt41
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifPipelineKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt26
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt51
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt23
-rw-r--r--services/accessibility/Android.bp16
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java17
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java9
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java25
-rw-r--r--services/core/java/com/android/server/BatteryService.java30
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java10
-rw-r--r--services/core/java/com/android/server/am/UserController.java65
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java5
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java5
-rw-r--r--services/core/java/com/android/server/audio/MusicFxHelper.java366
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java6
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java7
-rw-r--r--services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java6
-rw-r--r--services/core/java/com/android/server/media/LegacyBluetoothRouteController.java6
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterImpl.java11
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterUtils.java9
-rw-r--r--services/core/java/com/android/server/pm/Computer.java16
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java29
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java36
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java19
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java4
-rw-r--r--services/core/java/com/android/server/pm/Settings.java40
-rw-r--r--services/core/java/com/android/server/pm/SharedLibrariesImpl.java32
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java16
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java14
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java9
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java7
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java29
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageState.java8
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java5
-rw-r--r--services/core/java/com/android/server/power/ThermalManagerService.java30
-rw-r--r--services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java5
-rw-r--r--services/core/java/com/android/server/utils/WatchedSparseSetArray.java14
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java16
-rw-r--r--services/core/java/com/android/server/wm/AppTaskImpl.java17
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java30
-rw-r--r--services/core/java/com/android/server/wm/DeferredDisplayUpdater.java345
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java16
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java15
-rw-r--r--services/core/java/com/android/server/wm/TrustedPresentationListenerController.java448
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java36
-rw-r--r--services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java6
-rw-r--r--services/core/jni/Android.bp2
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java27
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java2
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java5
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java236
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt8
-rw-r--r--services/tests/servicestests/res/xml/usertypes_test_profile.xml2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java99
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java642
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java9
-rw-r--r--services/tests/wmtests/AndroidManifest.xml5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java267
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java237
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java53
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java154
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java104
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthLte.java38
273 files changed, 8029 insertions, 2803 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 904109b569b8..22f736e367c3 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -27,6 +27,7 @@ aconfig_srcjars = [
":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
":android.security.flags-aconfig-java{.generated_srcjars}",
":android.service.chooser.flags-aconfig-java{.generated_srcjars}",
+ ":android.service.dreams.flags-aconfig-java{.generated_srcjars}",
":android.service.notification.flags-aconfig-java{.generated_srcjars}",
":android.view.flags-aconfig-java{.generated_srcjars}",
":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
@@ -231,7 +232,6 @@ java_aconfig_library {
name: "android.security.flags-aconfig-java-host",
aconfig_declarations: "android.security.flags-aconfig",
host_supported: true,
- mode: "test",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -742,6 +742,19 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Dreams
+aconfig_declarations {
+ name: "android.service.dreams.flags-aconfig",
+ package: "android.service.dreams",
+ srcs: ["core/java/android/service/dreams/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.service.dreams.flags-aconfig-java",
+ aconfig_declarations: "android.service.dreams.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Notifications
aconfig_declarations {
name: "android.service.notification.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index fa7c97d3d21a..676a0f51d3f6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -106,7 +106,7 @@ filegroup {
":android.hardware.radio.voice-V3-java-source",
":android.hardware.security.keymint-V3-java-source",
":android.hardware.security.secureclock-V1-java-source",
- ":android.hardware.thermal-V1-java-source",
+ ":android.hardware.thermal-V2-java-source",
":android.hardware.tv.tuner-V2-java-source",
":android.security.apc-java-source",
":android.security.authorization-java-source",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index e06006f25d3f..f40508302ee3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -1769,7 +1769,8 @@ public final class ConnectivityController extends RestrictingController implemen
@VisibleForTesting
class CcConfig {
- private boolean mFlexIsEnabled = FlexibilityController.FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
+ private boolean mFlexIsEnabled =
+ FlexibilityController.FcConfig.DEFAULT_APPLIED_CONSTRAINTS != 0;
private boolean mShouldReprocessNetworkCapabilities = false;
/**
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 13a474ccf451..0e67b9ac944f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -24,7 +24,6 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
-import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
import android.annotation.ElapsedRealtimeLong;
@@ -74,17 +73,10 @@ public final class FlexibilityController extends StateController {
private static final int JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS = CONSTRAINT_CONNECTIVITY;
/** List of all flexible constraints. */
- private static final int FLEXIBLE_CONSTRAINTS =
+ @VisibleForTesting
+ static final int FLEXIBLE_CONSTRAINTS =
JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS | SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
- private static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
- Integer.bitCount(JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS);
-
- static final int NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS =
- Integer.bitCount(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
-
- static final int NUM_FLEXIBLE_CONSTRAINTS = Integer.bitCount(FLEXIBLE_CONSTRAINTS);
-
private static final long NO_LIFECYCLE_END = Long.MAX_VALUE;
/**
@@ -100,9 +92,15 @@ public final class FlexibilityController extends StateController {
private long mUnseenConstraintGracePeriodMs =
FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
- @VisibleForTesting
+ /** Set of constraints supported on this device for flex scheduling. */
+ private final int mSupportedFlexConstraints;
+
@GuardedBy("mLock")
- boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
+ private boolean mFlexibilityEnabled;
+
+ /** Set of constraints that will be used in the flex policy. */
+ @GuardedBy("mLock")
+ private int mAppliedConstraints = FcConfig.DEFAULT_APPLIED_CONSTRAINTS;
private long mMinTimeBetweenFlexibilityAlarmsMs =
FcConfig.DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
@@ -118,9 +116,6 @@ public final class FlexibilityController extends StateController {
*/
private int[] mPercentToDropConstraints;
- @VisibleForTesting
- boolean mDeviceSupportsFlexConstraints;
-
/**
* Keeps track of what flexible constraints are satisfied at the moment.
* Is updated by the other controllers.
@@ -178,7 +173,7 @@ public final class FlexibilityController extends StateController {
if (!js.hasFlexibilityConstraint()) {
continue;
}
- mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
}
}
@@ -186,15 +181,23 @@ public final class FlexibilityController extends StateController {
};
private static final int MSG_UPDATE_JOBS = 0;
+ private static final int MSG_UPDATE_JOB = 1;
public FlexibilityController(
JobSchedulerService service, PrefetchController prefetchController) {
super(service);
mHandler = new FcHandler(AppSchedulingModuleThread.get().getLooper());
- mDeviceSupportsFlexConstraints = !mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- mFlexibilityEnabled &= mDeviceSupportsFlexConstraints;
- mFlexibilityTracker = new FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS);
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) {
+ // Embedded devices have no user-installable apps. Assume all jobs are critical
+ // and can't be flexed.
+ mSupportedFlexConstraints = 0;
+ } else {
+ // TODO(236261941): handle devices without a battery
+ mSupportedFlexConstraints = FLEXIBLE_CONSTRAINTS;
+ }
+ mFlexibilityEnabled = (mAppliedConstraints & mSupportedFlexConstraints) != 0;
+ mFlexibilityTracker = new FlexibilityTracker(Integer.bitCount(mSupportedFlexConstraints));
mFcConfig = new FcConfig();
mFlexibilityAlarmQueue = new FlexibilityAlarmQueue(
mContext, AppSchedulingModuleThread.get().getLooper());
@@ -218,10 +221,12 @@ public final class FlexibilityController extends StateController {
public void maybeStartTrackingJobLocked(JobStatus js, JobStatus lastJob) {
if (js.hasFlexibilityConstraint()) {
final long nowElapsed = sElapsedRealtimeClock.millis();
- if (!mDeviceSupportsFlexConstraints) {
+ if (mSupportedFlexConstraints == 0) {
js.setFlexibilityConstraintSatisfied(nowElapsed, true);
return;
}
+ js.setNumAppliedFlexibleConstraints(
+ Integer.bitCount(getRelevantAppliedConstraintsLocked(js)));
js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
mFlexibilityTracker.add(js);
js.setTrackingController(JobStatus.TRACKING_FLEXIBILITY);
@@ -266,6 +271,14 @@ public final class FlexibilityController extends StateController {
|| mService.isCurrentlyRunningLocked(js);
}
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ int getRelevantAppliedConstraintsLocked(@NonNull JobStatus js) {
+ final int relevantConstraints = SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
+ | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0);
+ return mAppliedConstraints & relevantConstraints;
+ }
+
/**
* Returns whether there are enough constraints satisfied to allow running the job from flex's
* perspective. This takes into account unseen constraint combinations and expectations around
@@ -274,7 +287,7 @@ public final class FlexibilityController extends StateController {
@VisibleForTesting
@GuardedBy("mLock")
boolean hasEnoughSatisfiedConstraintsLocked(@NonNull JobStatus js) {
- final int satisfiedConstraints = mSatisfiedFlexibleConstraints
+ final int satisfiedConstraints = mSatisfiedFlexibleConstraints & mAppliedConstraints
& (SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
| (js.areTransportAffinitiesSatisfied() ? CONSTRAINT_CONNECTIVITY : 0));
final int numSatisfied = Integer.bitCount(satisfiedConstraints);
@@ -296,8 +309,7 @@ public final class FlexibilityController extends StateController {
// count have not been seen recently enough, then assume they won't be seen anytime soon,
// so don't force the job to wait longer. If any combinations with a higher count have been
// seen recently, then the job can potentially wait for those combinations.
- final int irrelevantConstraints = ~(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
- | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0));
+ final int irrelevantConstraints = ~getRelevantAppliedConstraintsLocked(js);
for (int i = mLastSeenConstraintTimesElapsed.size() - 1; i >= 0; --i) {
final int constraints = mLastSeenConstraintTimesElapsed.keyAt(i);
if ((constraints & irrelevantConstraints) != 0) {
@@ -515,9 +527,9 @@ public final class FlexibilityController extends StateController {
for (int j = 0; j < mFlexibilityTracker.size(); j++) {
final ArraySet<JobStatus> jobs = mFlexibilityTracker
.getJobsByNumRequiredConstraints(j);
- for (int i = 0; i < jobs.size(); i++) {
+ for (int i = jobs.size() - 1; i >= 0; --i) {
JobStatus js = jobs.valueAt(i);
- mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityTracker.updateFlexibleConstraints(js, nowElapsed);
mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
if (js.setFlexibilityConstraintSatisfied(
nowElapsed, isFlexibilitySatisfiedLocked(js))) {
@@ -579,18 +591,46 @@ public final class FlexibilityController extends StateController {
mTrackedJobs.get(js.getNumRequiredFlexibleConstraints()).remove(js);
}
- public void resetJobNumDroppedConstraints(JobStatus js, long nowElapsed) {
+ /**
+ * Updates applied and dropped constraints for the job.
+ */
+ public void updateFlexibleConstraints(JobStatus js, long nowElapsed) {
+ final int prevNumRequired = js.getNumRequiredFlexibleConstraints();
+
+ final int numAppliedConstraints =
+ Integer.bitCount(getRelevantAppliedConstraintsLocked(js));
+ js.setNumAppliedFlexibleConstraints(numAppliedConstraints);
+
final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed);
int toDrop = 0;
- final int jsMaxFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
- + (js.canApplyTransportAffinities() ? 1 : 0);
+ for (int i = 0; i < numAppliedConstraints; i++) {
+ if (curPercent >= mPercentToDropConstraints[i]) {
+ toDrop++;
+ }
+ }
+ js.setNumDroppedFlexibleConstraints(toDrop);
+
+ if (prevNumRequired == js.getNumRequiredFlexibleConstraints()) {
+ return;
+ }
+ mTrackedJobs.get(prevNumRequired).remove(js);
+ add(js);
+ }
+
+ /**
+ * Calculates the number of constraints that should be dropped for the job, based on how
+ * far along the job is into its lifecycle.
+ */
+ public void calculateNumDroppedConstraints(JobStatus js, long nowElapsed) {
+ final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed);
+ int toDrop = 0;
+ final int jsMaxFlexibleConstraints = js.getNumAppliedFlexibleConstraints();
for (int i = 0; i < jsMaxFlexibleConstraints; i++) {
if (curPercent >= mPercentToDropConstraints[i]) {
toDrop++;
}
}
- adjustJobsRequiredConstraints(
- js, js.getNumDroppedFlexibleConstraints() - toDrop, nowElapsed);
+ setNumDroppedFlexibleConstraints(js, toDrop);
}
/** Returns all tracked jobs. */
@@ -599,17 +639,14 @@ public final class FlexibilityController extends StateController {
}
/**
- * Adjusts number of required flexible constraints and sorts it into the tracker.
- * Returns false if the job status's number of flexible constraints is now 0.
+ * Updates the number of dropped flexible constraints and sorts it into the tracker.
*/
- public boolean adjustJobsRequiredConstraints(JobStatus js, int adjustBy, long nowElapsed) {
- if (adjustBy != 0) {
+ public void setNumDroppedFlexibleConstraints(JobStatus js, int numDropped) {
+ if (numDropped != js.getNumDroppedFlexibleConstraints()) {
remove(js);
- js.adjustNumRequiredFlexibleConstraints(adjustBy);
- js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
+ js.setNumDroppedFlexibleConstraints(numDropped);
add(js);
}
- return js.getNumRequiredFlexibleConstraints() > 0;
}
public int size() {
@@ -658,8 +695,10 @@ public final class FlexibilityController extends StateController {
if (DEBUG) {
Slog.d(TAG, "scheduleDropNumConstraintsAlarm: "
+ js.getSourcePackageName() + " " + js.getSourceUserId()
+ + " numApplied: " + js.getNumAppliedFlexibleConstraints()
+ " numRequired: " + js.getNumRequiredFlexibleConstraints()
- + " numSatisfied: " + Integer.bitCount(mSatisfiedFlexibleConstraints)
+ + " numSatisfied: " + Integer.bitCount(
+ mSatisfiedFlexibleConstraints & getRelevantAppliedConstraintsLocked(js))
+ " curTime: " + nowElapsed
+ " earliest: " + earliest
+ " latest: " + latest
@@ -669,8 +708,9 @@ public final class FlexibilityController extends StateController {
if (DEBUG) {
Slog.d(TAG, "deadline proximity met: " + js);
}
- mFlexibilityTracker.adjustJobsRequiredConstraints(js,
- -js.getNumRequiredFlexibleConstraints(), nowElapsed);
+ mFlexibilityTracker.setNumDroppedFlexibleConstraints(js,
+ js.getNumAppliedFlexibleConstraints());
+ mHandler.obtainMessage(MSG_UPDATE_JOB, js).sendToTarget();
return;
}
if (nextTimeElapsed == NO_LIFECYCLE_END) {
@@ -696,12 +736,15 @@ public final class FlexibilityController extends StateController {
final long nowElapsed = sElapsedRealtimeClock.millis();
for (int i = 0; i < expired.size(); i++) {
JobStatus js = expired.valueAt(i);
- boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE);
-
- if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1, nowElapsed)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Alarm fired for " + js.toShortString());
+ }
+ mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
+ if (js.getNumRequiredFlexibleConstraints() > 0) {
scheduleDropNumConstraintsAlarm(js, nowElapsed);
}
- if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) {
+ if (js.setFlexibilityConstraintSatisfied(nowElapsed,
+ isFlexibilitySatisfiedLocked(js))) {
changedJobs.add(js);
}
}
@@ -725,7 +768,9 @@ public final class FlexibilityController extends StateController {
final long nowElapsed = sElapsedRealtimeClock.millis();
final ArraySet<JobStatus> changedJobs = new ArraySet<>();
- for (int o = 0; o <= NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; ++o) {
+ final int numAppliedSystemWideConstraints = Integer.bitCount(
+ mAppliedConstraints & SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
+ for (int o = 0; o <= numAppliedSystemWideConstraints; ++o) {
final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker
.getJobsByNumRequiredConstraints(o);
@@ -744,6 +789,23 @@ public final class FlexibilityController extends StateController {
}
}
break;
+
+ case MSG_UPDATE_JOB:
+ synchronized (mLock) {
+ final JobStatus js = (JobStatus) msg.obj;
+ if (DEBUG) {
+ Slog.d("blah", "Checking on " + js.toShortString());
+ }
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (js.setFlexibilityConstraintSatisfied(
+ nowElapsed, isFlexibilitySatisfiedLocked(js))) {
+ // TODO(141645789): add method that will take a single job
+ ArraySet<JobStatus> changedJob = new ArraySet<>();
+ changedJob.add(js);
+ mStateChangedListener.onControllerStateChanged(changedJob);
+ }
+ }
+ break;
}
}
}
@@ -754,7 +816,8 @@ public final class FlexibilityController extends StateController {
/** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
private static final String FC_CONFIG_PREFIX = "fc_";
- static final String KEY_FLEXIBILITY_ENABLED = FC_CONFIG_PREFIX + "enable_flexibility";
+ @VisibleForTesting
+ static final String KEY_APPLIED_CONSTRAINTS = FC_CONFIG_PREFIX + "applied_constraints";
static final String KEY_DEADLINE_PROXIMITY_LIMIT =
FC_CONFIG_PREFIX + "flexibility_deadline_proximity_limit_ms";
static final String KEY_FALLBACK_FLEXIBILITY_DEADLINE =
@@ -770,7 +833,7 @@ public final class FlexibilityController extends StateController {
static final String KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS =
FC_CONFIG_PREFIX + "unseen_constraint_grace_period_ms";
- static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
+ static final int DEFAULT_APPLIED_CONSTRAINTS = 0;
@VisibleForTesting
static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
@VisibleForTesting
@@ -783,11 +846,8 @@ public final class FlexibilityController extends StateController {
@VisibleForTesting
static final long DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = 3 * DAY_IN_MILLIS;
- /**
- * If false the controller will not track new jobs
- * and the flexibility constraint will always be satisfied.
- */
- public boolean FLEXIBILITY_ENABLED = DEFAULT_FLEXIBILITY_ENABLED;
+ /** Which constraints to apply/consider in flex policy. */
+ public int APPLIED_CONSTRAINTS = DEFAULT_APPLIED_CONSTRAINTS;
/** How close to a jobs' deadline all flexible constraints will be dropped. */
public long DEADLINE_PROXIMITY_LIMIT_MS = DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS;
/** For jobs that lack a deadline, the time that will be used to drop all constraints by. */
@@ -811,16 +871,19 @@ public final class FlexibilityController extends StateController {
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
switch (key) {
- case KEY_FLEXIBILITY_ENABLED:
- FLEXIBILITY_ENABLED = properties.getBoolean(key, DEFAULT_FLEXIBILITY_ENABLED)
- && mDeviceSupportsFlexConstraints;
- if (mFlexibilityEnabled != FLEXIBILITY_ENABLED) {
- mFlexibilityEnabled = FLEXIBILITY_ENABLED;
+ case KEY_APPLIED_CONSTRAINTS:
+ APPLIED_CONSTRAINTS =
+ properties.getInt(key, DEFAULT_APPLIED_CONSTRAINTS)
+ & mSupportedFlexConstraints;
+ if (mAppliedConstraints != APPLIED_CONSTRAINTS) {
+ mAppliedConstraints = APPLIED_CONSTRAINTS;
mShouldReevaluateConstraints = true;
- if (mFlexibilityEnabled) {
+ if (mAppliedConstraints != 0) {
+ mFlexibilityEnabled = true;
mPrefetchController
.registerPrefetchChangedListener(mPrefetchChangedListener);
} else {
+ mFlexibilityEnabled = false;
mPrefetchController
.unRegisterPrefetchChangedListener(mPrefetchChangedListener);
}
@@ -893,7 +956,7 @@ public final class FlexibilityController extends StateController {
private int[] parsePercentToDropString(String s) {
String[] dropPercentString = s.split(",");
- int[] dropPercentInt = new int[NUM_FLEXIBLE_CONSTRAINTS];
+ int[] dropPercentInt = new int[Integer.bitCount(FLEXIBLE_CONSTRAINTS)];
if (dropPercentInt.length != dropPercentString.length) {
return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
}
@@ -922,7 +985,7 @@ public final class FlexibilityController extends StateController {
pw.println(":");
pw.increaseIndent();
- pw.print(KEY_FLEXIBILITY_ENABLED, FLEXIBILITY_ENABLED).println();
+ pw.print(KEY_APPLIED_CONSTRAINTS, APPLIED_CONSTRAINTS).println();
pw.print(KEY_DEADLINE_PROXIMITY_LIMIT, DEADLINE_PROXIMITY_LIMIT_MS).println();
pw.print(KEY_FALLBACK_FLEXIBILITY_DEADLINE, FALLBACK_FLEXIBILITY_DEADLINE_MS).println();
pw.print(KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index d1f575ef40c8..0d5d11e98860 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -24,7 +24,6 @@ import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-import static com.android.server.job.controllers.FlexibilityController.NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import android.annotation.ElapsedRealtimeLong;
@@ -155,7 +154,7 @@ public final class JobStatus {
/**
* Keeps track of how many flexible constraints must be satisfied for the job to execute.
*/
- private final int mNumRequiredFlexibleConstraints;
+ private int mNumAppliedFlexibleConstraints;
/**
* Number of required flexible constraints that have been dropped.
@@ -697,11 +696,7 @@ public final class JobStatus {
&& satisfiesMinWindowException
&& (numFailures + numSystemStops) != 1
&& lacksSomeFlexibleConstraints) {
- mNumRequiredFlexibleConstraints =
- NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mCanApplyTransportAffinities ? 1 : 0);
requiredConstraints |= CONSTRAINT_FLEXIBLE;
- } else {
- mNumRequiredFlexibleConstraints = 0;
}
this.requiredConstraints = requiredConstraints;
@@ -1527,9 +1522,14 @@ public final class JobStatus {
return (requiredConstraints & CONSTRAINT_FLEXIBLE) != 0;
}
+ /** Returns the number of flexible job constraints being applied to the job. */
+ public int getNumAppliedFlexibleConstraints() {
+ return mNumAppliedFlexibleConstraints;
+ }
+
/** Returns the number of flexible job constraints required to be satisfied to execute */
public int getNumRequiredFlexibleConstraints() {
- return mNumRequiredFlexibleConstraints - mNumDroppedFlexibleConstraints;
+ return mNumAppliedFlexibleConstraints - mNumDroppedFlexibleConstraints;
}
/**
@@ -2112,9 +2112,14 @@ public final class JobStatus {
}
/** Adjusts the number of required flexible constraints by the given number */
- public void adjustNumRequiredFlexibleConstraints(int adjustment) {
- mNumDroppedFlexibleConstraints = Math.max(0, Math.min(mNumRequiredFlexibleConstraints,
- mNumDroppedFlexibleConstraints - adjustment));
+ public void setNumAppliedFlexibleConstraints(int count) {
+ mNumAppliedFlexibleConstraints = count;
+ }
+
+ /** Sets the number of dropped flexible constraints to the given number */
+ public void setNumDroppedFlexibleConstraints(int count) {
+ mNumDroppedFlexibleConstraints = Math.max(0,
+ Math.min(mNumAppliedFlexibleConstraints, count));
}
/**
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt
index 9ffb70496c59..43caaecebdaf 100644
--- a/api/coverage/tools/ExtractFlaggedApis.kt
+++ b/api/coverage/tools/ExtractFlaggedApis.kt
@@ -23,35 +23,43 @@ import java.io.FileWriter
/** Usage: extract-flagged-apis <api text file> <output .pb file> */
fun main(args: Array<String>) {
var cb = ApiFile.parseApi(listOf(File(args[0])))
- val flagToApi = mutableMapOf<String, MutableList<String>>()
- cb.getPackages()
- .allClasses()
- .filter { it.methods().size > 0 }
- .forEach {
- for (method in it.methods()) {
- val flagValue =
- method.modifiers
- .findAnnotation("android.annotation.FlaggedApi")
- ?.findAttribute("value")
- ?.value
- ?.value()
- if (flagValue != null && flagValue is String) {
- val methodQualifiedName = "${it.qualifiedName()}.${method.name()}"
- if (flagToApi.containsKey(flagValue)) {
- flagToApi.get(flagValue)?.add(methodQualifiedName)
- } else {
- flagToApi.put(flagValue, mutableListOf(methodQualifiedName))
+ var builder = FlagApiMap.newBuilder()
+ for (pkg in cb.getPackages().packages) {
+ var packageName = pkg.qualifiedName()
+ pkg.allClasses()
+ .filter { it.methods().size > 0 }
+ .forEach {
+ for (method in it.methods()) {
+ val flagValue =
+ method.modifiers
+ .findAnnotation("android.annotation.FlaggedApi")
+ ?.findAttribute("value")
+ ?.value
+ ?.value()
+ if (flagValue != null && flagValue is String) {
+ var api =
+ JavaMethod.newBuilder()
+ .setPackageName(packageName)
+ .setClassName(it.fullName())
+ .setMethodName(method.name())
+ for (param in method.parameters()) {
+ api.addParameterTypes(param.type().toTypeString())
+ }
+ if (builder.containsFlagToApi(flagValue)) {
+ var updatedApis =
+ builder
+ .getFlagToApiOrThrow(flagValue)
+ .toBuilder()
+ .addJavaMethods(api)
+ .build()
+ builder.putFlagToApi(flagValue, updatedApis)
+ } else {
+ var apis = FlaggedApis.newBuilder().addJavaMethods(api).build()
+ builder.putFlagToApi(flagValue, apis)
+ }
}
}
}
- }
- var builder = FlagApiMap.newBuilder()
- for (flag in flagToApi.keys) {
- var flaggedApis = FlaggedApis.newBuilder()
- for (method in flagToApi.get(flag).orEmpty()) {
- flaggedApis.addFlaggedApi(FlaggedApi.newBuilder().setQualifiedName(method))
- }
- builder.putFlagToApi(flag, flaggedApis.build())
}
val flagApiMap = builder.build()
FileWriter(args[1]).use { it.write(flagApiMap.toString()) }
diff --git a/api/coverage/tools/extract_flagged_apis.proto b/api/coverage/tools/extract_flagged_apis.proto
index a858108a27b2..031d621b178f 100644
--- a/api/coverage/tools/extract_flagged_apis.proto
+++ b/api/coverage/tools/extract_flagged_apis.proto
@@ -25,10 +25,13 @@ message FlagApiMap {
}
message FlaggedApis {
- repeated FlaggedApi flagged_api = 1;
+ repeated JavaMethod java_methods = 1;
}
-message FlaggedApi {
- string qualified_name = 1;
+message JavaMethod {
+ string package_name = 1;
+ string class_name = 2;
+ string method_name = 3;
+ repeated string parameter_types = 4;
}
diff --git a/core/api/current.txt b/core/api/current.txt
index a761674f5f0b..8e5c4c417af2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1216,6 +1216,7 @@ package android {
field public static final int opticalInsetLeft = 16844168; // 0x1010588
field public static final int opticalInsetRight = 16844170; // 0x101058a
field public static final int opticalInsetTop = 16844169; // 0x1010589
+ field @FlaggedApi("android.content.pm.sdk_lib_independence") public static final int optional;
field public static final int order = 16843242; // 0x10101ea
field public static final int orderInCategory = 16843231; // 0x10101df
field public static final int ordering = 16843490; // 0x10102e2
@@ -18812,6 +18813,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE;
+ field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Float>> CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB;
@@ -19100,6 +19102,7 @@ package android.hardware.camera2 {
field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH = 2; // 0x2
field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4; // 0x4
field public static final int CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5; // 0x5
+ field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") public static final int CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY = 6; // 0x6
field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2; // 0x2
field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0; // 0x0
field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1; // 0x1
@@ -19165,6 +19168,8 @@ package android.hardware.camera2 {
field public static final int CONTROL_EXTENDED_SCENE_MODE_BOKEH_CONTINUOUS = 2; // 0x2
field public static final int CONTROL_EXTENDED_SCENE_MODE_BOKEH_STILL_CAPTURE = 1; // 0x1
field public static final int CONTROL_EXTENDED_SCENE_MODE_DISABLED = 0; // 0x0
+ field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") public static final int CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE = 1; // 0x1
+ field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") public static final int CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE = 0; // 0x0
field public static final int CONTROL_MODE_AUTO = 1; // 0x1
field public static final int CONTROL_MODE_OFF = 0; // 0x0
field public static final int CONTROL_MODE_OFF_KEEP_STATE = 3; // 0x3
@@ -19485,6 +19490,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_EFFECT_MODE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> CONTROL_ENABLE_ZSL;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_EXTENDED_SCENE_MODE;
+ field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_LOW_LIGHT_BOOST_STATE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_MODE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SCENE_MODE;
@@ -36905,6 +36911,7 @@ package android.provider {
field public static final String ACTION_REGIONAL_PREFERENCES_SETTINGS = "android.settings.REGIONAL_PREFERENCES_SETTINGS";
field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
field public static final String ACTION_REQUEST_MANAGE_MEDIA = "android.settings.REQUEST_MANAGE_MEDIA";
+ field @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") public static final String ACTION_REQUEST_MEDIA_ROUTING_CONTROL = "android.settings.REQUEST_MEDIA_ROUTING_CONTROL";
field public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = "android.settings.REQUEST_SCHEDULE_EXACT_ALARM";
field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE";
field public static final String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
@@ -43360,6 +43367,9 @@ package android.telephony {
field public static final String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final String KEY_MMS_USER_AGENT_STRING = "userAgent";
field public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY = "ntn_lte_rsrp_thresholds_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "ntn_lte_rsrq_thresholds_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "ntn_lte_rssnr_thresholds_int_array";
field public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network";
field public static final String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
@@ -43374,6 +43384,7 @@ package android.telephony {
field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int";
field public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = "opportunistic_network_max_backoff_time_long";
field public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT = "parameters_used_for_ntn_lte_signal_bar_int";
field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool";
field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
field public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT = "premium_capability_maximum_daily_notification_count_int";
@@ -53870,9 +53881,11 @@ package android.view {
method @Deprecated public android.view.Display getDefaultDisplay();
method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
method public default boolean isCrossWindowBlurEnabled();
+ method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void registerTrustedPresentationListener(@NonNull android.os.IBinder, @NonNull android.window.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer);
method public void removeViewImmediate(android.view.View);
+ method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void unregisterTrustedPresentationListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION";
@@ -60853,6 +60866,16 @@ package android.window {
method public void markSyncReady();
}
+ @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public final class TrustedPresentationThresholds implements android.os.Parcelable {
+ ctor @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int);
+ method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public int describeContents();
+ method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @NonNull public static final android.os.Parcelable.Creator<android.window.TrustedPresentationThresholds> CREATOR;
+ field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public final float minAlpha;
+ field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public final float minFractionRendered;
+ field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @IntRange(from=1) public final int stabilityRequirementMs;
+ }
+
}
package javax.microedition.khronos.egl {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a1465df963ad..46d7d76ce67d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3232,6 +3232,7 @@ package android.companion.virtual {
method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
+ method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDisplayImePolicy(int, int);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
}
@@ -4089,7 +4090,8 @@ package android.content.pm {
field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
- field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
+ field @Deprecated public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
+ field @FlaggedApi("android.content.pm.fix_duplicated_flags") public static final long MATCH_CLONE_PROFILE_LONG = 17179869184L; // 0x400000000L
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
field public static final int MATCH_INSTANT = 8388608; // 0x800000
@@ -4195,12 +4197,15 @@ package android.content.pm {
public final class UserProperties implements android.os.Parcelable {
method public int describeContents();
+ method public int getCrossProfileContentSharingStrategy();
method public int getShowInQuietMode();
method public int getShowInSharingSurfaces();
method public boolean isCredentialShareableWithParent();
method public boolean isMediaSharedWithParent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
+ field public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; // 0x1
+ field public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0; // 0x0
field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2
field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1
field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0
@@ -4536,6 +4541,7 @@ package android.hardware.camera2.extension {
@FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final class RequestProcessor.Request {
ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public RequestProcessor.Request(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<android.util.Pair<android.hardware.camera2.CaptureRequest.Key,java.lang.Object>>, int);
+ method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public java.util.List<android.util.Pair<android.hardware.camera2.CaptureRequest.Key,java.lang.Object>> getParameters();
}
@FlaggedApi("com.android.internal.camera.flags.concert_mode") public static interface RequestProcessor.RequestCallback {
@@ -17173,6 +17179,9 @@ package android.view {
method @NonNull public default java.util.List<android.content.ComponentName> notifyScreenshotListeners(int);
method public default void registerTaskFpsCallback(@IntRange(from=0) int, @NonNull java.util.concurrent.Executor, @NonNull android.window.TaskFpsCallback);
method public default void unregisterTaskFpsCallback(@NonNull android.window.TaskFpsCallback);
+ field public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; // 0x1
+ field public static final int DISPLAY_IME_POLICY_HIDE = 2; // 0x2
+ field public static final int DISPLAY_IME_POLICY_LOCAL = 0; // 0x0
}
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 7417137b3725..eaabda882da5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1166,6 +1166,7 @@ package android.content.pm {
public static final class UserProperties.Builder {
ctor public UserProperties.Builder();
method @NonNull public android.content.pm.UserProperties build();
+ method @NonNull public android.content.pm.UserProperties.Builder setCrossProfileContentSharingStrategy(int);
method @NonNull public android.content.pm.UserProperties.Builder setShowInQuietMode(int);
method @NonNull public android.content.pm.UserProperties.Builder setShowInSharingSurfaces(int);
}
@@ -3618,9 +3619,6 @@ package android.view {
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
method public default boolean shouldShowSystemDecors(int);
method @Nullable public default android.graphics.Bitmap snapshotTaskForRecents(@IntRange(from=0) int);
- field public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; // 0x1
- field public static final int DISPLAY_IME_POLICY_HIDE = 2; // 0x2
- field public static final int DISPLAY_IME_POLICY_LOCAL = 0; // 0x0
field public static final int LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP = 600; // 0x258
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index adaaee200895..8b39ed6fb411 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1544,11 +1544,12 @@ public final class ActivityThread extends ClientTransactionHandler
@Override
public void dumpMemInfo(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
- boolean dumpUnreachable, String[] args) {
+ boolean dumpUnreachable, boolean dumpAllocatorStats, String[] args) {
FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
PrintWriter pw = new FastPrintWriter(fout);
try {
- dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, dumpUnreachable);
+ dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly,
+ dumpUnreachable, dumpAllocatorStats);
} finally {
pw.flush();
IoUtils.closeQuietly(pfd);
@@ -1557,7 +1558,8 @@ public final class ActivityThread extends ClientTransactionHandler
@NeverCompile
private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
- boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable) {
+ boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
+ boolean dumpUnreachable, boolean dumpAllocatorStats) {
long nativeMax = Debug.getNativeHeapSize() / 1024;
long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
@@ -1710,6 +1712,9 @@ public final class ActivityThread extends ClientTransactionHandler
pw.println(" Unreachable memory");
pw.print(Debug.getUnreachableMemory(100, showContents));
}
+ if (dumpAllocatorStats) {
+ Debug.logAllocatorStats();
+ }
}
@NeverCompile
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 75d8c1012e27..5541e7aef160 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -129,7 +129,7 @@ oneway interface IApplicationThread {
void scheduleTrimMemory(int level);
void dumpMemInfo(in ParcelFileDescriptor fd, in Debug.MemoryInfo mem, boolean checkin,
boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable,
- in String[] args);
+ boolean dumpAllocatorLogs, in String[] args);
void dumpMemInfoProto(in ParcelFileDescriptor fd, in Debug.MemoryInfo mem,
boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable,
in String[] args);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1e538c52e635..90a265937082 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16640,6 +16640,7 @@ public class DevicePolicyManager {
== DEVICE_OWNER_TYPE_FINANCED;
}
+ // TODO(b/315298076): revert ag/25574027 and update the doc
/**
* Called by a device owner or profile owner of an organization-owned managed profile to enable
* or disable USB data signaling for the device. When disabled, USB data connections
@@ -16649,12 +16650,11 @@ public class DevicePolicyManager {
* {@link #canUsbDataSignalingBeDisabled()} to check whether enabling or disabling USB data
* signaling is supported on the device.
*
- * Starting from {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, after the USB data signaling
+ * Starting from Android 15, after the USB data signaling
* policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
* Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
* successfully set or not. This callback will contain:
* <ul>
- * li> The policy identifier {@link DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY}
* <li> The {@link TargetUser} that this policy relates to
* <li> The {@link PolicyUpdateResult}, which will be
* {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 3520c0b4d724..12229b12fb16 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -219,6 +219,10 @@ interface IVirtualDevice {
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
void setShowPointerIcon(boolean showPointerIcon);
+ /** Sets an IME policy for the given display. */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ void setDisplayImePolicy(int displayId, int policy);
+
/**
* Registers an intent interceptor that will intercept an intent attempting to launch
* when matching the provided IntentFilter and calls the callback with the intercepted
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 003dffb1f9c1..2abeeeecc1c6 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -53,6 +53,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.ArrayMap;
+import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
@@ -361,6 +362,14 @@ public class VirtualDeviceInternal {
}
}
+ void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
+ try {
+ mVirtualDevice.setDisplayImePolicy(displayId, policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
void addActivityListener(
@CallbackExecutor @NonNull Executor executor,
@NonNull VirtualDeviceManager.ActivityListener listener) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 41c90b96dc84..eef60f11fb1c 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -63,6 +63,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.Surface;
+import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
@@ -914,6 +915,24 @@ public final class VirtualDeviceManager {
}
/**
+ * Specifies the IME behavior on the given display. By default, all displays created by
+ * virtual devices have {@link WindowManager#DISPLAY_IME_POLICY_LOCAL}.
+ *
+ * @param displayId the ID of the display to change the IME policy for. It must be owned by
+ * this virtual device.
+ * @param policy the IME policy to use on that display
+ * @throws SecurityException if the display is not owned by this device or is not
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED trusted}
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @FlaggedApi(Flags.FLAG_VDM_CUSTOM_IME)
+ public void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
+ if (Flags.vdmCustomIme()) {
+ mVirtualDeviceInternal.setDisplayImePolicy(displayId, policy);
+ }
+ }
+
+ /**
* Adds an activity listener to listen for events such as top activity change or virtual
* display task stack became empty.
*
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 51f3d8ebab01..e395127dfaf3 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1244,7 +1244,7 @@ public class PackageInstaller {
* {@code statusReceiver} if timeout happens before commit.
* @throws IllegalArgumentException if the {@code statusReceiver} from an immutable
* {@link android.app.PendingIntent} when caller has a target SDK of API
- * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+ * 35 or above.
*/
public void commitSessionAfterInstallConstraintsAreMet(int sessionId,
@NonNull IntentSender statusReceiver, @NonNull InstallConstraints constraints,
@@ -1954,7 +1954,7 @@ public class PackageInstaller {
* {@link #openWrite(String, long, long)} are still open.
* @throws IllegalArgumentException if the {@code statusReceiver} from an immutable
* {@link android.app.PendingIntent} when caller has a target SDK of API
- * version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+ * version 35 or above.
*
* @see android.app.admin.DevicePolicyManager
* @see #requestUserPreapproval
@@ -1985,7 +1985,7 @@ public class PackageInstaller {
* individual status codes on how to handle them.
* @throws IllegalArgumentException if the {@code statusReceiver} from an immutable
* {@link android.app.PendingIntent} when caller has a target SDK of API
- * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+ * 35 or above.
*
* @hide
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 607e9043e9bf..f865a36ea544 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -919,6 +919,10 @@ public abstract class PackageManager {
@Retention(RetentionPolicy.SOURCE)
public @interface InstrumentationInfoFlags {}
+ //-------------------------------------------------------------------------
+ // Beginning of GET_ and MATCH_ flags
+ //-------------------------------------------------------------------------
+
/**
* {@link PackageInfo} flag: return information about
* activities in the package in {@link PackageInfo#activities}.
@@ -1216,30 +1220,21 @@ public abstract class PackageManager {
*/
public static final int MATCH_DIRECT_BOOT_AUTO = 0x10000000;
+ /** @hide */
+ @Deprecated
+ public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
+
/**
- * {@link ResolveInfo} flag: allow matching components across clone profile
- * <p>
- * This flag is used only for query and not resolution, the default behaviour would be to
- * restrict querying across clone profile. This flag would be honored only if caller have
- * permission {@link Manifest.permission.QUERY_CLONED_APPS}.
+ * @deprecated Use {@link #MATCH_CLONE_PROFILE_LONG} instead.
*
- * @hide
- * <p>
+ * @hide
*/
+ @SuppressLint("UnflaggedApi") // Just adding the @Deprecated annotation
+ @Deprecated
@SystemApi
public static final int MATCH_CLONE_PROFILE = 0x20000000;
/**
- * @deprecated Use {@link #GET_ATTRIBUTIONS_LONG} to avoid unintended sign extension.
- */
- @Deprecated
- public static final int GET_ATTRIBUTIONS = 0x80000000;
-
- /** @hide */
- @Deprecated
- public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
-
- /**
* {@link PackageInfo} flag: include system apps that are in the uninstalled state and have
* been set to be hidden until installed via {@link #setSystemAppState}.
* @hide
@@ -1257,6 +1252,12 @@ public abstract class PackageManager {
public static final int MATCH_APEX = 0x40000000;
/**
+ * @deprecated Use {@link #GET_ATTRIBUTIONS_LONG} to avoid unintended sign extension.
+ */
+ @Deprecated
+ public static final int GET_ATTRIBUTIONS = 0x80000000;
+
+ /**
* {@link PackageInfo} flag: return all attributions declared in the package manifest
*/
public static final long GET_ATTRIBUTIONS_LONG = 0x80000000L;
@@ -1282,6 +1283,23 @@ public abstract class PackageManager {
public static final long MATCH_QUARANTINED_COMPONENTS = 1L << 33;
/**
+ * {@link ResolveInfo} flag: allow matching components across clone profile
+ * <p>
+ * This flag is used only for query and not resolution, the default behaviour would be to
+ * restrict querying across clone profile. This flag would be honored only if caller have
+ * permission {@link Manifest.permission.QUERY_CLONED_APPS}.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.content.pm.Flags.FLAG_FIX_DUPLICATED_FLAGS)
+ @SystemApi
+ public static final long MATCH_CLONE_PROFILE_LONG = 1L << 34;
+
+ //-------------------------------------------------------------------------
+ // End of GET_ and MATCH_ flags
+ //-------------------------------------------------------------------------
+
+ /**
* Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
* resolving an intent that matches the {@code CrossProfileIntentFilter},
* the current profile will be skipped. Only activities in the target user
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index fdd2aa1fe5fa..25ba72551b04 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -142,8 +142,10 @@ public final class SharedLibraryInfo implements Parcelable {
mName = parcel.readString8();
mVersion = parcel.readLong();
mType = parcel.readInt();
- mDeclaringPackage = parcel.readParcelable(null, android.content.pm.VersionedPackage.class);
- mDependentPackages = parcel.readArrayList(null, android.content.pm.VersionedPackage.class);
+ mDeclaringPackage =
+ parcel.readParcelable(null, android.content.pm.VersionedPackage.class);
+ mDependentPackages =
+ parcel.readArrayList(null, android.content.pm.VersionedPackage.class);
mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
mIsNative = parcel.readBoolean();
}
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 56e8291f25e9..57749d43eb37 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -68,6 +68,11 @@ public final class UserProperties implements Parcelable {
"authAlwaysRequiredToDisableQuietMode";
private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent";
private static final String ATTR_ALWAYS_VISIBLE = "alwaysVisible";
+ private static final String ATTR_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING =
+ "allowStoppingUserWithDelayedLocking";
+
+ private static final String ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY =
+ "crossProfileContentSharingStrategy";
/** Index values of each property (to indicate whether they are present in this object). */
@IntDef(prefix = "INDEX_", value = {
@@ -86,6 +91,8 @@ public final class UserProperties implements Parcelable {
INDEX_SHOW_IN_QUIET_MODE,
INDEX_SHOW_IN_SHARING_SURFACES,
INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE,
+ INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY,
+ INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING,
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -105,6 +112,8 @@ public final class UserProperties implements Parcelable {
private static final int INDEX_SHOW_IN_QUIET_MODE = 12;
private static final int INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE = 13;
private static final int INDEX_SHOW_IN_SHARING_SURFACES = 14;
+ private static final int INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = 15;
+ private static final int INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING = 16;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -365,6 +374,45 @@ public final class UserProperties implements Parcelable {
*/
@SuppressLint("UnflaggedApi") // b/306636213
public static final int SHOW_IN_SHARING_SURFACES_NO = SHOW_IN_LAUNCHER_NO;
+ /**
+ * Possible values for cross profile content sharing strategy for this profile.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"CROSS_PROFILE_CONTENT_SHARING_STRATEGY_"}, value = {
+ CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION,
+ CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CrossProfileContentSharingStrategy {
+ }
+
+ /**
+ * Signifies that cross-profile content sharing strategy, both to and from this profile, should
+ * not be delegated to any other user/profile.
+ * For ex:
+ * If this property is set for a profile, content sharing applications (such as Android
+ * Sharesheet), should not delegate the decision to share content between that profile and
+ * another profile to whether content sharing is allowed between any other profile/user related
+ * to those profiles. They should instead decide, based upon whether content sharing is
+ * specifically allowed between the two profiles in question.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0;
+
+ /**
+ * Signifies that cross-profile content sharing strategy, both to and from this profile, should
+ * be based upon the strategy used by the parent user of the profile.
+ * For ex:
+ * If this property is set for a profile A, content sharing applications (such as Android
+ * Sharesheet), should share content between profile A and profile B, based upon whether content
+ * sharing is allowed between the parent of profile A and profile B.
+ * If it's also set for profile B, then decision should, in turn be made by considering content
+ * sharing strategy between the parents of both profiles.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1;
+
/**
* Creates a UserProperties (intended for the SystemServer) that stores a reference to the given
@@ -406,6 +454,7 @@ public final class UserProperties implements Parcelable {
setCrossProfileIntentResolutionStrategy(orig.getCrossProfileIntentResolutionStrategy());
setDeleteAppWithParent(orig.getDeleteAppWithParent());
setAlwaysVisible(orig.getAlwaysVisible());
+ setAllowStoppingUserWithDelayedLocking(orig.getAllowStoppingUserWithDelayedLocking());
}
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
@@ -423,6 +472,7 @@ public final class UserProperties implements Parcelable {
setCredentialShareableWithParent(orig.isCredentialShareableWithParent());
setShowInQuietMode(orig.getShowInQuietMode());
setShowInSharingSurfaces(orig.getShowInSharingSurfaces());
+ setCrossProfileContentSharingStrategy(orig.getCrossProfileContentSharingStrategy());
}
/**
@@ -680,6 +730,11 @@ public final class UserProperties implements Parcelable {
this.mUpdateCrossProfileIntentFiltersOnOTA = val;
setPresent(INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA);
}
+ /**
+ Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during
+ OTA update between user-parent
+ */
+ private boolean mUpdateCrossProfileIntentFiltersOnOTA;
/**
* Returns whether a profile shares media with its parent user.
@@ -741,12 +796,38 @@ public final class UserProperties implements Parcelable {
}
private boolean mAuthAlwaysRequiredToDisableQuietMode;
- /*
- Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during
- OTA update between user-parent
+ /**
+ * Returns whether a user (usually a profile) is allowed to leave the CE storage unlocked when
+ * stopped.
+ *
+ * <p> Setting this property to true will enable the user's CE storage to remain unlocked when
+ * the user is stopped using
+ * {@link com.android.server.am.ActivityManagerService#stopUserWithDelayedLocking(int,
+ * boolean, IStopUserCallback)}.
+ *
+ * <p> When this property is false, delayed locking may still be applicable at a global
+ * level for all users via the {@code config_multiuserDelayUserDataLocking}. That is, delayed
+ * locking for a user can happen if either the device configuration is set or if this property
+ * is set. When both, the config and the property value is false, the user storage is always
+ * locked when the user is stopped.
+ * @hide
*/
- private boolean mUpdateCrossProfileIntentFiltersOnOTA;
-
+ public boolean getAllowStoppingUserWithDelayedLocking() {
+ if (isPresent(INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING)) {
+ return mAllowStoppingUserWithDelayedLocking;
+ }
+ if (mDefaultProperties != null) {
+ return mDefaultProperties.mAllowStoppingUserWithDelayedLocking;
+ }
+ throw new SecurityException(
+ "You don't have permission to query allowStoppingUserWithDelayedLocking");
+ }
+ /** @hide */
+ public void setAllowStoppingUserWithDelayedLocking(boolean val) {
+ this.mAllowStoppingUserWithDelayedLocking = val;
+ setPresent(INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING);
+ }
+ private boolean mAllowStoppingUserWithDelayedLocking;
/**
* Returns the user's {@link CrossProfileIntentFilterAccessControlLevel}.
@@ -776,8 +857,7 @@ public final class UserProperties implements Parcelable {
private @CrossProfileIntentFilterAccessControlLevel int mCrossProfileIntentFilterAccessControl;
/**
- * Returns the user's {@link CrossProfileIntentResolutionStrategy}. If not explicitly
- * configured, default value is {@link #CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT}.
+ * Returns the user's {@link CrossProfileIntentResolutionStrategy}.
* @return user's {@link CrossProfileIntentResolutionStrategy}.
*
* @hide
@@ -792,11 +872,8 @@ public final class UserProperties implements Parcelable {
throw new SecurityException("You don't have permission to query "
+ "crossProfileIntentResolutionStrategy");
}
- /**
- * Sets {@link CrossProfileIntentResolutionStrategy} for the user.
- * @param val resolution strategy for user
- * @hide
- */
+
+ /** @hide */
public void setCrossProfileIntentResolutionStrategy(
@CrossProfileIntentResolutionStrategy int val) {
this.mCrossProfileIntentResolutionStrategy = val;
@@ -804,6 +881,39 @@ public final class UserProperties implements Parcelable {
}
private @CrossProfileIntentResolutionStrategy int mCrossProfileIntentResolutionStrategy;
+ /**
+ * Returns the user's {@link CrossProfileContentSharingStrategy}.
+ *
+ * Content sharing applications, such as Android Sharesheet allow sharing of content
+ * (an image, for ex.) between profiles, based upon cross-profile access checks between the
+ * originating and destined profile.
+ * In some cases however, we may want another user (such as profile parent) to serve as the
+ * delegated user to be used for such checks.
+ * To effect the same, clients can fetch this property and accordingly replace the
+ * originating/destined profile by another user for cross-profile access checks.
+ *
+ * @return user's {@link CrossProfileContentSharingStrategy}.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public @CrossProfileContentSharingStrategy int getCrossProfileContentSharingStrategy() {
+ if (isPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY)) {
+ return mCrossProfileContentSharingStrategy;
+ }
+ if (mDefaultProperties != null) {
+ return mDefaultProperties.mCrossProfileContentSharingStrategy;
+ }
+ throw new SecurityException("You don't have permission to query "
+ + "crossProfileContentSharingStrategy");
+ }
+
+ /** @hide */
+ public void setCrossProfileContentSharingStrategy(
+ @CrossProfileContentSharingStrategy int val) {
+ this.mCrossProfileContentSharingStrategy = val;
+ setPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY);
+ }
+ private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy;
+
@Override
public String toString() {
@@ -825,8 +935,11 @@ public final class UserProperties implements Parcelable {
+ ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
+ ", mAuthAlwaysRequiredToDisableQuietMode="
+ isAuthAlwaysRequiredToDisableQuietMode()
+ + ", mAllowStoppingUserWithDelayedLocking="
+ + getAllowStoppingUserWithDelayedLocking()
+ ", mDeleteAppWithParent=" + getDeleteAppWithParent()
+ ", mAlwaysVisible=" + getAlwaysVisible()
+ + ", mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy()
+ "}";
}
@@ -854,8 +967,12 @@ public final class UserProperties implements Parcelable {
+ isCredentialShareableWithParent());
pw.println(prefix + " mAuthAlwaysRequiredToDisableQuietMode="
+ isAuthAlwaysRequiredToDisableQuietMode());
+ pw.println(prefix + " mAllowStoppingUserWithDelayedLocking="
+ + getAllowStoppingUserWithDelayedLocking());
pw.println(prefix + " mDeleteAppWithParent=" + getDeleteAppWithParent());
pw.println(prefix + " mAlwaysVisible=" + getAlwaysVisible());
+ pw.println(prefix + " mCrossProfileContentSharingStrategy="
+ + getCrossProfileContentSharingStrategy());
}
/**
@@ -928,12 +1045,17 @@ public final class UserProperties implements Parcelable {
case ATTR_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE:
setAuthAlwaysRequiredToDisableQuietMode(parser.getAttributeBoolean(i));
break;
+ case ATTR_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING:
+ setAllowStoppingUserWithDelayedLocking(parser.getAttributeBoolean(i));
+ break;
case ATTR_DELETE_APP_WITH_PARENT:
setDeleteAppWithParent(parser.getAttributeBoolean(i));
break;
case ATTR_ALWAYS_VISIBLE:
setAlwaysVisible(parser.getAttributeBoolean(i));
break;
+ case ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY:
+ setCrossProfileContentSharingStrategy(parser.getAttributeInt(i));
default:
Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
}
@@ -1000,6 +1122,10 @@ public final class UserProperties implements Parcelable {
serializer.attributeBoolean(null, ATTR_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE,
mAuthAlwaysRequiredToDisableQuietMode);
}
+ if (isPresent(INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING)) {
+ serializer.attributeBoolean(null, ATTR_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING,
+ mAllowStoppingUserWithDelayedLocking);
+ }
if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) {
serializer.attributeBoolean(null, ATTR_DELETE_APP_WITH_PARENT,
mDeleteAppWithParent);
@@ -1008,6 +1134,10 @@ public final class UserProperties implements Parcelable {
serializer.attributeBoolean(null, ATTR_ALWAYS_VISIBLE,
mAlwaysVisible);
}
+ if (isPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY)) {
+ serializer.attributeInt(null, ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY,
+ mCrossProfileContentSharingStrategy);
+ }
}
// For use only with an object that has already had any permission-lacking fields stripped out.
@@ -1027,8 +1157,10 @@ public final class UserProperties implements Parcelable {
dest.writeBoolean(mMediaSharedWithParent);
dest.writeBoolean(mCredentialShareableWithParent);
dest.writeBoolean(mAuthAlwaysRequiredToDisableQuietMode);
+ dest.writeBoolean(mAllowStoppingUserWithDelayedLocking);
dest.writeBoolean(mDeleteAppWithParent);
dest.writeBoolean(mAlwaysVisible);
+ dest.writeInt(mCrossProfileContentSharingStrategy);
}
/**
@@ -1052,8 +1184,10 @@ public final class UserProperties implements Parcelable {
mMediaSharedWithParent = source.readBoolean();
mCredentialShareableWithParent = source.readBoolean();
mAuthAlwaysRequiredToDisableQuietMode = source.readBoolean();
+ mAllowStoppingUserWithDelayedLocking = source.readBoolean();
mDeleteAppWithParent = source.readBoolean();
mAlwaysVisible = source.readBoolean();
+ mCrossProfileContentSharingStrategy = source.readInt();
}
@Override
@@ -1098,8 +1232,11 @@ public final class UserProperties implements Parcelable {
private boolean mMediaSharedWithParent = false;
private boolean mCredentialShareableWithParent = false;
private boolean mAuthAlwaysRequiredToDisableQuietMode = false;
+ private boolean mAllowStoppingUserWithDelayedLocking = false;
private boolean mDeleteAppWithParent = false;
private boolean mAlwaysVisible = false;
+ private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy =
+ CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION;
/**
* @hide
@@ -1215,6 +1352,16 @@ public final class UserProperties implements Parcelable {
return this;
}
+ /** Sets the value for {@link #mAllowStoppingUserWithDelayedLocking}
+ * @hide
+ */
+ public Builder setAllowStoppingUserWithDelayedLocking(
+ boolean allowStoppingUserWithDelayedLocking) {
+ mAllowStoppingUserWithDelayedLocking =
+ allowStoppingUserWithDelayedLocking;
+ return this;
+ }
+
/** Sets the value for {@link #mDeleteAppWithParent}
* @hide
*/
@@ -1231,6 +1378,19 @@ public final class UserProperties implements Parcelable {
return this;
}
+ /** Sets the value for {@link #mCrossProfileContentSharingStrategy}
+ * @hide
+ */
+
+ @TestApi
+ @SuppressLint("UnflaggedApi") // b/306636213
+ @NonNull
+ public Builder setCrossProfileContentSharingStrategy(@CrossProfileContentSharingStrategy
+ int crossProfileContentSharingStrategy) {
+ mCrossProfileContentSharingStrategy = crossProfileContentSharingStrategy;
+ return this;
+ }
+
/** Builds a UserProperties object with *all* values populated.
* @hide
*/
@@ -1252,8 +1412,10 @@ public final class UserProperties implements Parcelable {
mMediaSharedWithParent,
mCredentialShareableWithParent,
mAuthAlwaysRequiredToDisableQuietMode,
+ mAllowStoppingUserWithDelayedLocking,
mDeleteAppWithParent,
- mAlwaysVisible);
+ mAlwaysVisible,
+ mCrossProfileContentSharingStrategy);
}
} // end Builder
@@ -1271,8 +1433,10 @@ public final class UserProperties implements Parcelable {
boolean mediaSharedWithParent,
boolean credentialShareableWithParent,
boolean authAlwaysRequiredToDisableQuietMode,
+ boolean allowStoppingUserWithDelayedLocking,
boolean deleteAppWithParent,
- boolean alwaysVisible) {
+ boolean alwaysVisible,
+ @CrossProfileContentSharingStrategy int crossProfileContentSharingStrategy) {
mDefaultProperties = null;
setShowInLauncher(showInLauncher);
setStartWithParent(startWithParent);
@@ -1288,7 +1452,9 @@ public final class UserProperties implements Parcelable {
setCredentialShareableWithParent(credentialShareableWithParent);
setAuthAlwaysRequiredToDisableQuietMode(
authAlwaysRequiredToDisableQuietMode);
+ setAllowStoppingUserWithDelayedLocking(allowStoppingUserWithDelayedLocking);
setDeleteAppWithParent(deleteAppWithParent);
setAlwaysVisible(alwaysVisible);
+ setCrossProfileContentSharingStrategy(crossProfileContentSharingStrategy);
}
}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 1b90570e4609..b04b7badbae0 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -108,3 +108,10 @@ flag {
description: "Feature flag to reduce app crashes caused by split installs with INSTALL_DONT_KILL"
bug: "291212866"
}
+
+flag {
+ name: "fix_duplicated_flags"
+ namespace: "package_manager_service"
+ description: "Feature flag to fix duplicated PackageManager flag values"
+ bug: "314815969"
+}
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index bab84aadc73b..f876eebe64c1 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -26,4 +26,11 @@ flag {
name: "new_settings_intents"
description: "Enables settings intents to redirect to new settings page"
bug: "307587989"
+}
+
+flag {
+ namespace: "credential_manager"
+ name: "new_settings_ui"
+ description: "Enables new settings UI for VIC"
+ bug: "315209085"
} \ No newline at end of file
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index dec5b9c7352c..e3a552023e4b 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1355,6 +1355,27 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<Boolean>("android.control.autoframingAvailable", boolean.class);
/**
+ * <p>The operating luminance range of low light boost measured in lux (lx).</p>
+ * <p><b>Range of valid values:</b><br></p>
+ * <p>The lower bound indicates the lowest scene luminance value the AE mode
+ * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' can operate within. Scenes of lower luminance
+ * than this may receive less brightening, increased noise, or artifacts.</p>
+ * <p>The upper bound indicates the luminance threshold at the point when the mode is enabled.
+ * For example, 'Range[0.3, 30.0]' defines 0.3 lux being the lowest scene luminance the
+ * mode can reliably support. 30.0 lux represents the threshold when this mode is
+ * activated. Scenes measured at less than or equal to 30 lux will activate low light
+ * boost.</p>
+ * <p>If this key is defined, then the AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' will
+ * also be present.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST)
+ public static final Key<android.util.Range<Float>> CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE =
+ new Key<android.util.Range<Float>>("android.control.lowLightBoostInfoLuminanceRange", new TypeReference<android.util.Range<Float>>() {{ }});
+
+ /**
* <p>List of edge enhancement modes for {@link CaptureRequest#EDGE_MODE android.edge.mode} that are supported by this camera
* device.</p>
* <p>Full-capability camera devices must always support OFF; camera devices that support
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 002c0b207506..bcce4b65be18 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1844,7 +1844,7 @@ public final class CameraManager {
* Remaps Camera Ids in the CameraService.
*
* @hide
- */
+ */
@RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA)
public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping)
throws CameraAccessException, SecurityException, IllegalArgumentException {
@@ -1852,6 +1852,29 @@ public final class CameraManager {
}
/**
+ * Injects session params into existing clients in the CameraService.
+ *
+ * @param cameraId The camera id of client to inject session params into.
+ * If no such client exists for cameraId, no injection will
+ * take place.
+ * @param sessionParams A {@link CaptureRequest} object containing the
+ * the sessionParams to inject into the existing client.
+ *
+ * @throws CameraAccessException {@link CameraAccessException#CAMERA_DISCONNECTED} will be
+ * thrown if camera service is not available. Further, if
+ * if no such client exists for cameraId,
+ * {@link CameraAccessException#CAMERA_ERROR} will be thrown.
+ * @throws SecurityException If the caller does not have permission to inject session
+ * params
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA)
+ public void injectSessionParams(@NonNull String cameraId, @NonNull CaptureRequest sessionParams)
+ throws CameraAccessException, SecurityException {
+ CameraManagerGlobal.get().injectSessionParams(cameraId, sessionParams);
+ }
+
+ /**
* Reports {@link CameraExtensionSessionStats} to the {@link ICameraService} to be logged for
* currently active session. Validation is done downstream.
*
@@ -2110,6 +2133,30 @@ public final class CameraManager {
}
}
+ /** Injects session params into an existing client for cameraid. */
+ public void injectSessionParams(@NonNull String cameraId,
+ @NonNull CaptureRequest sessionParams)
+ throws CameraAccessException, SecurityException {
+ synchronized (mLock) {
+ ICameraService cameraService = getCameraService();
+ if (cameraService == null) {
+ throw new CameraAccessException(
+ CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable.");
+ }
+
+ try {
+ cameraService.injectSessionParams(cameraId, sessionParams.getNativeMetadata());
+ } catch (ServiceSpecificException e) {
+ throwAsPublicException(e);
+ } catch (RemoteException e) {
+ throw new CameraAccessException(
+ CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable.");
+ }
+ }
+ }
+
private String[] extractCameraIdListLocked() {
String[] cameraIds = null;
int idCount = 0;
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 003718e6b54e..765a8f7c842c 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2333,6 +2333,46 @@ public abstract class CameraMetadata<TKey> {
*/
public static final int CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5;
+ /**
+ * <p>Like 'ON' but applies additional brightness boost in low light scenes.</p>
+ * <p>When the scene lighting conditions are within the range defined by
+ * {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange} this mode will apply additional
+ * brightness boost.</p>
+ * <p>This mode will automatically adjust the intensity of low light boost applied
+ * according to the scene lighting conditions. A darker scene will receive more boost
+ * while a brighter scene will receive less boost.</p>
+ * <p>This mode can ignore the set target frame rate to allow more light to be captured
+ * which can result in choppier motion. The frame rate can extend to lower than the
+ * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges} but will not go below 10 FPS. This mode
+ * can also increase the sensor sensitivity gain which can result in increased luma
+ * and chroma noise. The sensor sensitivity gain can extend to higher values beyond
+ * {@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}. This mode may also apply additional
+ * processing to recover details in dark and bright areas of the image,and noise
+ * reduction at high sensitivity gain settings to manage the trade-off between light
+ * sensitivity and capture noise.</p>
+ * <p>This mode is restricted to two output surfaces. One output surface type can either
+ * be SurfaceView or TextureView. Another output surface type can either be MediaCodec
+ * or MediaRecorder. This mode cannot be used with a target FPS range higher than 30
+ * FPS.</p>
+ * <p>If the session configuration is not supported, the AE mode reported in the
+ * CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p>
+ * <p>The application can observe the CapturerResult field
+ * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or
+ * 'INACTIVE'.</p>
+ * <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
+ * upper bound lux value defined by {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange}.
+ * This mode will be 'INACTIVE' once the scene lighting condition is greater than the
+ * upper bound lux value defined by {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange}.</p>
+ *
+ * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
+ * @see CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE
+ * @see CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE
+ * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
+ * @see CaptureRequest#CONTROL_AE_MODE
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST)
+ public static final int CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY = 6;
+
//
// Enumeration values for CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
//
@@ -4074,6 +4114,24 @@ public abstract class CameraMetadata<TKey> {
public static final int CONTROL_AUTOFRAMING_STATE_CONVERGED = 2;
//
+ // Enumeration values for CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE
+ //
+
+ /**
+ * <p>The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled but not applied.</p>
+ * @see CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST)
+ public static final int CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE = 0;
+
+ /**
+ * <p>The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled and applied.</p>
+ * @see CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST)
+ public static final int CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE = 1;
+
+ //
// Enumeration values for CaptureResult#FLASH_STATE
//
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 35f295a36d87..ab4406c37c8e 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2814,6 +2814,31 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<Integer>("android.control.autoframingState", int.class);
/**
+ * <p>Current state of the low light boost AE mode.</p>
+ * <p>When low light boost is enabled by setting the AE mode to
+ * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
+ * boost when the light level threshold is exceeded.</p>
+ * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
+ * indicate when it is not being applied by returning 'INACTIVE'.</p>
+ * <p>This key will be absent from the CaptureResult if AE mode is not set to
+ * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE INACTIVE}</li>
+ * <li>{@link #CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE ACTIVE}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE
+ * @see #CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST)
+ public static final Key<Integer> CONTROL_LOW_LIGHT_BOOST_STATE =
+ new Key<Integer>("android.control.lowLightBoostState", int.class);
+
+ /**
* <p>Operation mode for edge
* enhancement.</p>
* <p>Edge enhancement improves sharpness and details in the captured image. OFF means
diff --git a/core/java/android/hardware/camera2/extension/RequestProcessor.java b/core/java/android/hardware/camera2/extension/RequestProcessor.java
index 7c099d67e6e9..bf5ea12df358 100644
--- a/core/java/android/hardware/camera2/extension/RequestProcessor.java
+++ b/core/java/android/hardware/camera2/extension/RequestProcessor.java
@@ -250,7 +250,7 @@ public final class RequestProcessor {
*/
@FlaggedApi(Flags.FLAG_CONCERT_MODE)
@NonNull
- List<Pair<CaptureRequest.Key, Object>> getParameters() {
+ public List<Pair<CaptureRequest.Key, Object>> getParameters() {
return mParameters;
}
diff --git a/core/java/android/os/CoolingDevice.java b/core/java/android/os/CoolingDevice.java
index 4ddcd9d4ff8b..06ec72051c2f 100644
--- a/core/java/android/os/CoolingDevice.java
+++ b/core/java/android/os/CoolingDevice.java
@@ -55,7 +55,11 @@ public final class CoolingDevice implements Parcelable {
TYPE_TPU,
TYPE_POWER_AMPLIFIER,
TYPE_DISPLAY,
- TYPE_SPEAKER
+ TYPE_SPEAKER,
+ TYPE_WIFI,
+ TYPE_CAMERA,
+ TYPE_FLASHLIGHT,
+ TYPE_USB_PORT
})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
@@ -84,6 +88,14 @@ public final class CoolingDevice implements Parcelable {
public static final int TYPE_DISPLAY = CoolingType.DISPLAY;
/** Speaker cooling device */
public static final int TYPE_SPEAKER = CoolingType.SPEAKER;
+ /** WiFi cooling device */
+ public static final int TYPE_WIFI = CoolingType.WIFI;
+ /** Camera cooling device */
+ public static final int TYPE_CAMERA = CoolingType.CAMERA;
+ /** Flashlight cooling device */
+ public static final int TYPE_FLASHLIGHT = CoolingType.FLASHLIGHT;
+ /** USB PORT cooling device */
+ public static final int TYPE_USB_PORT = CoolingType.USB_PORT;
/**
* Verify a valid cooling device type.
@@ -91,7 +103,7 @@ public final class CoolingDevice implements Parcelable {
* @return true if a cooling device type is valid otherwise false.
*/
public static boolean isValidType(@Type int type) {
- return type >= TYPE_FAN && type <= TYPE_SPEAKER;
+ return type >= TYPE_FAN && type <= TYPE_USB_PORT;
}
public CoolingDevice(long value, @Type int type, @NonNull String name) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index c527cb5344c1..04d6f61d84c9 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2702,4 +2702,13 @@ public final class Debug
* @hide
*/
public static native boolean isVmapStack();
+
+ /**
+ * Log internal statistics about the allocator.
+ * @return true if the statistics were logged properly, false if not.
+ *
+ * @hide
+ */
+ public static native boolean logAllocatorStats();
+
}
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index a138431f745a..2e12278a0ad3 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -79,7 +79,13 @@ public final class Temperature implements Parcelable {
TYPE_TPU,
TYPE_DISPLAY,
TYPE_MODEM,
- TYPE_SOC
+ TYPE_SOC,
+ TYPE_WIFI,
+ TYPE_CAMERA,
+ TYPE_FLASHLIGHT,
+ TYPE_SPEAKER,
+ TYPE_AMBIENT,
+ TYPE_POGO
})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
@@ -101,6 +107,12 @@ public final class Temperature implements Parcelable {
public static final int TYPE_DISPLAY = TemperatureType.DISPLAY;
public static final int TYPE_MODEM = TemperatureType.MODEM;
public static final int TYPE_SOC = TemperatureType.SOC;
+ public static final int TYPE_WIFI = TemperatureType.WIFI;
+ public static final int TYPE_CAMERA = TemperatureType.CAMERA;
+ public static final int TYPE_FLASHLIGHT = TemperatureType.FLASHLIGHT;
+ public static final int TYPE_SPEAKER = TemperatureType.SPEAKER;
+ public static final int TYPE_AMBIENT = TemperatureType.AMBIENT;
+ public static final int TYPE_POGO = TemperatureType.POGO;
/**
* Verify a valid Temperature type.
@@ -108,7 +120,7 @@ public final class Temperature implements Parcelable {
* @return true if a Temperature type is valid otherwise false.
*/
public static boolean isValidType(@Type int type) {
- return type >= TYPE_UNKNOWN && type <= TYPE_SOC;
+ return type >= TYPE_UNKNOWN && type <= TYPE_POGO;
}
/**
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 980c13c5c2d6..145981c92283 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -76,3 +76,11 @@ flag {
description: "Guards the ADPF GPU APIs."
bug: "284324521"
}
+
+flag {
+ name: "battery_service_support_current_adb_command"
+ namespace: "backstage_power"
+ description: "Whether or not BatteryService supports adb commands for Current values."
+ is_fixed_read_only: true
+ bug: "315037695"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8b5995a740f8..9c27f19b349a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -608,6 +608,23 @@ public final class Settings {
/**
* Activity Action: Show settings to allow configuration of
+ * {@link Manifest.permission#MEDIA_ROUTING_CONTROL} permission.
+ *
+ * Input: Optionally, the Intent's data URI can specify the application package name to
+ * directly invoke the management GUI specific to the package name. For example
+ * "package:com.my.app". However, modifying this permission setting for any package is allowed
+ * only when that package holds an appropriate companion device profile such as
+ * {@link android.companion.AssociationRequest#DEVICE_PROFILE_WATCH}.
+ * <p>
+ * Output: Nothing.
+ */
+ @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REQUEST_MEDIA_ROUTING_CONTROL =
+ "android.settings.REQUEST_MEDIA_ROUTING_CONTROL";
+
+ /**
+ * Activity Action: Show settings to allow configuration of
* {@link Manifest.permission#RUN_USER_INITIATED_JOBS} permission
*
* Input: Optionally, the Intent's data URI can specify the application package name to
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
new file mode 100644
index 000000000000..91a713ee6250
--- /dev/null
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.service.dreams"
+
+flag {
+ name: "dream_overlay_host"
+ namespace: "communal"
+ description: "This flag enables using a host to handle displaying a dream's overlay rather than "
+ "relying on the dream's window"
+ bug: "291990564"
+}
diff --git a/core/java/android/service/voice/VisualQueryDetectionService.java b/core/java/android/service/voice/VisualQueryDetectionService.java
index d184b1eb96ab..76b076be8fab 100644
--- a/core/java/android/service/voice/VisualQueryDetectionService.java
+++ b/core/java/android/service/voice/VisualQueryDetectionService.java
@@ -338,16 +338,24 @@ public abstract class VisualQueryDetectionService extends Service
/**
* Overrides {@link Context#openFileInput} to read files with the given file names under the
- * internal app storage of the {@link VoiceInteractionService}, i.e., only files stored in
- * {@link Context#getFilesDir()} can be opened.
+ * internal app storage of the {@link VoiceInteractionService}, i.e., the input file path would
+ * be added with {@link Context#getFilesDir()} as prefix.
+ *
+ * @param filename Relative path of a file under {@link Context#getFilesDir()}.
+ * @throws FileNotFoundException if the file does not exist or cannot be open.
*/
@Override
- public @Nullable FileInputStream openFileInput(@NonNull String filename) throws
+ public @NonNull FileInputStream openFileInput(@NonNull String filename) throws
FileNotFoundException {
try {
AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+ assert mDetectorSessionStorageService != null;
mDetectorSessionStorageService.openFile(filename, future);
ParcelFileDescriptor pfd = future.get();
+ if (pfd == null) {
+ throw new FileNotFoundException(
+ "File does not exist. Unable to open " + filename + ".");
+ }
return new FileInputStream(pfd.getFileDescriptor());
} catch (RemoteException | ExecutionException | InterruptedException e) {
Log.w(TAG, "Cannot open file due to remote service failure");
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index 91de894c1d93..b7d97057a08b 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -447,12 +447,12 @@ public class VisualQueryDetector {
public void onOpenFile(String filename, AndroidFuture future) throws RemoteException {
Slog.v(TAG, "BinderCallback#onOpenFile " + filename);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
- Slog.v(TAG, "onOpenFile: " + filename);
+ Slog.v(TAG, "onOpenFile: " + filename + "under internal app storage.");
File f = new File(mContext.getFilesDir(), filename);
ParcelFileDescriptor pfd = null;
try {
- Slog.d(TAG, "opened a file with ParcelFileDescriptor.");
pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+ Slog.d(TAG, "Successfully opened a file with ParcelFileDescriptor.");
} catch (FileNotFoundException e) {
Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned.");
} finally {
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index fd5517d29d74..f28574ecb3b2 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -27,9 +27,6 @@ import android.window.SurfaceSyncGroup;
import com.android.window.flags.Flags;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
/**
* Provides an interface to the root-Surface of a View Hierarchy or Window. This
* is used in combination with the {@link android.view.SurfaceControl} API to enable
@@ -197,42 +194,6 @@ public interface AttachedSurfaceControl {
}
/**
- * Add a trusted presentation listener on the SurfaceControl associated with this window.
- *
- * @param t Transaction that the trusted presentation listener is added on. This should
- * be applied by the caller.
- * @param thresholds The {@link SurfaceControl.TrustedPresentationThresholds} that will specify
- * when the to invoke the callback.
- * @param executor The {@link Executor} where the callback will be invoked on.
- * @param listener The {@link Consumer} that will receive the callbacks when entered or
- * exited the threshold.
- *
- * @see SurfaceControl.Transaction#setTrustedPresentationCallback(SurfaceControl,
- * SurfaceControl.TrustedPresentationThresholds, Executor, Consumer)
- *
- * @hide b/287076178 un-hide with API bump
- */
- default void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t,
- @NonNull SurfaceControl.TrustedPresentationThresholds thresholds,
- @NonNull Executor executor, @NonNull Consumer<Boolean> listener) {
- }
-
- /**
- * Remove a trusted presentation listener on the SurfaceControl associated with this window.
- *
- * @param t Transaction that the trusted presentation listener removed on. This should
- * be applied by the caller.
- * @param listener The {@link Consumer} that was previously registered with
- * addTrustedPresentationCallback that should be removed.
- *
- * @see SurfaceControl.Transaction#clearTrustedPresentationCallback(SurfaceControl)
- * @hide b/287076178 un-hide with API bump
- */
- default void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t,
- @NonNull Consumer<Boolean> listener) {
- }
-
- /**
* Transfer the currently in progress touch gesture from the host to the requested
* {@link SurfaceControlViewHost.SurfacePackage}. This requires that the
* SurfaceControlViewHost was created with the current host's inputToken.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 17bbee6d020f..36b74e39072a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -73,6 +73,8 @@ import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ScreenCapture;
import android.window.WindowContextInfo;
+import android.window.ITrustedPresentationListener;
+import android.window.TrustedPresentationThresholds;
/**
* System private interface to the window manager.
@@ -1075,4 +1077,10 @@ interface IWindowManager
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MONITOR_INPUT)")
void unregisterDecorViewGestureListener(IDecorViewGestureListener listener, int displayId);
+
+ void registerTrustedPresentationListener(in IBinder window, in ITrustedPresentationListener listener,
+ in TrustedPresentationThresholds thresholds, int id);
+
+
+ void unregisterTrustedPresentationListener(in ITrustedPresentationListener listener, int id);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1530aa78d73d..659db5f59f39 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3216,6 +3216,12 @@ public final class ViewRootImpl implements ViewParent,
endDragResizing();
destroyHardwareResources();
}
+
+ if (sToolkitSetFrameRateReadOnlyFlagValue && viewVisibility == View.VISIBLE) {
+ // Boost frame rate when the viewVisibility becomes true.
+ // This is mainly for lanuchers that lanuch new windows.
+ boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME);
+ }
}
// Non-visible windows can't hold accessibility focus.
@@ -3925,6 +3931,11 @@ public final class ViewRootImpl implements ViewParent,
focused.restoreDefaultFocus();
}
}
+
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ // Boost the frame rate when the ViewRootImpl first becomes available.
+ boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME);
+ }
}
final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
@@ -12005,18 +12016,6 @@ public final class ViewRootImpl implements ViewParent,
scheduleTraversals();
}
- @Override
- public void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t,
- @NonNull SurfaceControl.TrustedPresentationThresholds thresholds,
- @NonNull Executor executor, @NonNull Consumer<Boolean> listener) {
- t.setTrustedPresentationCallback(getSurfaceControl(), thresholds, executor, listener);
- }
-
- @Override
- public void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t,
- @NonNull Consumer<Boolean> listener) {
- t.clearTrustedPresentationCallback(getSurfaceControl());
- }
private void logAndTrace(String msg) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
@@ -12036,7 +12035,7 @@ public final class ViewRootImpl implements ViewParent,
try {
if (mLastPreferredFrameRateCategory != frameRateCategory) {
mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
- frameRateCategory, false).applyAsyncUnsafe();
+ frameRateCategory, false).applyAsyncUnsafe();
mLastPreferredFrameRateCategory = frameRateCategory;
}
} catch (Exception e) {
@@ -12159,6 +12158,22 @@ public final class ViewRootImpl implements ViewParent,
return mPreferredFrameRate;
}
+ /**
+ * Get the value of mIsFrameRateBoosting
+ */
+ @VisibleForTesting
+ public boolean getIsFrameRateBoosting() {
+ return mIsFrameRateBoosting;
+ }
+
+ private void boostFrameRate(int boostTimeOut) {
+ mIsFrameRateBoosting = true;
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT,
+ boostTimeOut);
+ }
+
@Override
public boolean transferHostTouchGestureToEmbedded(
@NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 046ea77f196d..c7e180732fd4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -122,7 +122,11 @@ import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.window.ITrustedPresentationListener;
import android.window.TaskFpsCallback;
+import android.window.TrustedPresentationThresholds;
+
+import com.android.window.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -656,26 +660,35 @@ public interface WindowManager extends ViewManager {
/**
* Display IME Policy: The IME should appear on the local display.
+ *
* @hide
*/
- @TestApi
+ @SuppressLint("UnflaggedApi") // promoting from @TestApi.
+ @SystemApi
int DISPLAY_IME_POLICY_LOCAL = 0;
/**
- * Display IME Policy: The IME should appear on the fallback display.
+ * Display IME Policy: The IME should appear on a fallback display.
+ *
+ * <p>The fallback display is always {@link Display#DEFAULT_DISPLAY}.</p>
+ *
* @hide
*/
- @TestApi
+ @SuppressLint("UnflaggedApi") // promoting from @TestApi.
+ @SystemApi
int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1;
/**
* Display IME Policy: The IME should be hidden.
*
- * Setting this policy will prevent the IME from making a connection. This
- * will prevent any IME from receiving metadata about input.
+ * <p>Setting this policy will prevent the IME from making a connection. This
+ * will prevent any IME from receiving metadata about input and this display will effectively
+ * have no IME.</p>
+ *
* @hide
*/
- @TestApi
+ @SuppressLint("UnflaggedApi") // promoting from @TestApi.
+ @SystemApi
int DISPLAY_IME_POLICY_HIDE = 2;
/**
@@ -3251,6 +3264,13 @@ public interface WindowManager extends ViewManager {
public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 1 << 24;
/**
+ * Flag to indicate that the window consumes the insets of {@link Type#ime()}. This makes
+ * windows below this window unable to receive visible IME insets.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_CONSUME_IME_INSETS = 1 << 25;
+
+ /**
* Flag to indicate that the window is controlling the appearance of system bars. So we
* don't need to adjust it by reading its system UI flags for compatibility.
* @hide
@@ -3334,6 +3354,7 @@ public interface WindowManager extends ViewManager {
PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
PRIVATE_FLAG_NOT_MAGNIFIABLE,
PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
+ PRIVATE_FLAG_CONSUME_IME_INSETS,
PRIVATE_FLAG_APPEARANCE_CONTROLLED,
PRIVATE_FLAG_BEHAVIOR_CONTROLLED,
PRIVATE_FLAG_FIT_INSETS_CONTROLLED,
@@ -3432,6 +3453,10 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
name = "COLOR_SPACE_AGNOSTIC"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_CONSUME_IME_INSETS,
+ equals = PRIVATE_FLAG_CONSUME_IME_INSETS,
+ name = "CONSUME_IME_INSETS"),
+ @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_APPEARANCE_CONTROLLED,
equals = PRIVATE_FLAG_APPEARANCE_CONTROLLED,
name = "APPEARANCE_CONTROLLED"),
@@ -3458,7 +3483,7 @@ public interface WindowManager extends ViewManager {
@ViewDebug.FlagToString(
mask = PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY,
equals = PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY,
- name = "PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY")
+ name = "SYSTEM_APPLICATION_OVERLAY")
})
@PrivateFlags
@TestApi
@@ -5884,4 +5909,34 @@ public interface WindowManager extends ViewManager {
default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * Add a trusted presentation listener associated with a window.
+ *
+ * <p> If this listener is already registered then the window and thresholds will be updated.
+ *
+ * @param window The Window to add the trusted presentation listener for
+ * @param thresholds The {@link TrustedPresentationThresholds} that will specify
+ * when the to invoke the callback.
+ * @param executor The {@link Executor} where the callback will be invoked on.
+ * @param listener The {@link Consumer} that will receive the callbacks
+ * when entered or exited trusted presentation per the thresholds.
+ */
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ default void registerTrustedPresentationListener(@NonNull IBinder window,
+ @NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Removes a presentation listener associated with a window. If the listener was not previously
+ * registered, the call will be a noop.
+ *
+ * @see WindowManager#registerTrustedPresentationListener(IBinder, TrustedPresentationThresholds, Executor, Consumer)
+ */
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ default void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 214f1ec3d1ec..f1e406196abf 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -30,9 +30,13 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.view.inputmethod.InputMethodManager;
+import android.window.ITrustedPresentationListener;
+import android.window.TrustedPresentationThresholds;
import com.android.internal.util.FastPrintWriter;
@@ -43,6 +47,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import java.util.function.IntConsumer;
/**
@@ -143,6 +148,9 @@ public final class WindowManagerGlobal {
private Runnable mSystemPropertyUpdater;
+ private final TrustedPresentationListener mTrustedPresentationListener =
+ new TrustedPresentationListener();
+
private WindowManagerGlobal() {
}
@@ -324,7 +332,7 @@ public final class WindowManagerGlobal {
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
- & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
+ & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
@@ -482,7 +490,7 @@ public final class WindowManagerGlobal {
if (who != null) {
WindowLeaked leak = new WindowLeaked(
what + " " + who + " has leaked window "
- + root.getView() + " that was originally added here");
+ + root.getView() + " that was originally added here");
leak.setStackTrace(root.getLocation().getStackTrace());
Log.e(TAG, "", leak);
}
@@ -790,6 +798,87 @@ public final class WindowManagerGlobal {
}
}
+ public void registerTrustedPresentationListener(@NonNull IBinder window,
+ @NonNull TrustedPresentationThresholds thresholds, Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ mTrustedPresentationListener.addListener(window, thresholds, listener, executor);
+ }
+
+ public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) {
+ mTrustedPresentationListener.removeListener(listener);
+ }
+
+ private final class TrustedPresentationListener extends
+ ITrustedPresentationListener.Stub {
+ private static int sId = 0;
+ private final ArrayMap<Consumer<Boolean>, Pair<Integer, Executor>> mListeners =
+ new ArrayMap<>();
+
+ private final Object mTplLock = new Object();
+
+ private void addListener(IBinder window, TrustedPresentationThresholds thresholds,
+ Consumer<Boolean> listener, Executor executor) {
+ synchronized (mTplLock) {
+ if (mListeners.containsKey(listener)) {
+ Log.i(TAG, "Updating listener " + listener + " thresholds to " + thresholds);
+ removeListener(listener);
+ }
+ int id = sId++;
+ mListeners.put(listener, new Pair<>(id, executor));
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .registerTrustedPresentationListener(window, this, thresholds, id);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+ }
+
+ private void removeListener(Consumer<Boolean> listener) {
+ synchronized (mTplLock) {
+ var removedListener = mListeners.remove(listener);
+ if (removedListener == null) {
+ Log.i(TAG, "listener " + listener + " does not exist.");
+ return;
+ }
+
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .unregisterTrustedPresentationListener(this, removedListener.first);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+ }
+
+ @Override
+ public void onTrustedPresentationChanged(int[] inTrustedStateListenerIds,
+ int[] outOfTrustedStateListenerIds) {
+ ArrayList<Runnable> firedListeners = new ArrayList<>();
+ synchronized (mTplLock) {
+ mListeners.forEach((listener, idExecutorPair) -> {
+ final var listenerId = idExecutorPair.first;
+ final var executor = idExecutorPair.second;
+ for (int id : inTrustedStateListenerIds) {
+ if (listenerId == id) {
+ firedListeners.add(() -> executor.execute(
+ () -> listener.accept(/*presentationState*/true)));
+ }
+ }
+ for (int id : outOfTrustedStateListenerIds) {
+ if (listenerId == id) {
+ firedListeners.add(() -> executor.execute(
+ () -> listener.accept(/*presentationState*/false)));
+ }
+ }
+ });
+ }
+ for (int i = 0; i < firedListeners.size(); i++) {
+ firedListeners.get(i).run();
+ }
+ }
+ }
+
/** @hide */
public void addWindowlessRoot(ViewRootImpl impl) {
synchronized (mLock) {
@@ -801,7 +890,7 @@ public final class WindowManagerGlobal {
public void removeWindowlessRoot(ViewRootImpl impl) {
synchronized (mLock) {
mWindowlessRoots.remove(impl);
- }
+ }
}
public void setRecentsAppBehindSystemBars(boolean behindSystemBars) {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index d7b74b3bcfe2..b4b1fde89a46 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -37,6 +37,7 @@ import android.os.StrictMode;
import android.util.Log;
import android.window.ITaskFpsCallback;
import android.window.TaskFpsCallback;
+import android.window.TrustedPresentationThresholds;
import android.window.WindowContext;
import android.window.WindowMetricsController;
import android.window.WindowProvider;
@@ -508,4 +509,17 @@ public final class WindowManagerImpl implements WindowManager {
}
return false;
}
+
+ @Override
+ public void registerTrustedPresentationListener(@NonNull IBinder window,
+ @NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ mGlobal.registerTrustedPresentationListener(window, thresholds, executor, listener);
+ }
+
+ @Override
+ public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) {
+ mGlobal.unregisterTrustedPresentationListener(listener);
+
+ }
}
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index fab8c7796dfd..1b7d57b785f5 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -20,8 +20,8 @@ import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static com.android.internal.inputmethod.InputMethodDebug.softInputDisplayReasonToString;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_IME_INSETS_HIDE_ANIMATION;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_IME_INSETS_SHOW_ANIMATION;
+import static com.android.internal.jank.Cuj.CUJ_IME_INSETS_HIDE_ANIMATION;
+import static com.android.internal.jank.Cuj.CUJ_IME_INSETS_SHOW_ANIMATION;
import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_HIDDEN;
import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_SHOWN;
@@ -758,7 +758,7 @@ public interface ImeTracker {
* A helper method to translate animation type to CUJ type for IME animations.
*
* @param animType the animation type.
- * @return the integer in {@link com.android.internal.jank.InteractionJankMonitor.CujType},
+ * @return the integer in {@link com.android.internal.jank.Cuj.CujType},
* or {@code -1} if the animation type is not supported for tracking yet.
*/
private static int getImeInsetsCujFromAnimation(@AnimationType int animType) {
diff --git a/core/java/android/window/ITrustedPresentationListener.aidl b/core/java/android/window/ITrustedPresentationListener.aidl
new file mode 100644
index 000000000000..b33128abb7e5
--- /dev/null
+++ b/core/java/android/window/ITrustedPresentationListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/**
+ * @hide
+ */
+oneway interface ITrustedPresentationListener {
+ void onTrustedPresentationChanged(in int[] enteredTrustedStateIds, in int[] exitedTrustedStateIds);
+} \ No newline at end of file
diff --git a/core/java/android/window/TrustedPresentationListener.java b/core/java/android/window/TrustedPresentationListener.java
new file mode 100644
index 000000000000..02fd6d98fb0d
--- /dev/null
+++ b/core/java/android/window/TrustedPresentationListener.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/**
+ * @hide
+ */
+public interface TrustedPresentationListener {
+
+ void onTrustedPresentationChanged(boolean inTrustedPresentationState);
+
+}
diff --git a/core/java/android/window/TrustedPresentationThresholds.aidl b/core/java/android/window/TrustedPresentationThresholds.aidl
new file mode 100644
index 000000000000..d7088bf0fddc
--- /dev/null
+++ b/core/java/android/window/TrustedPresentationThresholds.aidl
@@ -0,0 +1,3 @@
+package android.window;
+
+parcelable TrustedPresentationThresholds;
diff --git a/core/java/android/window/TrustedPresentationThresholds.java b/core/java/android/window/TrustedPresentationThresholds.java
new file mode 100644
index 000000000000..90f8834b37d1
--- /dev/null
+++ b/core/java/android/window/TrustedPresentationThresholds.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.SuppressLint;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+import androidx.annotation.NonNull;
+
+import com.android.window.flags.Flags;
+
+/**
+ * Threshold values that are sent with
+ * {@link android.view.WindowManager#registerTrustedPresentationListener(IBinder,
+ * TrustedPresentationThresholds, Executor, Consumer)}
+ */
+@FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+public final class TrustedPresentationThresholds implements Parcelable {
+ /**
+ * The min alpha the {@link SurfaceControl} is required to have to be considered inside the
+ * threshold.
+ */
+ @FloatRange(from = 0f, fromInclusive = false, to = 1f)
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ @SuppressLint("InternalField") // simple data class
+ public final float minAlpha;
+
+ /**
+ * The min fraction of the SurfaceControl that was presented to the user to be considered
+ * inside the threshold.
+ */
+ @FloatRange(from = 0f, fromInclusive = false, to = 1f)
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ @SuppressLint("InternalField") // simple data class
+ public final float minFractionRendered;
+
+ /**
+ * The time in milliseconds required for the {@link SurfaceControl} to be in the threshold.
+ */
+ @IntRange(from = 1)
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ @SuppressLint("InternalField") // simple data class
+ public final int stabilityRequirementMs;
+
+ private void checkValid() {
+ if (minAlpha <= 0 || minFractionRendered <= 0 || stabilityRequirementMs < 1) {
+ throw new IllegalArgumentException(
+ "TrustedPresentationThresholds values are invalid");
+ }
+ }
+
+ /**
+ * Creates a new TrustedPresentationThresholds.
+ *
+ * @param minAlpha The min alpha the {@link SurfaceControl} is required to
+ * have to be considered inside the
+ * threshold.
+ * @param minFractionRendered The min fraction of the SurfaceControl that was presented
+ * to the user to be considered
+ * inside the threshold.
+ * @param stabilityRequirementMs The time in milliseconds required for the
+ * {@link SurfaceControl} to be in the threshold.
+ */
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public TrustedPresentationThresholds(
+ @FloatRange(from = 0f, fromInclusive = false, to = 1f) float minAlpha,
+ @FloatRange(from = 0f, fromInclusive = false, to = 1f) float minFractionRendered,
+ @IntRange(from = 1) int stabilityRequirementMs) {
+ this.minAlpha = minAlpha;
+ this.minFractionRendered = minFractionRendered;
+ this.stabilityRequirementMs = stabilityRequirementMs;
+ checkValid();
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public String toString() {
+ return "TrustedPresentationThresholds { "
+ + "minAlpha = " + minAlpha + ", "
+ + "minFractionRendered = " + minFractionRendered + ", "
+ + "stabilityRequirementMs = " + stabilityRequirementMs
+ + " }";
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeFloat(minAlpha);
+ dest.writeFloat(minFractionRendered);
+ dest.writeInt(stabilityRequirementMs);
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @hide
+ */
+ TrustedPresentationThresholds(@NonNull Parcel in) {
+ minAlpha = in.readFloat();
+ minFractionRendered = in.readFloat();
+ stabilityRequirementMs = in.readInt();
+
+ checkValid();
+ }
+
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public static final @NonNull Creator<TrustedPresentationThresholds> CREATOR =
+ new Creator<TrustedPresentationThresholds>() {
+ @Override
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public TrustedPresentationThresholds[] newArray(int size) {
+ return new TrustedPresentationThresholds[size];
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public TrustedPresentationThresholds createFromParcel(@NonNull Parcel in) {
+ return new TrustedPresentationThresholds(in);
+ }
+ };
+}
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 29932f342b74..56df49370379 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -56,3 +56,11 @@ flag {
is_fixed_read_only: true
bug: "308662081"
}
+
+flag {
+ namespace: "window_surfaces"
+ name: "trusted_presentation_listener_for_window"
+ description: "Enable trustedPresentationListener on windows public API"
+ is_fixed_read_only: true
+ bug: "278027319"
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
new file mode 100644
index 000000000000..f460233f0edd
--- /dev/null
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.jank;
+
+import android.annotation.IntDef;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/** @hide */
+public class Cuj {
+ @VisibleForTesting
+ public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
+
+ // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
+ public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2;
+ public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3;
+ public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4;
+ public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 5;
+ public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 6;
+ public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 7;
+ public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 8;
+ public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9;
+ public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10;
+ public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11;
+ public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = 12;
+ public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = 13;
+ public static final int CUJ_NOTIFICATION_ADD = 14;
+ public static final int CUJ_NOTIFICATION_REMOVE = 15;
+ public static final int CUJ_NOTIFICATION_APP_START = 16;
+ public static final int CUJ_LOCKSCREEN_PASSWORD_APPEAR = 17;
+ public static final int CUJ_LOCKSCREEN_PATTERN_APPEAR = 18;
+ public static final int CUJ_LOCKSCREEN_PIN_APPEAR = 19;
+ public static final int CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR = 20;
+ public static final int CUJ_LOCKSCREEN_PATTERN_DISAPPEAR = 21;
+ public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22;
+ public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23;
+ public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24;
+ public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25;
+ public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26;
+ public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27;
+ public static final int CUJ_SETTINGS_PAGE_SCROLL = 28;
+ public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33;
+ public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
+ public static final int CUJ_PIP_TRANSITION = 35;
+ public static final int CUJ_WALLPAPER_TRANSITION = 36;
+ public static final int CUJ_USER_SWITCH = 37;
+ public static final int CUJ_SPLASHSCREEN_AVD = 38;
+ public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39;
+ public static final int CUJ_SCREEN_OFF = 40;
+ public static final int CUJ_SCREEN_OFF_SHOW_AOD = 41;
+ public static final int CUJ_ONE_HANDED_ENTER_TRANSITION = 42;
+ public static final int CUJ_ONE_HANDED_EXIT_TRANSITION = 43;
+ public static final int CUJ_UNFOLD_ANIM = 44;
+ public static final int CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS = 45;
+ public static final int CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS = 46;
+ public static final int CUJ_SUW_LOADING_TO_NEXT_FLOW = 47;
+ public static final int CUJ_SUW_LOADING_SCREEN_FOR_STATUS = 48;
+ public static final int CUJ_SPLIT_SCREEN_ENTER = 49;
+ public static final int CUJ_SPLIT_SCREEN_EXIT = 50;
+ public static final int CUJ_LOCKSCREEN_LAUNCH_CAMERA = 51; // reserved.
+ public static final int CUJ_SPLIT_SCREEN_RESIZE = 52;
+ public static final int CUJ_SETTINGS_SLIDER = 53;
+ public static final int CUJ_TAKE_SCREENSHOT = 54;
+ public static final int CUJ_VOLUME_CONTROL = 55;
+ public static final int CUJ_BIOMETRIC_PROMPT_TRANSITION = 56;
+ public static final int CUJ_SETTINGS_TOGGLE = 57;
+ public static final int CUJ_SHADE_DIALOG_OPEN = 58;
+ public static final int CUJ_USER_DIALOG_OPEN = 59;
+ public static final int CUJ_TASKBAR_EXPAND = 60;
+ public static final int CUJ_TASKBAR_COLLAPSE = 61;
+ public static final int CUJ_SHADE_CLEAR_ALL = 62;
+ public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63;
+ public static final int CUJ_LOCKSCREEN_OCCLUSION = 64;
+ public static final int CUJ_RECENTS_SCROLLING = 65;
+ public static final int CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS = 66;
+ public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE = 67;
+ public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME = 68;
+ public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70;
+ public static final int CUJ_LAUNCHER_OPEN_SEARCH_RESULT = 71;
+ // 72 - 77 are reserved for b/281564325.
+
+ /**
+ * In some cases when we do not have any end-target, we play a simple slide-down animation.
+ * eg: Open an app from Overview/Task switcher such that there is no home-screen icon.
+ * eg: Exit the app using back gesture.
+ */
+ public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK = 78;
+ public static final int CUJ_IME_INSETS_SHOW_ANIMATION = 80;
+ public static final int CUJ_IME_INSETS_HIDE_ANIMATION = 81;
+
+ public static final int CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER = 82;
+
+ public static final int CUJ_LAUNCHER_UNFOLD_ANIM = 83;
+
+ public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84;
+ public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85;
+ public static final int CUJ_PREDICTIVE_BACK_HOME = 86;
+
+ // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
+ @VisibleForTesting
+ static final int LAST_CUJ = CUJ_PREDICTIVE_BACK_HOME;
+
+ /** @hide */
+ @IntDef({
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+ CUJ_NOTIFICATION_SHADE_SCROLL_FLING,
+ CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
+ CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
+ CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE,
+ CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS,
+ CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON,
+ CUJ_LAUNCHER_APP_CLOSE_TO_HOME,
+ CUJ_LAUNCHER_APP_CLOSE_TO_PIP,
+ CUJ_LAUNCHER_QUICK_SWITCH,
+ CUJ_NOTIFICATION_HEADS_UP_APPEAR,
+ CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR,
+ CUJ_NOTIFICATION_ADD,
+ CUJ_NOTIFICATION_REMOVE,
+ CUJ_NOTIFICATION_APP_START,
+ CUJ_LOCKSCREEN_PASSWORD_APPEAR,
+ CUJ_LOCKSCREEN_PATTERN_APPEAR,
+ CUJ_LOCKSCREEN_PIN_APPEAR,
+ CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR,
+ CUJ_LOCKSCREEN_PATTERN_DISAPPEAR,
+ CUJ_LOCKSCREEN_PIN_DISAPPEAR,
+ CUJ_LOCKSCREEN_TRANSITION_FROM_AOD,
+ CUJ_LOCKSCREEN_TRANSITION_TO_AOD,
+ CUJ_LAUNCHER_OPEN_ALL_APPS,
+ CUJ_LAUNCHER_ALL_APPS_SCROLL,
+ CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET,
+ CUJ_SETTINGS_PAGE_SCROLL,
+ CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
+ CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON,
+ CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER,
+ CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+ CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
+ CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+ CUJ_PIP_TRANSITION,
+ CUJ_WALLPAPER_TRANSITION,
+ CUJ_USER_SWITCH,
+ CUJ_SPLASHSCREEN_AVD,
+ CUJ_SPLASHSCREEN_EXIT_ANIM,
+ CUJ_SCREEN_OFF,
+ CUJ_SCREEN_OFF_SHOW_AOD,
+ CUJ_ONE_HANDED_ENTER_TRANSITION,
+ CUJ_ONE_HANDED_EXIT_TRANSITION,
+ CUJ_UNFOLD_ANIM,
+ CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS,
+ CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS,
+ CUJ_SUW_LOADING_TO_NEXT_FLOW,
+ CUJ_SUW_LOADING_SCREEN_FOR_STATUS,
+ CUJ_SPLIT_SCREEN_ENTER,
+ CUJ_SPLIT_SCREEN_EXIT,
+ CUJ_LOCKSCREEN_LAUNCH_CAMERA,
+ CUJ_SPLIT_SCREEN_RESIZE,
+ CUJ_SETTINGS_SLIDER,
+ CUJ_TAKE_SCREENSHOT,
+ CUJ_VOLUME_CONTROL,
+ CUJ_BIOMETRIC_PROMPT_TRANSITION,
+ CUJ_SETTINGS_TOGGLE,
+ CUJ_SHADE_DIALOG_OPEN,
+ CUJ_USER_DIALOG_OPEN,
+ CUJ_TASKBAR_EXPAND,
+ CUJ_TASKBAR_COLLAPSE,
+ CUJ_SHADE_CLEAR_ALL,
+ CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
+ CUJ_LOCKSCREEN_OCCLUSION,
+ CUJ_RECENTS_SCROLLING,
+ CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS,
+ CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE,
+ CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME,
+ CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION,
+ CUJ_LAUNCHER_OPEN_SEARCH_RESULT,
+ CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK,
+ CUJ_IME_INSETS_SHOW_ANIMATION,
+ CUJ_IME_INSETS_HIDE_ANIMATION,
+ CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER,
+ CUJ_LAUNCHER_UNFOLD_ANIM,
+ CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY,
+ CUJ_PREDICTIVE_BACK_CROSS_TASK,
+ CUJ_PREDICTIVE_BACK_HOME,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CujType {
+ }
+
+ private static final int NO_STATSD_LOGGING = -1;
+
+ // Used to convert CujType to InteractionType enum value for statsd logging.
+ // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd.
+ private static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = new int[LAST_CUJ + 1];
+ static {
+ Arrays.fill(CUJ_TO_STATSD_INTERACTION_TYPE, NO_STATSD_LOGGING);
+
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_SCROLL_FLING] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_EXPAND] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_SWIPE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_PIP] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_QUICK_SWITCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_APPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_ADD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_REMOVE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_APP_START] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_APPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_APPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_APPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_DISAPPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_DISAPPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_FROM_AOD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_TO_AOD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_ALL_APPS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_ALL_APPS_SCROLL] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_PAGE_SCROLL] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_UNLOCK_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PIP_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_SWITCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_AVD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_EXIT_ANIM] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF_SHOW_AOD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_ENTER_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_EXIT_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_UNFOLD_ANIM] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_NEXT_FLOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_SCREEN_FOR_STATUS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_ENTER] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_EXIT] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_LAUNCH_CAMERA] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_RESIZE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_SLIDER] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TAKE_SCREENSHOT] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_VOLUME_CONTROL] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BIOMETRIC_PROMPT_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_TOGGLE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_DIALOG_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_DIALOG_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_EXPAND] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_COLLAPSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_CLEAR_ALL] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_OCCLUSION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_RECENTS_SCROLLING] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_SEARCH_RESULT] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_SHOW_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_SHOW_ANIMATION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_HIDE_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNFOLD_ANIM] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNFOLD_ANIM;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
+ }
+
+ private Cuj() {
+ }
+
+ /**
+ * A helper method to translate CUJ type to CUJ name.
+ *
+ * @param cujType the cuj type defined in this file
+ * @return the name of the cuj type
+ */
+ public static String getNameOfCuj(int cujType) {
+ // Please note:
+ // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
+ // 2. The returned string should be the same with the name defined in atoms.proto.
+ switch (cujType) {
+ case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
+ return "NOTIFICATION_SHADE_EXPAND_COLLAPSE";
+ case CUJ_NOTIFICATION_SHADE_SCROLL_FLING:
+ return "NOTIFICATION_SHADE_SCROLL_FLING";
+ case CUJ_NOTIFICATION_SHADE_ROW_EXPAND:
+ return "NOTIFICATION_SHADE_ROW_EXPAND";
+ case CUJ_NOTIFICATION_SHADE_ROW_SWIPE:
+ return "NOTIFICATION_SHADE_ROW_SWIPE";
+ case CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE:
+ return "NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE";
+ case CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE:
+ return "NOTIFICATION_SHADE_QS_SCROLL_SWIPE";
+ case CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS:
+ return "LAUNCHER_APP_LAUNCH_FROM_RECENTS";
+ case CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON:
+ return "LAUNCHER_APP_LAUNCH_FROM_ICON";
+ case CUJ_LAUNCHER_APP_CLOSE_TO_HOME:
+ return "LAUNCHER_APP_CLOSE_TO_HOME";
+ case CUJ_LAUNCHER_APP_CLOSE_TO_PIP:
+ return "LAUNCHER_APP_CLOSE_TO_PIP";
+ case CUJ_LAUNCHER_QUICK_SWITCH:
+ return "LAUNCHER_QUICK_SWITCH";
+ case CUJ_NOTIFICATION_HEADS_UP_APPEAR:
+ return "NOTIFICATION_HEADS_UP_APPEAR";
+ case CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR:
+ return "NOTIFICATION_HEADS_UP_DISAPPEAR";
+ case CUJ_NOTIFICATION_ADD:
+ return "NOTIFICATION_ADD";
+ case CUJ_NOTIFICATION_REMOVE:
+ return "NOTIFICATION_REMOVE";
+ case CUJ_NOTIFICATION_APP_START:
+ return "NOTIFICATION_APP_START";
+ case CUJ_LOCKSCREEN_PASSWORD_APPEAR:
+ return "LOCKSCREEN_PASSWORD_APPEAR";
+ case CUJ_LOCKSCREEN_PATTERN_APPEAR:
+ return "LOCKSCREEN_PATTERN_APPEAR";
+ case CUJ_LOCKSCREEN_PIN_APPEAR:
+ return "LOCKSCREEN_PIN_APPEAR";
+ case CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR:
+ return "LOCKSCREEN_PASSWORD_DISAPPEAR";
+ case CUJ_LOCKSCREEN_PATTERN_DISAPPEAR:
+ return "LOCKSCREEN_PATTERN_DISAPPEAR";
+ case CUJ_LOCKSCREEN_PIN_DISAPPEAR:
+ return "LOCKSCREEN_PIN_DISAPPEAR";
+ case CUJ_LOCKSCREEN_TRANSITION_FROM_AOD:
+ return "LOCKSCREEN_TRANSITION_FROM_AOD";
+ case CUJ_LOCKSCREEN_TRANSITION_TO_AOD:
+ return "LOCKSCREEN_TRANSITION_TO_AOD";
+ case CUJ_LAUNCHER_OPEN_ALL_APPS :
+ return "LAUNCHER_OPEN_ALL_APPS";
+ case CUJ_LAUNCHER_ALL_APPS_SCROLL:
+ return "LAUNCHER_ALL_APPS_SCROLL";
+ case CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET:
+ return "LAUNCHER_APP_LAUNCH_FROM_WIDGET";
+ case CUJ_SETTINGS_PAGE_SCROLL:
+ return "SETTINGS_PAGE_SCROLL";
+ case CUJ_LOCKSCREEN_UNLOCK_ANIMATION:
+ return "LOCKSCREEN_UNLOCK_ANIMATION";
+ case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON:
+ return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON";
+ case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER:
+ return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER";
+ case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE:
+ return "SHADE_APP_LAUNCH_FROM_QS_TILE";
+ case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON:
+ return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON";
+ case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP:
+ return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP";
+ case CUJ_PIP_TRANSITION:
+ return "PIP_TRANSITION";
+ case CUJ_WALLPAPER_TRANSITION:
+ return "WALLPAPER_TRANSITION";
+ case CUJ_USER_SWITCH:
+ return "USER_SWITCH";
+ case CUJ_SPLASHSCREEN_AVD:
+ return "SPLASHSCREEN_AVD";
+ case CUJ_SPLASHSCREEN_EXIT_ANIM:
+ return "SPLASHSCREEN_EXIT_ANIM";
+ case CUJ_SCREEN_OFF:
+ return "SCREEN_OFF";
+ case CUJ_SCREEN_OFF_SHOW_AOD:
+ return "SCREEN_OFF_SHOW_AOD";
+ case CUJ_ONE_HANDED_ENTER_TRANSITION:
+ return "ONE_HANDED_ENTER_TRANSITION";
+ case CUJ_ONE_HANDED_EXIT_TRANSITION:
+ return "ONE_HANDED_EXIT_TRANSITION";
+ case CUJ_UNFOLD_ANIM:
+ return "UNFOLD_ANIM";
+ case CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS:
+ return "SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS";
+ case CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS:
+ return "SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS";
+ case CUJ_SUW_LOADING_TO_NEXT_FLOW:
+ return "SUW_LOADING_TO_NEXT_FLOW";
+ case CUJ_SUW_LOADING_SCREEN_FOR_STATUS:
+ return "SUW_LOADING_SCREEN_FOR_STATUS";
+ case CUJ_SPLIT_SCREEN_ENTER:
+ return "SPLIT_SCREEN_ENTER";
+ case CUJ_SPLIT_SCREEN_EXIT:
+ return "SPLIT_SCREEN_EXIT";
+ case CUJ_LOCKSCREEN_LAUNCH_CAMERA:
+ return "LOCKSCREEN_LAUNCH_CAMERA";
+ case CUJ_SPLIT_SCREEN_RESIZE:
+ return "SPLIT_SCREEN_RESIZE";
+ case CUJ_SETTINGS_SLIDER:
+ return "SETTINGS_SLIDER";
+ case CUJ_TAKE_SCREENSHOT:
+ return "TAKE_SCREENSHOT";
+ case CUJ_VOLUME_CONTROL:
+ return "VOLUME_CONTROL";
+ case CUJ_BIOMETRIC_PROMPT_TRANSITION:
+ return "BIOMETRIC_PROMPT_TRANSITION";
+ case CUJ_SETTINGS_TOGGLE:
+ return "SETTINGS_TOGGLE";
+ case CUJ_SHADE_DIALOG_OPEN:
+ return "SHADE_DIALOG_OPEN";
+ case CUJ_USER_DIALOG_OPEN:
+ return "USER_DIALOG_OPEN";
+ case CUJ_TASKBAR_EXPAND:
+ return "TASKBAR_EXPAND";
+ case CUJ_TASKBAR_COLLAPSE:
+ return "TASKBAR_COLLAPSE";
+ case CUJ_SHADE_CLEAR_ALL:
+ return "SHADE_CLEAR_ALL";
+ case CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION:
+ return "LAUNCHER_UNLOCK_ENTRANCE_ANIMATION";
+ case CUJ_LOCKSCREEN_OCCLUSION:
+ return "LOCKSCREEN_OCCLUSION";
+ case CUJ_RECENTS_SCROLLING:
+ return "RECENTS_SCROLLING";
+ case CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS:
+ return "LAUNCHER_APP_SWIPE_TO_RECENTS";
+ case CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE:
+ return "LAUNCHER_CLOSE_ALL_APPS_SWIPE";
+ case CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME:
+ return "LAUNCHER_CLOSE_ALL_APPS_TO_HOME";
+ case CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION:
+ return "LOCKSCREEN_CLOCK_MOVE_ANIMATION";
+ case CUJ_LAUNCHER_OPEN_SEARCH_RESULT:
+ return "LAUNCHER_OPEN_SEARCH_RESULT";
+ case CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK:
+ return "LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK";
+ case CUJ_IME_INSETS_SHOW_ANIMATION:
+ return "IME_INSETS_SHOW_ANIMATION";
+ case CUJ_IME_INSETS_HIDE_ANIMATION:
+ return "IME_INSETS_HIDE_ANIMATION";
+ case CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER:
+ return "SPLIT_SCREEN_DOUBLE_TAP_DIVIDER";
+ case CUJ_LAUNCHER_UNFOLD_ANIM:
+ return "LAUNCHER_UNFOLD_ANIM";
+ case CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY:
+ return "PREDICTIVE_BACK_CROSS_ACTIVITY";
+ case CUJ_PREDICTIVE_BACK_CROSS_TASK:
+ return "PREDICTIVE_BACK_CROSS_TASK";
+ case CUJ_PREDICTIVE_BACK_HOME:
+ return "PREDICTIVE_BACK_HOME";
+ }
+ return "UNKNOWN";
+ }
+
+ public static int getStatsdInteractionType(@CujType int cujType) {
+ return CUJ_TO_STATSD_INTERACTION_TYPE[cujType];
+ }
+
+ /** Returns whether the measurements for the given CUJ should be written to statsd. */
+ public static boolean logToStatsd(@CujType int cujType) {
+ return getStatsdInteractionType(cujType) != NO_STATSD_LOGGING;
+ }
+
+ /**
+ * A helper method to translate interaction type to CUJ name.
+ *
+ * @param interactionType the interaction type defined in AtomsProto.java
+ * @return the name of the interaction type
+ */
+ public static String getNameOfInteraction(int interactionType) {
+ // There is an offset amount of 1 between cujType and interactionType.
+ return Cuj.getNameOfCuj(getCujTypeFromInteraction(interactionType));
+ }
+
+ /**
+ * A helper method to translate interaction type to CUJ type.
+ *
+ * @param interactionType the interaction type defined in AtomsProto.java
+ * @return the integer in {@link Cuj.CujType}
+ */
+ private static int getCujTypeFromInteraction(int interactionType) {
+ return interactionType - 1;
+ }
+}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index c83452d88290..86729f74cd85 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -53,7 +53,6 @@ import android.view.WindowCallbacks;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.DisplayRefreshRate.RefreshRate;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
-import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
@@ -67,7 +66,6 @@ import java.util.concurrent.TimeUnit;
public class FrameTracker extends SurfaceControl.OnJankDataListener
implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
private static final String TAG = "FrameTracker";
- private static final boolean DEBUG = false;
private static final long INVALID_ID = -1;
public static final int NANOS_IN_MILLISECOND = 1_000_000;
@@ -93,20 +91,19 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
REASON_CANCEL_NORMAL,
REASON_CANCEL_NOT_BEGUN,
REASON_CANCEL_SAME_VSYNC,
+ REASON_CANCEL_TIMEOUT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Reasons {
}
- @VisibleForTesting
- public final InteractionJankMonitor mMonitor;
private final HardwareRendererObserver mObserver;
private final int mTraceThresholdMissedFrames;
private final int mTraceThresholdFrameTimeMillis;
private final ThreadedRendererWrapper mRendererWrapper;
private final FrameMetricsWrapper mMetricsWrapper;
private final SparseArray<JankInfo> mJankInfos = new SparseArray<>();
- private final Session mSession;
+ private final Configuration mConfig;
private final ViewRootWrapper mViewRoot;
private final SurfaceControlWrapper mSurfaceControlWrapper;
private final int mDisplayId;
@@ -197,19 +194,18 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
}
- public FrameTracker(@NonNull InteractionJankMonitor monitor, @NonNull Session session,
- @NonNull Handler handler, @Nullable ThreadedRendererWrapper renderer,
+ public FrameTracker(@NonNull Configuration config,
+ @Nullable ThreadedRendererWrapper renderer,
@Nullable ViewRootWrapper viewRootWrapper,
@NonNull SurfaceControlWrapper surfaceControlWrapper,
@NonNull ChoreographerWrapper choreographer,
@Nullable FrameMetricsWrapper metrics,
@NonNull StatsLogWrapper statsLog,
int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
- @Nullable FrameTrackerListener listener, @NonNull Configuration config) {
- mMonitor = monitor;
+ @Nullable FrameTrackerListener listener) {
mSurfaceOnly = config.isSurfaceOnly();
- mSession = session;
- mHandler = handler;
+ mConfig = config;
+ mHandler = config.getHandler();
mChoreographer = choreographer;
mSurfaceControlWrapper = surfaceControlWrapper;
mStatsLog = statsLog;
@@ -222,7 +218,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
mObserver = mSurfaceOnly
? null
: new HardwareRendererObserver(this, mMetricsWrapper.getTiming(),
- handler, /* waitForPresentTime= */ false);
+ mHandler, /* waitForPresentTime= */ false);
mTraceThresholdMissedFrames = traceThresholdMissedFrames;
mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
@@ -242,7 +238,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
@Override
public void surfaceCreated(SurfaceControl.Transaction t) {
- getHandler().runWithScissors(() -> {
+ mHandler.runWithScissors(() -> {
if (mSurfaceControl == null) {
mSurfaceControl = mViewRoot.getSurfaceControl();
if (mBeginVsyncId != INVALID_ID) {
@@ -262,13 +258,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
// Wait a while to give the system a chance for the remaining
// frames to arrive, then force finish the session.
- getHandler().postDelayed(() -> {
- if (DEBUG) {
- Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
- + ", finalized=" + mMetricsFinalized
- + ", info=" + mJankInfos.size()
- + ", vsync=" + mBeginVsyncId);
- }
+ mHandler.postDelayed(() -> {
if (!mMetricsFinalized) {
end(REASON_END_SURFACE_DESTROYED);
finish();
@@ -282,11 +272,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
}
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public Handler getHandler() {
- return mHandler;
- }
-
/**
* Begin a trace session of the CUJ.
*/
@@ -300,10 +285,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
mBeginVsyncId = mDeferMonitoring ? currentVsync + 1 : currentVsync;
}
if (mSurfaceControl != null) {
- if (DEBUG) {
- Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId
- + ", defer=" + mDeferMonitoring + ", current=" + currentVsync);
- }
if (mDeferMonitoring && currentVsync < mBeginVsyncId) {
markEvent("FT#deferMonitoring", 0);
// Normal case, we begin the instrument from the very beginning,
@@ -314,11 +295,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
// there is no need to skip the frame where the begin invocation happens.
beginInternal();
}
- } else {
- if (DEBUG) {
- Log.d(TAG, "begin: defer beginning since the surface is not ready for CUJ="
- + mSession.getName());
- }
}
}
@@ -336,8 +312,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
return;
}
mTracingStarted = true;
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, mSession.getName(), mSession.getName(),
- (int) mBeginVsyncId);
+ String name = mConfig.getSessionName();
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, name, name, (int) mBeginVsyncId);
markEvent("FT#beginVsync", mBeginVsyncId);
markEvent("FT#layerId", mSurfaceControl.getLayerId());
mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
@@ -361,15 +337,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
} else if (mEndVsyncId <= mBeginVsyncId) {
return cancel(REASON_CANCEL_SAME_VSYNC);
} else {
- if (DEBUG) {
- Log.d(TAG, "end: " + mSession.getName()
- + ", end=" + mEndVsyncId + ", reason=" + reason);
- }
+ final String name = mConfig.getSessionName();
markEvent("FT#end", reason);
markEvent("FT#endVsync", mEndVsyncId);
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, mSession.getName(),
- (int) mBeginVsyncId);
- mSession.setReason(reason);
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, name, (int) mBeginVsyncId);
// We don't remove observer here,
// will remove it when all the frame metrics in this duration are called back.
@@ -395,16 +366,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
mFlushAttempts++;
} else {
mWaitForFinishTimedOut = () -> {
- Log.e(TAG, "force finish cuj, time out: " + mSession.getName());
+ Log.e(TAG, "force finish cuj, time out: " + name);
finish();
};
delay = TimeUnit.SECONDS.toMillis(10);
}
- getHandler().postDelayed(mWaitForFinishTimedOut, delay);
+ mHandler.postDelayed(mWaitForFinishTimedOut, delay);
}
};
- getHandler().postDelayed(mWaitForFinishTimedOut, FLUSH_DELAY_MILLISECOND);
- notifyCujEvent(ACTION_SESSION_END);
+ mHandler.postDelayed(mWaitForFinishTimedOut, FLUSH_DELAY_MILLISECOND);
+ notifyCujEvent(ACTION_SESSION_END, reason);
return true;
}
}
@@ -421,22 +392,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
markEvent("FT#cancel", reason);
// We don't need to end the trace section if it has never begun.
if (mTracingStarted) {
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, mSession.getName(),
- (int) mBeginVsyncId);
+ Trace.asyncTraceForTrackEnd(
+ Trace.TRACE_TAG_APP, mConfig.getSessionName(), (int) mBeginVsyncId);
}
// Always remove the observers in cancel call to avoid leakage.
removeObservers();
- if (DEBUG) {
- Log.d(TAG, "cancel: " + mSession.getName() + ", begin=" + mBeginVsyncId
- + ", end=" + mEndVsyncId + ", reason=" + reason);
- }
-
- mSession.setReason(reason);
// Notify the listener the session has been cancelled.
// We don't notify the listeners if the session never begun.
- notifyCujEvent(ACTION_SESSION_CANCEL);
+ notifyCujEvent(ACTION_SESSION_CANCEL, reason);
return true;
}
@@ -455,13 +420,13 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
"The length of the trace event description <%s> exceeds %d",
event, MAX_LENGTH_EVENT_DESC));
}
- Trace.instantForTrack(Trace.TRACE_TAG_APP, mSession.getName(), event);
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, mConfig.getSessionName(), event);
}
}
- private void notifyCujEvent(String action) {
+ private void notifyCujEvent(String action, @Reasons int reason) {
if (mListener == null) return;
- mListener.onCujEvents(mSession, action);
+ mListener.onCujEvents(this, action, reason);
}
@Override
@@ -496,7 +461,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
*/
@VisibleForTesting
public void postCallback(Runnable callback) {
- getHandler().post(callback);
+ mHandler.post(callback);
}
@Nullable
@@ -587,13 +552,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
if (mMetricsFinalized || mCancelled) return;
mMetricsFinalized = true;
- getHandler().removeCallbacks(mWaitForFinishTimedOut);
+ mHandler.removeCallbacks(mWaitForFinishTimedOut);
mWaitForFinishTimedOut = null;
markEvent("FT#finish", mJankInfos.size());
// The tracing has been ended, remove the observer, see if need to trigger perfetto.
removeObservers();
+ final String name = mConfig.getSessionName();
+
int totalFramesCount = 0;
long maxFrameTimeNanos = 0;
int missedFramesCount = 0;
@@ -616,7 +583,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
totalFramesCount++;
boolean missedFrame = false;
if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
- Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + mSession.getName());
+ Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + name);
missedAppFramesCount++;
missedFrame = true;
}
@@ -625,7 +592,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|| (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
|| (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
|| (info.jankType & PREDICTION_ERROR) != 0) {
- Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + mSession.getName());
+ Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + name);
missedSfFramesCount++;
missedFrame = true;
}
@@ -646,7 +613,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
if (!mSurfaceOnly && !info.hwuiCallbackFired) {
markEvent("FT#MissedHWUICallback", info.frameVsyncId);
Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId
- + ", CUJ=" + mSession.getName());
+ + ", CUJ=" + name);
}
}
if (!mSurfaceOnly && info.hwuiCallbackFired) {
@@ -654,7 +621,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
if (!info.surfaceControlCallbackFired) {
markEvent("FT#MissedSFCallback", info.frameVsyncId);
Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId
- + ", CUJ=" + mSession.getName());
+ + ", CUJ=" + name);
}
}
}
@@ -662,29 +629,26 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
maxSuccessiveMissedFramesCount, successiveMissedFramesCount);
// Log the frame stats as counters to make them easily accessible in traces.
- Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedFrames",
- missedFramesCount);
- Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedAppFrames",
- missedAppFramesCount);
- Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedSfFrames",
- missedSfFramesCount);
- Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames",
- totalFramesCount);
- Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis",
+ Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#missedFrames", missedFramesCount);
+ Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#missedAppFrames", missedAppFramesCount);
+ Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#missedSfFrames", missedSfFramesCount);
+ Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#totalFrames", totalFramesCount);
+ Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#maxFrameTimeMillis",
(int) (maxFrameTimeNanos / NANOS_IN_MILLISECOND));
- Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxSuccessiveMissedFrames",
+ Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#maxSuccessiveMissedFrames",
maxSuccessiveMissedFramesCount);
// Trigger perfetto if necessary.
- if (shouldTriggerPerfetto(missedFramesCount, (int) maxFrameTimeNanos)) {
- triggerPerfetto();
+ if (mListener != null
+ && shouldTriggerPerfetto(missedFramesCount, (int) maxFrameTimeNanos)) {
+ mListener.triggerPerfetto(mConfig);
}
- if (mSession.logToStatsd()) {
+ if (mConfig.logToStatsd()) {
mStatsLog.write(
FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED,
mDisplayId,
refreshRate,
- mSession.getStatsdInteractionType(),
+ mConfig.getStatsdInteractionType(),
totalFramesCount,
missedFramesCount,
maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
@@ -692,16 +656,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
missedAppFramesCount,
maxSuccessiveMissedFramesCount);
}
- if (DEBUG) {
- Log.i(TAG, "finish: CUJ=" + mSession.getName()
- + " (" + mBeginVsyncId + "," + mEndVsyncId + ")"
- + " totalFrames=" + totalFramesCount
- + " missedAppFrames=" + missedAppFramesCount
- + " missedSfFrames=" + missedSfFramesCount
- + " missedFrames=" + missedFramesCount
- + " maxFrameTimeMillis=" + maxFrameTimeNanos / NANOS_IN_MILLISECOND
- + " maxSuccessiveMissedFramesCount=" + maxSuccessiveMissedFramesCount);
- }
}
ThreadedRendererWrapper getThreadedRenderer() {
@@ -737,13 +691,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
/**
- * Trigger the prefetto daemon.
- */
- public void triggerPerfetto() {
- mMonitor.trigger(mSession);
- }
-
- /**
* A wrapper class that we can spy FrameMetrics (a final class) in unit tests.
*/
public static class FrameMetricsWrapper {
@@ -895,9 +842,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
/**
* Notify that the CUJ session was created.
*
- * @param session the CUJ session
+ * @param tracker the tracker
* @param action the specific action
+ * @param reason the reason for the action
+ */
+ void onCujEvents(FrameTracker tracker, String action, @Reasons int reason);
+
+ /**
+ * Notify that the Perfetto trace should be triggered.
+ *
+ * @param config the tracker configuration
*/
- void onCujEvents(Session session, String action);
+ void triggerPerfetto(Configuration config);
}
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index e6b036cfaa19..8b1879f5225d 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -23,89 +23,9 @@ import static android.provider.DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR;
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
-import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_SHOW_ANIMATION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNFOLD_ANIM;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
import android.Manifest;
import android.annotation.ColorInt;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.UiThread;
@@ -137,34 +57,31 @@ import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
import com.android.internal.util.PerfettoTrigger;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.time.Instant;
-import java.util.Locale;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
/**
- * This class let users to begin and end the always on tracing mechanism.
+ * This class lets users begin and end the always on tracing mechanism.
*
* Enabling for local development:
- *
+ *<pre>
* adb shell device_config put interaction_jank_monitor enabled true
* adb shell device_config put interaction_jank_monitor sampling_interval 1
- *
+ * </pre>
* On debuggable builds, an overlay can be used to display the name of the
* currently running cuj using:
- *
+ * <pre>
* adb shell device_config put interaction_jank_monitor debug_overlay_enabled true
- *
- * NOTE: The overlay will interfere with metrics, so it should only be used
- * for understanding which UI events correspeond to which CUJs.
+ * </pre>
+ * <b>NOTE</b>: The overlay will interfere with metrics, so it should only be used
+ * for understanding which UI events correspond to which CUJs.
*
* @hide
*/
public class InteractionJankMonitor {
private static final String TAG = InteractionJankMonitor.class.getSimpleName();
- private static final boolean DEBUG = false;
private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();
private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
@@ -186,218 +103,79 @@ public class InteractionJankMonitor {
private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
private static final boolean DEFAULT_DEBUG_OVERLAY_ENABLED = false;
- @VisibleForTesting
- public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
private static final int MAX_LENGTH_SESSION_NAME = 100;
public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
- // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
- public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
- public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2;
- public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3;
- public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4;
- public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 5;
- public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 6;
- public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 7;
- public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 8;
- public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9;
- public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10;
- public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11;
- public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = 12;
- public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = 13;
- public static final int CUJ_NOTIFICATION_ADD = 14;
- public static final int CUJ_NOTIFICATION_REMOVE = 15;
- public static final int CUJ_NOTIFICATION_APP_START = 16;
- public static final int CUJ_LOCKSCREEN_PASSWORD_APPEAR = 17;
- public static final int CUJ_LOCKSCREEN_PATTERN_APPEAR = 18;
- public static final int CUJ_LOCKSCREEN_PIN_APPEAR = 19;
- public static final int CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR = 20;
- public static final int CUJ_LOCKSCREEN_PATTERN_DISAPPEAR = 21;
- public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22;
- public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23;
- public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24;
- public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25;
- public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26;
- public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27;
- public static final int CUJ_SETTINGS_PAGE_SCROLL = 28;
- public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29;
- public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30;
- public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31;
- public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32;
- public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33;
- public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
- public static final int CUJ_PIP_TRANSITION = 35;
- public static final int CUJ_WALLPAPER_TRANSITION = 36;
- public static final int CUJ_USER_SWITCH = 37;
- public static final int CUJ_SPLASHSCREEN_AVD = 38;
- public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39;
- public static final int CUJ_SCREEN_OFF = 40;
- public static final int CUJ_SCREEN_OFF_SHOW_AOD = 41;
- public static final int CUJ_ONE_HANDED_ENTER_TRANSITION = 42;
- public static final int CUJ_ONE_HANDED_EXIT_TRANSITION = 43;
- public static final int CUJ_UNFOLD_ANIM = 44;
- public static final int CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS = 45;
- public static final int CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS = 46;
- public static final int CUJ_SUW_LOADING_TO_NEXT_FLOW = 47;
- public static final int CUJ_SUW_LOADING_SCREEN_FOR_STATUS = 48;
- public static final int CUJ_SPLIT_SCREEN_ENTER = 49;
- public static final int CUJ_SPLIT_SCREEN_EXIT = 50;
- public static final int CUJ_LOCKSCREEN_LAUNCH_CAMERA = 51; // reserved.
- public static final int CUJ_SPLIT_SCREEN_RESIZE = 52;
- public static final int CUJ_SETTINGS_SLIDER = 53;
- public static final int CUJ_TAKE_SCREENSHOT = 54;
- public static final int CUJ_VOLUME_CONTROL = 55;
- public static final int CUJ_BIOMETRIC_PROMPT_TRANSITION = 56;
- public static final int CUJ_SETTINGS_TOGGLE = 57;
- public static final int CUJ_SHADE_DIALOG_OPEN = 58;
- public static final int CUJ_USER_DIALOG_OPEN = 59;
- public static final int CUJ_TASKBAR_EXPAND = 60;
- public static final int CUJ_TASKBAR_COLLAPSE = 61;
- public static final int CUJ_SHADE_CLEAR_ALL = 62;
- public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63;
- public static final int CUJ_LOCKSCREEN_OCCLUSION = 64;
- public static final int CUJ_RECENTS_SCROLLING = 65;
- public static final int CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS = 66;
- public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE = 67;
- public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME = 68;
- public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70;
- public static final int CUJ_LAUNCHER_OPEN_SEARCH_RESULT = 71;
- // 72 - 77 are reserved for b/281564325.
-
- /**
- * In some cases when we do not have any end-target, we play a simple slide-down animation.
- * eg: Open an app from Overview/Task switcher such that there is no home-screen icon.
- * eg: Exit the app using back gesture.
- */
- public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK = 78;
- public static final int CUJ_IME_INSETS_SHOW_ANIMATION = 80;
- public static final int CUJ_IME_INSETS_HIDE_ANIMATION = 81;
-
- public static final int CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER = 82;
-
- public static final int CUJ_LAUNCHER_UNFOLD_ANIM = 83;
-
- public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84;
- public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85;
- public static final int CUJ_PREDICTIVE_BACK_HOME = 86;
-
- private static final int LAST_CUJ = CUJ_PREDICTIVE_BACK_HOME;
- private static final int NO_STATSD_LOGGING = -1;
-
- // Used to convert CujType to InteractionType enum value for statsd logging.
- // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd.
- @VisibleForTesting
- public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = new int[LAST_CUJ + 1];
-
- static {
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
- CUJ_TO_STATSD_INTERACTION_TYPE[1] = NO_STATSD_LOGGING; // This is deprecated.
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_SCROLL_FLING] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_EXPAND] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_SWIPE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_PIP] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_QUICK_SWITCH] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_ADD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_REMOVE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_APP_START] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_FROM_AOD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_TO_AOD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_ALL_APPS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_ALL_APPS_SCROLL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_PAGE_SCROLL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_UNLOCK_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PIP_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_SWITCH] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_AVD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_EXIT_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF_SHOW_AOD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_ENTER_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_EXIT_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_UNFOLD_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_NEXT_FLOW] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_SCREEN_FOR_STATUS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_ENTER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_EXIT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_LAUNCH_CAMERA] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_RESIZE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_SLIDER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TAKE_SCREENSHOT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_VOLUME_CONTROL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BIOMETRIC_PROMPT_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_TOGGLE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_DIALOG_OPEN] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_DIALOG_OPEN] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_EXPAND] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_COLLAPSE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_CLEAR_ALL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_OCCLUSION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_RECENTS_SCROLLING] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME;
- CUJ_TO_STATSD_INTERACTION_TYPE[69] = NO_STATSD_LOGGING; // This is deprecated.
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_SEARCH_RESULT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT;
- // 72 - 77 are reserved for b/281564325.
- CUJ_TO_STATSD_INTERACTION_TYPE[72] = NO_STATSD_LOGGING;
- CUJ_TO_STATSD_INTERACTION_TYPE[73] = NO_STATSD_LOGGING;
- CUJ_TO_STATSD_INTERACTION_TYPE[74] = NO_STATSD_LOGGING;
- CUJ_TO_STATSD_INTERACTION_TYPE[75] = NO_STATSD_LOGGING;
- CUJ_TO_STATSD_INTERACTION_TYPE[76] = NO_STATSD_LOGGING;
- CUJ_TO_STATSD_INTERACTION_TYPE[77] = NO_STATSD_LOGGING;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK;
- CUJ_TO_STATSD_INTERACTION_TYPE[79] = NO_STATSD_LOGGING; // This is deprecated.
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_SHOW_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_SHOW_ANIMATION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_HIDE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNFOLD_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNFOLD_ANIM;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] =
- UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] =
- UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] =
- UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
- }
+ // These are not the CUJ constants you are looking for. These constants simply forward their
+ // definition from {@link Cuj}. They are here only as a transition measure until all references
+ // have been updated to the new location.
+ @Deprecated public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+ @Deprecated public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = Cuj.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
+ @Deprecated public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = Cuj.CUJ_NOTIFICATION_SHADE_ROW_EXPAND;
+ @Deprecated public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = Cuj.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
+ @Deprecated public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+ @Deprecated public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = Cuj.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
+ @Deprecated public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME;
+ @Deprecated public static final int CUJ_LAUNCHER_QUICK_SWITCH = Cuj.CUJ_LAUNCHER_QUICK_SWITCH;
+ @Deprecated public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = Cuj.CUJ_NOTIFICATION_HEADS_UP_APPEAR;
+ @Deprecated public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = Cuj.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR;
+ @Deprecated public static final int CUJ_NOTIFICATION_ADD = Cuj.CUJ_NOTIFICATION_ADD;
+ @Deprecated public static final int CUJ_NOTIFICATION_REMOVE = Cuj.CUJ_NOTIFICATION_REMOVE;
+ @Deprecated public static final int CUJ_NOTIFICATION_APP_START = Cuj.CUJ_NOTIFICATION_APP_START;
+ @Deprecated public static final int CUJ_LOCKSCREEN_PASSWORD_APPEAR = Cuj.CUJ_LOCKSCREEN_PASSWORD_APPEAR;
+ @Deprecated public static final int CUJ_LOCKSCREEN_PATTERN_APPEAR = Cuj.CUJ_LOCKSCREEN_PATTERN_APPEAR;
+ @Deprecated public static final int CUJ_LOCKSCREEN_PIN_APPEAR = Cuj.CUJ_LOCKSCREEN_PIN_APPEAR;
+ @Deprecated public static final int CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR = Cuj.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR;
+ @Deprecated public static final int CUJ_LOCKSCREEN_PATTERN_DISAPPEAR = Cuj.CUJ_LOCKSCREEN_PATTERN_DISAPPEAR;
+ @Deprecated public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = Cuj.CUJ_LOCKSCREEN_PIN_DISAPPEAR;
+ @Deprecated public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = Cuj.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
+ @Deprecated public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = Cuj.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
+ @Deprecated public static final int CUJ_SETTINGS_PAGE_SCROLL = Cuj.CUJ_SETTINGS_PAGE_SCROLL;
+ @Deprecated public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = Cuj.CUJ_LOCKSCREEN_UNLOCK_ANIMATION;
+ @Deprecated public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = Cuj.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
+ @Deprecated public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = Cuj.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
+ @Deprecated public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = Cuj.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE;
+ @Deprecated public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = Cuj.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
+ @Deprecated public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
+ @Deprecated public static final int CUJ_PIP_TRANSITION = Cuj.CUJ_PIP_TRANSITION;
+ @Deprecated public static final int CUJ_USER_SWITCH = Cuj.CUJ_USER_SWITCH;
+ @Deprecated public static final int CUJ_SPLASHSCREEN_AVD = Cuj.CUJ_SPLASHSCREEN_AVD;
+ @Deprecated public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = Cuj.CUJ_SPLASHSCREEN_EXIT_ANIM;
+ @Deprecated public static final int CUJ_SCREEN_OFF = Cuj.CUJ_SCREEN_OFF;
+ @Deprecated public static final int CUJ_SCREEN_OFF_SHOW_AOD = Cuj.CUJ_SCREEN_OFF_SHOW_AOD;
+ @Deprecated public static final int CUJ_UNFOLD_ANIM = Cuj.CUJ_UNFOLD_ANIM;
+ @Deprecated public static final int CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS = Cuj.CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS;
+ @Deprecated public static final int CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS = Cuj.CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS;
+ @Deprecated public static final int CUJ_SUW_LOADING_TO_NEXT_FLOW = Cuj.CUJ_SUW_LOADING_TO_NEXT_FLOW;
+ @Deprecated public static final int CUJ_SUW_LOADING_SCREEN_FOR_STATUS = Cuj.CUJ_SUW_LOADING_SCREEN_FOR_STATUS;
+ @Deprecated public static final int CUJ_SPLIT_SCREEN_RESIZE = Cuj.CUJ_SPLIT_SCREEN_RESIZE;
+ @Deprecated public static final int CUJ_SETTINGS_SLIDER = Cuj.CUJ_SETTINGS_SLIDER;
+ @Deprecated public static final int CUJ_TAKE_SCREENSHOT = Cuj.CUJ_TAKE_SCREENSHOT;
+ @Deprecated public static final int CUJ_VOLUME_CONTROL = Cuj.CUJ_VOLUME_CONTROL;
+ @Deprecated public static final int CUJ_BIOMETRIC_PROMPT_TRANSITION = Cuj.CUJ_BIOMETRIC_PROMPT_TRANSITION;
+ @Deprecated public static final int CUJ_SETTINGS_TOGGLE = Cuj.CUJ_SETTINGS_TOGGLE;
+ @Deprecated public static final int CUJ_SHADE_DIALOG_OPEN = Cuj.CUJ_SHADE_DIALOG_OPEN;
+ @Deprecated public static final int CUJ_USER_DIALOG_OPEN = Cuj.CUJ_USER_DIALOG_OPEN;
+ @Deprecated public static final int CUJ_TASKBAR_EXPAND = Cuj.CUJ_TASKBAR_EXPAND;
+ @Deprecated public static final int CUJ_TASKBAR_COLLAPSE = Cuj.CUJ_TASKBAR_COLLAPSE;
+ @Deprecated public static final int CUJ_SHADE_CLEAR_ALL = Cuj.CUJ_SHADE_CLEAR_ALL;
+ @Deprecated public static final int CUJ_LOCKSCREEN_OCCLUSION = Cuj.CUJ_LOCKSCREEN_OCCLUSION;
+ @Deprecated public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = Cuj.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION;
+ @Deprecated public static final int CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER = Cuj.CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
+ @Deprecated public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY;
+ @Deprecated public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = Cuj.CUJ_PREDICTIVE_BACK_CROSS_TASK;
+ @Deprecated public static final int CUJ_PREDICTIVE_BACK_HOME = Cuj.CUJ_PREDICTIVE_BACK_HOME;
private static class InstanceHolder {
public static final InteractionJankMonitor INSTANCE =
new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME));
}
- private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
- this::updateProperties;
-
@GuardedBy("mLock")
- private final SparseArray<FrameTracker> mRunningTrackers;
- @GuardedBy("mLock")
- private final SparseArray<Runnable> mTimeoutActions;
- private final HandlerThread mWorker;
+ private final SparseArray<RunningTracker> mRunningTrackers = new SparseArray<>();
+ private final Handler mWorker;
private final DisplayResolutionTracker mDisplayResolutionTracker;
private final Object mLock = new Object();
private @ColorInt int mDebugBgColor = Color.CYAN;
@@ -409,91 +187,6 @@ public class InteractionJankMonitor {
private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES;
private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS;
- /** @hide */
- @IntDef({
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
- CUJ_NOTIFICATION_SHADE_SCROLL_FLING,
- CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
- CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
- CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
- CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE,
- CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS,
- CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON,
- CUJ_LAUNCHER_APP_CLOSE_TO_HOME,
- CUJ_LAUNCHER_APP_CLOSE_TO_PIP,
- CUJ_LAUNCHER_QUICK_SWITCH,
- CUJ_NOTIFICATION_HEADS_UP_APPEAR,
- CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR,
- CUJ_NOTIFICATION_ADD,
- CUJ_NOTIFICATION_REMOVE,
- CUJ_NOTIFICATION_APP_START,
- CUJ_LOCKSCREEN_PASSWORD_APPEAR,
- CUJ_LOCKSCREEN_PATTERN_APPEAR,
- CUJ_LOCKSCREEN_PIN_APPEAR,
- CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR,
- CUJ_LOCKSCREEN_PATTERN_DISAPPEAR,
- CUJ_LOCKSCREEN_PIN_DISAPPEAR,
- CUJ_LOCKSCREEN_TRANSITION_FROM_AOD,
- CUJ_LOCKSCREEN_TRANSITION_TO_AOD,
- CUJ_LAUNCHER_OPEN_ALL_APPS,
- CUJ_LAUNCHER_ALL_APPS_SCROLL,
- CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET,
- CUJ_SETTINGS_PAGE_SCROLL,
- CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
- CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON,
- CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER,
- CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
- CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
- CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
- CUJ_PIP_TRANSITION,
- CUJ_WALLPAPER_TRANSITION,
- CUJ_USER_SWITCH,
- CUJ_SPLASHSCREEN_AVD,
- CUJ_SPLASHSCREEN_EXIT_ANIM,
- CUJ_SCREEN_OFF,
- CUJ_SCREEN_OFF_SHOW_AOD,
- CUJ_ONE_HANDED_ENTER_TRANSITION,
- CUJ_ONE_HANDED_EXIT_TRANSITION,
- CUJ_UNFOLD_ANIM,
- CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS,
- CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS,
- CUJ_SUW_LOADING_TO_NEXT_FLOW,
- CUJ_SUW_LOADING_SCREEN_FOR_STATUS,
- CUJ_SPLIT_SCREEN_ENTER,
- CUJ_SPLIT_SCREEN_EXIT,
- CUJ_LOCKSCREEN_LAUNCH_CAMERA,
- CUJ_SPLIT_SCREEN_RESIZE,
- CUJ_SETTINGS_SLIDER,
- CUJ_TAKE_SCREENSHOT,
- CUJ_VOLUME_CONTROL,
- CUJ_BIOMETRIC_PROMPT_TRANSITION,
- CUJ_SETTINGS_TOGGLE,
- CUJ_SHADE_DIALOG_OPEN,
- CUJ_USER_DIALOG_OPEN,
- CUJ_TASKBAR_EXPAND,
- CUJ_TASKBAR_COLLAPSE,
- CUJ_SHADE_CLEAR_ALL,
- CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
- CUJ_LOCKSCREEN_OCCLUSION,
- CUJ_RECENTS_SCROLLING,
- CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS,
- CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE,
- CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME,
- CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION,
- CUJ_LAUNCHER_OPEN_SEARCH_RESULT,
- CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK,
- CUJ_IME_INSETS_SHOW_ANIMATION,
- CUJ_IME_INSETS_HIDE_ANIMATION,
- CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER,
- CUJ_LAUNCHER_UNFOLD_ANIM,
- CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY,
- CUJ_PREDICTIVE_BACK_CROSS_TASK,
- CUJ_PREDICTIVE_BACK_HOME,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CujType {
- }
-
/**
* Get the singleton of InteractionJankMonitor.
*
@@ -511,71 +204,44 @@ public class InteractionJankMonitor {
@VisibleForTesting
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public InteractionJankMonitor(@NonNull HandlerThread worker) {
- mRunningTrackers = new SparseArray<>();
- mTimeoutActions = new SparseArray<>();
- mWorker = worker;
- mWorker.start();
- mDisplayResolutionTracker = new DisplayResolutionTracker(worker.getThreadHandler());
- mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
- mEnabled = DEFAULT_ENABLED;
+ worker.start();
+ mWorker = worker.getThreadHandler();
+ mDisplayResolutionTracker = new DisplayResolutionTracker(mWorker);
final Context context = ActivityThread.currentApplication();
- if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
- if (DEBUG) {
- Log.d(TAG, "Initialized the InteractionJankMonitor."
- + " (No READ_DEVICE_CONFIG permission to change configs)"
- + " enabled=" + mEnabled + ", interval=" + mSamplingInterval
- + ", missedFrameThreshold=" + mTraceThresholdMissedFrames
- + ", frameTimeThreshold=" + mTraceThresholdFrameTimeMillis
- + ", package=" + context.getPackageName());
- }
+ if (context == null || context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
+ Log.w(TAG, "Initializing without READ_DEVICE_CONFIG permission."
+ + " enabled=" + mEnabled + ", interval=" + mSamplingInterval
+ + ", missedFrameThreshold=" + mTraceThresholdMissedFrames
+ + ", frameTimeThreshold=" + mTraceThresholdFrameTimeMillis
+ + ", package=" + (context == null ? "null" : context.getPackageName()));
return;
}
// Post initialization to the background in case we're running on the main thread.
- mWorker.getThreadHandler().post(
- () -> {
- try {
- mPropertiesChangedListener.onPropertiesChanged(
- DeviceConfig.getProperties(NAMESPACE_INTERACTION_JANK_MONITOR));
- DeviceConfig.addOnPropertiesChangedListener(
- NAMESPACE_INTERACTION_JANK_MONITOR,
- new HandlerExecutor(mWorker.getThreadHandler()),
- mPropertiesChangedListener);
- } catch (SecurityException ex) {
- Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted="
- + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
- + ", package=" + context.getPackageName());
- }
- });
+ mWorker.post(() -> {
+ try {
+ updateProperties(DeviceConfig.getProperties(NAMESPACE_INTERACTION_JANK_MONITOR));
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_INTERACTION_JANK_MONITOR,
+ new HandlerExecutor(mWorker), this::updateProperties);
+ } catch (SecurityException ex) {
+ Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted="
+ + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
+ + ", package=" + context.getPackageName());
+ }
+ });
}
/**
* Creates a {@link FrameTracker} instance.
*
- * @param config the config used in instrumenting
- * @param session the session associates with this tracker
+ * @param config the conifg associates with this tracker
* @return instance of the FrameTracker
*/
@VisibleForTesting
- public FrameTracker createFrameTracker(Configuration config, Session session) {
+ public FrameTracker createFrameTracker(Configuration config) {
final View view = config.mView;
- if (!config.hasValidView()) {
- boolean attached = false;
- boolean hasViewRoot = false;
- boolean hasRenderer = false;
- if (view != null) {
- attached = view.isAttachedToWindow();
- hasViewRoot = view.getViewRootImpl() != null;
- hasRenderer = view.getThreadedRenderer() != null;
- }
- Log.d(TAG, "create FrameTracker fails: view=" + view
- + ", attached=" + attached + ", hasViewRoot=" + hasViewRoot
- + ", hasRenderer=" + hasRenderer, new Throwable());
- return null;
- }
-
final ThreadedRendererWrapper threadedRenderer =
view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer());
final ViewRootWrapper viewRoot =
@@ -583,52 +249,50 @@ public class InteractionJankMonitor {
final SurfaceControlWrapper surfaceControl = new SurfaceControlWrapper();
final ChoreographerWrapper choreographer =
new ChoreographerWrapper(Choreographer.getInstance());
- final FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(act, s);
+ final FrameTrackerListener eventsListener = new FrameTrackerListener() {
+ @Override
+ public void onCujEvents(FrameTracker tracker, String action, int reason) {
+ config.getHandler().runWithScissors(() ->
+ handleCujEvents(config.mCujType, tracker, action, reason),
+ EXECUTOR_TASK_TIMEOUT);
+ }
+
+ @Override
+ public void triggerPerfetto(Configuration config) {
+ mWorker.post(() -> PerfettoTrigger.trigger(config.getPerfettoTrigger()));
+ }
+ };
final FrameMetricsWrapper frameMetrics = new FrameMetricsWrapper();
- return new FrameTracker(this, session, config.getHandler(), threadedRenderer, viewRoot,
+ return new FrameTracker(config, threadedRenderer, viewRoot,
surfaceControl, choreographer, frameMetrics,
new FrameTracker.StatsLogWrapper(mDisplayResolutionTracker),
mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
- eventsListener, config);
+ eventsListener);
}
@UiThread
- private void handleCujEvents(String action, Session session) {
+ private void handleCujEvents(
+ @Cuj.CujType int cuj, FrameTracker tracker, String action, @Reasons int reason) {
// Clear the running and timeout tasks if the end / cancel was fired within the tracker.
// Or we might have memory leaks.
- if (needRemoveTasks(action, session)) {
- getTracker(session.getCuj()).getHandler().runWithScissors(() -> {
- removeTimeout(session.getCuj());
- removeTracker(session.getCuj(), session.getReason());
- }, EXECUTOR_TASK_TIMEOUT);
+ if (needRemoveTasks(action, reason)) {
+ removeTrackerIfCurrent(cuj, tracker, reason);
}
}
- private boolean needRemoveTasks(String action, Session session) {
- final boolean badEnd = action.equals(ACTION_SESSION_END)
- && session.getReason() != REASON_END_NORMAL;
+ private static boolean needRemoveTasks(String action, @Reasons int reason) {
+ final boolean badEnd = action.equals(ACTION_SESSION_END) && reason != REASON_END_NORMAL;
final boolean badCancel = action.equals(ACTION_SESSION_CANCEL)
- && !(session.getReason() == REASON_CANCEL_NORMAL
- || session.getReason() == REASON_CANCEL_TIMEOUT);
+ && !(reason == REASON_CANCEL_NORMAL || reason == REASON_CANCEL_TIMEOUT);
return badEnd || badCancel;
}
- private void removeTimeout(@CujType int cujType) {
- synchronized (mLock) {
- Runnable timeout = mTimeoutActions.get(cujType);
- if (timeout != null) {
- getTracker(cujType).getHandler().removeCallbacks(timeout);
- mTimeoutActions.remove(cujType);
- }
- }
- }
-
/**
* @param cujType cuj type
* @return true if the cuj is under instrumenting, false otherwise.
*/
- public boolean isInstrumenting(@CujType int cujType) {
+ public boolean isInstrumenting(@Cuj.CujType int cujType) {
synchronized (mLock) {
return mRunningTrackers.contains(cujType);
}
@@ -638,10 +302,10 @@ public class InteractionJankMonitor {
* Begins a trace session.
*
* @param v an attached view.
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link Cuj.CujType}.
* @return boolean true if the tracker is started successfully, false otherwise.
*/
- public boolean begin(View v, @CujType int cujType) {
+ public boolean begin(View v, @Cuj.CujType int cujType) {
try {
return begin(Configuration.Builder.withView(cujType, v));
} catch (IllegalArgumentException ex) {
@@ -667,7 +331,7 @@ public class InteractionJankMonitor {
final boolean success = config.getHandler().runWithScissors(
() -> result.mResult = beginInternal(config), EXECUTOR_TASK_TIMEOUT);
if (!success) {
- Log.d(TAG, "begin failed due to timeout, CUJ=" + getNameOfCuj(config.mCujType));
+ Log.d(TAG, "begin failed due to timeout, CUJ=" + Cuj.getNameOfCuj(config.mCujType));
return false;
}
return result.mResult;
@@ -680,75 +344,59 @@ public class InteractionJankMonitor {
@UiThread
private boolean beginInternal(@NonNull Configuration conf) {
int cujType = conf.mCujType;
- if (!shouldMonitor(cujType)) return false;
- FrameTracker tracker = getTracker(cujType);
- // Skip subsequent calls if we already have an ongoing tracing.
- if (tracker != null) return false;
+ if (!shouldMonitor()) {
+ return false;
+ }
- // begin a new trace session.
- tracker = createFrameTracker(conf, new Session(cujType, conf.mTag));
- if (tracker == null) return false;
- putTracker(cujType, tracker);
- tracker.begin();
+ RunningTracker tracker = putTrackerIfNoCurrent(cujType, () ->
+ new RunningTracker(
+ conf, createFrameTracker(conf), () -> cancel(cujType, REASON_CANCEL_TIMEOUT)));
+ if (tracker == null) {
+ return false;
+ }
+ tracker.mTracker.begin();
// Cancel the trace if we don't get an end() call in specified duration.
- scheduleTimeoutAction(
- cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT));
+ scheduleTimeoutAction(tracker.mConfig, tracker.mTimeoutAction);
+
return true;
}
/**
* Check if the monitoring is enabled and if it should be sampled.
*/
- @SuppressWarnings("RandomModInteger")
@VisibleForTesting
- public boolean shouldMonitor(@CujType int cujType) {
- boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
- if (!mEnabled || !shouldSample) {
- if (DEBUG) {
- Log.d(TAG, "Skip monitoring cuj: " + getNameOfCuj(cujType)
- + ", enable=" + mEnabled + ", debuggable=" + DEFAULT_ENABLED
- + ", sample=" + shouldSample + ", interval=" + mSamplingInterval);
- }
- return false;
- }
- return true;
+ public boolean shouldMonitor() {
+ return mEnabled && (ThreadLocalRandom.current().nextInt(mSamplingInterval) == 0);
}
- /**
- * Schedules a timeout action.
- * @param cuj cuj type
- * @param timeout duration to timeout
- * @param action action once timeout
- */
@VisibleForTesting
- public void scheduleTimeoutAction(@CujType int cuj, long timeout, Runnable action) {
- synchronized (mLock) {
- mTimeoutActions.put(cuj, action);
- getTracker(cuj).getHandler().postDelayed(action, timeout);
- }
+ public void scheduleTimeoutAction(Configuration config, Runnable action) {
+ config.getHandler().postDelayed(action, config.mTimeout);
}
/**
* Ends a trace session.
*
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link Cuj.CujType}.
* @return boolean true if the tracker is ended successfully, false otherwise.
*/
- public boolean end(@CujType int cujType) {
+ public boolean end(@Cuj.CujType int cujType) {
postEventLogToWorkerThread((unixNanos, elapsedNanos, realtimeNanos) -> {
EventLogTags.writeJankCujEventsEndRequest(
cujType, unixNanos, elapsedNanos, realtimeNanos);
});
- FrameTracker tracker = getTracker(cujType);
+ RunningTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
- if (tracker == null) return false;
+ if (tracker == null) {
+ return false;
+ }
try {
final TrackerResult result = new TrackerResult();
- final boolean success = tracker.getHandler().runWithScissors(
- () -> result.mResult = endInternal(cujType), EXECUTOR_TASK_TIMEOUT);
+ final boolean success = tracker.mConfig.getHandler().runWithScissors(
+ () -> result.mResult = endInternal(tracker), EXECUTOR_TASK_TIMEOUT);
if (!success) {
- Log.d(TAG, "end failed due to timeout, CUJ=" + getNameOfCuj(cujType));
+ Log.d(TAG, "end failed due to timeout, CUJ=" + Cuj.getNameOfCuj(cujType));
return false;
}
return result.mResult;
@@ -759,15 +407,11 @@ public class InteractionJankMonitor {
}
@UiThread
- private boolean endInternal(@CujType int cujType) {
- // remove the timeout action first.
- removeTimeout(cujType);
- FrameTracker tracker = getTracker(cujType);
- if (tracker == null) return false;
- // if the end call doesn't return true, another thread is handling end of the cuj.
- if (tracker.end(REASON_END_NORMAL)) {
- removeTracker(cujType, REASON_END_NORMAL);
+ private boolean endInternal(RunningTracker tracker) {
+ if (removeTrackerIfCurrent(tracker, REASON_END_NORMAL)) {
+ return false;
}
+ tracker.mTracker.end(REASON_END_NORMAL);
return true;
}
@@ -776,7 +420,7 @@ public class InteractionJankMonitor {
*
* @return boolean true if the tracker is cancelled successfully, false otherwise.
*/
- public boolean cancel(@CujType int cujType) {
+ public boolean cancel(@Cuj.CujType int cujType) {
postEventLogToWorkerThread((unixNanos, elapsedNanos, realtimeNanos) -> {
EventLogTags.writeJankCujEventsCancelRequest(
cujType, unixNanos, elapsedNanos, realtimeNanos);
@@ -790,16 +434,18 @@ public class InteractionJankMonitor {
* @return boolean true if the tracker is cancelled successfully, false otherwise.
*/
@VisibleForTesting
- public boolean cancel(@CujType int cujType, @Reasons int reason) {
- FrameTracker tracker = getTracker(cujType);
+ public boolean cancel(@Cuj.CujType int cujType, @Reasons int reason) {
+ RunningTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
- if (tracker == null) return false;
+ if (tracker == null) {
+ return false;
+ }
try {
final TrackerResult result = new TrackerResult();
- final boolean success = tracker.getHandler().runWithScissors(
- () -> result.mResult = cancelInternal(cujType, reason), EXECUTOR_TASK_TIMEOUT);
+ final boolean success = tracker.mConfig.getHandler().runWithScissors(
+ () -> result.mResult = cancelInternal(tracker, reason), EXECUTOR_TASK_TIMEOUT);
if (!success) {
- Log.d(TAG, "cancel failed due to timeout, CUJ=" + getNameOfCuj(cujType));
+ Log.d(TAG, "cancel failed due to timeout, CUJ=" + Cuj.getNameOfCuj(cujType));
return false;
}
return result.mResult;
@@ -810,71 +456,86 @@ public class InteractionJankMonitor {
}
@UiThread
- private boolean cancelInternal(@CujType int cujType, @Reasons int reason) {
- // remove the timeout action first.
- removeTimeout(cujType);
- FrameTracker tracker = getTracker(cujType);
- if (tracker == null) return false;
- // if the cancel call doesn't return true, another thread is handling cancel of the cuj.
- if (tracker.cancel(reason)) {
- removeTracker(cujType, reason);
+ private boolean cancelInternal(RunningTracker tracker, @Reasons int reason) {
+ if (removeTrackerIfCurrent(tracker, reason)) {
+ return false;
}
+ tracker.mTracker.cancel(reason);
return true;
}
@UiThread
- private void putTracker(@CujType int cuj, @NonNull FrameTracker tracker) {
+ private RunningTracker putTrackerIfNoCurrent(
+ @Cuj.CujType int cuj, Supplier<RunningTracker> supplier) {
synchronized (mLock) {
+ if (mRunningTrackers.contains(cuj)) {
+ return null;
+ }
+
+ RunningTracker tracker = supplier.get();
+ if (tracker == null) {
+ return null;
+ }
+
mRunningTrackers.put(cuj, tracker);
if (mDebugOverlay != null) {
mDebugOverlay.onTrackerAdded(cuj, tracker);
}
- if (DEBUG) {
- Log.d(TAG, "Added tracker for " + getNameOfCuj(cuj)
- + ". mRunningTrackers=" + listNamesOfCujs(mRunningTrackers));
- }
+
+ return tracker;
}
}
- private FrameTracker getTracker(@CujType int cuj) {
+ private RunningTracker getTracker(@Cuj.CujType int cuj) {
synchronized (mLock) {
return mRunningTrackers.get(cuj);
}
}
+ /**
+ * @return {@code true} if another tracker is current
+ */
+ @UiThread
+ private boolean removeTrackerIfCurrent(RunningTracker tracker, int reason) {
+ return removeTrackerIfCurrent(tracker.mConfig.mCujType, tracker.mTracker, reason);
+ }
+
+ /**
+ * @return {@code true} if another tracker is current
+ */
@UiThread
- private void removeTracker(@CujType int cuj, int reason) {
+ private boolean removeTrackerIfCurrent(@Cuj.CujType int cuj, FrameTracker tracker, int reason) {
synchronized (mLock) {
+ RunningTracker running = mRunningTrackers.get(cuj);
+ if (running == null || running.mTracker != tracker) {
+ return true;
+ }
+
+ running.mConfig.getHandler().removeCallbacks(running.mTimeoutAction);
mRunningTrackers.remove(cuj);
if (mDebugOverlay != null) {
mDebugOverlay.onTrackerRemoved(cuj, reason, mRunningTrackers);
}
- if (DEBUG) {
- Log.d(TAG, "Removed tracker for " + getNameOfCuj(cuj)
- + ". mRunningTrackers=" + listNamesOfCujs(mRunningTrackers));
- }
+ return false;
}
}
@WorkerThread
- private void updateProperties(DeviceConfig.Properties properties) {
+ @VisibleForTesting
+ public void updateProperties(DeviceConfig.Properties properties) {
for (String property : properties.getKeyset()) {
switch (property) {
- case SETTINGS_SAMPLING_INTERVAL_KEY:
- mSamplingInterval = properties.getInt(property, DEFAULT_SAMPLING_INTERVAL);
- break;
- case SETTINGS_THRESHOLD_MISSED_FRAMES_KEY:
- mTraceThresholdMissedFrames =
- properties.getInt(property, DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
- break;
- case SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY:
- mTraceThresholdFrameTimeMillis =
- properties.getInt(property, DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
- break;
- case SETTINGS_ENABLED_KEY:
- mEnabled = properties.getBoolean(property, DEFAULT_ENABLED);
- break;
- case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY:
+ case SETTINGS_SAMPLING_INTERVAL_KEY ->
+ mSamplingInterval = properties.getInt(property, DEFAULT_SAMPLING_INTERVAL);
+ case SETTINGS_THRESHOLD_MISSED_FRAMES_KEY ->
+ mTraceThresholdMissedFrames =
+ properties.getInt(property, DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
+ case SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY ->
+ mTraceThresholdFrameTimeMillis =
+ properties.getInt(property, DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
+ case SETTINGS_ENABLED_KEY ->
+ mEnabled = properties.getBoolean(property, DEFAULT_ENABLED);
+ case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY -> {
// Never allow the debug overlay to be used on user builds
boolean debugOverlayEnabled = Build.IS_DEBUGGABLE
&& properties.getBoolean(property, DEFAULT_DEBUG_OVERLAY_ENABLED);
@@ -885,49 +546,35 @@ public class InteractionJankMonitor {
mDebugOverlay.dispose();
mDebugOverlay = null;
}
- break;
- default:
- if (DEBUG) {
- Log.d(TAG, "Got a change event for an unknown property: "
- + property + " => " + properties.getString(property, ""));
- }
+ }
+ default -> Log.w(TAG, "Got a change event for an unknown property: "
+ + property + " => " + properties.getString(property, ""));
}
}
}
- @VisibleForTesting
- public DeviceConfig.OnPropertiesChangedListener getPropertiesChangedListener() {
- return mPropertiesChangedListener;
- }
-
- /**
- * Triggers the perfetto daemon to collect and upload data.
- */
- @VisibleForTesting
- public void trigger(Session session) {
- mWorker.getThreadHandler().post(
- () -> PerfettoTrigger.trigger(session.getPerfettoTrigger()));
- }
-
/**
* A helper method to translate interaction type to CUJ name.
*
* @param interactionType the interaction type defined in AtomsProto.java
* @return the name of the interaction type
+ * @deprecated use {@link Cuj#getNameOfInteraction(int)}
*/
+ @Deprecated
public static String getNameOfInteraction(int interactionType) {
- // There is an offset amount of 1 between cujType and interactionType.
- return getNameOfCuj(getCujTypeFromInteraction(interactionType));
+ return Cuj.getNameOfInteraction(interactionType);
}
/**
- * A helper method to translate interaction type to CUJ type.
+ * A helper method to translate CUJ type to CUJ name.
*
- * @param interactionType the interaction type defined in AtomsProto.java
- * @return the integer in {@link CujType}
+ * @param cujType the cuj type defined in this file
+ * @return the name of the cuj type
+ * @deprecated use {@link Cuj#getNameOfCuj(int)}
*/
- private static int getCujTypeFromInteraction(int interactionType) {
- return interactionType - 1;
+ @Deprecated
+ public static String getNameOfCuj(int cujType) {
+ return Cuj.getNameOfCuj(cujType);
}
/**
@@ -943,195 +590,14 @@ public class InteractionJankMonitor {
mDebugYOffset = yOffset;
}
- /**
- * A helper method for getting a string representation of all running CUJs. For example,
- * "(LOCKSCREEN_TRANSITION_FROM_AOD, IME_INSETS_ANIMATION)"
- */
- private static String listNamesOfCujs(SparseArray<FrameTracker> trackers) {
- if (!DEBUG) {
- return null;
- }
- StringBuilder sb = new StringBuilder();
- sb.append('(');
- for (int i = 0; i < trackers.size(); i++) {
- sb.append(getNameOfCuj(trackers.keyAt(i)));
- if (i < trackers.size() - 1) {
- sb.append(", ");
- }
- }
- sb.append(')');
- return sb.toString();
- }
+ private void postEventLogToWorkerThread(TimeFunction logFunction) {
+ final Instant now = Instant.now();
+ final long unixNanos = TimeUnit.NANOSECONDS.convert(now.getEpochSecond(), TimeUnit.SECONDS)
+ + now.getNano();
+ final long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ final long realtimeNanos = SystemClock.uptimeNanos();
- /**
- * A helper method to translate CUJ type to CUJ name.
- *
- * @param cujType the cuj type defined in this file
- * @return the name of the cuj type
- */
- public static String getNameOfCuj(int cujType) {
- // Please note:
- // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
- // 2. The returned string should be the same with the name defined in atoms.proto.
- switch (cujType) {
- case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
- return "NOTIFICATION_SHADE_EXPAND_COLLAPSE";
- case CUJ_NOTIFICATION_SHADE_SCROLL_FLING:
- return "NOTIFICATION_SHADE_SCROLL_FLING";
- case CUJ_NOTIFICATION_SHADE_ROW_EXPAND:
- return "NOTIFICATION_SHADE_ROW_EXPAND";
- case CUJ_NOTIFICATION_SHADE_ROW_SWIPE:
- return "NOTIFICATION_SHADE_ROW_SWIPE";
- case CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE:
- return "NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE";
- case CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE:
- return "NOTIFICATION_SHADE_QS_SCROLL_SWIPE";
- case CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS:
- return "LAUNCHER_APP_LAUNCH_FROM_RECENTS";
- case CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON:
- return "LAUNCHER_APP_LAUNCH_FROM_ICON";
- case CUJ_LAUNCHER_APP_CLOSE_TO_HOME:
- return "LAUNCHER_APP_CLOSE_TO_HOME";
- case CUJ_LAUNCHER_APP_CLOSE_TO_PIP:
- return "LAUNCHER_APP_CLOSE_TO_PIP";
- case CUJ_LAUNCHER_QUICK_SWITCH:
- return "LAUNCHER_QUICK_SWITCH";
- case CUJ_NOTIFICATION_HEADS_UP_APPEAR:
- return "NOTIFICATION_HEADS_UP_APPEAR";
- case CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR:
- return "NOTIFICATION_HEADS_UP_DISAPPEAR";
- case CUJ_NOTIFICATION_ADD:
- return "NOTIFICATION_ADD";
- case CUJ_NOTIFICATION_REMOVE:
- return "NOTIFICATION_REMOVE";
- case CUJ_NOTIFICATION_APP_START:
- return "NOTIFICATION_APP_START";
- case CUJ_LOCKSCREEN_PASSWORD_APPEAR:
- return "LOCKSCREEN_PASSWORD_APPEAR";
- case CUJ_LOCKSCREEN_PATTERN_APPEAR:
- return "LOCKSCREEN_PATTERN_APPEAR";
- case CUJ_LOCKSCREEN_PIN_APPEAR:
- return "LOCKSCREEN_PIN_APPEAR";
- case CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR:
- return "LOCKSCREEN_PASSWORD_DISAPPEAR";
- case CUJ_LOCKSCREEN_PATTERN_DISAPPEAR:
- return "LOCKSCREEN_PATTERN_DISAPPEAR";
- case CUJ_LOCKSCREEN_PIN_DISAPPEAR:
- return "LOCKSCREEN_PIN_DISAPPEAR";
- case CUJ_LOCKSCREEN_TRANSITION_FROM_AOD:
- return "LOCKSCREEN_TRANSITION_FROM_AOD";
- case CUJ_LOCKSCREEN_TRANSITION_TO_AOD:
- return "LOCKSCREEN_TRANSITION_TO_AOD";
- case CUJ_LAUNCHER_OPEN_ALL_APPS :
- return "LAUNCHER_OPEN_ALL_APPS";
- case CUJ_LAUNCHER_ALL_APPS_SCROLL:
- return "LAUNCHER_ALL_APPS_SCROLL";
- case CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET:
- return "LAUNCHER_APP_LAUNCH_FROM_WIDGET";
- case CUJ_SETTINGS_PAGE_SCROLL:
- return "SETTINGS_PAGE_SCROLL";
- case CUJ_LOCKSCREEN_UNLOCK_ANIMATION:
- return "LOCKSCREEN_UNLOCK_ANIMATION";
- case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON:
- return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON";
- case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER:
- return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER";
- case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE:
- return "SHADE_APP_LAUNCH_FROM_QS_TILE";
- case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON:
- return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON";
- case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP:
- return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP";
- case CUJ_PIP_TRANSITION:
- return "PIP_TRANSITION";
- case CUJ_WALLPAPER_TRANSITION:
- return "WALLPAPER_TRANSITION";
- case CUJ_USER_SWITCH:
- return "USER_SWITCH";
- case CUJ_SPLASHSCREEN_AVD:
- return "SPLASHSCREEN_AVD";
- case CUJ_SPLASHSCREEN_EXIT_ANIM:
- return "SPLASHSCREEN_EXIT_ANIM";
- case CUJ_SCREEN_OFF:
- return "SCREEN_OFF";
- case CUJ_SCREEN_OFF_SHOW_AOD:
- return "SCREEN_OFF_SHOW_AOD";
- case CUJ_ONE_HANDED_ENTER_TRANSITION:
- return "ONE_HANDED_ENTER_TRANSITION";
- case CUJ_ONE_HANDED_EXIT_TRANSITION:
- return "ONE_HANDED_EXIT_TRANSITION";
- case CUJ_UNFOLD_ANIM:
- return "UNFOLD_ANIM";
- case CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS:
- return "SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS";
- case CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS:
- return "SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS";
- case CUJ_SUW_LOADING_TO_NEXT_FLOW:
- return "SUW_LOADING_TO_NEXT_FLOW";
- case CUJ_SUW_LOADING_SCREEN_FOR_STATUS:
- return "SUW_LOADING_SCREEN_FOR_STATUS";
- case CUJ_SPLIT_SCREEN_ENTER:
- return "SPLIT_SCREEN_ENTER";
- case CUJ_SPLIT_SCREEN_EXIT:
- return "SPLIT_SCREEN_EXIT";
- case CUJ_LOCKSCREEN_LAUNCH_CAMERA:
- return "LOCKSCREEN_LAUNCH_CAMERA";
- case CUJ_SPLIT_SCREEN_RESIZE:
- return "SPLIT_SCREEN_RESIZE";
- case CUJ_SETTINGS_SLIDER:
- return "SETTINGS_SLIDER";
- case CUJ_TAKE_SCREENSHOT:
- return "TAKE_SCREENSHOT";
- case CUJ_VOLUME_CONTROL:
- return "VOLUME_CONTROL";
- case CUJ_BIOMETRIC_PROMPT_TRANSITION:
- return "BIOMETRIC_PROMPT_TRANSITION";
- case CUJ_SETTINGS_TOGGLE:
- return "SETTINGS_TOGGLE";
- case CUJ_SHADE_DIALOG_OPEN:
- return "SHADE_DIALOG_OPEN";
- case CUJ_USER_DIALOG_OPEN:
- return "USER_DIALOG_OPEN";
- case CUJ_TASKBAR_EXPAND:
- return "TASKBAR_EXPAND";
- case CUJ_TASKBAR_COLLAPSE:
- return "TASKBAR_COLLAPSE";
- case CUJ_SHADE_CLEAR_ALL:
- return "SHADE_CLEAR_ALL";
- case CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION:
- return "LAUNCHER_UNLOCK_ENTRANCE_ANIMATION";
- case CUJ_LOCKSCREEN_OCCLUSION:
- return "LOCKSCREEN_OCCLUSION";
- case CUJ_RECENTS_SCROLLING:
- return "RECENTS_SCROLLING";
- case CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS:
- return "LAUNCHER_APP_SWIPE_TO_RECENTS";
- case CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE:
- return "LAUNCHER_CLOSE_ALL_APPS_SWIPE";
- case CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME:
- return "LAUNCHER_CLOSE_ALL_APPS_TO_HOME";
- case CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION:
- return "LOCKSCREEN_CLOCK_MOVE_ANIMATION";
- case CUJ_LAUNCHER_OPEN_SEARCH_RESULT:
- return "LAUNCHER_OPEN_SEARCH_RESULT";
- case CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK:
- return "LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK";
- case CUJ_IME_INSETS_SHOW_ANIMATION:
- return "IME_INSETS_SHOW_ANIMATION";
- case CUJ_IME_INSETS_HIDE_ANIMATION:
- return "IME_INSETS_HIDE_ANIMATION";
- case CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER:
- return "SPLIT_SCREEN_DOUBLE_TAP_DIVIDER";
- case CUJ_LAUNCHER_UNFOLD_ANIM:
- return "LAUNCHER_UNFOLD_ANIM";
- case CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY:
- return "PREDICTIVE_BACK_CROSS_ACTIVITY";
- case CUJ_PREDICTIVE_BACK_CROSS_TASK:
- return "PREDICTIVE_BACK_CROSS_TASK";
- case CUJ_PREDICTIVE_BACK_HOME:
- return "PREDICTIVE_BACK_HOME";
- }
- return "UNKNOWN";
+ mWorker.post(() -> logFunction.invoke(unixNanos, elapsedNanos, realtimeNanos));
}
private static class TrackerResult {
@@ -1147,9 +613,10 @@ public class InteractionJankMonitor {
private final Context mContext;
private final long mTimeout;
private final String mTag;
+ private final String mSessionName;
private final boolean mSurfaceOnly;
private final SurfaceControl mSurfaceControl;
- private final @CujType int mCujType;
+ private final @Cuj.CujType int mCujType;
private final boolean mDeferMonitor;
private final Handler mHandler;
@@ -1167,17 +634,17 @@ public class InteractionJankMonitor {
private String mAttrTag = "";
private boolean mAttrSurfaceOnly;
private SurfaceControl mAttrSurfaceControl;
- private @CujType int mAttrCujType;
+ private final @Cuj.CujType int mAttrCujType;
private boolean mAttrDeferMonitor = true;
/**
* Creates a builder which instruments only surface.
- * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
+ * @param cuj The enum defined in {@link Cuj.CujType}.
* @param context context
* @param surfaceControl surface control
* @return builder
*/
- public static Builder withSurface(@CujType int cuj, @NonNull Context context,
+ public static Builder withSurface(@Cuj.CujType int cuj, @NonNull Context context,
@NonNull SurfaceControl surfaceControl) {
return new Builder(cuj)
.setContext(context)
@@ -1187,16 +654,17 @@ public class InteractionJankMonitor {
/**
* Creates a builder which instruments both surface and view.
- * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
+ * @param cuj The enum defined in {@link Cuj.CujType}.
* @param view view
* @return builder
*/
- public static Builder withView(@CujType int cuj, @NonNull View view) {
- return new Builder(cuj).setView(view)
+ public static Builder withView(@Cuj.CujType int cuj, @NonNull View view) {
+ return new Builder(cuj)
+ .setView(view)
.setContext(view.getContext());
}
- private Builder(@CujType int cuj) {
+ private Builder(@Cuj.CujType int cuj) {
mAttrCujType = cuj;
}
@@ -1281,11 +749,12 @@ public class InteractionJankMonitor {
}
}
- private Configuration(@CujType int cuj, View view, String tag, long timeout,
+ private Configuration(@Cuj.CujType int cuj, View view, @NonNull String tag, long timeout,
boolean surfaceOnly, Context context, SurfaceControl surfaceControl,
boolean deferMonitor) {
mCujType = cuj;
mTag = tag;
+ mSessionName = generateSessionName(Cuj.getNameOfCuj(cuj), tag);
mTimeout = timeout;
mView = view;
mSurfaceOnly = surfaceOnly;
@@ -1298,6 +767,23 @@ public class InteractionJankMonitor {
mHandler = mSurfaceOnly ? mContext.getMainThreadHandler() : mView.getHandler();
}
+ @VisibleForTesting
+ public static String generateSessionName(
+ @NonNull String cujName, @NonNull String cujPostfix) {
+ final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix);
+ if (hasPostfix) {
+ final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length();
+ if (cujPostfix.length() > remaining) {
+ cujPostfix = cujPostfix.substring(0, remaining - 3).concat("...");
+ }
+ }
+ // The max length of the whole string should be:
+ // 105 with postfix, 83 without postfix
+ return hasPostfix
+ ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix)
+ : TextUtils.formatSimple("J<%s>", cujName);
+ }
+
private void validate() {
boolean shouldThrow = false;
final StringBuilder msg = new StringBuilder();
@@ -1360,10 +846,10 @@ public class InteractionJankMonitor {
return mSurfaceControl;
}
- @VisibleForTesting
/**
* @return a view which is attached to the view tree.
*/
+ @VisibleForTesting
public View getView() {
return mView;
}
@@ -1375,7 +861,7 @@ public class InteractionJankMonitor {
return mDeferMonitor;
}
- @VisibleForTesting
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public Handler getHandler() {
return mHandler;
}
@@ -1387,79 +873,27 @@ public class InteractionJankMonitor {
public int getDisplayId() {
return (mSurfaceOnly ? mContext : mView.getContext()).getDisplayId();
}
- }
-
- /**
- * A class to represent a session.
- */
- public static class Session {
- @CujType
- private final int mCujType;
- private final long mTimeStamp;
- @Reasons
- private int mReason = REASON_END_UNKNOWN;
- private final String mName;
-
- public Session(@CujType int cujType, @NonNull String postfix) {
- mCujType = cujType;
- mTimeStamp = System.nanoTime();
- mName = generateSessionName(getNameOfCuj(cujType), postfix);
- }
-
- private String generateSessionName(@NonNull String cujName, @NonNull String cujPostfix) {
- final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix);
- // We assert that the cujName shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
- if (cujName.length() > MAX_LENGTH_OF_CUJ_NAME) {
- throw new IllegalArgumentException(TextUtils.formatSimple(
- "The length of cuj name <%s> exceeds %d", cujName, MAX_LENGTH_OF_CUJ_NAME));
- }
- if (hasPostfix) {
- final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length();
- if (cujPostfix.length() > remaining) {
- cujPostfix = cujPostfix.substring(0, remaining - 3).concat("...");
- }
- }
- // The max length of the whole string should be:
- // 105 with postfix, 83 without postfix
- return hasPostfix
- ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix)
- : TextUtils.formatSimple("J<%s>", cujName);
- }
- @CujType
- public int getCuj() {
- return mCujType;
+ public String getSessionName() {
+ return mSessionName;
}
public int getStatsdInteractionType() {
- return CUJ_TO_STATSD_INTERACTION_TYPE[mCujType];
+ return Cuj.getStatsdInteractionType(mCujType);
}
/** Describes whether the measurement from this session should be written to statsd. */
public boolean logToStatsd() {
- return getStatsdInteractionType() != NO_STATSD_LOGGING;
+ return Cuj.logToStatsd(mCujType);
}
public String getPerfettoTrigger() {
- return String.format(Locale.US, "com.android.telemetry.interaction-jank-monitor-%d",
- mCujType);
- }
-
- public String getName() {
- return mName;
- }
-
- public long getTimeStamp() {
- return mTimeStamp;
- }
-
- public void setReason(@Reasons int reason) {
- mReason = reason;
+ return TextUtils.formatSimple(
+ "com.android.telemetry.interaction-jank-monitor-%d", mCujType);
}
- @Reasons
- public int getReason() {
- return mReason;
+ public @Cuj.CujType int getCujType() {
+ return mCujType;
}
}
@@ -1468,15 +902,15 @@ public class InteractionJankMonitor {
void invoke(long unixNanos, long elapsedNanos, long realtimeNanos);
}
- private void postEventLogToWorkerThread(TimeFunction logFunction) {
- final Instant now = Instant.now();
- final long unixNanos = TimeUnit.NANOSECONDS.convert(now.getEpochSecond(), TimeUnit.SECONDS)
- + now.getNano();
- final long elapsedNanos = SystemClock.elapsedRealtimeNanos();
- final long realtimeNanos = SystemClock.uptimeNanos();
+ static class RunningTracker {
+ public final Configuration mConfig;
+ public final FrameTracker mTracker;
+ public final Runnable mTimeoutAction;
- mWorker.getThreadHandler().post(() -> {
- logFunction.invoke(unixNanos, elapsedNanos, realtimeNanos);
- });
+ RunningTracker(Configuration config, FrameTracker tracker, Runnable timeoutAction) {
+ this.mConfig = config;
+ this.mTracker = tracker;
+ this.mTimeoutAction = timeoutAction;
+ }
}
}
diff --git a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
index ef7944c21ad2..f3f16a0c662d 100644
--- a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
+++ b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
@@ -34,7 +34,6 @@ import android.view.WindowCallbacks;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.jank.FrameTracker.Reasons;
-import com.android.internal.jank.InteractionJankMonitor.CujType;
/**
* An overlay that uses WindowCallbacks to draw the names of all running CUJs to the window
@@ -94,14 +93,14 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
}
@UiThread
- private boolean attachViewRootIfNeeded(FrameTracker tracker) {
- FrameTracker.ViewRootWrapper viewRoot = tracker.getViewRoot();
+ private boolean attachViewRootIfNeeded(InteractionJankMonitor.RunningTracker tracker) {
+ FrameTracker.ViewRootWrapper viewRoot = tracker.mTracker.getViewRoot();
if (mViewRoot == null && viewRoot != null) {
// Add a trace marker so we can identify traces that were captured while the debug
// overlay was enabled. Traces that use the debug overlay should NOT be used for
// performance analysis.
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, "DEBUG_OVERLAY_DRAW", 0);
- mHandler = tracker.getHandler();
+ mHandler = tracker.mConfig.getHandler();
mViewRoot = viewRoot;
mHandler.runWithScissors(() -> viewRoot.addWindowCallbacks(this),
InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
@@ -111,11 +110,12 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
return false;
}
+ @GuardedBy("mLock")
private float getWidthOfLongestCujName(int cujFontSize) {
mDebugPaint.setTextSize(cujFontSize);
float maxLength = 0;
for (int i = 0; i < mRunningCujs.size(); i++) {
- String cujName = InteractionJankMonitor.getNameOfCuj(mRunningCujs.keyAt(i));
+ String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i));
float textLength = mDebugPaint.measureText(cujName);
if (textLength > maxLength) {
maxLength = textLength;
@@ -149,8 +149,8 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
}
@UiThread
- void onTrackerRemoved(@CujType int removedCuj, @Reasons int reason,
- SparseArray<FrameTracker> runningTrackers) {
+ void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason,
+ SparseArray<InteractionJankMonitor.RunningTracker> runningTrackers) {
synchronized (mLock) {
mRunningCujs.put(removedCuj, reason);
// If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended
@@ -164,7 +164,7 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
// trackers
for (int i = 0; i < runningTrackers.size(); i++) {
if (mViewRoot.equals(
- runningTrackers.valueAt(i).getViewRoot())) {
+ runningTrackers.valueAt(i).mTracker.getViewRoot())) {
needsNewViewRoot = false;
break;
}
@@ -185,7 +185,7 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
}
@UiThread
- void onTrackerAdded(@CujType int addedCuj, FrameTracker tracker) {
+ void onTrackerAdded(@Cuj.CujType int addedCuj, InteractionJankMonitor.RunningTracker tracker) {
synchronized (mLock) {
// Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ
// is still running
@@ -230,41 +230,44 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
int cujFontSize = dipToPx(18);
final float cujNameTextHeight = getTextHeight(cujFontSize);
final float packageNameTextHeight = getTextHeight(packageNameFontSize);
- float maxLength = getWidthOfLongestCujName(cujFontSize);
- final int dx = (int) ((w - maxLength) / 2f);
- canvas.translate(dx, dy);
- // Draw background rectangle for displaying the text showing the CUJ name
- mDebugPaint.setColor(mBgColor);
- canvas.drawRect(
- -padding * 2, // more padding on top so we can draw the package name
- -padding,
- padding * 2 + maxLength,
- padding * 2 + packageNameTextHeight + cujNameTextHeight * mRunningCujs.size(),
- mDebugPaint);
- mDebugPaint.setTextSize(packageNameFontSize);
- mDebugPaint.setColor(Color.BLACK);
- mDebugPaint.setStrikeThruText(false);
- canvas.translate(0, packageNameTextHeight);
- canvas.drawText("package:" + mPackageName, 0, 0, mDebugPaint);
- mDebugPaint.setTextSize(cujFontSize);
- // Draw text for CUJ names
- for (int i = 0; i < mRunningCujs.size(); i++) {
- int status = mRunningCujs.valueAt(i);
- if (status == REASON_STILL_RUNNING) {
- mDebugPaint.setColor(Color.BLACK);
- mDebugPaint.setStrikeThruText(false);
- } else if (status == REASON_END_NORMAL) {
- mDebugPaint.setColor(Color.GRAY);
- mDebugPaint.setStrikeThruText(false);
- } else {
- // Cancelled, or otherwise ended for a bad reason
- mDebugPaint.setColor(Color.RED);
- mDebugPaint.setStrikeThruText(true);
+ synchronized (mLock) {
+ float maxLength = getWidthOfLongestCujName(cujFontSize);
+
+ final int dx = (int) ((w - maxLength) / 2f);
+ canvas.translate(dx, dy);
+ // Draw background rectangle for displaying the text showing the CUJ name
+ mDebugPaint.setColor(mBgColor);
+ canvas.drawRect(
+ -padding * 2, // more padding on top so we can draw the package name
+ -padding,
+ padding * 2 + maxLength,
+ padding * 2 + packageNameTextHeight + cujNameTextHeight * mRunningCujs.size(),
+ mDebugPaint);
+ mDebugPaint.setTextSize(packageNameFontSize);
+ mDebugPaint.setColor(Color.BLACK);
+ mDebugPaint.setStrikeThruText(false);
+ canvas.translate(0, packageNameTextHeight);
+ canvas.drawText("package:" + mPackageName, 0, 0, mDebugPaint);
+ mDebugPaint.setTextSize(cujFontSize);
+ // Draw text for CUJ names
+ for (int i = 0; i < mRunningCujs.size(); i++) {
+ int status = mRunningCujs.valueAt(i);
+ if (status == REASON_STILL_RUNNING) {
+ mDebugPaint.setColor(Color.BLACK);
+ mDebugPaint.setStrikeThruText(false);
+ } else if (status == REASON_END_NORMAL) {
+ mDebugPaint.setColor(Color.GRAY);
+ mDebugPaint.setStrikeThruText(false);
+ } else {
+ // Cancelled, or otherwise ended for a bad reason
+ mDebugPaint.setColor(Color.RED);
+ mDebugPaint.setStrikeThruText(true);
+ }
+ String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i));
+ canvas.translate(0, cujNameTextHeight);
+ canvas.drawText(cujName, 0, 0, mDebugPaint);
}
- String cujName = InteractionJankMonitor.getNameOfCuj(mRunningCujs.keyAt(i));
- canvas.translate(0, cujNameTextHeight);
- canvas.drawText(cujName, 0, 0, mDebugPaint);
}
}
}
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
index 4ed361f24439..6c09b7c04fa7 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -112,7 +112,7 @@ public interface ParsingPackage {
ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
ParsingPackage addUsesSdkLibrary(String libraryName, long versionMajor,
- String[] certSha256Digests);
+ String[] certSha256Digests, boolean usesSdkLibrariesOptional);
ParsingPackage addUsesStaticLibrary(String libraryName, long version,
String[] certSha256Digests);
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 4bb7c33b41e2..8c2a52560050 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -93,6 +93,7 @@ public enum ProtoLogGroup implements IProtoLogGroup {
WM_DEBUG_DREAM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM),
WM_DEBUG_DIMMER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
+ WM_DEBUG_TPL(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
private final boolean mEnabled;
diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 4e4f26cd6ad6..f86595f1ea7b 100644
--- a/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -1340,6 +1340,15 @@ public interface AndroidPackage {
@Nullable
long[] getUsesSdkLibrariesVersionsMajor();
+
+ /**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary_optional
+ * @hide
+ */
+ @Immutable.Ignore
+ @Nullable
+ boolean[] getUsesSdkLibrariesOptional();
+
/**
* TODO(b/135203078): Move static library stuff to an inner data class
*
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index e0bcef642d82..de1ce4e29198 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -1014,6 +1014,10 @@ static jboolean android_os_Debug_isVmapStack(JNIEnv *env, jobject clazz)
return cfg_state == CONFIG_SET;
}
+static jboolean android_os_Debug_logAllocatorStats(JNIEnv*, jobject) {
+ return mallopt(M_LOG_STATS, 0) == 1 ? JNI_TRUE : JNI_FALSE;
+}
+
/*
* JNI registration.
*/
@@ -1056,6 +1060,7 @@ static const JNINativeMethod gMethods[] = {
{"getDmabufHeapPoolsSizeKb", "()J", (void*)android_os_Debug_getDmabufHeapPoolsSizeKb},
{"getGpuTotalUsageKb", "()J", (void*)android_os_Debug_getGpuTotalUsageKb},
{"isVmapStack", "()Z", (void*)android_os_Debug_isVmapStack},
+ {"logAllocatorStats", "()Z", (void*)android_os_Debug_logAllocatorStats},
};
int register_android_os_Debug(JNIEnv *env)
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index f8546b73a37e..596cfe5a695d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2515,6 +2515,10 @@
<attr name="versionMajor" format="integer" />
<!-- The SHA-256 digest of the SDK library signing certificate. -->
<attr name="certDigest" format="string" />
+ <!-- Specify whether the SDK is optional. The default is false, false means app can be
+ installed even if the SDK library doesn't exist, and the SDK library can be uninstalled
+ when the app is still installed. -->
+ <attr name="optional" format="boolean" />
</declare-styleable>
<!-- The <code>static-library</code> tag declares that this apk is providing itself
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index b12f30242e80..f10e7f8c337e 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -115,6 +115,8 @@
<!-- @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime")
@hide @SystemApi -->
<public name="isVirtualDeviceOnly"/>
+ <!-- @FlaggedApi("android.content.pm.sdk_lib_independence") -->
+ <public name="optional"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01bc0000">
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 617262203c6b..b30a0c8fbbd3 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -472,6 +472,7 @@ public class ViewRootImplTest {
* Test the value of the frame rate cateogry based on the visibility of a view
* Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE
* Visible: FRAME_RATE_CATEGORY_NORMAL
+ * Also, mIsFrameRateBoosting should be true when the visibility becomes visible
*/
@Test
@RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
@@ -485,6 +486,7 @@ public class ViewRootImplTest {
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NO_PREFERENCE);
});
+ sInstrumentation.waitForIdleSync();
sInstrumentation.runOnMainSync(() -> {
view.setVisibility(View.VISIBLE);
@@ -492,6 +494,11 @@ public class ViewRootImplTest {
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NORMAL);
});
+ sInstrumentation.waitForIdleSync();
+
+ sInstrumentation.runOnMainSync(() -> {
+ assertEquals(viewRootImpl.getIsFrameRateBoosting(), true);
+ });
}
/**
diff --git a/core/tests/coretests/src/com/android/internal/jank/CujTest.java b/core/tests/coretests/src/com/android/internal/jank/CujTest.java
new file mode 100644
index 000000000000..bf35ed0a1601
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/jank/CujTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.jank;
+
+import static android.text.TextUtils.formatSimple;
+
+import static com.android.internal.jank.Cuj.getNameOfCuj;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@SmallTest
+public class CujTest {
+ private static final String ENUM_NAME_PREFIX =
+ "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__";
+ private static final Set<String> DEPRECATED_VALUES = new HashSet<>() {
+ {
+ add(ENUM_NAME_PREFIX + "IME_INSETS_ANIMATION");
+ }
+ };
+ private static final Map<Integer, String> ENUM_NAME_EXCEPTION_MAP = new HashMap<>() {
+ {
+ put(Cuj.CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD"));
+ put(Cuj.CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR"));
+ put(Cuj.CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH"));
+ put(Cuj.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR"));
+ put(Cuj.CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE"));
+ put(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE"));
+ put(Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE"));
+ put(Cuj.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE"));
+ put(Cuj.CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND"));
+ put(Cuj.CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE"));
+ put(Cuj.CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING"));
+ }
+ };
+
+ @Rule
+ public final Expect mExpect = Expect.create();
+
+ @Test
+ public void testCujNameLimit() {
+ getCujConstants().forEach(f -> {
+ final int cuj = getIntFieldChecked(f);
+ mExpect.withMessage(formatSimple("Too long CUJ(%d) name: %s", cuj, getNameOfCuj(cuj)))
+ .that(getNameOfCuj(cuj).length())
+ .isAtMost(Cuj.MAX_LENGTH_OF_CUJ_NAME);
+ });
+ }
+
+ @Test
+ public void testCujTypeEnumCorrectlyDefined() throws Exception {
+ List<Field> cujEnumFields = getCujConstants().toList();
+
+ HashSet<Integer> allValues = new HashSet<>();
+ for (Field field : cujEnumFields) {
+ int fieldValue = field.getInt(null);
+ assertWithMessage("All CujType values must be unique. Field %s repeats existing value.",
+ field.getName())
+ .that(allValues.add(fieldValue))
+ .isTrue();
+ assertWithMessage("Field %s must have a value <= LAST_CUJ", field.getName())
+ .that(fieldValue)
+ .isAtMost(Cuj.LAST_CUJ);
+ assertWithMessage("Field %s must have a statsd mapping.", field.getName())
+ .that(Cuj.logToStatsd(fieldValue))
+ .isTrue();
+ }
+ }
+
+ @Test
+ public void testCujsMapToEnumsCorrectly() {
+ List<Field> cujs = getCujConstants().toList();
+
+ Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
+ && !DEPRECATED_VALUES.contains(f.getName())
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class)
+ .collect(Collectors.toMap(CujTest::getIntFieldChecked, Field::getName));
+
+ assertThat(enumsMap.size() - 1).isEqualTo(cujs.size());
+
+ cujs.forEach(f -> {
+ final int cuj = getIntFieldChecked(f);
+ final String cujName = f.getName();
+ final String expectedEnumName =
+ ENUM_NAME_EXCEPTION_MAP.getOrDefault(cuj, getEnumName(cujName.substring(4)));
+ final int enumKey = Cuj.getStatsdInteractionType(cuj);
+ final String enumName = enumsMap.get(enumKey);
+ final String expectedNameOfCuj = formatSimple("CUJ_%s", getNameOfCuj(cuj));
+
+ mExpect.withMessage(
+ formatSimple("%s (%d) not matches %s (%d)", cujName, cuj, enumName, enumKey))
+ .that(expectedEnumName.equals(enumName))
+ .isTrue();
+ mExpect.withMessage(
+ formatSimple("getNameOfCuj(%d) not matches: %s, expected=%s",
+ cuj, cujName, expectedNameOfCuj))
+ .that(cujName.equals(expectedNameOfCuj))
+ .isTrue();
+ });
+ }
+
+ private static String getEnumName(String name) {
+ return formatSimple("%s%s", ENUM_NAME_PREFIX, name);
+ }
+
+ private static int getIntFieldChecked(Field field) {
+ try {
+ return field.getInt(null);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private static Stream<Field> getCujConstants() {
+ return Arrays.stream(Cuj.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith("CUJ_")
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 1b9717a6dfc5..1a7117e3b4a1 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -22,9 +22,8 @@ import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_WALLPAPER_TRANSITION;
+import static com.android.internal.jank.Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.Cuj.CUJ_WALLPAPER_TRANSITION;
import static com.android.internal.util.FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED;
import static com.google.common.truth.Truth.assertThat;
@@ -49,15 +48,14 @@ import android.view.SurfaceControl.OnJankDataListener;
import android.view.View;
import android.view.ViewAttachTestActivity;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.StatsLogWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
-import com.android.internal.jank.InteractionJankMonitor.Session;
import org.junit.Before;
import org.junit.Rule;
@@ -69,14 +67,14 @@ import java.util.concurrent.TimeUnit;
@SmallTest
public class FrameTrackerTest {
- private static final String CUJ_POSTFIX = "";
+ private static final String SESSION_NAME = "SessionName";
private static final long FRAME_TIME_60Hz = (long) 1e9 / 60;
private ViewAttachTestActivity mActivity;
@Rule
- public ActivityTestRule<ViewAttachTestActivity> mRule =
- new ActivityTestRule<>(ViewAttachTestActivity.class);
+ public ActivityScenarioRule<ViewAttachTestActivity> mRule =
+ new ActivityScenarioRule<>(ViewAttachTestActivity.class);
private ThreadedRendererWrapper mRenderer;
private FrameMetricsWrapper mWrapper;
@@ -86,12 +84,13 @@ public class FrameTrackerTest {
private StatsLogWrapper mStatsLog;
private ArgumentCaptor<OnJankDataListener> mListenerCapture;
private SurfaceControl mSurfaceControl;
+ private FrameTracker.FrameTrackerListener mTrackerListener;
private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
@Before
public void setup() {
// Prepare an activity for getting ThreadedRenderer later.
- mActivity = mRule.getActivity();
+ mRule.getScenario().onActivity(activity -> mActivity = activity);
View view = mActivity.getWindow().getDecorView();
assertThat(view.isAttachedToWindow()).isTrue();
@@ -116,36 +115,38 @@ public class FrameTrackerTest {
mChoreographer = mock(ChoreographerWrapper.class);
mStatsLog = mock(StatsLogWrapper.class);
mRunnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ mTrackerListener = mock(FrameTracker.FrameTrackerListener.class);
}
- private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) {
- InteractionJankMonitor monitor = mock(InteractionJankMonitor.class);
- Handler handler = mRule.getActivity().getMainThreadHandler();
- Session session = new Session(cuj, postfix);
+ private FrameTracker spyFrameTracker(boolean surfaceOnly) {
+ Handler handler = mActivity.getMainThreadHandler();
Configuration config = mock(Configuration.class);
+ when(config.getSessionName()).thenReturn(SESSION_NAME);
when(config.isSurfaceOnly()).thenReturn(surfaceOnly);
when(config.getSurfaceControl()).thenReturn(mSurfaceControl);
when(config.shouldDeferMonitor()).thenReturn(true);
when(config.getDisplayId()).thenReturn(42);
- View view = mRule.getActivity().getWindow().getDecorView();
+ View view = mActivity.getWindow().getDecorView();
Handler spyHandler = spy(new Handler(handler.getLooper()));
when(config.getView()).thenReturn(surfaceOnly ? null : view);
when(config.getHandler()).thenReturn(spyHandler);
+ when(config.logToStatsd()).thenReturn(true);
+ when(config.getStatsdInteractionType()).thenReturn(surfaceOnly
+ ? Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION)
+ : Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE));
FrameTracker frameTracker = Mockito.spy(
- new FrameTracker(monitor, session, spyHandler, mRenderer, mViewRootWrapper,
+ new FrameTracker(config, mRenderer, mViewRootWrapper,
mSurfaceControlWrapper, mChoreographer, mWrapper, mStatsLog,
/* traceThresholdMissedFrames= */ 1,
/* traceThresholdFrameTimeMillis= */ -1,
- /* FrameTrackerListener= */ null, config));
- doNothing().when(frameTracker).triggerPerfetto();
+ mTrackerListener));
doNothing().when(frameTracker).postTraceStartMarker(mRunnableArgumentCaptor.capture());
return frameTracker;
}
@Test
public void testOnlyFirstWindowFrameOverThreshold() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
// Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP
when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
@@ -169,11 +170,11 @@ public class FrameTrackerTest {
sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L);
verify(tracker).removeObservers();
- verify(tracker, never()).triggerPerfetto();
+ verify(mTrackerListener, never()).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
+ eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
eq(5000000L) /* maxFrameTimeNanos */,
@@ -184,8 +185,7 @@ public class FrameTrackerTest {
@Test
public void testSfJank() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -206,12 +206,12 @@ public class FrameTrackerTest {
verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto
- verify(tracker).triggerPerfetto();
+ verify(mTrackerListener).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
+ eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
eq(40000000L) /* maxFrameTimeNanos */,
@@ -222,8 +222,7 @@ public class FrameTrackerTest {
@Test
public void testFirstFrameJankyNoTrigger() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -243,13 +242,12 @@ public class FrameTrackerTest {
verify(tracker).removeObservers();
- // We detected a janky frame - trigger Perfetto
- verify(tracker, never()).triggerPerfetto();
+ verify(mTrackerListener, never()).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
+ eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
eq(4000000L) /* maxFrameTimeNanos */,
@@ -260,8 +258,7 @@ public class FrameTrackerTest {
@Test
public void testOtherFrameOverThreshold() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -282,12 +279,12 @@ public class FrameTrackerTest {
verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto
- verify(tracker).triggerPerfetto();
+ verify(mTrackerListener).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
+ eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
eq(40000000L) /* maxFrameTimeNanos */,
@@ -298,8 +295,7 @@ public class FrameTrackerTest {
@Test
public void testLastFrameOverThresholdBeforeEnd() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -323,12 +319,12 @@ public class FrameTrackerTest {
verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto
- verify(tracker).triggerPerfetto();
+ verify(mTrackerListener).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
+ eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
eq(50000000L) /* maxFrameTimeNanos */,
@@ -342,8 +338,7 @@ public class FrameTrackerTest {
*/
@Test
public void testNoOvercountingAfterEnd() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -367,11 +362,11 @@ public class FrameTrackerTest {
sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
verify(tracker).removeObservers();
- verify(tracker, never()).triggerPerfetto();
+ verify(mTrackerListener, never()).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
+ eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
eq(4000000L) /* maxFrameTimeNanos */,
@@ -382,8 +377,7 @@ public class FrameTrackerTest {
@Test
public void testBeginCancel() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -402,13 +396,12 @@ public class FrameTrackerTest {
tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
verify(tracker).removeObservers();
// Since the tracker has been cancelled, shouldn't trigger perfetto.
- verify(tracker, never()).triggerPerfetto();
+ verify(mTrackerListener, never()).triggerPerfetto(any());
}
@Test
public void testCancelIfEndVsyncIdEqualsToBeginVsyncId() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -426,13 +419,12 @@ public class FrameTrackerTest {
verify(tracker).removeObservers();
// Should never trigger Perfetto since it is a cancel.
- verify(tracker, never()).triggerPerfetto();
+ verify(mTrackerListener, never()).triggerPerfetto(any());
}
@Test
public void testCancelIfEndVsyncIdLessThanBeginVsyncId() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -450,13 +442,12 @@ public class FrameTrackerTest {
verify(tracker).removeObservers();
// Should never trigger Perfetto since it is a cancel.
- verify(tracker, never()).triggerPerfetto();
+ verify(mTrackerListener, never()).triggerPerfetto(any());
}
@Test
public void testCancelWhenSessionNeverBegun() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
verify(tracker).removeObservers();
@@ -464,8 +455,7 @@ public class FrameTrackerTest {
@Test
public void testEndWhenSessionNeverBegun() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false);
tracker.end(FrameTracker.REASON_END_NORMAL);
verify(tracker).removeObservers();
@@ -473,8 +463,7 @@ public class FrameTrackerTest {
@Test
public void testSurfaceOnlyOtherFrameJanky() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ true);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -495,12 +484,12 @@ public class FrameTrackerTest {
sendFrame(tracker, JANK_NONE, 103L);
verify(mSurfaceControlWrapper).removeJankStatsListener(any());
- verify(tracker).triggerPerfetto();
+ verify(mTrackerListener).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
+ eq(Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION)),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
eq(0L) /* maxFrameTimeNanos */,
@@ -511,8 +500,7 @@ public class FrameTrackerTest {
@Test
public void testSurfaceOnlyFirstFrameJanky() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ true);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -533,12 +521,12 @@ public class FrameTrackerTest {
sendFrame(tracker, JANK_NONE, 103L);
verify(mSurfaceControlWrapper).removeJankStatsListener(any());
- verify(tracker, never()).triggerPerfetto();
+ verify(mTrackerListener, never()).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
+ eq(Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION)),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
eq(0L) /* maxFrameTimeNanos */,
@@ -549,8 +537,7 @@ public class FrameTrackerTest {
@Test
public void testSurfaceOnlyLastFrameJanky() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ true);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
@@ -571,12 +558,12 @@ public class FrameTrackerTest {
sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L);
verify(mSurfaceControlWrapper).removeJankStatsListener(any());
- verify(tracker, never()).triggerPerfetto();
+ verify(mTrackerListener, never()).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
+ eq(Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION)),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
eq(0L) /* maxFrameTimeNanos */,
@@ -587,8 +574,7 @@ public class FrameTrackerTest {
@Test
public void testMaxSuccessiveMissedFramesCount() {
- FrameTracker tracker = spyFrameTracker(
- CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+ FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ true);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
mRunnableArgumentCaptor.getValue().run();
@@ -604,11 +590,11 @@ public class FrameTrackerTest {
sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 106L);
sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 107L);
verify(mSurfaceControlWrapper).removeJankStatsListener(any());
- verify(tracker).triggerPerfetto();
+ verify(mTrackerListener).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
- eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
+ eq(Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION)),
eq(6L) /* totalFrames */,
eq(5L) /* missedFrames */,
eq(0L) /* maxFrameTimeNanos */,
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index b61f995724e5..68095e5eb46c 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -20,23 +20,9 @@ import static android.text.TextUtils.formatSimple;
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ADD;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_APPEAR;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_REMOVE;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
-import static com.android.internal.jank.InteractionJankMonitor.MAX_LENGTH_OF_CUJ_NAME;
-import static com.android.internal.jank.InteractionJankMonitor.getNameOfCuj;
+import static com.android.internal.jank.InteractionJankMonitor.Configuration.generateSessionName;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -52,12 +38,11 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.provider.DeviceConfig;
-import android.util.SparseArray;
import android.view.View;
import android.view.ViewAttachTestActivity;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
@@ -66,101 +51,54 @@ import com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
-import com.android.internal.jank.InteractionJankMonitor.Session;
-import com.android.internal.util.FrameworkStatsLog;
import com.google.common.truth.Expect;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
@SmallTest
public class InteractionJankMonitorTest {
- private static final String CUJ_POSTFIX = "";
- private static final SparseArray<String> ENUM_NAME_EXCEPTION_MAP = new SparseArray<>();
- private static final String ENUM_NAME_PREFIX =
- "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__";
-
- private static final ArrayList<String> DEPRECATED_VALUES = new ArrayList<>();
-
private ViewAttachTestActivity mActivity;
private View mView;
+ private Handler mHandler;
private HandlerThread mWorker;
@Rule
- public ActivityTestRule<ViewAttachTestActivity> mRule =
- new ActivityTestRule<>(ViewAttachTestActivity.class);
+ public ActivityScenarioRule<ViewAttachTestActivity> mRule =
+ new ActivityScenarioRule<>(ViewAttachTestActivity.class);
@Rule
public final Expect mExpect = Expect.create();
- @BeforeClass
- public static void initialize() {
- ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD"));
- ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH"));
- ENUM_NAME_EXCEPTION_MAP.put(
- CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR"));
- ENUM_NAME_EXCEPTION_MAP.put(
- CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR"));
- ENUM_NAME_EXCEPTION_MAP.put(
- CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE"));
- ENUM_NAME_EXCEPTION_MAP.put(
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE"));
- ENUM_NAME_EXCEPTION_MAP.put(
- CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE"));
- ENUM_NAME_EXCEPTION_MAP.put(
- CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE"));
- ENUM_NAME_EXCEPTION_MAP.put(
- CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND"));
- ENUM_NAME_EXCEPTION_MAP.put(
- CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE"));
- ENUM_NAME_EXCEPTION_MAP.put(
- CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING"));
- DEPRECATED_VALUES.add(ENUM_NAME_PREFIX + "IME_INSETS_ANIMATION");
- }
-
- private static String getEnumName(String name) {
- return formatSimple("%s%s", ENUM_NAME_PREFIX, name);
- }
-
@Before
public void setup() {
- // Prepare an activity for getting ThreadedRenderer later.
- mActivity = mRule.getActivity();
+ mRule.getScenario().onActivity(activity -> mActivity = activity);
mView = mActivity.getWindow().getDecorView();
assertThat(mView.isAttachedToWindow()).isTrue();
- Handler handler = spy(new Handler(mActivity.getMainLooper()));
- doReturn(true).when(handler).sendMessageAtTime(any(), anyLong());
+ mHandler = spy(new Handler(mActivity.getMainLooper()));
+ doReturn(true).when(mHandler).sendMessageAtTime(any(), anyLong());
mWorker = mock(HandlerThread.class);
- doReturn(handler).when(mWorker).getThreadHandler();
+ doReturn(mHandler).when(mWorker).getThreadHandler();
}
@Test
public void testBeginEnd() {
InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
- FrameTracker tracker = createMockedFrameTracker(monitor, null);
- doReturn(tracker).when(monitor).createFrameTracker(any(), any());
+ FrameTracker tracker = createMockedFrameTracker();
+ doReturn(tracker).when(monitor).createFrameTracker(any());
doNothing().when(tracker).begin();
doReturn(true).when(tracker).end(anyInt());
// Simulate a trace session and see if begin / end are invoked.
- assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
+ assertThat(monitor.begin(mView, Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
verify(tracker).begin();
- assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
+ assertThat(monitor.end(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
verify(tracker).end(REASON_END_NORMAL);
}
@@ -172,10 +110,10 @@ public class InteractionJankMonitorTest {
propertiesValues.put("enabled", "false");
DeviceConfig.Properties properties = new DeviceConfig.Properties(
DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, propertiesValues);
- monitor.getPropertiesChangedListener().onPropertiesChanged(properties);
+ monitor.updateProperties(properties);
- assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
- assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+ assertThat(monitor.begin(mView, Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+ assertThat(monitor.end(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
}
@Test
@@ -185,145 +123,57 @@ public class InteractionJankMonitorTest {
assertThat(view.isAttachedToWindow()).isFalse();
// Should return false if the view passed in is not attached to window yet.
- assertThat(monitor.begin(view, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
- assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+ assertThat(monitor.begin(view, Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+ assertThat(monitor.end(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
}
@Test
public void testBeginTimeout() {
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
- FrameTracker tracker = createMockedFrameTracker(monitor, null);
- doReturn(tracker).when(monitor).createFrameTracker(any(), any());
+ FrameTracker tracker = createMockedFrameTracker();
+ doReturn(tracker).when(monitor).createFrameTracker(any());
doNothing().when(tracker).begin();
doReturn(true).when(tracker).cancel(anyInt());
- assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
+ assertThat(monitor.begin(mView, Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
verify(tracker).begin();
- verify(monitor).scheduleTimeoutAction(anyInt(), anyLong(), captor.capture());
+ verify(monitor).scheduleTimeoutAction(any(), captor.capture());
Runnable runnable = captor.getValue();
assertThat(runnable).isNotNull();
- mWorker.getThreadHandler().removeCallbacks(runnable);
runnable.run();
- verify(monitor).cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, REASON_CANCEL_TIMEOUT);
+ verify(monitor).cancel(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, REASON_CANCEL_TIMEOUT);
verify(tracker).cancel(REASON_CANCEL_TIMEOUT);
}
@Test
- public void testCujTypeEnumCorrectlyDefined() throws Exception {
- List<Field> cujEnumFields =
- Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
- .filter(field -> field.getName().startsWith("CUJ_")
- && Modifier.isStatic(field.getModifiers())
- && field.getType() == int.class)
- .collect(Collectors.toList());
-
- HashSet<Integer> allValues = new HashSet<>();
- for (Field field : cujEnumFields) {
- int fieldValue = field.getInt(null);
- assertWithMessage(
- "Field %s must have a mapping to a value in CUJ_TO_STATSD_INTERACTION_TYPE",
- field.getName())
- .that(fieldValue < CUJ_TO_STATSD_INTERACTION_TYPE.length)
- .isTrue();
- assertWithMessage("All CujType values must be unique. Field %s repeats existing value.",
- field.getName())
- .that(allValues.add(fieldValue))
- .isTrue();
- }
- }
-
- @Test
- public void testCujsMapToEnumsCorrectly() {
- List<Field> cujs = Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
- .filter(f -> f.getName().startsWith("CUJ_")
- && Modifier.isStatic(f.getModifiers())
- && f.getType() == int.class)
- .collect(Collectors.toList());
-
- Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
- .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
- && !DEPRECATED_VALUES.contains(f.getName())
- && Modifier.isStatic(f.getModifiers())
- && f.getType() == int.class)
- .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
-
- assertThat(enumsMap.size() - 1).isEqualTo(cujs.size());
-
- cujs.forEach(f -> {
- final int cuj = getIntFieldChecked(f);
- final String cujName = f.getName();
- final String expectedEnumName = ENUM_NAME_EXCEPTION_MAP.contains(cuj)
- ? ENUM_NAME_EXCEPTION_MAP.get(cuj)
- : formatSimple("%s%s", ENUM_NAME_PREFIX, cujName.substring(4));
- final int enumKey = CUJ_TO_STATSD_INTERACTION_TYPE[cuj];
- final String enumName = enumsMap.get(enumKey);
- final String expectedNameOfCuj = formatSimple("CUJ_%s", getNameOfCuj(cuj));
-
- mExpect
- .withMessage(formatSimple(
- "%s (%d) not matches %s (%d)", cujName, cuj, enumName, enumKey))
- .that(expectedEnumName.equals(enumName))
- .isTrue();
- mExpect
- .withMessage(
- formatSimple("getNameOfCuj(%d) not matches: %s, expected=%s",
- cuj, cujName, expectedNameOfCuj))
- .that(cujName.equals(expectedNameOfCuj))
- .isTrue();
- });
- }
-
- @Test
- public void testCujNameLimit() {
- Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
- .filter(f -> f.getName().startsWith("CUJ_")
- && Modifier.isStatic(f.getModifiers())
- && f.getType() == int.class)
- .forEach(f -> {
- final int cuj = getIntFieldChecked(f);
- mExpect
- .withMessage(formatSimple(
- "Too long CUJ(%d) name: %s", cuj, getNameOfCuj(cuj)))
- .that(getNameOfCuj(cuj).length())
- .isAtMost(MAX_LENGTH_OF_CUJ_NAME);
- });
- }
-
- @Test
public void testSessionNameLengthLimit() {
- final int cujType = CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
- final String cujName = getNameOfCuj(cujType);
+ final int cujType = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+ final String cujName = Cuj.getNameOfCuj(cujType);
final String cujTag = "ThisIsTheCujTag";
final String tooLongTag = cujTag.repeat(10);
// Normal case, no postfix.
- Session noPostfix = new Session(cujType, "");
- assertThat(noPostfix.getName()).isEqualTo(formatSimple("J<%s>", cujName));
+ assertThat(generateSessionName(cujName, "")).isEqualTo(formatSimple("J<%s>", cujName));
// Normal case, with postfix.
- Session withPostfix = new Session(cujType, cujTag);
- assertThat(withPostfix.getName()).isEqualTo(formatSimple("J<%s::%s>", cujName, cujTag));
+ assertThat(generateSessionName(cujName, cujTag))
+ .isEqualTo(formatSimple("J<%s::%s>", cujName, cujTag));
// Since the length of the cuj name is tested in another test, no need to test it here.
// Too long postfix case, should trim the postfix and keep the cuj name completed.
final String expectedTrimmedName = formatSimple("J<%s::%s>", cujName,
"ThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThi...");
- Session longPostfix = new Session(cujType, tooLongTag);
- assertThat(longPostfix.getName()).isEqualTo(expectedTrimmedName);
+ assertThat(generateSessionName(cujName, tooLongTag)).isEqualTo(expectedTrimmedName);
}
private InteractionJankMonitor createMockedInteractionJankMonitor() {
InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
- doReturn(true).when(monitor).shouldMonitor(anyInt());
+ doReturn(true).when(monitor).shouldMonitor();
return monitor;
}
- private FrameTracker createMockedFrameTracker(InteractionJankMonitor monitor,
- FrameTracker.FrameTrackerListener listener) {
- Session session = spy(new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX));
- doReturn(false).when(session).logToStatsd();
-
+ private FrameTracker createMockedFrameTracker() {
ThreadedRendererWrapper threadedRenderer = mock(ThreadedRendererWrapper.class);
doNothing().when(threadedRenderer).addObserver(any());
doNothing().when(threadedRenderer).removeObserver(any());
@@ -342,27 +192,19 @@ public class InteractionJankMonitorTest {
Configuration configuration = mock(Configuration.class);
when(configuration.isSurfaceOnly()).thenReturn(false);
when(configuration.getView()).thenReturn(mView);
- when(configuration.getHandler()).thenReturn(mView.getHandler());
when(configuration.getDisplayId()).thenReturn(42);
+ when(configuration.logToStatsd()).thenReturn(false);
+ when(configuration.getHandler()).thenReturn(mHandler);
- FrameTracker tracker = spy(new FrameTracker(monitor, session, mWorker.getThreadHandler(),
+ FrameTracker tracker = spy(new FrameTracker(configuration,
threadedRenderer, viewRoot, surfaceControl, choreographer,
new FrameMetricsWrapper(), new StatsLogWrapper(null),
/* traceThresholdMissedFrames= */ 1,
- /* traceThresholdFrameTimeMillis= */ -1, listener, configuration));
+ /* traceThresholdFrameTimeMillis= */ -1,
+ /* listener */ null));
doNothing().when(tracker).postTraceStartMarker(any());
- doNothing().when(tracker).triggerPerfetto();
- doReturn(configuration.getHandler()).when(tracker).getHandler();
return tracker;
}
-
- private int getIntFieldChecked(Field field) {
- try {
- return field.getInt(null);
- } catch (IllegalAccessException ex) {
- throw new RuntimeException(ex);
- }
- }
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 2237ba1924db..aaddf0e50ddd 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -169,6 +169,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
+ "-1961637874": {
+ "message": "DeferredDisplayUpdater: applying DisplayInfo immediately",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java"
+ },
"-1949279037": {
"message": "Attempted to add input method window with bad token %s. Aborting.",
"level": "WARN",
@@ -313,6 +319,12 @@
"group": "WM_DEBUG_RESIZE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-1818910559": {
+ "message": "DeferredDisplayUpdater: applied DisplayInfo after deferring",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java"
+ },
"-1814361639": {
"message": "Set IME snapshot position: (%d, %d)",
"level": "INFO",
@@ -595,6 +607,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1518132958": {
+ "message": "fractionRendered boundsOverSource=%f",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"-1517908912": {
"message": "requestScrollCapture: caught exception dispatching to window.token=%s",
"level": "WARN",
@@ -961,6 +979,12 @@
"group": "WM_DEBUG_CONTENT_RECORDING",
"at": "com\/android\/server\/wm\/ContentRecorder.java"
},
+ "-1209762265": {
+ "message": "Registering listener=%s with id=%d for window=%s with %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"-1209252064": {
"message": "Clear animatingExit: reason=clearAnimatingFlags win=%s",
"level": "DEBUG",
@@ -1333,6 +1357,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "-888703350": {
+ "message": "Skipping %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"-883738232": {
"message": "Adding more than one toast window for UID at a time.",
"level": "WARN",
@@ -1909,6 +1939,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "-415346336": {
+ "message": "DeferredDisplayUpdater: partially applying DisplayInfo immediately",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java"
+ },
"-401282500": {
"message": "destroyIfPossible: r=%s destroy returned removed=%s",
"level": "DEBUG",
@@ -1957,6 +1993,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "-376950429": {
+ "message": "DeferredDisplayUpdater: deferring DisplayInfo update",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java"
+ },
"-374767836": {
"message": "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
"level": "VERBOSE",
@@ -2803,6 +2845,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "360319850": {
+ "message": "fractionRendered scale=%f",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"364992694": {
"message": "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s",
"level": "VERBOSE",
@@ -2983,6 +3031,12 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
+ "532771960": {
+ "message": "Adding untrusted state listener=%s with id=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"535103992": {
"message": "Wallpaper may change! Adjusting",
"level": "VERBOSE",
@@ -3061,6 +3115,12 @@
"group": "WM_DEBUG_DREAM",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
+ "605179032": {
+ "message": "checkIfInThreshold fractionRendered=%f alpha=%f currTimeMs=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"608694300": {
"message": " NEW SURFACE SESSION %s",
"level": "INFO",
@@ -3289,6 +3349,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "824532141": {
+ "message": "lastState=%s newState=%s alpha=%f minAlpha=%f fractionRendered=%f minFractionRendered=%f",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"829434921": {
"message": "Draw state now committed in %s",
"level": "VERBOSE",
@@ -3583,6 +3649,12 @@
"group": "WM_SHOW_SURFACE_ALLOC",
"at": "com\/android\/server\/wm\/ScreenRotationAnimation.java"
},
+ "1090378847": {
+ "message": "Checking %d windows",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"1100065297": {
"message": "Attempted to get IME policy of a display that does not exist: %d",
"level": "WARN",
@@ -3715,6 +3787,12 @@
"group": "WM_DEBUG_FOCUS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1251721200": {
+ "message": "unregister failed, couldn't find deathRecipient for %s with id=%d",
+ "level": "ERROR",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"1252594551": {
"message": "Window types in WindowContext and LayoutParams.type should match! Type from LayoutParams is %d, but type from WindowContext is %d",
"level": "WARN",
@@ -3853,6 +3931,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/TaskDisplayArea.java"
},
+ "1382634842": {
+ "message": "Unregistering listener=%s with id=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"1393721079": {
"message": "Starting remote display change: from [rot = %d], to [%dx%d, rot = %d]",
"level": "VERBOSE",
@@ -3901,6 +3985,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1445704347": {
+ "message": "coveredRegionsAbove updated with %s frame:%s region:%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"1448683958": {
"message": "Override pending remote transitionSet=%b adapter=%s",
"level": "INFO",
@@ -4201,6 +4291,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "1786463281": {
+ "message": "Adding trusted state listener=%s with id=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"1789321832": {
"message": "Then token:%s is invalid. It might be removed",
"level": "WARN",
@@ -4375,6 +4471,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "1955470028": {
+ "message": "computeFractionRendered: visibleRegion=%s screenBounds=%s contentSize=%s scale=%f,%f",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_TPL",
+ "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java"
+ },
"1964565370": {
"message": "Starting remote animation",
"level": "INFO",
@@ -4659,6 +4761,9 @@
"WM_DEBUG_TASKS": {
"tag": "WindowManager"
},
+ "WM_DEBUG_TPL": {
+ "tag": "WindowManager"
+ },
"WM_DEBUG_WALLPAPER": {
"tag": "WindowManager"
},
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 92c4de6490a3..f10cdb82022e 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1739,10 +1739,6 @@ public class Paint {
/**
* Get the elegant metrics flag.
*
- * From API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the default value will be true by
- * default if the app has a target SDK of API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or
- * later.
- *
* @return true if elegant metrics are enabled for text drawing.
*/
public boolean isElegantTextHeight() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
index dc413b059fd7..a32b435ff99e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
@@ -28,7 +28,7 @@ import android.view.RemoteAnimationTarget;
import android.window.IBackAnimationRunner;
import android.window.IOnBackInvokedCallback;
-import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.jank.Cuj.CujType;
import com.android.wm.shell.common.InteractionJankMonitorUtils;
/**
@@ -42,7 +42,7 @@ public class BackAnimationRunner {
private final IOnBackInvokedCallback mCallback;
private final IRemoteAnimationRunner mRunner;
- private final @InteractionJankMonitor.CujType int mCujType;
+ private final @CujType int mCujType;
private final Context mContext;
// Whether we are waiting to receive onAnimationStart
@@ -55,7 +55,7 @@ public class BackAnimationRunner {
@NonNull IOnBackInvokedCallback callback,
@NonNull IRemoteAnimationRunner runner,
@NonNull Context context,
- @InteractionJankMonitor.CujType int cujType) {
+ @CujType int cujType) {
mCallback = callback;
mRunner = runner;
mCujType = cujType;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java
index ec344d345139..86f00b83cadd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java
@@ -23,6 +23,7 @@ import android.text.TextUtils;
import android.view.SurfaceControl;
import android.view.View;
+import com.android.internal.jank.Cuj.CujType;
import com.android.internal.jank.InteractionJankMonitor;
/** Utils class for simplfy InteractionJank trancing call */
@@ -31,11 +32,11 @@ public class InteractionJankMonitorUtils {
/**
* Begin a trace session.
*
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link CujType}.
* @param view the view to trace
* @param tag the tag to distinguish different flow of same type CUJ.
*/
- public static void beginTracing(@InteractionJankMonitor.CujType int cujType,
+ public static void beginTracing(@CujType int cujType,
@NonNull View view, @Nullable String tag) {
final InteractionJankMonitor.Configuration.Builder builder =
InteractionJankMonitor.Configuration.Builder.withView(cujType, view);
@@ -48,12 +49,12 @@ public class InteractionJankMonitorUtils {
/**
* Begin a trace session.
*
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link CujType}.
* @param context the context
* @param surface the surface to trace
* @param tag the tag to distinguish different flow of same type CUJ.
*/
- public static void beginTracing(@InteractionJankMonitor.CujType int cujType,
+ public static void beginTracing(@CujType int cujType,
@NonNull Context context, @NonNull SurfaceControl surface, @Nullable String tag) {
final InteractionJankMonitor.Configuration.Builder builder =
InteractionJankMonitor.Configuration.Builder.withSurface(cujType, context, surface);
@@ -66,18 +67,18 @@ public class InteractionJankMonitorUtils {
/**
* End a trace session.
*
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link CujType}.
*/
- public static void endTracing(@InteractionJankMonitor.CujType int cujType) {
+ public static void endTracing(@CujType int cujType) {
InteractionJankMonitor.getInstance().end(cujType);
}
/**
* Cancel the trace session.
*
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link CujType}.
*/
- public static void cancelTracing(@InteractionJankMonitor.CujType int cujType) {
+ public static void cancelTracing(@CujType int cujType) {
InteractionJankMonitor.getInstance().cancel(cujType);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 38ce16489b06..d157ca837608 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -16,8 +16,8 @@
package com.android.wm.shell.onehanded;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_ENTER_TRANSITION;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_EXIT_TRANSITION;
+import static com.android.internal.jank.Cuj.CUJ_ONE_HANDED_ENTER_TRANSITION;
+import static com.android.internal.jank.Cuj.CUJ_ONE_HANDED_EXIT_TRANSITION;
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
@@ -37,6 +37,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.Cuj.CujType;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
@@ -327,7 +328,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
mTransitionCallbacks.add(callback);
}
- void beginCUJTracing(@InteractionJankMonitor.CujType int cujType, @Nullable String tag) {
+ void beginCUJTracing(@CujType int cujType, @Nullable String tag) {
final Map.Entry<WindowContainerToken, SurfaceControl> firstEntry =
getDisplayAreaTokenMap().entrySet().iterator().next();
final InteractionJankMonitor.Configuration.Builder builder =
@@ -339,11 +340,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
mJankMonitor.begin(builder);
}
- void endCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+ void endCUJTracing(@CujType int cujType) {
mJankMonitor.end(cujType);
}
- void cancelCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+ void cancelCUJTracing(@CujType int cujType) {
mJankMonitor.cancel(cujType);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 0f168c7b4ce6..0367ba160605 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -255,6 +255,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
private ArrayList<TaskState> mOpeningTasks = null;
private WindowContainerToken mPipTask = null;
+ private int mPipTaskId = -1;
private WindowContainerToken mRecentsTask = null;
private int mRecentsTaskId = -1;
private TransitionInfo mInfo = null;
@@ -904,12 +905,14 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
@Override
public void setFinishTaskTransaction(int taskId,
PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "[%d] RecentsController.setFinishTaskTransaction: taskId=%d",
- mInstanceId, taskId);
mExecutor.execute(() -> {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.setFinishTaskTransaction: taskId=%d,"
+ + " [mFinishCB is non-null]=%b",
+ mInstanceId, taskId, mFinishCB != null);
if (mFinishCB == null) return;
mPipTransaction = finishTransaction;
+ mPipTaskId = taskId;
});
}
@@ -1003,10 +1006,35 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
// the leaf-tasks are not "independent" so aren't hidden by normal setup).
t.hide(mPausingTasks.get(i).mTaskSurface);
}
- if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) {
- t.show(mInfo.getChange(mPipTask).getLeash());
- PictureInPictureSurfaceTransaction.apply(mPipTransaction,
- mInfo.getChange(mPipTask).getLeash(), t);
+ if (mPipTransaction != null && sendUserLeaveHint) {
+ SurfaceControl pipLeash = null;
+ if (mPipTask != null) {
+ pipLeash = mInfo.getChange(mPipTask).getLeash();
+ } else if (mPipTaskId != -1) {
+ // find a task with taskId from #setFinishTaskTransaction()
+ for (TransitionInfo.Change change : mInfo.getChanges()) {
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().taskId == mPipTaskId) {
+ pipLeash = change.getLeash();
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.finishInner:"
+ + " found a change with taskId=%d", mPipTaskId);
+ }
+ }
+ }
+ if (pipLeash == null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.finishInner: no valid PiP leash;"
+ + "mPipTransaction=%s, mPipTask=%s, mPipTaskId=%d",
+ mPipTransaction.toString(), mPipTask.toString(), mPipTaskId);
+ } else {
+ t.show(pipLeash);
+ PictureInPictureSurfaceTransaction.apply(mPipTransaction, pipLeash, t);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.finishInner: PiP transaction %s merged",
+ mPipTransaction);
+ }
+ mPipTaskId = -1;
mPipTask = null;
mPipTransaction = null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index ce7fef2d1fdf..eb301192c90b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -50,7 +50,6 @@ import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.common.split.SplitScreenUtils;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
@@ -298,7 +297,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
mixed.mLeftoversHandler = handler.first;
mActiveTransitions.add(mixed);
return handler.second;
- } else if (mUnfoldHandler != null && mUnfoldHandler.hasUnfold(request)) {
+ } else if (mUnfoldHandler != null && mUnfoldHandler.shouldPlayUnfoldAnimation(request)) {
final WindowContainerTransaction wct =
mUnfoldHandler.handleRequest(transition, request);
if (wct != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 07c54293111c..20ff79f7318e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -106,7 +106,7 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull TransitionFinishCallback finishCallback) {
- if (hasUnfold(info) && transition != mTransition) {
+ if (shouldPlayUnfoldAnimation(info) && transition != mTransition) {
// Take over transition that has unfold, we might receive it if no other handler
// accepted request in handleRequest, e.g. for rotation + unfold or
// TRANSIT_NONE + unfold transitions
@@ -213,14 +213,36 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
}
/** Whether `request` contains an unfold action. */
- public boolean hasUnfold(@NonNull TransitionRequestInfo request) {
+ public boolean shouldPlayUnfoldAnimation(@NonNull TransitionRequestInfo request) {
+ // Unfold animation won't play when animations are disabled
+ if (!ValueAnimator.areAnimatorsEnabled()) return false;
+
return (request.getType() == TRANSIT_CHANGE
&& request.getDisplayChange() != null
- && request.getDisplayChange().isPhysicalDisplayChanged());
+ && isUnfoldDisplayChange(request.getDisplayChange()));
+ }
+
+ private boolean isUnfoldDisplayChange(
+ @NonNull TransitionRequestInfo.DisplayChange displayChange) {
+ if (!displayChange.isPhysicalDisplayChanged()) {
+ return false;
+ }
+
+ if (displayChange.getStartAbsBounds() == null || displayChange.getEndAbsBounds() == null) {
+ return false;
+ }
+
+ // Handle only unfolding, currently we don't have an animation when folding
+ final int endArea =
+ displayChange.getEndAbsBounds().width() * displayChange.getEndAbsBounds().height();
+ final int startArea = displayChange.getStartAbsBounds().width()
+ * displayChange.getStartAbsBounds().height();
+
+ return endArea > startArea;
}
/** Whether `transitionInfo` contains an unfold action. */
- public boolean hasUnfold(@NonNull TransitionInfo transitionInfo) {
+ public boolean shouldPlayUnfoldAnimation(@NonNull TransitionInfo transitionInfo) {
// Unfold animation won't play when animations are disabled
if (!ValueAnimator.areAnimatorsEnabled()) return false;
@@ -250,7 +272,7 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
- if (hasUnfold(request)) {
+ if (shouldPlayUnfoldAnimation(request)) {
mTransition = transition;
return new WindowContainerTransaction();
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
index d06cf775ca60..e272958d78f8 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -105,7 +105,8 @@ open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition
)
locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true)
mockLocationEnabled = true
- mainHandler.post(updateLocation)
+ // postpone first location update to make sure GPS is set as test provider
+ mainHandler.postDelayed(updateLocation, 200)
// normal app open through the Launcher All Apps
// var mapsAddressOption = "Golden Gate Bridge"
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index 6d73c12dc304..fc1fe1cd0acc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -98,10 +98,13 @@ public class UnfoldTransitionHandlerTest {
}
@Test
- public void handleRequest_physicalDisplayChange_handlesTransition() {
+ public void handleRequest_physicalDisplayChangeUnfold_handlesTransition() {
ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
- Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true);
+ Display.DEFAULT_DISPLAY)
+ .setPhysicalDisplayChanged(true)
+ .setStartAbsBounds(new Rect(0, 0, 100, 100))
+ .setEndAbsBounds(new Rect(0, 0, 200, 200));
TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE,
triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
@@ -112,6 +115,23 @@ public class UnfoldTransitionHandlerTest {
}
@Test
+ public void handleRequest_physicalDisplayChangeFold_doesNotHandleTransition() {
+ ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
+ TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
+ Display.DEFAULT_DISPLAY)
+ .setPhysicalDisplayChanged(true)
+ .setStartAbsBounds(new Rect(0, 0, 200, 200))
+ .setEndAbsBounds(new Rect(0, 0, 100, 100));
+ TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE,
+ triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
+
+ WindowContainerTransaction result = mUnfoldTransitionHandler.handleRequest(mTransition,
+ requestInfo);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
public void handleRequest_noPhysicalDisplayChange_doesNotHandleTransition() {
ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
@@ -306,7 +326,10 @@ public class UnfoldTransitionHandlerTest {
private TransitionRequestInfo createUnfoldTransitionRequestInfo() {
ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
- Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true);
+ Display.DEFAULT_DISPLAY)
+ .setPhysicalDisplayChanged(true)
+ .setStartAbsBounds(new Rect(0, 0, 100, 100))
+ .setEndAbsBounds(new Rect(0, 0, 200, 200));
return new TransitionRequestInfo(TRANSIT_CHANGE,
triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
}
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index 32e636f8658b..5e3f8033d116 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -78,7 +78,7 @@ public final class GnssNavigationMessage implements Parcelable {
public static final int TYPE_GAL_F = 0x0602;
/**
* NavIC L5 C/A message contained in the structure.
- * @deprecated Use {@link #TYPE_IRN_L5} instead.
+ * @deprecated deprecated.
*/
@Deprecated
public static final int TYPE_IRN_L5CA = 0x0701;
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 3c0b00262c70..07f63e5441af 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -62,3 +62,10 @@ flag {
description: "Allows clients of privileged MediaRouter2 that hold INTERACT_ACROSS_USERS_FULL to control routing across users."
bug: "288580225"
}
+
+flag {
+ name: "enable_use_of_bluetooth_device_get_alias_for_mr2info_get_name"
+ namespace: "media_solutions"
+ description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos."
+ bug: "314324170"
+}
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 60bf48c8b75e..8b136da04405 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.6.0-beta01"
+ extra["jetpackComposeVersion"] = "1.6.0-beta02"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 90c7d46c3004..f4edb36b5e76 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -48,6 +48,7 @@ import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
+import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.CopyablePageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
@@ -100,6 +101,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
SettingsExposedDropdownMenuCheckBoxProvider,
SettingsTextFieldPasswordPageProvider,
SearchScaffoldPageProvider,
+ SuwScaffoldPageProvider,
CardPageProvider,
CopyablePageProvider,
),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 1d897f77011e..6a2e5985a735 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -43,6 +43,7 @@ import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
+import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.CopyablePageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
@@ -59,6 +60,7 @@ object HomePageProvider : SettingsPageProvider {
OperateListPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ArgumentPageProvider.buildInjectEntry("foo")!!.setLink(fromPage = owner).build(),
SearchScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ SuwScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt
new file mode 100644
index 000000000000..6fc8de3ac49c
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.scaffold
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.SignalCellularAlt
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.gallery.R
+import com.android.settingslib.spa.widget.illustration.Illustration
+import com.android.settingslib.spa.widget.illustration.IllustrationModel
+import com.android.settingslib.spa.widget.illustration.ResourceType
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
+import com.android.settingslib.spa.widget.scaffold.SuwScaffold
+import com.android.settingslib.spa.widget.ui.SettingsBody
+
+private const val TITLE = "Sample SuwScaffold"
+
+object SuwScaffoldPageProvider : SettingsPageProvider {
+ override val name = "SuwScaffold"
+
+ private val owner = createSettingsPage()
+
+ fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ Page()
+ }
+}
+
+@Composable
+private fun Page() {
+ SuwScaffold(
+ imageVector = Icons.Outlined.SignalCellularAlt,
+ title = "Connect to mobile network",
+ actionButton = BottomAppBarButton("Next") {},
+ dismissButton = BottomAppBarButton("Cancel") {},
+ ) {
+ Column(Modifier.padding(SettingsDimension.itemPadding)) {
+ SettingsBody("To add another SIM, download a new eSIM.")
+ }
+ Illustration(object : IllustrationModel {
+ override val resId = R.drawable.accessibility_captioning_banner
+ override val resourceType = ResourceType.IMAGE
+ })
+ Column(Modifier.padding(SettingsDimension.itemPadding)) {
+ SettingsBody("To add another SIM, download a new eSIM.")
+ }
+ Illustration(object : IllustrationModel {
+ override val resId = R.drawable.accessibility_captioning_banner
+ override val resourceType = ResourceType.IMAGE
+ })
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index acd90f3c4f4d..7eccfe5ed508 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,7 +57,7 @@ dependencies {
api("androidx.slice:slice-builders:1.1.0-alpha02")
api("androidx.slice:slice-core:1.1.0-alpha02")
api("androidx.slice:slice-view:1.1.0-alpha02")
- api("androidx.compose.material3:material3:1.2.0-alpha11")
+ api("androidx.compose.material3:material3:1.2.0-alpha12")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 5a1120e6c7a1..c143390f269c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -37,11 +37,13 @@ object SettingsDimension {
val itemPaddingAround = 8.dp
val itemDividerHeight = 32.dp
+ val iconLarge = 48.dp
+
/** The size when app icon is displayed in list. */
val appIconItemSize = 32.dp
/** The size when app icon is displayed in App info page. */
- val appIconInfoSize = 48.dp
+ val appIconInfoSize = iconLarge
/** The vertical padding for buttons. */
val buttonPaddingVertical = 12.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
index 3819a1075adb..e0dd4e17ce38 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
@@ -34,6 +34,7 @@ import com.android.settingslib.spa.framework.theme.SettingsTheme
fun SettingsOutlinedTextField(
value: String,
label: String,
+ errorMessage: String? = null,
singleLine: Boolean = true,
enabled: Boolean = true,
onTextChange: (String) -> Unit,
@@ -49,6 +50,12 @@ fun SettingsOutlinedTextField(
},
singleLine = singleLine,
enabled = enabled,
+ isError = errorMessage != null,
+ supportingText = {
+ if (errorMessage != null) {
+ Text(text = errorMessage)
+ }
+ }
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
new file mode 100644
index 000000000000..354b95ddcbfe
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.scaffold
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.toMediumWeight
+
+data class BottomAppBarButton(
+ val text: String,
+ val onClick: () -> Unit,
+)
+
+@Composable
+fun SuwScaffold(
+ imageVector: ImageVector,
+ title: String,
+ actionButton: BottomAppBarButton? = null,
+ dismissButton: BottomAppBarButton? = null,
+ content: @Composable ColumnScope.() -> Unit,
+) {
+ ActivityTitle(title)
+ Scaffold { innerPadding ->
+ BoxWithConstraints(
+ Modifier
+ .padding(innerPadding)
+ .padding(top = SettingsDimension.itemPaddingAround)
+ ) {
+ // Use single column layout in portrait, two columns in landscape.
+ val useSingleColumn = maxWidth < maxHeight
+ if (useSingleColumn) {
+ Column {
+ Column(
+ Modifier
+ .weight(1f)
+ .verticalScroll(rememberScrollState())
+ ) {
+ Header(imageVector, title)
+ content()
+ }
+ BottomBar(actionButton, dismissButton)
+ }
+ } else {
+ Column(Modifier.padding(horizontal = SettingsDimension.itemPaddingAround)) {
+ Row((Modifier.weight(1f))) {
+ Box(Modifier.weight(1f)) {
+ Header(imageVector, title)
+ }
+ Column(
+ Modifier
+ .weight(1f)
+ .verticalScroll(rememberScrollState())) {
+ content()
+ }
+ }
+ BottomBar(actionButton, dismissButton)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun Header(
+ imageVector: ImageVector,
+ title: String
+) {
+ Column(Modifier.padding(SettingsDimension.itemPadding)) {
+ Icon(
+ imageVector = imageVector,
+ contentDescription = null,
+ modifier = Modifier
+ .padding(vertical = SettingsDimension.itemPaddingAround)
+ .size(SettingsDimension.iconLarge),
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ Text(
+ text = title,
+ modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingVertical),
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.displaySmall,
+ )
+ }
+}
+
+@Composable
+private fun BottomBar(
+ actionButton: BottomAppBarButton?,
+ dismissButton: BottomAppBarButton?,
+) {
+ Row(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
+ dismissButton?.apply {
+ TextButton(onClick) {
+ ActionText(text)
+ }
+ }
+ Spacer(modifier = Modifier.weight(1f))
+ actionButton?.apply {
+ Button(onClick) {
+ ActionText(text)
+ }
+ }
+ }
+}
+
+@Composable
+private fun ActionText(text: String) {
+ Text(
+ text = text,
+ style = MaterialTheme.typography.bodyMedium.toMediumWeight(),
+ )
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SuwScaffoldTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SuwScaffoldTest.kt
new file mode 100644
index 000000000000..35c9f78ada68
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SuwScaffoldTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.scaffold
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.SignalCellularAlt
+import androidx.compose.material3.Text
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SuwScaffoldTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun suwScaffold_titleIsDisplayed() {
+ composeTestRule.setContent {
+ SuwScaffold(imageVector = Icons.Outlined.SignalCellularAlt, title = TITLE) {
+ Text(text = "AAA")
+ Text(text = "BBB")
+ }
+ }
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun suwScaffold_itemsAreDisplayed() {
+ composeTestRule.setContent {
+ SuwScaffold(imageVector = Icons.Outlined.SignalCellularAlt, title = TITLE) {
+ Text(text = "AAA")
+ Text(text = "BBB")
+ }
+ }
+
+ composeTestRule.onNodeWithText("AAA").assertIsDisplayed()
+ composeTestRule.onNodeWithText("BBB").assertIsDisplayed()
+ }
+
+ @Test
+ fun suwScaffold_actionButtonDisplayed() {
+ composeTestRule.setContent {
+ SuwScaffold(
+ imageVector = Icons.Outlined.SignalCellularAlt,
+ title = TITLE,
+ actionButton = BottomAppBarButton(TEXT) {},
+ ) {}
+ }
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+ }
+
+ @Test
+ fun suwScaffold_dismissButtonDisplayed() {
+ composeTestRule.setContent {
+ SuwScaffold(
+ imageVector = Icons.Outlined.SignalCellularAlt,
+ title = TITLE,
+ dismissButton = BottomAppBarButton(TEXT) {},
+ ) {}
+ }
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val TEXT = "Text"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
index 3acc9ad83d2c..b6d92422c333 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
@@ -18,6 +18,7 @@ package com.android.settingslib.spaprivileged.model.enterprise
import android.app.admin.DevicePolicyResources.Strings.Settings
import android.content.Context
+import android.content.Intent
import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.widget.restricted.R
@@ -32,6 +33,11 @@ interface BlockedByAdmin : RestrictedMode {
fun sendShowAdminSupportDetailsIntent()
}
+interface BlockedByEcm : RestrictedMode {
+ fun showRestrictedSettingsDetails()
+}
+
+
internal data class BlockedByAdminImpl(
private val context: Context,
private val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin,
@@ -55,3 +61,13 @@ internal data class BlockedByAdminImpl(
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, enforcedAdmin)
}
}
+
+internal data class BlockedByEcmImpl(
+ private val context: Context,
+ private val intent: Intent,
+) : BlockedByEcm {
+
+ override fun showRestrictedSettingsDetails() {
+ context.startActivity(intent)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index 550966beb0b8..9432d5995151 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -29,10 +29,20 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
+data class EnhancedConfirmation(
+ val key: String,
+ val uid: Int,
+ val packageName: String,
+)
data class Restrictions(
val userId: Int = UserHandle.myUserId(),
val keys: List<String>,
-)
+ val enhancedConfirmation: EnhancedConfirmation? = null,
+) {
+ fun isEmpty(): Boolean {
+ return keys.isEmpty() && enhancedConfirmation == null
+ }
+}
interface RestrictionsProvider {
@Composable
@@ -77,6 +87,14 @@ internal class RestrictionsProviderImpl(
.checkIfRestrictionEnforced(context, key, restrictions.userId)
?.let { return BlockedByAdminImpl(context = context, enforcedAdmin = it) }
}
+
+ restrictions.enhancedConfirmation?.let { ec ->
+ RestrictedLockUtilsInternal
+ .checkIfRequiresEnhancedConfirmation(context, ec.key,
+ ec.uid, ec.packageName)
+ ?.let { intent -> return BlockedByEcmImpl(context = context, intent = intent) }
+ }
+
return NoRestricted
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
index 06b3eabfad26..25c3bc541249 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
@@ -47,6 +47,9 @@ abstract class AppOpPermissionListModel(
abstract val appOp: Int
abstract val permission: String
+ override val enhancedConfirmationKey: String?
+ get() = AppOpsManager.opToPublicName(appOp)
+
/**
* When set, specifies the broader permission who trumps the [permission].
*
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 565543614866..1c830c1c5b06 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -41,6 +41,7 @@ import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.app.PackageManagers
import com.android.settingslib.spaprivileged.model.app.toRoute
+import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
@@ -154,7 +155,12 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp
override val changeable = { isChangeable }
override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) }
}
- val restrictions = Restrictions(userId, switchRestrictionKeys)
+ val restrictions = Restrictions(userId = userId,
+ keys = switchRestrictionKeys,
+ enhancedConfirmation = enhancedConfirmationKey?.let { EnhancedConfirmation(
+ key = it,
+ uid = checkNotNull(applicationInfo).uid,
+ packageName = packageName) })
RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index 8704f20f5c4a..916d83af3f8f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -36,6 +36,9 @@ interface TogglePermissionAppListModel<T : AppRecord> {
val switchRestrictionKeys: List<String>
get() = emptyList()
+ val enhancedConfirmationKey: String?
+ get() = null
+
/**
* Loads the extra info for the App List, and generates the [AppRecord] List.
*
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 36c91f463efe..4b474379c54b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -41,6 +41,7 @@ import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.userId
+import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
@@ -149,11 +150,17 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>(
@Composable
fun getSummary(record: T): () -> String {
- val restrictions = remember(record.app.userId) {
+ val restrictions = remember(record.app.userId,
+ record.app.uid, record.app.packageName) {
Restrictions(
userId = record.app.userId,
keys = listModel.switchRestrictionKeys,
- )
+ enhancedConfirmation = listModel.enhancedConfirmationKey?.let {
+ EnhancedConfirmation(
+ key = it,
+ uid = record.app.uid,
+ packageName = record.app.packageName)
+ })
}
val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions)
val allowed = listModel.isAllowed(record)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
index d5c5574a0450..cd720252e485 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
@@ -40,7 +40,7 @@ internal fun RestrictedSwitchPreference(
restrictions: Restrictions,
restrictionsProviderFactory: RestrictionsProviderFactory,
) {
- if (restrictions.keys.isEmpty()) {
+ if (restrictions.isEmpty()) {
SwitchPreference(model)
return
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
index fa44ecb92ed5..aba3460fc1b9 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
@@ -31,6 +31,7 @@ import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
+import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
@@ -56,6 +57,7 @@ internal class RestrictedSwitchPreferenceModel(
is NoRestricted -> model.checked
is BaseUserRestricted -> ({ false })
is BlockedByAdmin -> model.checked
+ is BlockedByEcm -> model.checked
}
override val changeable = if (restrictedMode is NoRestricted) model.changeable else ({ false })
@@ -68,24 +70,42 @@ internal class RestrictedSwitchPreferenceModel(
is BaseUserRestricted -> model.onCheckedChange
// Pass null since semantics ToggleableState is provided in RestrictionWrapper.
is BlockedByAdmin -> null
+ is BlockedByEcm -> null
}
@Composable
fun RestrictionWrapper(content: @Composable () -> Unit) {
- if (restrictedMode !is BlockedByAdmin) {
- content()
- return
+ when (restrictedMode) {
+ is BlockedByAdmin -> {
+ Box(
+ Modifier
+ .clickable(
+ role = Role.Switch,
+ onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() },
+ )
+ .semantics {
+ this.toggleableState = ToggleableState(checked())
+ },
+ ) { content() }
+ }
+
+ is BlockedByEcm -> {
+ Box(
+ Modifier
+ .clickable(
+ role = Role.Switch,
+ onClick = { restrictedMode.showRestrictedSettingsDetails() },
+ )
+ .semantics {
+ this.toggleableState = ToggleableState(checked())
+ },
+ ) { content() }
+ }
+
+ else -> {
+ content()
+ }
}
- Box(
- Modifier
- .clickable(
- role = Role.Switch,
- onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() },
- )
- .semantics {
- this.toggleableState = ToggleableState(checked())
- },
- ) { content() }
}
private fun ToggleableState(value: Boolean?) = when (value) {
@@ -123,6 +143,9 @@ internal class RestrictedSwitchPreferenceModel(
context.getString(com.android.settingslib.R.string.disabled)
is BlockedByAdmin -> restrictedMode.getSummary(checked())
+ is BlockedByEcm ->
+ context.getString(com.android.settingslib.R.string.disabled)
+
null -> context.getPlaceholder()
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
index f9abefc11e24..977615b55a6a 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
@@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope
import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
+import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
@@ -47,6 +48,7 @@ internal fun MoreOptionsScope.RestrictedMenuItemImpl(
MenuItem(text = text, enabled = restrictedMode !== BaseUserRestricted) {
when (restrictedMode) {
is BlockedByAdmin -> restrictedMode.sendShowAdminSupportDetailsIntent()
+ is BlockedByEcm -> restrictedMode.showRestrictedSettingsDetails()
else -> onClick()
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index e77964c6fa0c..4454b710b7e4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -22,13 +22,16 @@ import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AF
import static com.android.settingslib.Utils.getColorAttrDefaultColor;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -39,10 +42,12 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.EnforcingUser;
+import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
+import android.util.ArraySet;
import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
@@ -53,6 +58,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import java.util.List;
+import java.util.Set;
/**
* Utility class to host methods usable in adding a restricted padlock icon and showing admin
@@ -62,6 +68,16 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
private static final String LOG_TAG = "RestrictedLockUtils";
private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
+ private static final Set<String> ECM_KEYS = new ArraySet<>();
+
+ static {
+ if (android.security.Flags.extendEcmToAllSettings()) {
+ ECM_KEYS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW);
+ ECM_KEYS.add(AppOpsManager.OPSTR_GET_USAGE_STATS);
+ ECM_KEYS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS);
+ ECM_KEYS.add(Manifest.permission.BIND_DEVICE_ADMIN);
+ }
+ }
/**
* @return drawables for displaying with settings that are locked by a device admin.
@@ -81,6 +97,38 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
}
/**
+ * Checks if a given permission requires additional confirmation for the given package
+ *
+ * @return An intent to show the user if additional confirmation is required, null otherwise
+ */
+ @Nullable
+ public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context,
+ @NonNull String restriction,
+ int uid,
+ @Nullable String packageName) {
+ // TODO(b/297372999): Replace with call to mainline module once ready
+
+ if (!ECM_KEYS.contains(restriction)) {
+ return null;
+ }
+
+ final AppOpsManager appOps = (AppOpsManager) context
+ .getSystemService(Context.APP_OPS_SERVICE);
+ final int mode = appOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid, packageName, null, null);
+ final boolean ecmEnabled = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
+ if (ecmEnabled && mode != AppOpsManager.MODE_ALLOWED) {
+ final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ return intent;
+ }
+
+ return null;
+ }
+
+ /**
* Checks if a restriction is enforced on a user and returns the enforced admin and
* admin userId.
*
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
index 25833b3ddbba..5aee8cdb8b9e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
@@ -16,7 +16,7 @@
package com.android.settingslib.core.instrumentation;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_SETTINGS_TOGGLE;
+import static com.android.internal.jank.Cuj.CUJ_SETTINGS_TOGGLE;
import static com.android.settingslib.core.instrumentation.SettingsJankMonitor.MONITORED_ANIMATION_DURATION_MS;
import static com.google.common.truth.Truth.assertThat;
@@ -34,8 +34,8 @@ import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.SwitchPreference;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.internal.jank.Cuj.CujType;
import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.jank.InteractionJankMonitor.CujType;
import com.android.settingslib.testutils.OverpoweredReflectionHelper;
import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index e842967e6150..50ed7ab7c85a 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -23,6 +23,7 @@ package {
default_visibility: [
"//visibility:override",
"//frameworks/base/packages/SystemUI:__subpackages__",
+ "//platform_testing:__subpackages__"
],
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 187d0739b734..168039ed5d3d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -33,8 +33,8 @@ import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
import com.android.app.animation.Interpolators
+import com.android.internal.jank.Cuj.CujType
import com.android.internal.jank.InteractionJankMonitor
-import com.android.internal.jank.InteractionJankMonitor.CujType
import com.android.systemui.util.maybeForceFullscreen
import com.android.systemui.util.registerAnimationOnBackInvoked
import kotlin.math.roundToInt
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index af35ea44322f..b738e2bc972b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -33,6 +33,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
import android.widget.FrameLayout
+import com.android.internal.jank.Cuj.CujType
import com.android.internal.jank.InteractionJankMonitor
import java.util.LinkedList
import kotlin.math.min
@@ -58,7 +59,7 @@ constructor(
/** The view that will be ghosted and from which the background will be extracted. */
private val ghostedView: View,
- /** The [InteractionJankMonitor.CujType] associated to this animation. */
+ /** The [CujType] associated to this animation. */
private val cujType: Int? = null,
private var interactionJankMonitor: InteractionJankMonitor =
InteractionJankMonitor.getInstance(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index 1a653c316db1..87c28862f6f6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -203,7 +203,7 @@ private fun SceneScope.FoldSplittable(
modifier: Modifier = Modifier,
) {
val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
- val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
+ val dialogMessage: String? by viewModel.dialogMessage.collectAsState()
var dialog: Dialog? by remember { mutableStateOf(null) }
val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
val splitRatio =
@@ -320,7 +320,7 @@ private fun SceneScope.FoldSplittable(
DialogInterface.BUTTON_NEUTRAL,
context.getString(R.string.ok),
) { _, _ ->
- viewModel.onThrottlingDialogDismissed()
+ viewModel.onDialogDismissed()
}
setCancelable(false)
setCanceledOnTouchOutside(false)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index c49c19785624..12f1b301c836 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -27,11 +27,13 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onPlaced
@@ -89,13 +91,18 @@ fun SceneScope.NotificationStack(
isScrimVisible: Boolean,
modifier: Modifier = Modifier,
) {
+ val cornerRadius by viewModel.cornerRadiusDp.collectAsState()
+
Box(modifier = modifier) {
if (isScrimVisible) {
Box(
modifier =
Modifier.element(Notifications.Elements.NotificationScrim)
.fillMaxSize()
- .clip(RoundedCornerShape(32.dp))
+ .graphicsLayer {
+ shape = RoundedCornerShape(cornerRadius.dp)
+ clip = true
+ }
.background(MaterialTheme.colorScheme.surface)
)
}
@@ -167,7 +174,9 @@ private fun SceneScope.NotificationPlaceholder(
}
val boundsInWindow = coordinates.boundsInWindow()
viewModel.onBoundsChanged(
+ left = boundsInWindow.left,
top = boundsInWindow.top,
+ right = boundsInWindow.right,
bottom = boundsInWindow.bottom,
)
}
diff --git a/packages/SystemUI/docs/executors.md b/packages/SystemUI/docs/executors.md
index 8520ce228c9d..2d9438cdcecf 100644
--- a/packages/SystemUI/docs/executors.md
+++ b/packages/SystemUI/docs/executors.md
@@ -14,10 +14,10 @@ Executor available, as well as our own sub-interface,
[FakeExecutor][FakeExecutor] is available.
[Executor]: https://developer.android.com/reference/java/util/concurrent/Executor.html
-[Handler]: https://developer.android.com/reference/android/os/Handler
+[Handler]: https://developer.android.com/reference/android/os/Handler.html
[Runnable]: https://developer.android.com/reference/java/lang/Runnable.html
[DelayableExecutor]: /packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java
-[FakeExecutor]: /packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java
+[FakeExecutor]: /packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutor.java
## Rationale
@@ -117,7 +117,7 @@ post() | execute() | execute()
postDelayed() | `none` | executeDelayed()
postAtTime() | `none` | executeAtTime()
-There is one notable gap in this implementation: `Handler.postAtFrontOfQueue()`.
+There are some notable gaps in this implementation: `Handler.postAtFrontOfQueue()`.
If you require this method, or similar, please reach out. The idea of a
PriorityQueueExecutor has been floated, but will not be implemented until there
is a clear need.
@@ -173,13 +173,20 @@ fields in your class.
If you feel that you have a use case that this does not cover, please reach out.
-### Handlers Are Still Necessary
+### ContentObserver
+
+One notable place where Handlers have been a requirement in the past is with
+[ContentObserver], which takes a Handler as an argument. However, we have created
+[ExecutorContentObserver], which is a hidden API that accepts an [Executor] in its
+constructor instead of a [Handler], and is otherwise identical.
+
+[ContentObserver]: https://developer.android.com/reference/android/database/ContentObserver.html
+[ExecutorContentObserver]: /core/java/android/database/ExecutorContentObserver.java
-Handlers aren't going away. There are Android APIs that still require them (even
-if future API development discourages them). A simple example is
-[ContentObserver][ContentObserver]. Use them where necessary.
+### Handlers Are Still Necessary
-[ContentObserver]: https://developer.android.com/reference/android/database/ContentObserver
+Handlers aren't going away. There are other Android APIs that still require them.
+Avoid Handlers when possible, but use them where necessary.
## Testing (FakeExecutor)
@@ -314,6 +321,15 @@ clock.setUptimeMillis(500);
The Runnables _will not_ interleave. All of one Executor's callbacks will run,
then all of the other's.
+### Testing Handlers without Loopers
+
+If a [Handler] is required because it is used by Android APIs, but is only
+used in simple ways (i.e. just `Handler.post(Runnable)`), you may still
+want the benefits of [FakeExecutor] when writing your tests, which
+you can get by wrapping the [Executor] in a mock for testing. This can be
+done with `com.android.systemui.util.concurrency.mockExecutorHandler` in
+`MockExecutorHandler.kt`.
+
### TestableLooper.RunWithLooper
As long as you're using FakeExecutors in all the code under test (and no
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 543b2910bbda..695d888d94f5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -465,29 +465,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
}
@Test
- fun showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() {
- // GIVEN the current security method is SimPin
- whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false)
- whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID))
- .thenReturn(false)
- underTest.showSecurityScreen(SecurityMode.SimPin)
-
- // WHEN a request is made from the SimPin screens to show the next security method
- whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.PIN)
- underTest.showNextSecurityScreenOrFinish(
- /* authenticated= */ true,
- TARGET_USER_ID,
- /* bypassSecondaryLockScreen= */ true,
- SecurityMode.SimPin
- )
-
- // THEN the next security method of PIN is set, and the keyguard is not marked as done
- verify(viewMediatorCallback, never()).keyguardDonePending(anyInt())
- verify(viewMediatorCallback, never()).keyguardDone(anyInt())
- Truth.assertThat(underTest.currentSecurityMode).isEqualTo(SecurityMode.PIN)
- }
-
- @Test
fun showNextSecurityScreenOrFinish_DeviceNotSecure() {
// GIVEN the current security method is SimPin
whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false)
@@ -578,6 +555,57 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
}
@Test
+ fun showNextSecurityScreenOrFinish_SimPin_Password() {
+ // GIVEN the current security method is SimPin
+ whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false)
+ whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID))
+ .thenReturn(false)
+ underTest.showSecurityScreen(SecurityMode.SimPin)
+
+ // WHEN a request is made from the SimPin screens to show the next security method
+ whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID))
+ .thenReturn(SecurityMode.Password)
+ // WHEN security method is SWIPE
+ whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false)
+ whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(false)
+ underTest.showNextSecurityScreenOrFinish(
+ /* authenticated= */ true,
+ TARGET_USER_ID,
+ /* bypassSecondaryLockScreen= */ true,
+ SecurityMode.SimPin
+ )
+
+ // THEN we will not show the password screen.
+ verify(viewFlipperController, never())
+ .getSecurityView(eq(SecurityMode.Password), any(), any())
+ }
+
+ @Test
+ fun showNextSecurityScreenOrFinish_SimPin_SimPin() {
+ // GIVEN the current security method is SimPin
+ whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false)
+ whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID))
+ .thenReturn(false)
+ underTest.showSecurityScreen(SecurityMode.SimPin)
+
+ // WHEN a request is made from the SimPin screens to show the next security method
+ whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID))
+ .thenReturn(SecurityMode.SimPin)
+ // WHEN security method is SWIPE
+ whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false)
+ whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(false)
+ underTest.showNextSecurityScreenOrFinish(
+ /* authenticated= */ true,
+ TARGET_USER_ID,
+ /* bypassSecondaryLockScreen= */ true,
+ SecurityMode.SimPin
+ )
+
+ // THEN we will not show the password screen.
+ verify(viewFlipperController).getSecurityView(eq(SecurityMode.SimPin), any(), any())
+ }
+
+ @Test
fun onSwipeUp_forwardsItToFaceAuthInteractor() {
val registeredSwipeListener = registeredSwipeListener
setupGetSecurityView(SecurityMode.Password)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 94c3bde29597..84d735430edd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -34,11 +34,14 @@ import com.android.systemui.util.mockito.any
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -63,6 +66,8 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+ private val updateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
@Before
fun setup() {
@@ -95,6 +100,9 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() {
mSelectedUserInteractor
)
underTest.init()
+ underTest.onResume(0)
+ verify(keyguardUpdateMonitor)
+ .registerCallback(updateMonitorCallbackArgumentCaptor.capture())
}
@Test
@@ -111,6 +119,7 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() {
@Test
fun onResume() {
+ reset(keyguardUpdateMonitor)
underTest.onResume(KeyguardSecurityView.VIEW_REVEALED)
verify(keyguardUpdateMonitor)
.registerCallback(any(KeyguardUpdateMonitorCallback::class.java))
@@ -137,4 +146,22 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() {
underTest.resetState()
verify(keyguardMessageAreaController).setMessage("")
}
+
+ @Test
+ fun onSimStateChangedFromPinToPuk_showsCurrentSecurityScreen() {
+ updateMonitorCallbackArgumentCaptor.value.onSimStateChanged(
+ /* subId= */ 0,
+ /* slotId= */ 0,
+ TelephonyManager.SIM_STATE_PIN_REQUIRED
+ )
+ verify(keyguardSecurityCallback, never()).showCurrentSecurityScreen()
+
+ updateMonitorCallbackArgumentCaptor.value.onSimStateChanged(
+ /* subId= */ 0,
+ /* slotId= */ 0,
+ TelephonyManager.SIM_STATE_PUK_REQUIRED
+ )
+
+ verify(keyguardSecurityCallback).showCurrentSecurityScreen()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 661c3458574e..08cd7edba6af 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -21,9 +21,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
-import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
@@ -76,12 +76,13 @@ class AuthenticationInteractorTest : SysuiTestCase() {
@Test
fun authenticate_withCorrectPin_succeeds() =
testScope.runTest {
- val throttling by collectLastValue(underTest.throttling)
+ val lockout by collectLastValue(underTest.lockout)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(throttling).isNull()
+ assertThat(lockout).isNull()
+ assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0)
}
@Test
@@ -129,14 +130,15 @@ class AuthenticationInteractorTest : SysuiTestCase() {
@Test
fun authenticate_withCorrectPassword_succeeds() =
testScope.runTest {
- val throttling by collectLastValue(underTest.throttling)
+ val lockout by collectLastValue(underTest.lockout)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password
)
assertThat(underTest.authenticate("password".toList()))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(throttling).isNull()
+ assertThat(lockout).isNull()
+ assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0)
}
@Test
@@ -185,7 +187,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNull() =
testScope.runTest {
val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
- val throttling by collectLastValue(underTest.throttling)
+ val lockout by collectLastValue(underTest.lockout)
utils.authenticationRepository.apply {
setAuthenticationMethod(AuthenticationMethodModel.Pin)
setAutoConfirmFeatureEnabled(true)
@@ -201,7 +203,8 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
)
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(throttling).isNull()
+ assertThat(lockout).isNull()
+ assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0)
}
@Test
@@ -262,7 +265,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
}
@Test
- fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringThrottling_returnsNull() =
+ fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringLockout_returnsNull() =
testScope.runTest {
val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
@@ -270,7 +273,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
utils.authenticationRepository.apply {
setAuthenticationMethod(AuthenticationMethodModel.Pin)
setAutoConfirmFeatureEnabled(true)
- setThrottleDuration(42)
+ setLockoutDuration(42)
}
val authResult =
@@ -313,65 +316,121 @@ class AuthenticationInteractorTest : SysuiTestCase() {
}
@Test
- fun throttling() =
+ fun isAutoConfirmEnabled_featureDisabled_returnsFalse() =
testScope.runTest {
- val throttling by collectLastValue(underTest.throttling)
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(false)
+
+ assertThat(isAutoConfirmEnabled).isFalse()
+ }
+
+ @Test
+ fun isAutoConfirmEnabled_featureEnabled_returnsTrue() =
+ testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+
+ assertThat(isAutoConfirmEnabled).isTrue()
+ }
+
+ @Test
+ fun isAutoConfirmEnabled_featureEnabledButDisabledByLockout() =
+ testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
+ val lockout by collectLastValue(underTest.lockout)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+
+ // The feature is enabled.
+ assertThat(isAutoConfirmEnabled).isTrue()
+
+ // Make many wrong attempts to trigger lockout.
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
+ underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
+ }
+ assertThat(lockout).isNotNull()
+ assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1)
+
+ // Lockout disabled auto-confirm.
+ assertThat(isAutoConfirmEnabled).isFalse()
+
+ // Move the clock forward one more second, to completely finish the lockout period:
+ advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_MS + 1000L)
+ assertThat(lockout).isNull()
+
+ // Auto-confirm is still disabled, because lockout occurred at least once in this
+ // session.
+ assertThat(isAutoConfirmEnabled).isFalse()
+
+ // Correct PIN and unlocks successfully, resetting the 'session'.
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
+
+ // Auto-confirm is re-enabled.
+ assertThat(isAutoConfirmEnabled).isTrue()
+
+ assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1)
+ }
+
+ @Test
+ fun lockout() =
+ testScope.runTest {
+ val lockout by collectLastValue(underTest.lockout)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
- assertThat(throttling).isNull()
+ assertThat(lockout).isNull()
- // Make many wrong attempts, but just shy of what's needed to get throttled:
- repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1) {
+ // Make many wrong attempts, but just shy of what's needed to get locked out:
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
- assertThat(throttling).isNull()
+ assertThat(lockout).isNull()
}
- // Make one more wrong attempt, leading to throttling:
+ // Make one more wrong attempt, leading to lockout:
underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
- assertThat(throttling)
+ assertThat(lockout)
.isEqualTo(
- AuthenticationThrottlingModel(
+ AuthenticationLockoutModel(
failedAttemptCount =
- FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
- remainingSeconds = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS,
+ FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT,
+ remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS,
)
)
+ assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1)
- // Correct PIN, but throttled, so doesn't attempt it:
+ // Correct PIN, but locked out, so doesn't attempt it:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(throttling)
+ assertThat(lockout)
.isEqualTo(
- AuthenticationThrottlingModel(
+ AuthenticationLockoutModel(
failedAttemptCount =
- FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
- remainingSeconds = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS,
+ FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT,
+ remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS,
)
)
- // Move the clock forward to ALMOST skip the throttling, leaving one second to go:
- repeat(FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS - 1) { time ->
+ // Move the clock forward to ALMOST skip the lockout, leaving one second to go:
+ val lockoutTimeoutSec = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS
+ repeat(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS - 1) { time ->
advanceTimeBy(1000)
- assertThat(throttling)
+ assertThat(lockout)
.isEqualTo(
- AuthenticationThrottlingModel(
+ AuthenticationLockoutModel(
failedAttemptCount =
- FakeAuthenticationRepository
- .MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
- remainingSeconds =
- FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS - (time + 1),
+ FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT,
+ remainingSeconds = lockoutTimeoutSec - (time + 1),
)
)
}
- // Move the clock forward one more second, to completely finish the throttling period:
+ // Move the clock forward one more second, to completely finish the lockout period:
advanceTimeBy(1000)
- assertThat(throttling).isNull()
+ assertThat(lockout).isNull()
- // Correct PIN and no longer throttled so unlocks successfully:
+ // Correct PIN and no longer locked out so unlocks successfully:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(throttling).isNull()
+ assertThat(lockout).isNull()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 3e3a1a947bd4..9b1df7c0ffc0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -21,9 +21,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
+import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
-import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.res.R
@@ -246,46 +246,42 @@ class BouncerInteractorTest : SysuiTestCase() {
}
@Test
- fun throttling() =
+ fun lockout() =
testScope.runTest {
- val throttling by collectLastValue(underTest.throttling)
+ val lockout by collectLastValue(underTest.lockout)
val message by collectLastValue(underTest.message)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- assertThat(throttling).isNull()
- repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { times ->
+ assertThat(lockout).isNull()
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { times ->
// Wrong PIN.
assertThat(underTest.authenticate(listOf(6, 7, 8, 9)))
.isEqualTo(AuthenticationResult.FAILED)
- if (
- times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1
- ) {
+ if (times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
}
}
- assertThat(throttling)
+ assertThat(lockout)
.isEqualTo(
- AuthenticationThrottlingModel(
+ AuthenticationLockoutModel(
failedAttemptCount =
- FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
- remainingSeconds = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS,
+ FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT,
+ remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS,
)
)
assertTryAgainMessage(
message,
- FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds
- .toInt()
+ FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt()
)
- // Correct PIN, but throttled, so doesn't change away from the bouncer scene:
+ // Correct PIN, but locked out, so doesn't change away from the bouncer scene:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SKIPPED)
assertTryAgainMessage(
message,
- FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds
- .toInt()
+ FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt()
)
- throttling?.remainingSeconds?.let { seconds ->
+ lockout?.remainingSeconds?.let { seconds ->
repeat(seconds) { time ->
advanceTimeBy(1000)
val remainingTimeSec = seconds - time - 1
@@ -295,12 +291,12 @@ class BouncerInteractorTest : SysuiTestCase() {
}
}
assertThat(message).isEqualTo("")
- assertThat(throttling).isNull()
+ assertThat(lockout).isNull()
- // Correct PIN and no longer throttled so changes to the Gone scene:
+ // Correct PIN and no longer locked out so changes to the Gone scene:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(throttling).isNull()
+ assertThat(lockout).isNull()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 45c186dc3a77..2f0843b202a0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -35,7 +35,6 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() {
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val sceneInteractor = utils.sceneInteractor()
private val bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = utils.authenticationInteractor(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 2b64d8eca032..16a935943dbf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -135,17 +135,17 @@ class BouncerViewModelTest : SysuiTestCase() {
fun message() =
testScope.runTest {
val message by collectLastValue(underTest.message)
- val throttling by collectLastValue(bouncerInteractor.throttling)
+ val lockout by collectLastValue(bouncerInteractor.lockout)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(message?.isUpdateAnimated).isTrue()
- repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
// Wrong PIN.
bouncerInteractor.authenticate(listOf(3, 4, 5, 6))
}
assertThat(message?.isUpdateAnimated).isFalse()
- throttling?.remainingSeconds?.let { remainingSeconds ->
+ lockout?.remainingSeconds?.let { remainingSeconds ->
advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds)
}
assertThat(message?.isUpdateAnimated).isTrue()
@@ -160,37 +160,37 @@ class BouncerViewModelTest : SysuiTestCase() {
authViewModel?.isInputEnabled ?: emptyFlow()
}
)
- val throttling by collectLastValue(bouncerInteractor.throttling)
+ val lockout by collectLastValue(bouncerInteractor.lockout)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(isInputEnabled).isTrue()
- repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
// Wrong PIN.
bouncerInteractor.authenticate(listOf(3, 4, 5, 6))
}
assertThat(isInputEnabled).isFalse()
- throttling?.remainingSeconds?.let { remainingSeconds ->
+ lockout?.remainingSeconds?.let { remainingSeconds ->
advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds)
}
assertThat(isInputEnabled).isTrue()
}
@Test
- fun throttlingDialogMessage() =
+ fun dialogMessage() =
testScope.runTest {
- val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage)
+ val dialogMessage by collectLastValue(underTest.dialogMessage)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
// Wrong PIN.
- assertThat(throttlingDialogMessage).isNull()
+ assertThat(dialogMessage).isNull()
bouncerInteractor.authenticate(listOf(3, 4, 5, 6))
}
- assertThat(throttlingDialogMessage).isNotEmpty()
+ assertThat(dialogMessage).isNotEmpty()
- underTest.onThrottlingDialogDismissed()
- assertThat(throttlingDialogMessage).isNull()
+ underTest.onDialogDismissed()
+ assertThat(dialogMessage).isNull()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index a217d93c79ae..6d6baa57bb9d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -19,8 +19,8 @@ package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.res.R
@@ -243,12 +243,12 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
}
@Test
- fun onImeVisibilityChanged_falseAfterTrue_whileThrottling_doesNothing() =
+ fun onImeVisibilityChanged_falseAfterTrue_whileLockedOut_doesNothing() =
testScope.runTest {
val events by collectValues(bouncerInteractor.onImeHiddenByUser)
assertThat(events).isEmpty()
underTest.onImeVisibilityChanged(isVisible = true)
- setThrottling(true)
+ setLockout(true)
underTest.onImeVisibilityChanged(isVisible = false)
@@ -284,11 +284,11 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
}
@Test
- fun isTextFieldFocusRequested_focusLostWhileThrottling_staysFalse() =
+ fun isTextFieldFocusRequested_focusLostWhileLockedOut_staysFalse() =
testScope.runTest {
val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
underTest.onTextFieldFocusChanged(isFocused = true)
- setThrottling(true)
+ setLockout(true)
underTest.onTextFieldFocusChanged(isFocused = false)
@@ -296,14 +296,14 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
}
@Test
- fun isTextFieldFocusRequested_throttlingCountdownEnds_becomesTrue() =
+ fun isTextFieldFocusRequested_lockoutCountdownEnds_becomesTrue() =
testScope.runTest {
val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
underTest.onTextFieldFocusChanged(isFocused = true)
- setThrottling(true)
+ setLockout(true)
underTest.onTextFieldFocusChanged(isFocused = false)
- setThrottling(false)
+ setLockout(false)
assertThat(isTextFieldFocusRequested).isTrue()
}
@@ -327,24 +327,24 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
switchToScene(SceneKey.Bouncer)
}
- private suspend fun TestScope.setThrottling(
- isThrottling: Boolean,
+ private suspend fun TestScope.setLockout(
+ isLockedOut: Boolean,
failedAttemptCount: Int = 5,
) {
- if (isThrottling) {
+ if (isLockedOut) {
repeat(failedAttemptCount) {
authenticationRepository.reportAuthenticationAttempt(false)
}
val remainingTimeSeconds = 30
- authenticationRepository.setThrottleDuration(remainingTimeSeconds * 1000)
- authenticationRepository.throttling.value =
- AuthenticationThrottlingModel(
+ authenticationRepository.setLockoutDuration(remainingTimeSeconds * 1000)
+ authenticationRepository.lockout.value =
+ AuthenticationLockoutModel(
failedAttemptCount = failedAttemptCount,
remainingSeconds = remainingTimeSeconds,
)
} else {
authenticationRepository.reportAuthenticationAttempt(true)
- authenticationRepository.throttling.value = null
+ authenticationRepository.lockout.value = null
}
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 862c39c9d4cc..8971423edd52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -304,13 +304,12 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
fun onDragEnd_whenPatternTooShort() =
testScope.runTest {
val message by collectLastValue(bouncerViewModel.message)
- val throttlingDialogMessage by
- collectLastValue(bouncerViewModel.throttlingDialogMessage)
+ val dialogMessage by collectLastValue(bouncerViewModel.dialogMessage)
lockDeviceAndOpenPatternBouncer()
// Enter a pattern that's too short more than enough times that would normally trigger
- // throttling if the pattern were not too short and wrong:
- val attempts = FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING + 1
+ // lockout if the pattern were not too short and wrong:
+ val attempts = FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT + 1
repeat(attempts) { attempt ->
underTest.onDragStart()
CORRECT_PATTERN.subList(
@@ -328,7 +327,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
underTest.onDragEnd()
assertWithMessage("Attempt #$attempt").that(message?.text).isEqualTo(WRONG_PATTERN)
- assertWithMessage("Attempt #$attempt").that(throttlingDialogMessage).isNull()
+ assertWithMessage("Attempt #$attempt").that(dialogMessage).isNull()
}
}
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 bc4bae0ed959..058b35e71023 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
@@ -49,8 +49,10 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -93,6 +95,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
private lateinit var dockManager: DockManagerFake
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+ private val kosmos = testKosmos()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -179,6 +183,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = withDeps.keyguardInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 9226c0d61a3c..a346e8b45795 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -24,7 +24,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -48,7 +48,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
private val testScope = kosmos.testScope
private val repository = kosmos.fakeKeyguardTransitionRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index bcad72bef1e6..274bde1ccfdf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -25,7 +25,7 @@ import com.android.systemui.common.ui.data.repository.fakeConfigurationRepositor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -49,7 +49,7 @@ import org.junit.runner.RunWith
class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
private val testScope = kosmos.testScope
private val repository = kosmos.fakeKeyguardTransitionRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 78d87a680c5b..f027bc849e51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -22,7 +22,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -43,7 +43,7 @@ import org.junit.runner.RunWith
class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply {
+ fakeFeatureFlagsClassic.apply {
set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
index 00572d3163eb..7f7490d9ecc3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+package com.android.systemui.qs.tiles.impl.flashlight.domain.interactor
+
import android.os.UserHandle
import android.testing.LeakCheck
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -22,7 +24,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileDataInteractor
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.utils.leaks.FakeFlashlightController
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
index f819f53838b8..28d43b369dd4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
@@ -14,12 +14,13 @@
* limitations under the License.
*/
+package com.android.systemui.qs.tiles.impl.flashlight.domain.interactor
+
import android.app.ActivityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
-import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.statusbar.policy.FlashlightController
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index f04dfd1b8fdb..4cdb08afb22e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -24,7 +24,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
@@ -51,7 +51,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
- featureFlagsClassic.apply {
+ fakeFeatureFlagsClassic.apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.NSSL_DEBUG_LINES, false)
}
@@ -67,9 +67,24 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
val bounds by collectLastValue(appearanceViewModel.stackBounds)
val top = 200f
+ val left = 0f
val bottom = 550f
- placeholderViewModel.onBoundsChanged(top, bottom)
- assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom))
+ val right = 100f
+ placeholderViewModel.onBoundsChanged(
+ left = left,
+ top = top,
+ right = right,
+ bottom = bottom
+ )
+ assertThat(bounds)
+ .isEqualTo(
+ NotificationContainerBounds(
+ left = left,
+ top = top,
+ right = right,
+ bottom = bottom
+ )
+ )
}
@Test
diff --git a/packages/SystemUI/res/drawable/qs_record_issue_icon_off.xml b/packages/SystemUI/res/drawable/qs_record_issue_icon_off.xml
new file mode 100644
index 000000000000..bd604317bbb8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_record_issue_icon_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M5.76 12.89c0 3.28 2.54 5.97 5.76 6.22v-8.26h-5.4c-.23.64-.36 1.33-.36 2.04zm9.27-5.45l1.01-1.59c.19-.29.1-.67-.19-.86-.29-.19-.68-.1-.86.19l-1.12 1.76c-.59-.19-1.22-.29-1.87-.29s-1.28.1-1.87.29L9.01 5.18c-.18-.29-.57-.38-.86-.19-.29.18-.38.57-.19.86l1.01 1.59c-1.02.57-1.86 1.43-2.43 2.45h10.92c-.57-1.02-1.41-1.88-2.43-2.45zm2.85 3.41h-5.4v8.26c3.22-.25 5.76-2.93 5.76-6.22 0-.71-.13-1.4-.36-2.04z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_record_issue_icon_on.xml b/packages/SystemUI/res/drawable/qs_record_issue_icon_on.xml
new file mode 100644
index 000000000000..fadaf7896ae4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_record_issue_icon_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M5.76 12.89c0 3.28 2.54 5.97 5.76 6.22v-8.26h-5.4c-.23.64-.36 1.33-.36 2.04zm9.27-5.45l1.01-1.59c.19-.29.1-.67-.19-.86-.29-.19-.68-.1-.86.19l-1.12 1.76c-.59-.19-1.22-.29-1.87-.29s-1.28.1-1.87.29L9.01 5.18c-.18-.29-.57-.38-.86-.19-.29.18-.38.57-.19.86l1.01 1.59c-1.02.57-1.86 1.43-2.43 2.45h10.92c-.57-1.02-1.41-1.88-2.43-2.45zm2.85 3.41h-5.4v8.26c3.22-.25 5.76-2.93 5.76-6.22 0-.71-.13-1.4-.36-2.04z"
+ android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 66c57fc2a9ac..6d7ce0623817 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -106,5 +106,5 @@
</FrameLayout>
<include layout="@layout/ambient_indication"
- android:id="@+id/ambient_indication_container" />
+ android:id="@id/ambient_indication_container" />
</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 1838795a57d6..cf63cc74521d 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -223,6 +223,8 @@
<item type="id" name="lock_icon_bg" />
<item type="id" name="burn_in_layer" />
<item type="id" name="communal_tutorial_indicator" />
+ <item type="id" name="nssl_placeholder_barrier_bottom" />
+ <item type="id" name="ambient_indication_container" />
<!-- Privacy dialog -->
<item type="id" name="privacy_dialog_close_app_button" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7ca0b6ee8d9f..78b701caa3b6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -824,6 +824,13 @@
<!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] -->
<string name="quick_settings_screen_record_stop">Stop</string>
+ <!-- QuickSettings: Record Issue tile [CHAR LIMIT=NONE] -->
+ <string name="qs_record_issue_label">Record Issue</string>
+ <!-- QuickSettings: Text to prompt the user to begin a new recording [CHAR LIMIT=20] -->
+ <string name="qs_record_issue_start">Start</string>
+ <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] -->
+ <string name="qs_record_issue_stop">Stop</string>
+
<!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] -->
<string name="quick_settings_onehanded_label">One-handed mode</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index c505bd502985..df7182b90f12 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -21,6 +21,7 @@ import android.os.Build;
import android.text.TextUtils;
import android.view.View;
+import com.android.internal.jank.Cuj;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
@@ -29,40 +30,25 @@ import java.lang.annotation.RetentionPolicy;
public final class InteractionJankMonitorWrapper {
// Launcher journeys.
- public static final int CUJ_APP_LAUNCH_FROM_RECENTS =
- InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS;
- public static final int CUJ_APP_LAUNCH_FROM_ICON =
- InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON;
- public static final int CUJ_APP_CLOSE_TO_HOME =
- InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_HOME;
+ public static final int CUJ_APP_LAUNCH_FROM_RECENTS = Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+ public static final int CUJ_APP_LAUNCH_FROM_ICON = Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON;
+ public static final int CUJ_APP_CLOSE_TO_HOME = Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME;
public static final int CUJ_APP_CLOSE_TO_HOME_FALLBACK =
- InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK;
- public static final int CUJ_APP_CLOSE_TO_PIP =
- InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP;
- public static final int CUJ_QUICK_SWITCH =
- InteractionJankMonitor.CUJ_LAUNCHER_QUICK_SWITCH;
- public static final int CUJ_OPEN_ALL_APPS =
- InteractionJankMonitor.CUJ_LAUNCHER_OPEN_ALL_APPS;
- public static final int CUJ_CLOSE_ALL_APPS_SWIPE =
- InteractionJankMonitor.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE;
- public static final int CUJ_CLOSE_ALL_APPS_TO_HOME =
- InteractionJankMonitor.CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME;
- public static final int CUJ_ALL_APPS_SCROLL =
- InteractionJankMonitor.CUJ_LAUNCHER_ALL_APPS_SCROLL;
- public static final int CUJ_APP_LAUNCH_FROM_WIDGET =
- InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET;
- public static final int CUJ_SPLIT_SCREEN_ENTER =
- InteractionJankMonitor.CUJ_SPLIT_SCREEN_ENTER;
+ Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK;
+ public static final int CUJ_APP_CLOSE_TO_PIP = Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_PIP;
+ public static final int CUJ_QUICK_SWITCH = Cuj.CUJ_LAUNCHER_QUICK_SWITCH;
+ public static final int CUJ_OPEN_ALL_APPS = Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS;
+ public static final int CUJ_CLOSE_ALL_APPS_SWIPE = Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE;
+ public static final int CUJ_CLOSE_ALL_APPS_TO_HOME = Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME;
+ public static final int CUJ_ALL_APPS_SCROLL = Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL;
+ public static final int CUJ_APP_LAUNCH_FROM_WIDGET = Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET;
+ public static final int CUJ_SPLIT_SCREEN_ENTER = Cuj.CUJ_SPLIT_SCREEN_ENTER;
public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION =
- InteractionJankMonitor.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
- public static final int CUJ_RECENTS_SCROLLING =
- InteractionJankMonitor.CUJ_RECENTS_SCROLLING;
- public static final int CUJ_APP_SWIPE_TO_RECENTS =
- InteractionJankMonitor.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS;
- public static final int CUJ_OPEN_SEARCH_RESULT =
- InteractionJankMonitor.CUJ_LAUNCHER_OPEN_SEARCH_RESULT;
- public static final int CUJ_LAUNCHER_UNFOLD_ANIM =
- InteractionJankMonitor.CUJ_LAUNCHER_UNFOLD_ANIM;
+ Cuj.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
+ public static final int CUJ_RECENTS_SCROLLING = Cuj.CUJ_RECENTS_SCROLLING;
+ public static final int CUJ_APP_SWIPE_TO_RECENTS = Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS;
+ public static final int CUJ_OPEN_SEARCH_RESULT = Cuj.CUJ_LAUNCHER_OPEN_SEARCH_RESULT;
+ public static final int CUJ_LAUNCHER_UNFOLD_ANIM = Cuj.CUJ_LAUNCHER_UNFOLD_ANIM;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
@@ -89,7 +75,7 @@ public final class InteractionJankMonitorWrapper {
* Begin a trace session.
*
* @param v an attached view.
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link Cuj.CujType}.
*/
public static void begin(View v, @CujType int cujType) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
@@ -100,7 +86,7 @@ public final class InteractionJankMonitorWrapper {
* Begin a trace session.
*
* @param v an attached view.
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link Cuj.CujType}.
* @param timeout duration to cancel the instrumentation in ms
*/
public static void begin(View v, @CujType int cujType, long timeout) {
@@ -115,7 +101,7 @@ public final class InteractionJankMonitorWrapper {
* Begin a trace session.
*
* @param v an attached view.
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link Cuj.CujType}.
* @param tag the tag to distinguish different flow of same type CUJ.
*/
public static void begin(View v, @CujType int cujType, String tag) {
@@ -131,7 +117,7 @@ public final class InteractionJankMonitorWrapper {
/**
* End a trace session.
*
- * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param cujType the specific {@link Cuj.CujType}.
*/
public static void end(@CujType int cujType) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index be2c65fa4a45..cdd7b804fdf8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -72,7 +72,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.SecureSettings;
@@ -105,7 +105,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final NotificationIconContainerAlwaysOnDisplayViewModel mAodIconsViewModel;
private final KeyguardRootViewModel mKeyguardRootViewModel;
private final ConfigurationState mConfigurationState;
- private final ConfigurationController mConfigurationController;
+ private final SystemBarUtilsState mSystemBarUtilsState;
private final DozeParameters mDozeParameters;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final AlwaysOnDisplayNotificationIconViewStore mAodIconViewStore;
@@ -183,7 +183,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
LockscreenSmartspaceController smartspaceController,
- ConfigurationController configurationController,
+ SystemBarUtilsState systemBarUtilsState,
ScreenOffAnimationController screenOffAnimationController,
StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -208,7 +208,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
mSmartspaceController = smartspaceController;
- mConfigurationController = configurationController;
+ mSystemBarUtilsState = systemBarUtilsState;
mScreenOffAnimationController = screenOffAnimationController;
mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
mSecureSettings = secureSettings;
@@ -619,13 +619,14 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mAodIconsBindHandle.dispose();
}
if (nic != null) {
- final DisposableHandle viewHandle = NotificationIconContainerViewBinder.bind(
- nic,
- mAodIconsViewModel,
- mConfigurationState,
- mConfigurationController,
- mIconViewBindingFailureTracker,
- mAodIconViewStore);
+ final DisposableHandle viewHandle =
+ NotificationIconContainerViewBinder.bindWhileAttached(
+ nic,
+ mAodIconsViewModel,
+ mConfigurationState,
+ mSystemBarUtilsState,
+ mIconViewBindingFailureTracker,
+ mAodIconViewStore);
final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
nic,
mKeyguardRootViewModel.isNotifIconContainerVisible(),
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
index 38a8cd39a078..c4aa7a26be18 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -104,4 +104,14 @@ public interface KeyguardSecurityCallback {
*/
default void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
}
+
+ /**
+ * Shows the security screen that should be shown.
+ *
+ * This can be considered as a "refresh" of the bouncer view. Based on certain parameters,
+ * we might switch to a different bouncer screen. e.g. SimPin to SimPuk.
+ */
+ default void showCurrentSecurityScreen() {
+
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index f706301df1ca..0a4378e07b45 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -27,6 +27,8 @@ import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM
import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_PRIMARY;
import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER;
import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE;
+import static com.android.keyguard.KeyguardSecurityModel.SecurityMode.SimPin;
+import static com.android.keyguard.KeyguardSecurityModel.SecurityMode.SimPuk;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import static com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES;
@@ -99,6 +101,7 @@ import com.android.systemui.util.settings.GlobalSettings;
import dagger.Lazy;
import java.io.File;
+import java.util.Arrays;
import java.util.Optional;
import javax.inject.Inject;
@@ -164,8 +167,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
mCurrentUser = mSelectedUserInteractor.getSelectedUserId();
showPrimarySecurityScreen(false);
- if (mCurrentSecurityMode != SecurityMode.SimPin
- && mCurrentSecurityMode != SecurityMode.SimPuk) {
+ if (mCurrentSecurityMode != SimPin
+ && mCurrentSecurityMode != SimPuk) {
reinflateViewFlipper((l) -> {
});
}
@@ -334,6 +337,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
mViewMediatorCallback.setNeedsInput(needsInput);
}
+
+ @Override
+ public void showCurrentSecurityScreen() {
+ showPrimarySecurityScreen(false);
+ }
};
private final SwipeListener mSwipeListener = new SwipeListener() {
@@ -888,7 +896,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
finish = true;
eventSubtype = BOUNCER_DISMISS_SIM;
uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
- } else {
+ } else if (Arrays.asList(SimPin, SimPuk).contains(securityMode)) {
+ // There are additional screens to the sim pin/puk flow.
showSecurityScreen(securityMode);
}
break;
@@ -1095,8 +1104,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
private void configureMode() {
- boolean useSimSecurity = mCurrentSecurityMode == SecurityMode.SimPin
- || mCurrentSecurityMode == SecurityMode.SimPuk;
+ boolean useSimSecurity = mCurrentSecurityMode == SimPin
+ || mCurrentSecurityMode == SimPuk;
int mode = KeyguardSecurityContainer.MODE_DEFAULT;
if (canDisplayUserSwitcher() && !useSimSecurity) {
mode = KeyguardSecurityContainer.MODE_USER_SWITCHER;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 6e242084d68c..c5e70703cd2b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.annotation.NonNull;
@@ -60,7 +62,7 @@ public class KeyguardSimPinViewController
// When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
// be displayed to inform user about the number of remaining PIN attempts left.
private boolean mShowDefaultMessage;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private int mSubId = INVALID_SUBSCRIPTION_ID;
private AlertDialog mRemainingAttemptsDialog;
private ImageView mSimImageView;
@@ -68,6 +70,12 @@ public class KeyguardSimPinViewController
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ // If subId has gone to PUK required then we need to go to the PUK screen.
+ if (subId == mSubId && simState == TelephonyManager.SIM_STATE_PUK_REQUIRED) {
+ getKeyguardSecurityCallback().showCurrentSecurityScreen();
+ return;
+ }
+
if (simState == TelephonyManager.SIM_STATE_READY) {
mRemainingAttempts = -1;
resetState();
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index dd4ca92fcc02..fda23b7f2a9c 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -24,9 +24,9 @@ import android.os.UserHandle
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardSecurityModel
+import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
-import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -60,14 +60,6 @@ import kotlinx.coroutines.withContext
/** Defines interface for classes that can access authentication-related application state. */
interface AuthenticationRepository {
/**
- * Whether the auto confirm feature is enabled for the currently-selected user.
- *
- * Note that the length of the PIN is also important to take into consideration, please see
- * [hintedPinLength].
- */
- val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
-
- /**
* Emits the result whenever a PIN/Pattern/Password security challenge is attempted by the user
* in order to unlock the device.
*/
@@ -88,10 +80,22 @@ interface AuthenticationRepository {
val isPatternVisible: StateFlow<Boolean>
/**
- * The current authentication throttling state, set when the user has to wait before being able
- * to try another authentication attempt. `null` indicates throttling isn't active.
+ * The current authentication lockout (aka "throttling") state, set when the user has to wait
+ * before being able to try another authentication attempt. `null` indicates throttling isn't
+ * active.
*/
- val throttling: MutableStateFlow<AuthenticationThrottlingModel?>
+ val lockout: MutableStateFlow<AuthenticationLockoutModel?>
+
+ /** Whether throttling has occurred at least once since the last successful authentication. */
+ val hasLockoutOccurred: MutableStateFlow<Boolean>
+
+ /**
+ * Whether the auto confirm feature is enabled for the currently-selected user.
+ *
+ * Note that the length of the PIN is also important to take into consideration, please see
+ * [hintedPinLength].
+ */
+ val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
/**
* The currently-configured authentication method. This determines how the authentication
@@ -135,22 +139,25 @@ interface AuthenticationRepository {
/** Reports an authentication attempt. */
suspend fun reportAuthenticationAttempt(isSuccessful: Boolean)
+ /** Reports that the user has entered a temporary device lockout (throttling). */
+ suspend fun reportLockoutStarted(durationMs: Int)
+
/** Returns the current number of failed authentication attempts. */
suspend fun getFailedAuthenticationAttemptCount(): Int
/**
- * Returns the timestamp for when the current throttling will end, allowing the user to attempt
+ * Returns the timestamp for when the current lockout will end, allowing the user to attempt
* authentication again.
*
* Note that this is in milliseconds and it matches [SystemClock.elapsedRealtime].
*/
- suspend fun getThrottlingEndTimestamp(): Long
+ suspend fun getLockoutEndTimestamp(): Long
/**
- * Sets the throttling timeout duration (time during which the user should not be allowed to
+ * Sets the lockout timeout duration (time during which the user should not be allowed to
* attempt authentication).
*/
- suspend fun setThrottleDuration(durationMs: Int)
+ suspend fun setLockoutDuration(durationMs: Int)
/**
* Checks the given [LockscreenCredential] to see if it's correct, returning an
@@ -172,11 +179,6 @@ constructor(
mobileConnectionsRepository: MobileConnectionsRepository,
) : AuthenticationRepository {
- override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
- refreshingFlow(
- initialValue = false,
- getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled,
- )
override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
override val hintedPinLength: Int = 6
@@ -187,11 +189,15 @@ constructor(
getFreshValue = lockPatternUtils::isVisiblePatternEnabled,
)
- override val throttling: MutableStateFlow<AuthenticationThrottlingModel?> =
- MutableStateFlow(null)
+ override val lockout: MutableStateFlow<AuthenticationLockoutModel?> = MutableStateFlow(null)
- private val selectedUserId: Int
- get() = userRepository.getSelectedUserInfo().id
+ override val hasLockoutOccurred: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
+ refreshingFlow(
+ initialValue = false,
+ getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled,
+ )
override val authenticationMethod: Flow<AuthenticationMethodModel> =
combine(userRepository.selectedUserInfo, mobileConnectionsRepository.isAnySimSecure) {
@@ -249,19 +255,25 @@ constructor(
}
}
+ override suspend fun reportLockoutStarted(durationMs: Int) {
+ return withContext(backgroundDispatcher) {
+ lockPatternUtils.reportPasswordLockout(durationMs, selectedUserId)
+ }
+ }
+
override suspend fun getFailedAuthenticationAttemptCount(): Int {
return withContext(backgroundDispatcher) {
lockPatternUtils.getCurrentFailedPasswordAttempts(selectedUserId)
}
}
- override suspend fun getThrottlingEndTimestamp(): Long {
+ override suspend fun getLockoutEndTimestamp(): Long {
return withContext(backgroundDispatcher) {
lockPatternUtils.getLockoutAttemptDeadline(selectedUserId)
}
}
- override suspend fun setThrottleDuration(durationMs: Int) {
+ override suspend fun setLockoutDuration(durationMs: Int) {
withContext(backgroundDispatcher) {
lockPatternUtils.setLockoutAttemptDeadline(selectedUserId, durationMs)
}
@@ -273,13 +285,16 @@ constructor(
return withContext(backgroundDispatcher) {
try {
val matched = lockPatternUtils.checkCredential(credential, selectedUserId) {}
- AuthenticationResultModel(isSuccessful = matched, throttleDurationMs = 0)
+ AuthenticationResultModel(isSuccessful = matched, lockoutDurationMs = 0)
} catch (ex: LockPatternUtils.RequestThrottledException) {
- AuthenticationResultModel(isSuccessful = false, throttleDurationMs = ex.timeoutMs)
+ AuthenticationResultModel(isSuccessful = false, lockoutDurationMs = ex.timeoutMs)
}
}
}
+ private val selectedUserId: Int
+ get() = userRepository.getSelectedUserInfo().id
+
/**
* Returns a [StateFlow] that's automatically kept fresh. The passed-in [getFreshValue] is
* invoked on a background thread every time the selected user is changed and every time a new
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 4e67771cba82..797154e85082 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -20,9 +20,9 @@ import com.android.app.tracing.TraceUtils.Companion.withContext
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
import com.android.systemui.authentication.data.repository.AuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
-import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -85,25 +85,25 @@ constructor(
val authenticationMethod: Flow<AuthenticationMethodModel> = repository.authenticationMethod
/**
- * The current authentication throttling state, set when the user has to wait before being able
- * to try another authentication attempt. `null` indicates throttling isn't active.
+ * The current authentication lockout (aka "throttling") state, set when the user has to wait
+ * before being able to try another authentication attempt. `null` indicates lockout isn't
+ * active.
*/
- val throttling: StateFlow<AuthenticationThrottlingModel?> = repository.throttling
+ val lockout: StateFlow<AuthenticationLockoutModel?> = repository.lockout
/**
* Whether the auto confirm feature is enabled for the currently-selected user.
*
* Note that the length of the PIN is also important to take into consideration, please see
* [hintedPinLength].
- *
- * During throttling, this is always disabled (`false`).
*/
val isAutoConfirmEnabled: StateFlow<Boolean> =
- combine(repository.isAutoConfirmFeatureEnabled, repository.throttling) {
+ combine(repository.isAutoConfirmFeatureEnabled, repository.hasLockoutOccurred) {
featureEnabled,
- throttling ->
- // Disable auto-confirm during throttling.
- featureEnabled && throttling == null
+ hasLockoutOccurred ->
+ // Disable auto-confirm if lockout occurred since the last successful
+ // authentication attempt.
+ featureEnabled && !hasLockoutOccurred
}
.stateIn(
scope = applicationScope,
@@ -140,7 +140,7 @@ constructor(
/** Whether the "enhanced PIN privacy" setting is enabled for the current user. */
val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> = repository.isPinEnhancedPrivacyEnabled
- private var throttlingCountdownJob: Job? = null
+ private var lockoutCountdownJob: Job? = null
init {
applicationScope.launch {
@@ -189,8 +189,8 @@ constructor(
val authMethod = getAuthenticationMethod()
val skipCheck =
when {
- // Throttling is active, the UI layer should not have called this; skip the attempt.
- throttling.value != null -> true
+ // Lockout is active, the UI layer should not have called this; skip the attempt.
+ lockout.value != null -> true
// The input is too short; skip the attempt.
input.isTooShort(authMethod) -> true
// Auto-confirm attempt when the feature is not enabled; skip the attempt.
@@ -216,18 +216,22 @@ constructor(
)
}
- // Check if we need to throttle and, if so, kick off the throttle countdown:
- if (!authenticationResult.isSuccessful && authenticationResult.throttleDurationMs > 0) {
- repository.setThrottleDuration(
- durationMs = authenticationResult.throttleDurationMs,
- )
- startThrottlingCountdown()
+ // Check if lockout should start and, if so, kick off the countdown:
+ if (!authenticationResult.isSuccessful && authenticationResult.lockoutDurationMs > 0) {
+ repository.apply {
+ setLockoutDuration(durationMs = authenticationResult.lockoutDurationMs)
+ reportLockoutStarted(durationMs = authenticationResult.lockoutDurationMs)
+ hasLockoutOccurred.value = true
+ }
+ startLockoutCountdown()
}
if (authenticationResult.isSuccessful) {
- // Since authentication succeeded, we should refresh throttling to make sure that our
- // state is completely reflecting the upstream source of truth.
- refreshThrottling()
+ // Since authentication succeeded, refresh lockout to make sure the state is completely
+ // reflecting the upstream source of truth.
+ refreshLockout()
+
+ repository.hasLockoutOccurred.value = false
}
return if (authenticationResult.isSuccessful) {
@@ -245,52 +249,52 @@ constructor(
}
}
- /** Starts refreshing the throttling state every second. */
- private suspend fun startThrottlingCountdown() {
- cancelThrottlingCountdown()
- throttlingCountdownJob =
+ /** Starts refreshing the lockout state every second. */
+ private suspend fun startLockoutCountdown() {
+ cancelLockoutCountdown()
+ lockoutCountdownJob =
applicationScope.launch {
- while (refreshThrottling()) {
+ while (refreshLockout()) {
delay(1.seconds.inWholeMilliseconds)
}
}
}
- /** Cancels any throttling state countdown started in [startThrottlingCountdown]. */
- private fun cancelThrottlingCountdown() {
- throttlingCountdownJob?.cancel()
- throttlingCountdownJob = null
+ /** Cancels any lockout state countdown started in [startLockoutCountdown]. */
+ private fun cancelLockoutCountdown() {
+ lockoutCountdownJob?.cancel()
+ lockoutCountdownJob = null
}
/** Notifies that the currently-selected user has changed. */
private suspend fun onSelectedUserChanged() {
- cancelThrottlingCountdown()
- if (refreshThrottling()) {
- startThrottlingCountdown()
+ cancelLockoutCountdown()
+ if (refreshLockout()) {
+ startLockoutCountdown()
}
}
/**
- * Refreshes the throttling state, hydrating the repository with the latest state.
+ * Refreshes the lockout state, hydrating the repository with the latest state.
*
- * @return Whether throttling is active or not.
+ * @return Whether lockout is active or not.
*/
- private suspend fun refreshThrottling(): Boolean {
- withContext("$TAG#refreshThrottling", backgroundDispatcher) {
+ private suspend fun refreshLockout(): Boolean {
+ withContext("$TAG#refreshLockout", backgroundDispatcher) {
val failedAttemptCount = async { repository.getFailedAuthenticationAttemptCount() }
- val deadline = async { repository.getThrottlingEndTimestamp() }
+ val deadline = async { repository.getLockoutEndTimestamp() }
val remainingMs = max(0, deadline.await() - clock.elapsedRealtime())
- repository.throttling.value =
+ repository.lockout.value =
if (remainingMs > 0) {
- AuthenticationThrottlingModel(
+ AuthenticationLockoutModel(
failedAttemptCount = failedAttemptCount.await(),
remainingSeconds = ceil(remainingMs / 1000f).toInt(),
)
} else {
- null // Throttling ended.
+ null // Lockout ended.
}
}
- return repository.throttling.value != null
+ return repository.lockout.value != null
}
private fun AuthenticationMethodModel.createCredential(
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationLockoutModel.kt
index 8392528b86aa..8ee2d5e02bad 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationLockoutModel.kt
@@ -16,17 +16,17 @@
package com.android.systemui.authentication.shared.model
-/** Models a state for throttling the next authentication attempt. */
-data class AuthenticationThrottlingModel(
+/** Models a state for temporarily locking out the next authentication attempt. */
+data class AuthenticationLockoutModel(
- /** Number of failed authentication attempts so far. If not throttling this will be `0`. */
+ /** Number of failed authentication attempts so far. If not locked out this will be `0`. */
val failedAttemptCount: Int = 0,
/**
* Remaining amount of time, in seconds, before another authentication attempt can be done. If
- * not throttling this will be `0`.
+ * not locked out this will be `0`.
*
- * This number is changed throughout the timeout.
+ * This number is changed throughout the lockout.
*
* Note: this isn't precise (in milliseconds), but rounded up to ensure "at most" this amount of
* seconds remains.
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt
index f2a3e74700db..addc75e52fad 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt
@@ -21,5 +21,5 @@ data class AuthenticationResultModel(
/** Whether authentication was successful. */
val isSuccessful: Boolean = false,
/** If [isSuccessful] is `false`, how long the user must wait before trying again. */
- val throttleDurationMs: Int = 0,
+ val lockoutDurationMs: Int = 0,
)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 677f60d405b8..724c0fe1e4e4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -19,8 +19,8 @@ package com.android.systemui.bouncer.domain.interactor
import android.content.Context
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
+import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.classifier.FalsingClassifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
@@ -60,24 +60,25 @@ constructor(
/** The user-facing message to show in the bouncer. */
val message: StateFlow<String?> =
- combine(repository.message, authenticationInteractor.throttling) { message, throttling ->
- messageOrThrottlingMessage(message, throttling)
+ combine(repository.message, authenticationInteractor.lockout) { message, lockout ->
+ messageOrLockoutMessage(message, lockout)
}
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue =
- messageOrThrottlingMessage(
+ messageOrLockoutMessage(
repository.message.value,
- authenticationInteractor.throttling.value,
+ authenticationInteractor.lockout.value,
)
)
/**
- * The current authentication throttling state, set when the user has to wait before being able
- * to try another authentication attempt. `null` indicates throttling isn't active.
+ * The current authentication lockout (aka "throttling") state, set when the user has to wait
+ * before being able to try another authentication attempt. `null` indicates lockout isn't
+ * active.
*/
- val throttling: StateFlow<AuthenticationThrottlingModel?> = authenticationInteractor.throttling
+ val lockout: StateFlow<AuthenticationLockoutModel?> = authenticationInteractor.lockout
/** Whether the auto confirm feature is enabled for the currently-selected user. */
val isAutoConfirmEnabled: StateFlow<Boolean> = authenticationInteractor.isAutoConfirmEnabled
@@ -102,9 +103,9 @@ constructor(
init {
if (flags.isEnabled()) {
- // Clear the message if moved from throttling to no-longer throttling.
+ // Clear the message if moved from locked-out to no-longer locked-out.
applicationScope.launch {
- throttling.pairwise().collect { (previous, current) ->
+ lockout.pairwise().collect { (previous, current) ->
if (previous != null && current == null) {
clearMessage()
}
@@ -213,9 +214,9 @@ constructor(
* Shows the error message.
*
* Callers should use this instead of [authenticate] when they know ahead of time that an auth
- * attempt will fail but aren't interested in the other side effects like triggering throttling.
+ * attempt will fail but aren't interested in the other side effects like triggering lockout.
* For example, if the user entered a pattern that's too short, the system can show the error
- * message without having the attempt trigger throttling.
+ * message without having the attempt trigger lockout.
*/
private suspend fun showErrorMessage() {
repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
@@ -250,15 +251,15 @@ constructor(
}
}
- private fun messageOrThrottlingMessage(
+ private fun messageOrLockoutMessage(
message: String?,
- throttlingModel: AuthenticationThrottlingModel?,
+ lockoutModel: AuthenticationLockoutModel?,
): String {
return when {
- throttlingModel != null ->
+ lockoutModel != null ->
applicationContext.getString(
com.android.internal.R.string.lockscreen_too_many_failed_attempts_countdown,
- throttlingModel.remainingSeconds,
+ lockoutModel.remainingSeconds,
)
message != null -> message
else -> ""
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index e379dab918ef..0d7f6dcce1c7 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -50,12 +50,12 @@ sealed class AuthMethodBouncerViewModel(
abstract val authenticationMethod: AuthenticationMethodModel
/**
- * String resource ID of the failure message to be shown during throttling.
+ * String resource ID of the failure message to be shown during lockout.
*
* The message must include 2 number parameters: the first one indicating how many unsuccessful
- * attempts were made, and the second one indicating in how many seconds throttling will expire.
+ * attempts were made, and the second one indicating in how many seconds lockout will expire.
*/
- @get:StringRes abstract val throttlingMessageId: Int
+ @get:StringRes abstract val lockoutMessageId: Int
/** Notifies that the UI has been shown to the user. */
fun onShown() {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index fefc3e3411dd..4b1434323886 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -105,17 +105,17 @@ class BouncerViewModel(
get() = bouncerInteractor.isUserSwitcherVisible
private val isInputEnabled: StateFlow<Boolean> =
- bouncerInteractor.throttling
+ bouncerInteractor.lockout
.map { it == null }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = bouncerInteractor.throttling.value == null,
+ initialValue = bouncerInteractor.lockout.value == null,
)
// Handle to the scope of the child ViewModel (stored in [authMethod]).
private var childViewModelScope: CoroutineScope? = null
- private val _throttlingDialogMessage = MutableStateFlow<String?>(null)
+ private val _dialogMessage = MutableStateFlow<String?>(null)
/** View-model for the current UI, based on the current authentication method. */
val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> =
@@ -128,20 +128,20 @@ class BouncerViewModel(
)
/**
- * A message for a throttling dialog to show when the user has attempted the wrong credential
- * too many times and now must wait a while before attempting again.
+ * A message for a dialog to show when the user has attempted the wrong credential too many
+ * times and now must wait a while before attempting again.
*
* If `null`, no dialog should be shown.
*
- * Once the dialog is shown, the UI should call [onThrottlingDialogDismissed] when the user
- * dismisses this dialog.
+ * Once the dialog is shown, the UI should call [onDialogDismissed] when the user dismisses this
+ * dialog.
*/
- val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow()
+ val dialogMessage: StateFlow<String?> = _dialogMessage.asStateFlow()
/** The user-facing message to show in the bouncer. */
val message: StateFlow<MessageViewModel> =
- combine(bouncerInteractor.message, bouncerInteractor.throttling) { message, throttling ->
- toMessageViewModel(message, isThrottled = throttling != null)
+ combine(bouncerInteractor.message, bouncerInteractor.lockout) { message, lockout ->
+ toMessageViewModel(message, isLockedOut = lockout != null)
}
.stateIn(
scope = applicationScope,
@@ -149,7 +149,7 @@ class BouncerViewModel(
initialValue =
toMessageViewModel(
message = bouncerInteractor.message.value,
- isThrottled = bouncerInteractor.throttling.value != null,
+ isLockedOut = bouncerInteractor.lockout.value != null,
),
)
@@ -197,28 +197,28 @@ class BouncerViewModel(
init {
if (flags.isEnabled()) {
applicationScope.launch {
- combine(bouncerInteractor.throttling, authMethodViewModel) {
- throttling,
+ combine(bouncerInteractor.lockout, authMethodViewModel) {
+ lockout,
authMethodViewModel ->
- if (throttling != null && authMethodViewModel != null) {
+ if (lockout != null && authMethodViewModel != null) {
applicationContext.getString(
- authMethodViewModel.throttlingMessageId,
- throttling.failedAttemptCount,
- throttling.remainingSeconds,
+ authMethodViewModel.lockoutMessageId,
+ lockout.failedAttemptCount,
+ lockout.remainingSeconds,
)
} else {
null
}
}
.distinctUntilChanged()
- .collect { dialogMessage -> _throttlingDialogMessage.value = dialogMessage }
+ .collect { dialogMessage -> _dialogMessage.value = dialogMessage }
}
}
}
- /** Notifies that a throttling dialog has been dismissed by the user. */
- fun onThrottlingDialogDismissed() {
- _throttlingDialogMessage.value = null
+ /** Notifies that the dialog has been dismissed by the user. */
+ fun onDialogDismissed() {
+ _dialogMessage.value = null
}
private fun isSideBySideSupported(authMethod: AuthMethodBouncerViewModel?): Boolean {
@@ -231,11 +231,11 @@ class BouncerViewModel(
private fun toMessageViewModel(
message: String?,
- isThrottled: Boolean,
+ isLockedOut: Boolean,
): MessageViewModel {
return MessageViewModel(
text = message ?: "",
- isUpdateAnimated = !isThrottled,
+ isUpdateAnimated = !isLockedOut,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index 3b7e32140560..b68271767fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -46,7 +46,7 @@ class PasswordBouncerViewModel(
override val authenticationMethod = AuthenticationMethodModel.Password
- override val throttlingMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message
+ override val lockoutMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message
/** Whether the input method editor (for example, the software keyboard) is visible. */
private var isImeVisible: Boolean = false
@@ -56,13 +56,13 @@ class PasswordBouncerViewModel(
/** Whether the UI should request focus on the text field element. */
val isTextFieldFocusRequested =
- combine(interactor.throttling, isTextFieldFocused) { throttling, hasFocus ->
+ combine(interactor.lockout, isTextFieldFocused) { throttling, hasFocus ->
throttling == null && !hasFocus
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = interactor.throttling.value == null && !isTextFieldFocused.value,
+ initialValue = interactor.lockout.value == null && !isTextFieldFocused.value,
)
override fun onHidden() {
@@ -104,7 +104,7 @@ class PasswordBouncerViewModel(
* hidden.
*/
suspend fun onImeVisibilityChanged(isVisible: Boolean) {
- if (isImeVisible && !isVisible && interactor.throttling.value == null) {
+ if (isImeVisible && !isVisible && interactor.lockout.value == null) {
interactor.onImeHiddenByUser()
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index b1c5ab6122fe..69f8032ef4f2 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -80,7 +80,7 @@ class PatternBouncerViewModel(
override val authenticationMethod = AuthenticationMethodModel.Pattern
- override val throttlingMessageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message
+ override val lockoutMessageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message
/** Notifies that the user has started a drag gesture across the dot grid. */
fun onDragStart() {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index e25e82fe04c3..7f4a0296ebdc 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -104,7 +104,7 @@ class PinBouncerViewModel(
override val authenticationMethod: AuthenticationMethodModel = authenticationMethod
- override val throttlingMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message
+ override val lockoutMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message
init {
viewModelScope.launch { simBouncerInteractor.subId.collect { onResetSimFlow() } }
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt
index fdd98bec0a2d..3063ebd60b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt
@@ -18,8 +18,12 @@ package com.android.systemui.common.shared.model
/** Models the bounds of the notification container. */
data class NotificationContainerBounds(
+ /** The position of the left of the container in its window coordinate system, in pixels. */
+ val left: Float = 0f,
/** The position of the top of the container in its window coordinate system, in pixels. */
val top: Float = 0f,
+ /** The position of the right of the container in its window coordinate system, in pixels. */
+ val right: Float = 0f,
/** The position of the bottom of the container in its window coordinate system, in pixels. */
val bottom: Float = 0f,
/** Whether any modifications to top/bottom should be smoothly animated. */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 0405ca43320f..ca8268dc89a3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -82,6 +82,7 @@ import com.android.systemui.qs.FgsManagerControllerImpl;
import com.android.systemui.qs.QSFragmentStartableModule;
import com.android.systemui.qs.footer.dagger.FooterActionsModule;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recordissue.RecordIssueModule;
import com.android.systemui.retail.dagger.RetailModeModule;
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
import com.android.systemui.screenrecord.ScreenRecordModule;
@@ -209,6 +210,7 @@ import javax.inject.Named;
PrivacyModule.class,
QRCodeScannerModule.class,
QSFragmentStartableModule.class,
+ RecordIssueModule.class,
ReferenceModule.class,
RetailModeModule.class,
ScreenshotModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 6a0e88246027..d5b95d6721f9 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -16,7 +16,10 @@
package com.android.systemui.flags
+import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
@@ -29,5 +32,9 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha
override fun defineDependencies() {
NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token
FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token
+
+ val keyguardBottomAreaRefactor = FlagToken(
+ FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor())
+ KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index cbfd17ff7ae4..9fe5c3f53d96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -214,7 +214,7 @@ constructor(
private fun listenForLockscreenToPrimaryBouncerDragging() {
var transitionId: UUID? = null
scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
- shadeRepository.shadeModel
+ shadeRepository.legacyShadeExpansion
.sample(
combine(
transitionInteractor.startedKeyguardTransitionStep,
@@ -224,23 +224,23 @@ constructor(
),
::toQuad
)
- .collect { (shadeModel, keyguardState, statusBarState, isKeyguardUnlocked) ->
+ .collect { (shadeExpansion, keyguardState, statusBarState, isKeyguardUnlocked) ->
val id = transitionId
if (id != null) {
if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) {
// An existing `id` means a transition is started, and calls to
// `updateTransition` will control it until FINISHED or CANCELED
var nextState =
- if (shadeModel.expansionAmount == 0f) {
+ if (shadeExpansion == 0f) {
TransitionState.FINISHED
- } else if (shadeModel.expansionAmount == 1f) {
+ } else if (shadeExpansion == 1f) {
TransitionState.CANCELED
} else {
TransitionState.RUNNING
}
transitionRepository.updateTransition(
id,
- 1f - shadeModel.expansionAmount,
+ 1f - shadeExpansion,
nextState,
)
@@ -274,7 +274,7 @@ constructor(
// integrated into KeyguardTransitionRepository
if (
keyguardState.to == KeyguardState.LOCKSCREEN &&
- shadeModel.isUserDragging &&
+ shadeRepository.legacyShadeTracking.value &&
!isKeyguardUnlocked &&
statusBarState == KEYGUARD
) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 702386d3b498..c12efe875b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -224,8 +224,8 @@ constructor(
configurationInteractor
.dimensionPixelSize(R.dimen.keyguard_translate_distance_on_swipe_up)
.flatMapLatest { translationDistance ->
- shadeRepository.shadeModel.map {
- if (it.expansionAmount == 0f) {
+ shadeRepository.legacyShadeExpansion.map {
+ if (it == 0f) {
// Reset the translation value
0f
} else {
@@ -233,7 +233,7 @@ constructor(
MathUtils.lerp(
translationDistance,
0,
- Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it.expansionAmount)
+ Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it)
)
}
}
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 448411edb168..8dde399ed754 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
@@ -46,6 +46,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAfforda
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -66,6 +67,7 @@ class KeyguardQuickAffordanceInteractor
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor,
+ private val shadeInteractor: ShadeInteractor,
private val lockPatternUtils: LockPatternUtils,
private val keyguardStateController: KeyguardStateController,
private val userTracker: UserTracker,
@@ -100,9 +102,10 @@ constructor(
quickAffordanceAlwaysVisible(position),
keyguardInteractor.isDozing,
keyguardInteractor.isKeyguardShowing,
+ shadeInteractor.anyExpansion,
biometricSettingsRepository.isCurrentUserInLockdown,
- ) { affordance, isDozing, isKeyguardShowing, isUserInLockdown ->
- if (!isDozing && isKeyguardShowing && !isUserInLockdown) {
+ ) { affordance, isDozing, isKeyguardShowing, qsExpansion, isUserInLockdown ->
+ if (!isDozing && isKeyguardShowing && (qsExpansion < 1.0f) && !isUserInLockdown) {
affordance
} else {
KeyguardQuickAffordanceModel.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 96efb237047e..39a0547ded26 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -40,7 +40,7 @@ import com.android.systemui.statusbar.notification.icon.ui.viewmodel.Notificatio
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
@@ -49,13 +49,13 @@ class AodNotificationIconsSection
constructor(
private val context: Context,
private val configurationState: ConfigurationState,
- private val configurationController: ConfigurationController,
private val featureFlags: FeatureFlagsClassic,
private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
private val notificationIconAreaController: NotificationIconAreaController,
private val smartspaceViewModel: KeyguardSmartspaceViewModel,
+ private val systemBarUtilsState: SystemBarUtilsState,
) : KeyguardSection() {
private var nicBindingDisposable: DisposableHandle? = null
@@ -89,11 +89,11 @@ constructor(
if (NotificationIconContainerRefactor.isEnabled) {
nicBindingDisposable?.dispose()
nicBindingDisposable =
- NotificationIconContainerViewBinder.bind(
+ NotificationIconContainerViewBinder.bindWhileAttached(
nic,
nicAodViewModel,
configurationState,
- configurationController,
+ systemBarUtilsState,
iconBindingFailureTracker,
nicAodIconViewStore,
)
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 a64a422a1924..e7b6e44450bd 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
@@ -26,7 +26,6 @@ import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
@@ -92,13 +91,7 @@ constructor(
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
- val lockId =
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- R.id.device_entry_icon_view
- } else {
- R.id.lock_icon_view
- }
- connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP)
+ addNotificationPlaceholderBarrier(this)
}
}
}
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 a25471cba66d..400d0dc2b242 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
@@ -20,7 +20,12 @@ package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
import android.view.View
import android.view.ViewGroup
+import androidx.constraintlayout.widget.Barrier
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.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.res.R
@@ -54,6 +59,29 @@ constructor(
private val placeHolderId = R.id.nssl_placeholder
private var disposableHandle: DisposableHandle? = null
+ /**
+ * Align the notification placeholder bottom to the top of either the lock icon or the ambient
+ * indication area, whichever is higher.
+ */
+ protected fun addNotificationPlaceholderBarrier(constraintSet: ConstraintSet) {
+ val lockId =
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ R.id.device_entry_icon_view
+ } else {
+ R.id.lock_icon_view
+ }
+
+ constraintSet.apply {
+ createBarrier(
+ R.id.nssl_placeholder_barrier_bottom,
+ Barrier.TOP,
+ 0,
+ *intArrayOf(lockId, R.id.ambient_indication_container)
+ )
+ connect(R.id.nssl_placeholder, BOTTOM, R.id.nssl_placeholder_barrier_bottom, TOP)
+ }
+ }
+
override fun addViews(constraintLayout: ConstraintLayout) {
if (!KeyguardShadeMigrationNssl.isEnabled) {
return
@@ -85,9 +113,9 @@ constructor(
)
if (sceneContainerFlags.flexiNotifsEnabled()) {
NotificationStackAppearanceViewBinder.bind(
+ context,
sharedNotificationContainer,
notificationStackAppearanceViewModel,
- sceneContainerFlags,
ambientState,
controller,
)
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 f5963be55b2d..b0b5c81dd11c 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
@@ -19,14 +19,12 @@ package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
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
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
@@ -97,13 +95,7 @@ constructor(
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
- val lockId =
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- R.id.device_entry_icon_view
- } else {
- R.id.lock_icon_view
- }
- connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP)
+ addNotificationPlaceholderBarrier(this)
}
}
}
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 4588e02df10e..1d4520ff8f03 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
@@ -111,10 +111,21 @@ constructor(
/** An observable for the alpha level for the entire keyguard root view. */
val alpha: Flow<Float> =
- merge(
- keyguardInteractor.keyguardAlpha.distinctUntilChanged(),
- occludedToLockscreenTransitionViewModel.lockscreenAlpha,
- )
+ combine(
+ keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
+ merge(
+ keyguardInteractor.keyguardAlpha,
+ occludedToLockscreenTransitionViewModel.lockscreenAlpha,
+ )
+ ) { transitionToGone, alpha ->
+ if (transitionToGone == 1f) {
+ // Ensures content is not visible when in GONE state
+ 0f
+ } else {
+ alpha
+ }
+ }
+ .distinctUntilChanged()
private fun burnIn(): Flow<BurnInModel> {
val dozingAmount: Flow<Float> =
@@ -229,7 +240,9 @@ constructor(
.distinctUntilChanged()
fun onNotificationContainerBoundsChanged(top: Float, bottom: Float) {
- keyguardInteractor.setNotificationContainerBounds(NotificationContainerBounds(top, bottom))
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = top, bottom = bottom)
+ )
}
/** Is there an expanded pulse, are we animating in response? */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
new file mode 100644
index 000000000000..a4088f81f062
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.service.quicksettings.Tile
+import android.text.TextUtils
+import android.view.View
+import android.widget.Switch
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.Flags.recordIssueQsTile
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class RecordIssueTile
+@Inject
+constructor(
+ host: QSHost,
+ uiEventLogger: QsEventLogger,
+ @Background backgroundLooper: Looper,
+ @Main mainHandler: Handler,
+ falsingManager: FalsingManager,
+ metricsLogger: MetricsLogger,
+ statusBarStateController: StatusBarStateController,
+ activityStarter: ActivityStarter,
+ qsLogger: QSLogger
+) :
+ QSTileImpl<QSTile.BooleanState>(
+ host,
+ uiEventLogger,
+ backgroundLooper,
+ mainHandler,
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+ ) {
+
+ @VisibleForTesting var isRecording: Boolean = false
+
+ override fun getTileLabel(): CharSequence = mContext.getString(R.string.qs_record_issue_label)
+
+ override fun isAvailable(): Boolean = recordIssueQsTile()
+
+ override fun newTileState(): QSTile.BooleanState =
+ QSTile.BooleanState().apply {
+ label = tileLabel
+ handlesLongClick = false
+ }
+
+ override fun handleClick(view: View?) {
+ isRecording = !isRecording
+ refreshState()
+ }
+
+ override fun getLongClickIntent(): Intent? = null
+
+ @VisibleForTesting
+ public override fun handleUpdateState(qsTileState: QSTile.BooleanState, arg: Any?) {
+ qsTileState.apply {
+ if (isRecording) {
+ value = true
+ state = Tile.STATE_ACTIVE
+ forceExpandIcon = false
+ secondaryLabel = mContext.getString(R.string.qs_record_issue_stop)
+ icon = ResourceIcon.get(R.drawable.qs_record_issue_icon_on)
+ } else {
+ value = false
+ state = Tile.STATE_INACTIVE
+ forceExpandIcon = true
+ secondaryLabel = mContext.getString(R.string.qs_record_issue_start)
+ icon = ResourceIcon.get(R.drawable.qs_record_issue_icon_off)
+ }
+ label = tileLabel
+ contentDescription =
+ if (TextUtils.isEmpty(secondaryLabel)) label else "$label, $secondaryLabel"
+ expandedAccessibilityClassName = Switch::class.java.name
+ }
+ }
+
+ companion object {
+ const val TILE_SPEC = "record_issue"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
new file mode 100644
index 000000000000..d67cf4d3d098
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.RecordIssueTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface RecordIssueModule {
+ /** Inject RecordIssueTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(RecordIssueTile.TILE_SPEC)
+ fun bindRecordIssueTile(recordIssueTile: RecordIssueTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index e94a3eb5db22..2445bdb17955 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -15,25 +15,14 @@
*/
package com.android.systemui.shade.data.repository
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shade.ShadeExpansionChangeEvent
-import com.android.systemui.shade.ShadeExpansionListener
-import com.android.systemui.shade.ShadeExpansionStateManager
-import com.android.systemui.shade.domain.model.ShadeModel
import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
+/** Data for the shade, mostly related to expansion of the shade and quick settings. */
interface ShadeRepository {
- /** ShadeModel information regarding shade expansion events */
- val shadeModel: Flow<ShadeModel>
-
/**
* Amount qs has expanded, [0-1]. 0 means fully collapsed, 1 means fully expanded. Quick
* Settings can be expanded without the full shade expansion.
@@ -167,34 +156,7 @@ interface ShadeRepository {
/** Business logic for shade interactions */
@SysUISingleton
-class ShadeRepositoryImpl
-@Inject
-constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepository {
- override val shadeModel: Flow<ShadeModel> =
- conflatedCallbackFlow {
- val callback =
- object : ShadeExpansionListener {
- override fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) {
- // Don't propagate ShadeExpansionChangeEvent.dragDownPxAmount field.
- // It is too noisy and produces extra events that consumers won't care
- // about
- val info =
- ShadeModel(
- expansionAmount = event.fraction,
- isExpanded = event.expanded,
- isUserDragging = event.tracking
- )
- trySendWithFailureLogging(info, TAG, "updated shade expansion info")
- }
- }
-
- val currentState = shadeExpansionStateManager.addExpansionListener(callback)
- callback.onPanelExpansionChanged(currentState)
-
- awaitClose { shadeExpansionStateManager.removeExpansionListener(callback) }
- }
- .distinctUntilChanged()
-
+class ShadeRepositoryImpl @Inject constructor() : ShadeRepository {
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt
deleted file mode 100644
index ce0f4283ff83..000000000000
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2022 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.model
-
-import android.annotation.FloatRange
-
-/** Information about shade (NotificationPanel) expansion */
-data class ShadeModel(
- /** 0 when collapsed, 1 when fully expanded. */
- @FloatRange(from = 0.0, to = 1.0) val expansionAmount: Float = 0f,
- /** Whether the panel should be considered expanded */
- val isExpanded: Boolean = false,
- /** Whether the user is actively dragging the panel. */
- val isUserDragging: Boolean = false,
-)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index faffb3e118fc..d23c85a6d796 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
-import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityEvent;
@@ -29,6 +28,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.time.SystemClock;
import java.util.stream.Stream;
@@ -39,21 +39,23 @@ import java.util.stream.Stream;
*/
public abstract class AlertingNotificationManager {
private static final String TAG = "AlertNotifManager";
- protected final Clock mClock = new Clock();
+ protected final SystemClock mSystemClock;
protected final ArrayMap<String, AlertEntry> mAlertEntries = new ArrayMap<>();
protected final HeadsUpManagerLogger mLogger;
- public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler) {
- mLogger = logger;
- mHandler = handler;
- }
-
protected int mMinimumDisplayTime;
- protected int mStickyDisplayTime;
- protected int mAutoDismissNotificationDecay;
+ protected int mStickyForSomeTimeAutoDismissTime;
+ protected int mAutoDismissTime;
@VisibleForTesting
public Handler mHandler;
+ public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler,
+ SystemClock systemClock) {
+ mLogger = logger;
+ mHandler = handler;
+ mSystemClock = systemClock;
+ }
+
/**
* Called when posting a new notification that should alert the user and appear on screen.
* Adds the notification to be managed.
@@ -251,7 +253,7 @@ public abstract class AlertingNotificationManager {
public long getEarliestRemovalTime(String key) {
AlertEntry alerting = mAlertEntries.get(key);
if (alerting != null) {
- return Math.max(0, alerting.mEarliestRemovaltime - mClock.currentTimeMillis());
+ return Math.max(0, alerting.mEarliestRemovalTime - mSystemClock.elapsedRealtime());
}
return 0;
}
@@ -259,7 +261,7 @@ public abstract class AlertingNotificationManager {
protected class AlertEntry implements Comparable<AlertEntry> {
@Nullable public NotificationEntry mEntry;
public long mPostTime;
- public long mEarliestRemovaltime;
+ public long mEarliestRemovalTime;
@Nullable protected Runnable mRemoveAlertRunnable;
@@ -283,8 +285,8 @@ public abstract class AlertingNotificationManager {
public void updateEntry(boolean updatePostTime, @Nullable String reason) {
mLogger.logUpdateEntry(mEntry, updatePostTime, reason);
- final long now = mClock.currentTimeMillis();
- mEarliestRemovaltime = now + mMinimumDisplayTime;
+ final long now = mSystemClock.elapsedRealtime();
+ mEarliestRemovalTime = now + mMinimumDisplayTime;
if (updatePostTime) {
mPostTime = Math.max(mPostTime, now);
@@ -318,7 +320,7 @@ public abstract class AlertingNotificationManager {
* @return true if the notification has been on screen long enough
*/
public boolean wasShownLongEnough() {
- return mEarliestRemovaltime < mClock.currentTimeMillis();
+ return mEarliestRemovalTime < mSystemClock.elapsedRealtime();
}
@Override
@@ -351,7 +353,7 @@ public abstract class AlertingNotificationManager {
if (mRemoveAlertRunnable != null) {
removeAutoRemovalCallbacks("removeAsSoonAsPossible (will be rescheduled)");
- final long timeLeft = mEarliestRemovaltime - mClock.currentTimeMillis();
+ final long timeLeft = mEarliestRemovalTime - mSystemClock.elapsedRealtime();
mHandler.postDelayed(mRemoveAlertRunnable, timeLeft);
}
}
@@ -361,22 +363,16 @@ public abstract class AlertingNotificationManager {
* @return the post time
*/
protected long calculatePostTime() {
- return mClock.currentTimeMillis();
+ return mSystemClock.elapsedRealtime();
}
/**
* @return When the notification should auto-dismiss itself, based on
- * {@link SystemClock#elapsedRealTime()}
+ * {@link SystemClock#elapsedRealtime()}
*/
protected long calculateFinishTime() {
// Overridden by HeadsUpManager HeadsUpEntry #calculateFinishTime
return 0;
}
}
-
- protected final static class Clock {
- public long currentTimeMillis() {
- return SystemClock.elapsedRealtime();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 22912df71334..85f4c366b370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -20,6 +20,7 @@ import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.data.StatusBarDataLayerModule
import com.android.systemui.statusbar.phone.LightBarController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
+import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -33,7 +34,7 @@ import dagger.multibindings.IntoMap
* ([com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule],
* [com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule], etc.).
*/
-@Module(includes = [StatusBarDataLayerModule::class])
+@Module(includes = [StatusBarDataLayerModule::class, SystemBarUtilsProxyImpl.Module::class])
abstract class StatusBarModule {
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index ecca9731f003..92391e7c76f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -22,7 +22,6 @@ import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
import androidx.lifecycle.lifecycleScope
-import com.android.internal.policy.SystemBarUtils
import com.android.internal.statusbar.StatusBarIcon
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.common.ui.ConfigurationState
@@ -39,10 +38,8 @@ import com.android.systemui.statusbar.notification.icon.ui.viewmodel.Notificatio
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType
import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
import com.android.systemui.util.kotlin.mapValuesNotNullTo
-import com.android.systemui.util.kotlin.stateFlow
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
@@ -51,7 +48,6 @@ import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
@@ -59,20 +55,20 @@ import kotlinx.coroutines.launch
/** Binds a view-model to a [NotificationIconContainer]. */
object NotificationIconContainerViewBinder {
@JvmStatic
- fun bind(
+ fun bindWhileAttached(
view: NotificationIconContainer,
viewModel: NotificationIconContainerShelfViewModel,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
failureTracker: StatusBarIconViewBindingFailureTracker,
- viewStore: ShelfNotificationIconViewStore,
+ viewStore: IconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
lifecycleScope.launch {
viewModel.icons.bindIcons(
view,
configuration,
- configurationController,
+ systemBarUtilsState,
notifyBindingFailures = { failureTracker.shelfFailures = it },
viewStore,
)
@@ -81,66 +77,87 @@ object NotificationIconContainerViewBinder {
}
@JvmStatic
- fun bind(
+ fun bindWhileAttached(
view: NotificationIconContainer,
viewModel: NotificationIconContainerStatusBarViewModel,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
failureTracker: StatusBarIconViewBindingFailureTracker,
- viewStore: StatusBarNotificationIconViewStore,
- ): DisposableHandle {
- val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
- return view.repeatWhenAttached {
- lifecycleScope.run {
- launch {
- val iconColors: Flow<NotificationIconColors> =
- viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
- viewModel.icons.bindIcons(
- view,
- configuration,
- configurationController,
- notifyBindingFailures = { failureTracker.statusBarFailures = it },
- viewStore,
- ) { _, sbiv ->
- StatusBarIconViewBinder.bindIconColors(
- sbiv,
- iconColors,
- contrastColorUtil,
- )
- }
- }
- launch { viewModel.bindIsolatedIcon(view, viewStore) }
- launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
+ viewStore: IconViewStore,
+ ): DisposableHandle =
+ view.repeatWhenAttached {
+ lifecycleScope.launch {
+ bind(view, viewModel, configuration, systemBarUtilsState, failureTracker, viewStore)
+ }
+ }
+
+ suspend fun bind(
+ view: NotificationIconContainer,
+ viewModel: NotificationIconContainerStatusBarViewModel,
+ configuration: ConfigurationState,
+ systemBarUtilsState: SystemBarUtilsState,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
+ viewStore: IconViewStore,
+ ): Unit = coroutineScope {
+ launch {
+ val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
+ val iconColors: Flow<NotificationIconColors> =
+ viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
+ viewModel.icons.bindIcons(
+ view,
+ configuration,
+ systemBarUtilsState,
+ notifyBindingFailures = { failureTracker.statusBarFailures = it },
+ viewStore,
+ ) { _, sbiv ->
+ StatusBarIconViewBinder.bindIconColors(
+ sbiv,
+ iconColors,
+ contrastColorUtil,
+ )
}
}
+ launch { viewModel.bindIsolatedIcon(view, viewStore) }
+ launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
}
@JvmStatic
- fun bind(
+ fun bindWhileAttached(
view: NotificationIconContainer,
viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
failureTracker: StatusBarIconViewBindingFailureTracker,
viewStore: IconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
lifecycleScope.launch {
- view.setUseIncreasedIconScale(true)
- launch {
- viewModel.icons.bindIcons(
- view,
- configuration,
- configurationController,
- notifyBindingFailures = { failureTracker.aodFailures = it },
- viewStore,
- ) { _, sbiv ->
- viewModel.bindAodStatusBarIconView(sbiv, configuration)
- }
- }
- launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) }
+ bind(view, viewModel, configuration, systemBarUtilsState, failureTracker, viewStore)
+ }
+ }
+ }
+
+ suspend fun bind(
+ view: NotificationIconContainer,
+ viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
+ configuration: ConfigurationState,
+ systemBarUtilsState: SystemBarUtilsState,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
+ viewStore: IconViewStore,
+ ): Unit = coroutineScope {
+ view.setUseIncreasedIconScale(true)
+ launch {
+ viewModel.icons.bindIcons(
+ view,
+ configuration,
+ systemBarUtilsState,
+ notifyBindingFailures = { failureTracker.aodFailures = it },
+ viewStore,
+ ) { _, sbiv ->
+ viewModel.bindAodStatusBarIconView(sbiv, configuration)
}
}
+ launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) }
}
private suspend fun NotificationIconContainerAlwaysOnDisplayViewModel.bindAodStatusBarIconView(
@@ -199,7 +216,7 @@ object NotificationIconContainerViewBinder {
private suspend fun Flow<NotificationIconsViewData>.bindIcons(
view: NotificationIconContainer,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
notifyBindingFailures: (Collection<String>) -> Unit,
viewStore: IconViewStore,
bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
@@ -210,12 +227,8 @@ object NotificationIconContainerViewBinder {
)
val iconHorizontalPaddingFlow: Flow<Int> =
configuration.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin)
- val statusBarHeightFlow: StateFlow<Int> =
- stateFlow(changedSignals = configurationController.onConfigChanged) {
- SystemBarUtils.getStatusBarHeight(view.context)
- }
val layoutParams: Flow<FrameLayout.LayoutParams> =
- combine(iconSizeFlow, iconHorizontalPaddingFlow, statusBarHeightFlow) {
+ combine(iconSizeFlow, iconHorizontalPaddingFlow, systemBarUtilsState.statusBarHeight) {
iconSize,
iconHPadding,
statusBarHeight,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 5cdead407891..699e1406bc18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -29,7 +29,7 @@ import com.android.systemui.statusbar.notification.row.ui.viewbinder.Activatable
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
@@ -39,7 +39,7 @@ object NotificationShelfViewBinder {
shelf: NotificationShelf,
viewModel: NotificationShelfViewModel,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
falsingManager: FalsingManager,
iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
notificationIconAreaController: NotificationIconAreaController,
@@ -48,11 +48,11 @@ object NotificationShelfViewBinder {
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
shelf.apply {
if (NotificationIconContainerRefactor.isEnabled) {
- NotificationIconContainerViewBinder.bind(
+ NotificationIconContainerViewBinder.bindWhileAttached(
shelfIcons,
viewModel.icons,
configuration,
- configurationController,
+ systemBarUtilsState,
iconViewBindingFailureTracker,
shelfIconViewStore,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
index abf09ae9844c..e78a694735e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
@@ -26,5 +26,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
@SysUISingleton
class NotificationStackAppearanceRepository @Inject constructor() {
/** The bounds of the notification stack in the current scene. */
- val stackBounds = MutableStateFlow(NotificationContainerBounds(0f, 0f))
+ val stackBounds = MutableStateFlow(NotificationContainerBounds())
+
+ /** The corner radius of the notification stack, in dp. */
+ val cornerRadiusDp = MutableStateFlow(32f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 32e4e89c42c5..61a4dfcbd201 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -39,4 +39,7 @@ constructor(
check(bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
repository.stackBounds.value = bounds
}
+
+ /** The corner radius of the notification stack, in dp. */
+ val cornerRadiusDp: StateFlow<Float> = repository.cornerRadiusDp.asStateFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index a4e1a9c502f3..9373d497ffa7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -40,7 +40,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.HideNotificationsBinder.bindHideList
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.combine
@@ -53,12 +53,12 @@ constructor(
private val viewModel: NotificationListViewModel,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val configuration: ConfigurationState,
- private val configurationController: ConfigurationController,
private val falsingManager: FalsingManager,
private val iconAreaController: NotificationIconAreaController,
private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val metricsLogger: MetricsLogger,
private val shelfIconViewStore: ShelfNotificationIconViewStore,
+ private val systemBarUtilsState: SystemBarUtilsState,
) {
fun bind(
@@ -91,7 +91,7 @@ constructor(
shelf,
viewModel.shelf,
configuration,
- configurationController,
+ systemBarUtilsState,
falsingManager,
iconViewBindingFailureTracker,
iconAreaController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
index fa7a8fdb7495..a9b542dcce2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
@@ -16,14 +16,16 @@
package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+import android.content.Context
+import android.util.TypedValue
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
+import kotlin.math.roundToInt
import kotlinx.coroutines.launch
/** Binds the shared notification container to its view-model. */
@@ -31,9 +33,9 @@ object NotificationStackAppearanceViewBinder {
@JvmStatic
fun bind(
+ context: Context,
view: SharedNotificationContainer,
viewModel: NotificationStackAppearanceViewModel,
- sceneContainerFlags: SceneContainerFlags,
ambientState: AmbientState,
controller: NotificationStackScrollLayoutController,
) {
@@ -45,6 +47,14 @@ object NotificationStackAppearanceViewBinder {
bounds.top,
controller.isAddOrRemoveAnimationPending
)
+ controller.setRoundedClippingBounds(
+ it.left,
+ it.top,
+ it.right,
+ it.bottom,
+ viewModel.cornerRadiusDp.value.dpToPx(context),
+ viewModel.cornerRadiusDp.value.dpToPx(context),
+ )
}
}
launch {
@@ -56,4 +66,13 @@ object NotificationStackAppearanceViewBinder {
}
}
}
+
+ private fun Float.dpToPx(context: Context): Int {
+ return TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ this,
+ context.resources.displayMetrics
+ )
+ .roundToInt()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
index f4c0e92b0e87..834d3ffe63c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
@@ -23,6 +23,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
/** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
@SysUISingleton
@@ -37,4 +38,7 @@ constructor(
/** The bounds of the notification stack in the current scene. */
val stackBounds: Flow<NotificationContainerBounds> = stackAppearanceInteractor.stackBounds
+
+ /** The corner radius of the notification stack, in dp. */
+ val cornerRadiusDp: StateFlow<Float> = stackAppearanceInteractor.cornerRadiusDp
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index c6fd98ea2223..9f22118e3332 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -24,6 +24,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
/**
* ViewModel used by the Notification placeholders inside the scene container to update the
@@ -55,9 +56,14 @@ constructor(
* pixels.
*/
fun onBoundsChanged(
+ left: Float,
top: Float,
+ right: Float,
bottom: Float,
) {
- interactor.setStackBounds(NotificationContainerBounds(top, bottom))
+ interactor.setStackBounds(NotificationContainerBounds(left, top, right, bottom))
}
+
+ /** The corner radius of the placeholder, in dp. */
+ val cornerRadiusDp: StateFlow<Float> = interactor.cornerRadiusDp
}
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 5b854e6c8ee7..9594bc3bfd86 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
@@ -177,8 +177,8 @@ constructor(
}
.stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = NotificationContainerBounds(0f, 0f),
+ started = SharingStarted.Lazily,
+ initialValue = NotificationContainerBounds(),
)
val alpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 3a95e6d053e8..644c8962b93d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -49,6 +49,8 @@ import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -115,11 +117,14 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp
VisualStabilityProvider visualStabilityProvider,
ConfigurationController configurationController,
@Main Handler handler,
+ GlobalSettings globalSettings,
+ SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
JavaAdapter javaAdapter,
ShadeInteractor shadeInteractor) {
- super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
+ super(context, logger, handler, globalSettings, systemClock, accessibilityManagerWrapper,
+ uiEventLogger);
Resources resources = mContext.getResources();
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
statusBarStateController.addCallback(mStatusBarStateListener);
@@ -206,7 +211,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp
@Override
public boolean shouldSwallowClick(@NonNull String key) {
BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
- return entry != null && mClock.currentTimeMillis() < entry.mPostTime;
+ return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime;
}
public void onExpandingFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 49880d4475da..cd999349d055 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -74,8 +74,8 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder;
import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
import com.android.systemui.util.CarrierConfigTracker;
@@ -83,8 +83,6 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Unit;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -95,6 +93,8 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
+import kotlin.Unit;
+
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -153,7 +153,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final NotificationIconContainerStatusBarViewModel mStatusBarIconsViewModel;
private final ConfigurationState mConfigurationState;
- private final ConfigurationController mConfigurationController;
+ private final SystemBarUtilsState mSystemBarUtilsState;
private final StatusBarNotificationIconViewStore mStatusBarIconViewStore;
private final DemoModeController mDemoModeController;
@@ -246,7 +246,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
KeyguardUpdateMonitor keyguardUpdateMonitor,
NotificationIconContainerStatusBarViewModel statusBarIconsViewModel,
ConfigurationState configurationState,
- ConfigurationController configurationController,
+ SystemBarUtilsState systemBarUtilsState,
StatusBarNotificationIconViewStore statusBarIconViewStore,
DemoModeController demoModeController) {
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
@@ -275,7 +275,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mStatusBarIconsViewModel = statusBarIconsViewModel;
mConfigurationState = configurationState;
- mConfigurationController = configurationController;
+ mSystemBarUtilsState = systemBarUtilsState;
mStatusBarIconViewStore = statusBarIconViewStore;
mDemoModeController = demoModeController;
}
@@ -466,11 +466,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
.inflate(R.layout.notification_icon_area, notificationIconArea, true);
NotificationIconContainer notificationIcons =
notificationIconArea.requireViewById(R.id.notificationIcons);
- NotificationIconContainerViewBinder.bind(
+ NotificationIconContainerViewBinder.bindWhileAttached(
notificationIcons,
mStatusBarIconsViewModel,
mConfigurationState,
- mConfigurationController,
+ mSystemBarUtilsState,
mIconViewBindingFailureTracker,
mStatusBarIconViewStore);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
index 99b123fbd702..ae58398753e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
@@ -161,13 +161,13 @@ constructor(
if (it == null) {
notConnectedFlow
} else {
- val secondary = it.contentDescription.toString()
+ val secondary = it.contentDescription
flowOf(
InternetTileModel.Active(
- secondaryTitle = secondary,
+ secondaryLabel = secondary?.toText(),
iconId = it.res,
stateDescription = null,
- contentDescription = ContentDescription.Loaded(secondary),
+ contentDescription = secondary,
)
)
}
@@ -241,5 +241,11 @@ constructor(
string.substring(1, length - 1)
} else string
}
+
+ private fun ContentDescription.toText(): Text =
+ when (this) {
+ is ContentDescription.Loaded -> Text.Loaded(this.description)
+ is ContentDescription.Resource -> Text.Resource(this.res)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index cec76f3140cd..8054b04529c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -25,8 +25,6 @@ import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
-import android.os.SystemClock;
-import android.provider.Settings;
import android.util.ArrayMap;
import android.view.accessibility.AccessibilityManager;
@@ -40,6 +38,8 @@ import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.util.ListenerSet;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import java.io.PrintWriter;
@@ -85,36 +85,40 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp
public BaseHeadsUpManager(@NonNull final Context context,
HeadsUpManagerLogger logger,
@Main Handler handler,
+ GlobalSettings globalSettings,
+ SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger) {
- super(logger, handler);
+ super(logger, handler, systemClock);
mContext = context;
mAccessibilityMgr = accessibilityManagerWrapper;
mUiEventLogger = uiEventLogger;
Resources resources = context.getResources();
mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
- mStickyDisplayTime = resources.getInteger(R.integer.sticky_heads_up_notification_time);
- mAutoDismissNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
+ mStickyForSomeTimeAutoDismissTime = resources.getInteger(
+ R.integer.sticky_heads_up_notification_time);
+ mAutoDismissTime = resources.getInteger(R.integer.heads_up_notification_decay);
mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
mSnoozedPackages = new ArrayMap<>();
int defaultSnoozeLengthMs =
resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
- mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
- SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
+ mSnoozeLengthMs = globalSettings.getInt(SETTING_HEADS_UP_SNOOZE_LENGTH_MS,
+ defaultSnoozeLengthMs);
ContentObserver settingsObserver = new ContentObserver(handler) {
@Override
public void onChange(boolean selfChange) {
- final int packageSnoozeLengthMs = Settings.Global.getInt(
- context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
+ final int packageSnoozeLengthMs = globalSettings.getInt(
+ SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
mSnoozeLengthMs = packageSnoozeLengthMs;
mLogger.logSnoozeLengthChange(packageSnoozeLengthMs);
}
}
};
- context.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
+ globalSettings.registerContentObserver(
+ globalSettings.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS),
+ /* notifyForDescendants = */ false,
settingsObserver);
}
@@ -231,7 +235,7 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp
final String key = snoozeKey(packageName, mUser);
Long snoozedUntil = mSnoozedPackages.get(key);
if (snoozedUntil != null) {
- if (snoozedUntil > mClock.currentTimeMillis()) {
+ if (snoozedUntil > mSystemClock.elapsedRealtime()) {
mLogger.logIsSnoozedReturned(key);
return true;
}
@@ -250,7 +254,7 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp
String packageName = entry.mEntry.getSbn().getPackageName();
String snoozeKey = snoozeKey(packageName, mUser);
mLogger.logPackageSnoozed(snoozeKey);
- mSnoozedPackages.put(snoozeKey, mClock.currentTimeMillis() + mSnoozeLengthMs);
+ mSnoozedPackages.put(snoozeKey, mSystemClock.elapsedRealtime() + mSnoozeLengthMs);
}
}
@@ -308,7 +312,7 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp
protected void dumpInternal(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
- pw.print(" now="); pw.println(mClock.currentTimeMillis());
+ pw.print(" now="); pw.println(mSystemClock.elapsedRealtime());
pw.print(" mUser="); pw.println(mUser);
for (AlertEntry entry: mAlertEntries.values()) {
pw.print(" HeadsUpEntry="); pw.println(entry.mEntry);
@@ -519,12 +523,12 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp
/**
* @return When the notification should auto-dismiss itself, based on
- * {@link SystemClock#elapsedRealTime()}
+ * {@link SystemClock#elapsedRealtime()}
*/
@Override
protected long calculateFinishTime() {
final long duration = getRecommendedHeadsUpTimeoutMs(
- isStickyForSomeTime() ? mStickyDisplayTime : mAutoDismissNotificationDecay);
+ isStickyForSomeTime() ? mStickyForSomeTimeAutoDismissTime : mAutoDismissTime);
return mPostTime + duration;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt
new file mode 100644
index 000000000000..2b3fb70301e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.ui
+
+import android.content.Context
+import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.dagger.qualifiers.Application
+import dagger.Binds
+import javax.inject.Inject
+
+/**
+ * Proxy interface to [SystemBarUtils], allowing injection of different logic for testing.
+ *
+ * Developers should almost always prefer [SystemBarUtilsState] instead.
+ */
+interface SystemBarUtilsProxy {
+ fun getStatusBarHeight(): Int
+}
+
+class SystemBarUtilsProxyImpl
+@Inject
+constructor(
+ @Application private val context: Context,
+) : SystemBarUtilsProxy {
+ override fun getStatusBarHeight(): Int = SystemBarUtils.getStatusBarHeight(context)
+
+ @dagger.Module
+ interface Module {
+ @Binds fun bindImpl(impl: SystemBarUtilsProxyImpl): SystemBarUtilsProxy
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt
new file mode 100644
index 000000000000..ce811e205436
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.ui
+
+import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.onConfigChanged
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Tracks state from [SystemBarUtils]. Using this is both more efficient and more testable than
+ * using [SystemBarUtils] directly.
+ */
+class SystemBarUtilsState
+@Inject
+constructor(
+ configurationController: ConfigurationController,
+ proxy: SystemBarUtilsProxy,
+) {
+ /** @see SystemBarUtils.getStatusBarHeight */
+ val statusBarHeight: Flow<Int> =
+ configurationController.onConfigChanged
+ .onStart<Any> { emit(Unit) }
+ .map { proxy.getStatusBarHeight() }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 24917b37c8b1..88f63ad9c8cb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -63,7 +63,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -185,7 +185,7 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase {
mKeyguardSliceViewController,
mNotificationIconAreaController,
mSmartspaceController,
- mock(ConfigurationController.class),
+ mock(SystemBarUtilsState.class),
mock(ScreenOffAnimationController.class),
mock(StatusBarIconViewBindingFailureTracker.class),
mKeyguardUnlockAnimationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
index 575d8bf8248c..fa176728619b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
@@ -30,7 +30,6 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.runCurrent
import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.domain.model.ShadeModel
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.user.domain.UserDomainLayerModule
import com.android.systemui.util.mockito.mock
@@ -83,23 +82,13 @@ class DefaultUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
private fun TestComponent.shadeExpanded(expanded: Boolean) {
if (expanded) {
- shadeRepository.setShadeModel(
- ShadeModel(
- expansionAmount = 1f,
- isExpanded = true,
- isUserDragging = false,
- )
- )
+ shadeRepository.setLegacyShadeExpansion(1f)
+ shadeRepository.setLegacyShadeTracking(false)
shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(true)
} else {
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
- shadeRepository.setShadeModel(
- ShadeModel(
- expansionAmount = 0f,
- isExpanded = false,
- isUserDragging = false,
- )
- )
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
index f5f1622ac69b..863d9ebb41e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
@@ -20,7 +20,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.viewmodel.deviceEntryIconViewModelTransitionsMock
import com.android.systemui.kosmos.testScope
@@ -49,7 +49,7 @@ import org.mockito.MockitoAnnotations
class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }
}
val testScope = kosmos.testScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 40c9432d543d..076d72513633 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -53,9 +53,11 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -101,6 +103,8 @@ class CustomizationProviderTest : SysuiTestCase() {
private lateinit var underTest: CustomizationProvider
private lateinit var testScope: TestScope
+ private val kosmos = testKosmos()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -185,6 +189,7 @@ class CustomizationProviderTest : SysuiTestCase() {
},
)
.keyguardInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
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 66c8a229f0a1..b4ae7e3a7ca9 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
@@ -46,7 +46,9 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -242,6 +244,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var userTracker: UserTracker
+ private val kosmos = testKosmos()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -311,6 +315,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
featureFlags = featureFlags,
)
.keyguardInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index bf6d5c4535ec..976dc5f01ff9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -43,7 +43,6 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.domain.model.ShadeModel
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -1329,12 +1328,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// GIVEN the keyguard is showing locked
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
runCurrent()
- shadeRepository.setShadeModel(
- ShadeModel(
- expansionAmount = .9f,
- isUserDragging = true,
- )
- )
+ shadeRepository.setLegacyShadeTracking(true)
+ shadeRepository.setLegacyShadeExpansion(.9f)
runCurrent()
// THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
@@ -1350,12 +1345,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// WHEN the user stops dragging and shade is back to expanded
clearInvocations(transitionRepository)
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
- shadeRepository.setShadeModel(
- ShadeModel(
- expansionAmount = 1f,
- isUserDragging = false,
- )
- )
+ shadeRepository.setLegacyShadeTracking(false)
+ shadeRepository.setLegacyShadeExpansion(1f)
runCurrent()
// THEN a transition from PRIMARY_BOUNCER => LOCKSCREEN should occur
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index c9b14a41edca..daafe12514ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -22,7 +22,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
@@ -55,7 +55,7 @@ class BouncerToGoneFlowsTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply {
+ fakeFeatureFlagsClassic.apply {
set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 1584be0ab565..af38523c2fd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -54,9 +54,11 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -108,6 +110,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
private lateinit var dockManager: DockManagerFake
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+ private val kosmos = testKosmos()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -221,6 +225,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 0c30d10ea563..b6a661be8c74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -264,12 +264,14 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
whenever(lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha)
.thenReturn(emptyFlow())
whenever(shadeInteractor.qsExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
+ whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f))
underTest =
KeyguardQuickAffordancesCombinedViewModel(
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
+ shadeInteractor = shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 58624d356e60..687800714e05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -144,19 +144,43 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
@Test
fun alpha() =
testScope.runTest {
- val value = collectLastValue(underTest.alpha)
- assertThat(value()).isEqualTo(0f)
+ val alpha by collectLastValue(underTest.alpha)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.OFF,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
repository.setKeyguardAlpha(0.1f)
- assertThat(value()).isEqualTo(0.1f)
+ assertThat(alpha).isEqualTo(0.1f)
repository.setKeyguardAlpha(0.5f)
- assertThat(value()).isEqualTo(0.5f)
+ assertThat(alpha).isEqualTo(0.5f)
repository.setKeyguardAlpha(0.2f)
- assertThat(value()).isEqualTo(0.2f)
+ assertThat(alpha).isEqualTo(0.2f)
repository.setKeyguardAlpha(0f)
- assertThat(value()).isEqualTo(0f)
+ assertThat(alpha).isEqualTo(0f)
occludedToLockscreenAlpha.value = 0.8f
- assertThat(value()).isEqualTo(0.8f)
+ assertThat(alpha).isEqualTo(0.8f)
+ }
+
+ @Test
+ fun alphaWhenGoneEqualsZero() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope = testScope,
+ )
+
+ repository.setKeyguardAlpha(0.1f)
+ assertThat(alpha).isEqualTo(0f)
+ repository.setKeyguardAlpha(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ repository.setKeyguardAlpha(1f)
+ assertThat(alpha).isEqualTo(0f)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
index c15a2c6a3df7..e139466c8096 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -23,7 +23,7 @@ import com.android.systemui.biometrics.data.repository.fingerprintPropertyReposi
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -47,7 +47,9 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class LockscreenToAodTransitionViewModelTest : SysuiTestCase() {
private val kosmos =
- testKosmos().apply { featureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) } }
+ testKosmos().apply {
+ fakeFeatureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) }
+ }
private val testScope = kosmos.testScope
private val repository = kosmos.fakeKeyguardTransitionRepository
private val shadeRepository = kosmos.shadeRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index b31968c79647..7a564aca00bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -21,7 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -45,7 +45,7 @@ import org.junit.runner.RunWith
class LockscreenToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
private val testScope = kosmos.testScope
private val repository = kosmos.fakeKeyguardTransitionRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
new file mode 100644
index 000000000000..d8199c527cac
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.os.Handler
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.res.R
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+/**
+ * This class tests the functionality of the RecordIssueTile. The initial state of the tile is
+ * always be inactive at the start of these tests.
+ */
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class RecordIssueTileTest : SysuiTestCase() {
+
+ @Mock private lateinit var host: QSHost
+ @Mock private lateinit var qsEventLogger: QsEventLogger
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var qsLogger: QSLogger
+
+ private lateinit var tile: RecordIssueTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(host.context).thenReturn(mContext)
+
+ val testableLooper = TestableLooper.get(this)
+ tile =
+ RecordIssueTile(
+ host,
+ qsEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ FalsingManagerFake(),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+ )
+ }
+
+ @Test
+ fun qsTileUi_shouldLookCorrect_whenInactive() {
+ tile.isRecording = false
+
+ val testState = tile.newTileState()
+ tile.handleUpdateState(testState, null)
+
+ assertThat(testState.state).isEqualTo(Tile.STATE_INACTIVE)
+ assertThat(testState.secondaryLabel.toString())
+ .isEqualTo(mContext.getString(R.string.qs_record_issue_start))
+ }
+
+ @Test
+ fun qsTileUi_shouldLookCorrect_whenRecording() {
+ tile.isRecording = true
+
+ val testState = tile.newTileState()
+ tile.handleUpdateState(testState, null)
+
+ assertThat(testState.state).isEqualTo(Tile.STATE_ACTIVE)
+ assertThat(testState.secondaryLabel.toString())
+ .isEqualTo(mContext.getString(R.string.qs_record_issue_stop))
+ }
+
+ @Test
+ fun inActiveQsTile_switchesToActive_whenClicked() {
+ tile.isRecording = false
+
+ val testState = tile.newTileState()
+ tile.handleUpdateState(testState, null)
+
+ assertThat(testState.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
+ fun activeQsTile_switchesToInActive_whenClicked() {
+ tile.isRecording = true
+
+ val testState = tile.newTileState()
+ tile.handleUpdateState(testState, null)
+
+ assertThat(testState.state).isEqualTo(Tile.STATE_ACTIVE)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index f8aa359b569d..750693c483a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -19,33 +19,20 @@ package com.android.systemui.shade.data.repository
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.shade.ShadeExpansionChangeEvent
-import com.android.systemui.shade.ShadeExpansionStateManager
-import com.android.systemui.shade.domain.model.ShadeModel
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-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.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class ShadeRepositoryImplTest : SysuiTestCase() {
- @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -53,57 +40,10 @@ class ShadeRepositoryImplTest : SysuiTestCase() {
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- underTest = ShadeRepositoryImpl(shadeExpansionStateManager)
- `when`(shadeExpansionStateManager.addExpansionListener(any()))
- .thenReturn(ShadeExpansionChangeEvent(0f, false, false, 0f))
+ underTest = ShadeRepositoryImpl()
}
@Test
- fun shadeExpansionChangeEvent() =
- testScope.runTest {
- var latest: ShadeModel? = null
- val job = underTest.shadeModel.onEach { latest = it }.launchIn(this)
- runCurrent()
- assertThat(latest?.expansionAmount).isEqualTo(0f)
- assertThat(latest?.isExpanded).isEqualTo(false)
- assertThat(latest?.isUserDragging).isEqualTo(false)
-
- val captor = withArgCaptor {
- verify(shadeExpansionStateManager).addExpansionListener(capture())
- }
-
- captor.onPanelExpansionChanged(
- ShadeExpansionChangeEvent(
- fraction = 1f,
- expanded = true,
- tracking = false,
- dragDownPxAmount = 0f,
- )
- )
- runCurrent()
- assertThat(latest?.expansionAmount).isEqualTo(1f)
- assertThat(latest?.isExpanded).isEqualTo(true)
- assertThat(latest?.isUserDragging).isEqualTo(false)
-
- captor.onPanelExpansionChanged(
- ShadeExpansionChangeEvent(
- fraction = .67f,
- expanded = false,
- tracking = true,
- dragDownPxAmount = 0f,
- )
- )
- runCurrent()
- assertThat(latest?.expansionAmount).isEqualTo(.67f)
- assertThat(latest?.isExpanded).isEqualTo(false)
- assertThat(latest?.isUserDragging).isEqualTo(true)
-
- job.cancel()
- }
-
- @Test
fun updateQsExpansion() =
testScope.runTest {
assertThat(underTest.qsExpansion.value).isEqualTo(0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index b98dc0016066..a3cff87e63b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -39,12 +39,15 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.util.time.SystemClockImpl;
import org.junit.After;
import org.junit.Before;
@@ -74,18 +77,26 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
protected final Runnable mTestTimeoutRunnable = () -> mTimedOut = true;
protected Handler mTestHandler;
+ protected final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings();
+ protected final SystemClock mSystemClock = new SystemClockImpl();
protected boolean mTimedOut = false;
@Mock protected ExpandableNotificationRow mRow;
+ static {
+ assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
+ assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
+ assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_TIMEOUT_TIME);
+ }
+
private static class TestableAlertingNotificationManager extends AlertingNotificationManager {
private AlertEntry mLastCreatedEntry;
- private TestableAlertingNotificationManager(Handler handler) {
- super(new HeadsUpManagerLogger(logcatLogBuffer()), handler);
+ private TestableAlertingNotificationManager(Handler handler, SystemClock systemClock) {
+ super(new HeadsUpManagerLogger(logcatLogBuffer()), handler, systemClock);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
- mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
- mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME;
+ mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
+ mStickyForSomeTimeAutoDismissTime = TEST_STICKY_AUTO_DISMISS_TIME;
}
@Override
@@ -107,7 +118,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
}
protected AlertingNotificationManager createAlertingNotificationManager() {
- return new TestableAlertingNotificationManager(mTestHandler);
+ return new TestableAlertingNotificationManager(mTestHandler, mSystemClock);
}
protected StatusBarNotification createSbn(int id, Notification n) {
@@ -167,10 +178,6 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
@Before
public void setUp() {
mTestHandler = Handler.createAsync(Looper.myLooper());
-
- assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
- assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
- assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_TIMEOUT_TIME);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 2209e5e64a6d..f0205b3f5974 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -26,7 +26,7 @@ import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -55,7 +55,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
val testScope = kosmos.testScope
val configurationRepository = kosmos.fakeConfigurationRepository
@@ -413,7 +413,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
val top = 123f
val bottom = 456f
keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
- assertThat(bounds).isEqualTo(NotificationContainerBounds(top, bottom))
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 48b95d407246..37ee32204f32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -46,6 +46,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import org.junit.After;
import org.junit.Before;
@@ -87,6 +89,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
KeyguardBypassController keyguardBypassController,
ConfigurationController configurationController,
Handler handler,
+ GlobalSettings globalSettings,
+ SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
JavaAdapter javaAdapter,
@@ -101,13 +105,15 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
visualStabilityProvider,
configurationController,
handler,
+ globalSettings,
+ systemClock,
accessibilityManagerWrapper,
uiEventLogger,
javaAdapter,
shadeInteractor
);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
- mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
+ mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
}
}
@@ -121,6 +127,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
mBypassController,
mConfigurationController,
mTestHandler,
+ mGlobalSettings,
+ mSystemClock,
mAccessibilityManagerWrapper,
mUiEventLogger,
mJavaAdapter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 17c29382b39a..1cc611c2df87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -69,8 +69,8 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewBinder;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
import com.android.systemui.util.CarrierConfigTracker;
@@ -717,7 +717,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mKeyguardUpdateMonitor,
mock(NotificationIconContainerStatusBarViewModel.class),
mock(ConfigurationState.class),
- mock(ConfigurationController.class),
+ mock(SystemBarUtilsState.class),
mock(StatusBarNotificationIconViewStore.class),
mock(DemoModeController.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
index 22dce3a2ff64..1bdf64434fcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -21,6 +21,7 @@ import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVIC
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -355,14 +356,14 @@ class InternetTileViewModelTest : SysuiTestCase() {
connectivityRepository.setEthernetConnected(default = true, validated = true)
- assertThat(latest?.secondaryLabel).isNull()
- assertThat(latest?.secondaryTitle)
- .isEqualTo(ethernetIcon!!.contentDescription.toString())
+ assertThat(latest?.secondaryLabel.loadText(context))
+ .isEqualTo(ethernetIcon!!.contentDescription.loadContentDescription(context))
+ assertThat(latest?.secondaryTitle).isNull()
assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet_fully)
assertThat(latest?.icon).isNull()
assertThat(latest?.stateDescription).isNull()
assertThat(latest?.contentDescription.loadContentDescription(context))
- .isEqualTo(latest?.secondaryTitle)
+ .isEqualTo(latest?.secondaryLabel.loadText(context))
}
@Test
@@ -373,14 +374,14 @@ class InternetTileViewModelTest : SysuiTestCase() {
connectivityRepository.setEthernetConnected(default = true, validated = false)
- assertThat(latest?.secondaryLabel).isNull()
- assertThat(latest?.secondaryTitle)
- .isEqualTo(ethernetIcon!!.contentDescription.toString())
+ assertThat(latest?.secondaryLabel.loadText(context))
+ .isEqualTo(ethernetIcon!!.contentDescription.loadContentDescription(context))
+ assertThat(latest?.secondaryTitle).isNull()
assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet)
assertThat(latest?.icon).isNull()
assertThat(latest?.stateDescription).isNull()
assertThat(latest?.contentDescription.loadContentDescription(context))
- .isEqualTo(latest?.secondaryTitle)
+ .isEqualTo(latest?.secondaryLabel.loadText(context))
}
private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 4f3f56423eb0..2940c398c315 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -34,7 +34,6 @@ 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 static org.mockito.MockitoAnnotations.initMocks;
import android.app.Notification;
import android.app.PendingIntent;
@@ -57,17 +56,25 @@ import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class HeadsUpManagerTest extends AlertingNotificationManagerTest {
+public class BaseHeadsUpManagerTest extends AlertingNotificationManagerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
private static final int TEST_TOUCH_ACCEPTANCE_TIME = 200;
private static final int TEST_A11Y_AUTO_DISMISS_TIME = 1_000;
private static final int TEST_A11Y_TIMEOUT_TIME = 3_000;
@@ -76,17 +83,33 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest {
private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
@Mock private AccessibilityManagerWrapper mAccessibilityMgr;
+ static {
+ assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
+ assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
+ assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME);
+
+ assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_AUTO_DISMISS_TIME).isLessThan(
+ TEST_TIMEOUT_TIME);
+ assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(
+ TEST_TIMEOUT_TIME);
+ assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_A11Y_AUTO_DISMISS_TIME).isLessThan(
+ TEST_A11Y_TIMEOUT_TIME);
+ }
+
private final class TestableHeadsUpManager extends BaseHeadsUpManager {
TestableHeadsUpManager(Context context,
HeadsUpManagerLogger logger,
Handler handler,
+ GlobalSettings globalSettings,
+ SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger) {
- super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
+ super(context, logger, handler, globalSettings, systemClock,
+ accessibilityManagerWrapper, uiEventLogger);
mTouchAcceptanceDelay = TEST_TOUCH_ACCEPTANCE_TIME;
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
- mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
- mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME;
+ mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
+ mStickyForSomeTimeAutoDismissTime = TEST_STICKY_AUTO_DISMISS_TIME;
}
// The following are only implemented by HeadsUpManagerPhone. If you need them, use that.
@@ -160,8 +183,8 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest {
}
private BaseHeadsUpManager createHeadsUpManager() {
- return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mAccessibilityMgr,
- mUiEventLoggerFake);
+ return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mGlobalSettings,
+ mSystemClock, mAccessibilityMgr, mUiEventLoggerFake);
}
@Override
@@ -214,19 +237,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest {
@Before
@Override
public void setUp() {
- initMocks(this);
super.setUp();
-
- assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
- assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
- assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME);
-
- assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_AUTO_DISMISS_TIME).isLessThan(
- TEST_TIMEOUT_TIME);
- assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(
- TEST_TIMEOUT_TIME);
- assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_A11Y_AUTO_DISMISS_TIME).isLessThan(
- TEST_A11Y_TIMEOUT_TIME);
}
@After
diff --git a/packages/SystemUI/tests/utils/src/android/content/pm/LauncherAppsKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/pm/LauncherAppsKosmos.kt
new file mode 100644
index 000000000000..94fc1fce7f6f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/content/pm/LauncherAppsKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.launcherApps by Kosmos.Fixture { mock<LauncherApps>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index 03270870bccd..7c5696c716d8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -20,10 +20,10 @@ import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
+import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
-import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.dagger.SysUISingleton
import dagger.Binds
import dagger.Module
@@ -40,9 +40,6 @@ class FakeAuthenticationRepository(
private val currentTime: () -> Long,
) : AuthenticationRepository {
- private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false)
- override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
- _isAutoConfirmFeatureEnabled.asStateFlow()
override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
override val hintedPinLength: Int = HINTING_PIN_LENGTH
@@ -50,8 +47,13 @@ class FakeAuthenticationRepository(
private val _isPatternVisible = MutableStateFlow(true)
override val isPatternVisible: StateFlow<Boolean> = _isPatternVisible.asStateFlow()
- override val throttling: MutableStateFlow<AuthenticationThrottlingModel?> =
- MutableStateFlow(null)
+ override val lockout: MutableStateFlow<AuthenticationLockoutModel?> = MutableStateFlow(null)
+
+ override val hasLockoutOccurred = MutableStateFlow(false)
+
+ private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false)
+ override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
+ _isAutoConfirmFeatureEnabled.asStateFlow()
private val _authenticationMethod =
MutableStateFlow<AuthenticationMethodModel>(DEFAULT_AUTHENTICATION_METHOD)
@@ -67,10 +69,12 @@ class FakeAuthenticationRepository(
_isPinEnhancedPrivacyEnabled.asStateFlow()
private var failedAttemptCount = 0
- private var throttlingEndTimestamp = 0L
+ private var lockoutEndTimestamp = 0L
private var credentialOverride: List<Any>? = null
private var securityMode: SecurityMode = DEFAULT_AUTHENTICATION_METHOD.toSecurityMode()
+ var lockoutStartedReportCount = 0
+
override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
return authenticationMethod.value
}
@@ -89,6 +93,10 @@ class FakeAuthenticationRepository(
authenticationChallengeResult.emit(isSuccessful)
}
+ override suspend fun reportLockoutStarted(durationMs: Int) {
+ lockoutStartedReportCount++
+ }
+
override suspend fun getPinLength(): Int {
return (credentialOverride ?: DEFAULT_PIN).size
}
@@ -97,16 +105,19 @@ class FakeAuthenticationRepository(
return failedAttemptCount
}
- override suspend fun getThrottlingEndTimestamp(): Long {
- return throttlingEndTimestamp
+ override suspend fun getLockoutEndTimestamp(): Long {
+ return lockoutEndTimestamp
}
fun setAutoConfirmFeatureEnabled(isEnabled: Boolean) {
_isAutoConfirmFeatureEnabled.value = isEnabled
}
- override suspend fun setThrottleDuration(durationMs: Int) {
- throttlingEndTimestamp = if (durationMs > 0) currentTime() + durationMs else 0
+ override suspend fun setLockoutDuration(durationMs: Int) {
+ lockoutEndTimestamp = if (durationMs > 0) currentTime() + durationMs else 0
+ if (durationMs > 0) {
+ hasLockoutOccurred.value = true
+ }
}
override suspend fun checkCredential(
@@ -125,17 +136,16 @@ class FakeAuthenticationRepository(
else -> error("Unexpected credential type ${credential.type}!")
}
- return if (
- isSuccessful || failedAttemptCount < MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1
- ) {
+ return if (isSuccessful || failedAttemptCount < MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
+ hasLockoutOccurred.value = false
AuthenticationResultModel(
isSuccessful = isSuccessful,
- throttleDurationMs = 0,
+ lockoutDurationMs = 0,
)
} else {
AuthenticationResultModel(
isSuccessful = false,
- throttleDurationMs = THROTTLE_DURATION_MS,
+ lockoutDurationMs = LOCKOUT_DURATION_MS,
)
}
}
@@ -165,9 +175,9 @@ class FakeAuthenticationRepository(
AuthenticationPatternCoordinate(0, 1),
AuthenticationPatternCoordinate(0, 2),
)
- const val MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING = 5
- const val THROTTLE_DURATION_SECONDS = 30
- const val THROTTLE_DURATION_MS = THROTTLE_DURATION_SECONDS * 1000
+ const val MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT = 5
+ const val LOCKOUT_DURATION_SECONDS = 30
+ const val LOCKOUT_DURATION_MS = LOCKOUT_DURATION_SECONDS * 1000
const val HINTING_PIN_LENGTH = 6
val DEFAULT_PIN = buildList { repeat(HINTING_PIN_LENGTH) { add(it + 1) } }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
index e6b7f62c7d5f..abadaf754c30 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
@@ -16,6 +16,45 @@
package com.android.systemui.flags
+import android.content.res.mainResources
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
-val Kosmos.featureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+/**
+ * Main fixture for supplying a [FeatureFlagsClassic]. Should be used by other fixtures. Unless
+ * overridden in the test, this by default uses [fakeFeatureFlagsClassic].
+ */
+var Kosmos.featureFlagsClassic: FeatureFlagsClassic by Kosmos.Fixture { fakeFeatureFlagsClassic }
+
+/**
+ * Fixture supplying a shared [FakeFeatureFlagsClassic] instance. Can be accessed in tests in order
+ * to override flag values.
+ */
+val Kosmos.fakeFeatureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+
+/**
+ * Fixture supplying a real [FeatureFlagsClassicRelease] instance, for use by tests that want to
+ * reflect the current state of the device in release builds (example: screenshot tests).
+ *
+ * By default, this fixture is unused; tests should override [featureFlagsClassic] in order to
+ * utilize this fixture:
+ * ```kotlin
+ * val kosmos = Kosmos()
+ * kosmos.featureFlagsClassic = kosmos.featureFlagsClassicRelease
+ * ```
+ */
+val Kosmos.featureFlagsClassicRelease by
+ Kosmos.Fixture {
+ FeatureFlagsClassicRelease(
+ /* resources = */ mainResources,
+ /* systemProperties = */ systemPropertiesHelper,
+ /* serverFlagReader = */ serverFlagReader,
+ /* allFlags = */ FlagsCommonModule.providesAllFlags(),
+ /* restarter = */ restarter,
+ )
+ }
+
+val Kosmos.systemPropertiesHelper by Kosmos.Fixture { SystemPropertiesHelper() }
+var Kosmos.serverFlagReader: ServerFlagReader by Kosmos.Fixture { serverFlagReaderFake }
+val Kosmos.serverFlagReaderFake by Kosmos.Fixture { ServerFlagReaderFake() }
+var Kosmos.restarter: Restarter by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 9c108487e4c5..f7005ab45ea0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -18,21 +18,14 @@
package com.android.systemui.shade.data.repository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shade.domain.model.ShadeModel
import dagger.Binds
import dagger.Module
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
/** Fake implementation of [ShadeRepository] */
@SysUISingleton
class FakeShadeRepository @Inject constructor() : ShadeRepository {
-
- private val _shadeModel = MutableStateFlow(ShadeModel())
- override val shadeModel: Flow<ShadeModel> = _shadeModel
-
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion = _qsExpansion
@@ -114,10 +107,6 @@ class FakeShadeRepository @Inject constructor() : ShadeRepository {
_legacyIsClosing.value = isClosing
}
- fun setShadeModel(model: ShadeModel) {
- _shadeModel.value = model
- }
-
override fun setQsExpansion(qsExpansion: Float) {
_qsExpansion.value = qsExpansion
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifPipelineKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifPipelineKosmos.kt
new file mode 100644
index 000000000000..a48b27015c02
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifPipelineKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.notifPipeline by Kosmos.Fixture { mock<NotifPipeline>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt
new file mode 100644
index 000000000000..f00538e3fbcc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.notifcollection
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.collection.notifPipeline
+
+var Kosmos.commonNotifCollection by Kosmos.Fixture { notifPipeline }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt
new file mode 100644
index 000000000000..e7c4085cabfd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.provider
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.sectionStyleProvider: SectionStyleProvider by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
new file mode 100644
index 000000000000..f7acae9846df
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.collection.provider.sectionStyleProvider
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+
+val Kosmos.renderNotificationListInteractor by
+ Kosmos.Fixture {
+ RenderNotificationListInteractor(activeNotificationListRepository, sectionStyleProvider)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt
new file mode 100644
index 000000000000..4535652eb8c5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon
+
+import android.content.applicationContext
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.iconBuilder by Kosmos.Fixture { IconBuilder(applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt
new file mode 100644
index 000000000000..d3a8e0c5970c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon
+
+import android.content.pm.launcherApps
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.collection.notifcollection.commonNotifCollection
+
+val Kosmos.iconManager by
+ Kosmos.Fixture { IconManager(commonNotifCollection, launcherApps, iconBuilder) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
index 75e5aeafd52d..ca5b4010fd7d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
@@ -26,18 +26,18 @@ import com.android.systemui.statusbar.notification.icon.ui.viewbinder.shelfNotif
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.statusBarIconViewBindingFailureTracker
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationListViewModel
import com.android.systemui.statusbar.phone.notificationIconAreaController
-import com.android.systemui.statusbar.policy.configurationController
+import com.android.systemui.statusbar.ui.systemBarUtilsState
val Kosmos.notificationListViewBinder by Fixture {
NotificationListViewBinder(
viewModel = notificationListViewModel,
backgroundDispatcher = testDispatcher,
configuration = configurationState,
- configurationController = configurationController,
falsingManager = falsingManager,
iconAreaController = notificationIconAreaController,
iconViewBindingFailureTracker = statusBarIconViewBindingFailureTracker,
metricsLogger = metricsLogger,
shelfIconViewStore = shelfNotificationIconViewStore,
+ systemBarUtilsState = systemBarUtilsState,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt
new file mode 100644
index 000000000000..d38baba8876f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.ui
+
+class FakeSystemBarUtilsProxy(private var statusBarHeight: Int) : SystemBarUtilsProxy {
+ override fun getStatusBarHeight(): Int = statusBarHeight
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt
new file mode 100644
index 000000000000..f24037d4aea2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.ui
+
+import android.content.applicationContext
+import android.content.res.mainResources
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.res.R
+
+/**
+ * Main fixture for supplying a [SystemBarUtilsProxy]. Should be used by other fixtures. Unless
+ * overridden in the test, this by default uses [fakeSystemBarUtilsProxy].
+ */
+var Kosmos.systemBarUtilsProxy: SystemBarUtilsProxy by Fixture { fakeSystemBarUtilsProxy }
+
+/**
+ * Fixture supplying a real [SystemBarUtilsProxyImpl] instance, for use by tests that want to use
+ * the real device logic to determine system bar properties. Note this this real instance does *not*
+ * support Robolectric tests; by opting in, you are explicitly opting-out of using Robolectric.
+ *
+ * By default, this fixture is unused; tests should override [systemBarUtilsProxy] in order to
+ * utilize this fixture:
+ * ```kotlin
+ * val kosmos = Kosmos()
+ * kosmos.systemBarUtilsProxy = kosmos.systemBarUtilsProxyImpl
+ * ```
+ */
+val Kosmos.systemBarUtilsProxyImpl by Fixture { SystemBarUtilsProxyImpl(applicationContext) }
+
+/**
+ * Fixture supplying a shared [FakeSystemBarUtilsProxy] instance. Can be accessed or overridden in
+ * tests in order to provide custom results.
+ */
+var Kosmos.fakeSystemBarUtilsProxy by Fixture {
+ FakeSystemBarUtilsProxy(mainResources.getDimensionPixelSize(R.dimen.status_bar_height))
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt
new file mode 100644
index 000000000000..e208add32efa
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.ui
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.configurationController
+
+val Kosmos.systemBarUtilsState by
+ Kosmos.Fixture { SystemBarUtilsState(configurationController, systemBarUtilsProxy) }
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index e9bb763e143a..b8cf13b11534 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -32,6 +32,22 @@ java_library_static {
],
}
+java_library_static {
+ name: "AccessibilityGestureUtils",
+ srcs: [
+ "java/**/gestures/GestureMatcher.java",
+ "java/**/gestures/GestureManifold.java",
+ "java/**/gestures/MultiFingerMultiTap.java",
+ "java/**/gestures/TouchState.java",
+ ],
+ static_libs: [
+ "services.accessibility",
+ ],
+ libs: [
+ "androidx.annotation_annotation",
+ ],
+}
+
aconfig_declarations {
name: "com_android_server_accessibility_flags",
package: "com.android.server.accessibility",
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 903a07140eae..e54f0c12c0ca 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -82,7 +82,7 @@ import java.util.List;
* detector. Gesture matchers are tied to a single gesture. It calls listener callback functions
* when a gesture starts or completes.
*/
-class GestureManifold implements GestureMatcher.StateChangeListener {
+public class GestureManifold implements GestureMatcher.StateChangeListener {
private static final String LOG_TAG = "GestureManifold";
@@ -111,7 +111,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
// Shared state information.
private TouchState mState;
- GestureManifold(Context context, Listener listener, TouchState state, Handler handler) {
+ public GestureManifold(Context context, Listener listener, TouchState state, Handler handler) {
mContext = context;
mHandler = handler;
mListener = listener;
@@ -222,7 +222,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
* @return True if the event has been appropriately handled by the gesture manifold and related
* callback functions, false if it should be handled further by the calling function.
*/
- boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ public boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (mState.isClear()) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
// Validity safeguard: if touch state is clear, then matchers should always be clear
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index f55ecb05c55f..0a2a780adf45 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -981,17 +981,22 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
transitionToDelegatingStateAndClear();
}
transitToSinglePanningStateAndClear();
- } else {
+ } else if (!mIsTwoFingerCountReached) {
+ // If it is a two-finger gesture, do not transition to the
+ // delegating state to ensure the reachability of
+ // the two-finger triple tap (triggerable with ACTION_UP)
transitionToDelegatingStateAndClear();
}
} else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
&& distanceClosestPointerToPoint(
- mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance
- // If mCompleteTapCount is not zero, it means that it is a multi tap
- // gesture. So, we should not transit to the PanningScalingState.
- && mCompletedTapCount == 0) {
+ mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
// Second pointer is swiping, so transit to PanningScalingState
- transitToPanningScalingStateAndClear();
+ // Delay an ACTION_MOVE for tap timeout to ensure it is not trigger from
+ // multi finger multi tap
+ storePointerDownLocation(mSecondPointerDownLocation, event);
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
+ ViewConfiguration.getTapTimeout());
}
}
break;
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index c111ec3213d9..1f89e57b90d7 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -253,13 +253,8 @@ class InputController {
mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible);
}
- void setLocalIme(int displayId) {
- // WM throws a SecurityException if the display is untrusted.
- if ((mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED)
- == Display.FLAG_TRUSTED) {
- mWindowManager.setDisplayImePolicy(displayId,
- WindowManager.DISPLAY_IME_POLICY_LOCAL);
- }
+ void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
+ mWindowManager.setDisplayImePolicy(displayId, policy);
}
@GuardedBy("mLock")
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 13c79248eb38..58aa2c303345 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -899,6 +899,24 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
+ super.setDisplayImePolicy_enforcePermission();
+ synchronized (mVirtualDeviceLock) {
+ if (!mVirtualDisplays.contains(displayId)) {
+ throw new SecurityException("Display ID " + displayId
+ + " not found for this virtual device");
+ }
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mInputController.setDisplayImePolicy(displayId, policy);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@Nullable
public List<VirtualSensor> getVirtualSensorList() {
super.getVirtualSensorList_enforcePermission();
@@ -1095,7 +1113,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mInputController.setPointerAcceleration(1f, displayId);
mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
displayId);
- mInputController.setLocalIme(displayId);
+ // WM throws a SecurityException if the display is untrusted.
+ if ((mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED)
+ == Display.FLAG_TRUSTED) {
+ mInputController.setDisplayImePolicy(displayId,
+ WindowManager.DISPLAY_IME_POLICY_LOCAL);
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index b9a3c0c41841..33726d17da62 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -17,6 +17,7 @@
package com.android.server;
import static android.os.Flags.stateOfHealthPublic;
+import static android.os.Flags.batteryServiceSupportCurrentAdbCommand;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import static com.android.server.health.Utils.copyV1Battery;
@@ -927,9 +928,12 @@ public final class BatteryService extends SystemService {
pw.println("Battery service (battery) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" get [-f] [ac|usb|wireless|dock|status|level|temp|present|counter|invalid]");
- pw.println(" set [-f] "
- + "[ac|usb|wireless|dock|status|level|temp|present|counter|invalid] <value>");
+ String getSetOptions = "ac|usb|wireless|dock|status|level|temp|present|counter|invalid";
+ if (batteryServiceSupportCurrentAdbCommand()) {
+ getSetOptions += "|current_now|current_average";
+ }
+ pw.println(" get [-f] [" + getSetOptions + "]");
+ pw.println(" set [-f] [" + getSetOptions + "] <value>");
pw.println(" Force a battery property value, freezing battery state.");
pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
pw.println(" unplug [-f]");
@@ -1001,6 +1005,16 @@ public final class BatteryService extends SystemService {
case "counter":
pw.println(mHealthInfo.batteryChargeCounterUah);
break;
+ case "current_now":
+ if (batteryServiceSupportCurrentAdbCommand()) {
+ pw.println(mHealthInfo.batteryCurrentMicroamps);
+ }
+ break;
+ case "current_average":
+ if (batteryServiceSupportCurrentAdbCommand()) {
+ pw.println(mHealthInfo.batteryCurrentAverageMicroamps);
+ }
+ break;
case "temp":
pw.println(mHealthInfo.batteryTemperatureTenthsCelsius);
break;
@@ -1058,6 +1072,16 @@ public final class BatteryService extends SystemService {
case "counter":
mHealthInfo.batteryChargeCounterUah = Integer.parseInt(value);
break;
+ case "current_now":
+ if (batteryServiceSupportCurrentAdbCommand()) {
+ mHealthInfo.batteryCurrentMicroamps = Integer.parseInt(value);
+ }
+ break;
+ case "current_average":
+ if (batteryServiceSupportCurrentAdbCommand()) {
+ mHealthInfo.batteryCurrentAverageMicroamps =
+ Integer.parseInt(value);
+ }
case "temp":
mHealthInfo.batteryTemperatureTenthsCelsius = Integer.parseInt(value);
break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3e533a6ce601..ac173f3b5b7a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -11972,6 +11972,7 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean dumpSwapPss;
boolean dumpProto;
boolean mDumpPrivateDirty;
+ boolean mDumpAllocatorStats;
}
@NeverCompile // Avoid size overhead of debugging code.
@@ -11991,6 +11992,7 @@ public class ActivityManagerService extends IActivityManager.Stub
opts.dumpSwapPss = false;
opts.dumpProto = asProto;
opts.mDumpPrivateDirty = false;
+ opts.mDumpAllocatorStats = false;
int opti = 0;
while (opti < args.length) {
@@ -12027,7 +12029,8 @@ public class ActivityManagerService extends IActivityManager.Stub
opts.isCheckinRequest = true;
} else if ("--proto".equals(opt)) {
opts.dumpProto = true;
-
+ } else if ("--logstats".equals(opt)) {
+ opts.mDumpAllocatorStats = true;
} else if ("-h".equals(opt)) {
pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]");
pw.println(" -a: include all available information for each process.");
@@ -12238,7 +12241,8 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
thread.dumpMemInfo(tp.getWriteFd(),
mi, opts.isCheckinRequest, opts.dumpFullDetails,
- opts.dumpDalvik, opts.dumpSummaryOnly, opts.dumpUnreachable, innerArgs);
+ opts.dumpDalvik, opts.dumpSummaryOnly, opts.dumpUnreachable,
+ opts.mDumpAllocatorStats, innerArgs);
tp.go(fd, opts.dumpUnreachable ? 30000 : 5000);
} finally {
tp.kill();
@@ -17510,6 +17514,8 @@ public class ActivityManagerService extends IActivityManager.Stub
* other {@code ActivityManager#USER_OP_*} codes for failure.
*
*/
+ // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows
+ // delayed locking behavior once the private space flag is finalized.
@Override
public int stopUserWithDelayedLocking(final int userId, boolean force,
final IStopUserCallback callback) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 47a99fe24ec4..a6b532cdef09 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -356,6 +356,8 @@ class UserController implements Handler.Callback {
* Once total number of unlocked users reach mMaxRunningUsers, least recently used user
* will be locked.
*/
+ // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows
+ // delayed locking behavior once the private space flag is finalized.
@GuardedBy("mLock")
private boolean mDelayUserDataLocking;
@@ -365,11 +367,12 @@ class UserController implements Handler.Callback {
private volatile boolean mAllowUserUnlocking;
/**
- * Keep track of last active users for mDelayUserDataLocking.
- * The latest stopped user is placed in front while the least recently stopped user in back.
+ * Keep track of last active users for delayUserDataLocking.
+ * The most recently stopped user with delayed locking is placed in front, while the least
+ * recently stopped user in back.
*/
@GuardedBy("mLock")
- private final ArrayList<Integer> mLastActiveUsers = new ArrayList<>();
+ private final ArrayList<Integer> mLastActiveUsersForDelayedLocking = new ArrayList<>();
/**
* Map of userId to {@link UserCompletedEventType} event flags, indicating which as-yet-
@@ -1011,20 +1014,21 @@ class UserController implements Handler.Callback {
Slogf.i(TAG, "stopSingleUserLU userId=" + userId);
final UserState uss = mStartedUsers.get(userId);
if (uss == null) { // User is not started
- // If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock
- // the requested user as the client wants to stop and lock the user. On the other hand,
- // having keyEvictedCallback set will lead into locking user if mDelayUserDataLocking
- // is set as that means client wants to lock the user immediately.
- // If mDelayUserDataLocking is not set, the user was already locked when it was stopped
- // and no further action is necessary.
- if (mDelayUserDataLocking) {
+ // If canDelayDataLockingForUser() is true and allowDelayedLocking is false, we need
+ // to lock the requested user as the client wants to stop and lock the user. On the
+ // other hand, having keyEvictedCallback set will lead into locking user if
+ // canDelayDataLockingForUser() is true as that means client wants to lock the user
+ // immediately.
+ // If canDelayDataLockingForUser() is false, the user was already locked when it was
+ // stopped and no further action is necessary.
+ if (canDelayDataLockingForUser(userId)) {
if (allowDelayedLocking && keyEvictedCallback != null) {
Slogf.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it"
+ " and lock user:" + userId, new RuntimeException());
allowDelayedLocking = false;
}
if (!allowDelayedLocking) {
- if (mLastActiveUsers.remove(Integer.valueOf(userId))) {
+ if (mLastActiveUsersForDelayedLocking.remove(Integer.valueOf(userId))) {
// should lock the user, user is already gone
final ArrayList<KeyEvictedCallback> keyEvictedCallbacks;
if (keyEvictedCallback != null) {
@@ -1354,14 +1358,21 @@ class UserController implements Handler.Callback {
@GuardedBy("mLock")
private int updateUserToLockLU(@UserIdInt int userId, boolean allowDelayedLocking) {
int userIdToLock = userId;
- if (mDelayUserDataLocking && allowDelayedLocking && !getUserInfo(userId).isEphemeral()
+ // TODO: Decouple the delayed locking flows from mMaxRunningUsers or rename the property to
+ // state maximum running unlocked users specifically
+ if (canDelayDataLockingForUser(userIdToLock) && allowDelayedLocking
+ && !getUserInfo(userId).isEphemeral()
&& !hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, userId)) {
- mLastActiveUsers.remove((Integer) userId); // arg should be object, not index
- mLastActiveUsers.add(0, userId);
- int totalUnlockedUsers = mStartedUsers.size() + mLastActiveUsers.size();
+ // arg should be object, not index
+ mLastActiveUsersForDelayedLocking.remove((Integer) userId);
+ mLastActiveUsersForDelayedLocking.add(0, userId);
+ int totalUnlockedUsers = mStartedUsers.size()
+ + mLastActiveUsersForDelayedLocking.size();
if (totalUnlockedUsers > mMaxRunningUsers) { // should lock a user
- userIdToLock = mLastActiveUsers.get(mLastActiveUsers.size() - 1);
- mLastActiveUsers.remove(mLastActiveUsers.size() - 1);
+ userIdToLock = mLastActiveUsersForDelayedLocking.get(
+ mLastActiveUsersForDelayedLocking.size() - 1);
+ mLastActiveUsersForDelayedLocking
+ .remove(mLastActiveUsersForDelayedLocking.size() - 1);
Slogf.i(TAG, "finishUserStopped, stopping user:" + userId
+ " lock user:" + userIdToLock);
} else {
@@ -1374,6 +1385,24 @@ class UserController implements Handler.Callback {
}
/**
+ * Returns whether the user can have its CE storage left unlocked, even when it is stopped,
+ * either due to a global device configuration or an individual user's property.
+ */
+ private boolean canDelayDataLockingForUser(@UserIdInt int userIdToLock) {
+ if (allowBiometricUnlockForPrivateProfile()) {
+ final UserProperties userProperties = getUserProperties(userIdToLock);
+ return (mDelayUserDataLocking || (userProperties != null
+ && userProperties.getAllowStoppingUserWithDelayedLocking()));
+ }
+ return mDelayUserDataLocking;
+ }
+
+ private boolean allowBiometricUnlockForPrivateProfile() {
+ return android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace();
+ }
+
+ /**
* Determines the list of users that should be stopped together with the specified
* {@code userId}. The returned list includes {@code userId}.
*/
@@ -3161,7 +3190,7 @@ class UserController implements Handler.Callback {
pw.println(" mCurrentProfileIds:" + Arrays.toString(mCurrentProfileIds));
pw.println(" mCurrentUserId:" + mCurrentUserId);
pw.println(" mTargetUserId:" + mTargetUserId);
- pw.println(" mLastActiveUsers:" + mLastActiveUsers);
+ pw.println(" mLastActiveUsersForDelayedLocking:" + mLastActiveUsersForDelayedLocking);
pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking);
pw.println(" mAllowUserUnlocking:" + mAllowUserUnlocking);
pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch());
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9cfcb1679429..9cf15184027b 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -2037,9 +2037,8 @@ public class AudioDeviceBroker {
} break;
case MSG_L_UPDATED_ADI_DEVICE_STATE:
- synchronized (mDeviceStateLock) {
- mAudioService.onUpdatedAdiDeviceState((AdiDeviceState) msg.obj);
- } break;
+ mAudioService.onUpdatedAdiDeviceState((AdiDeviceState) msg.obj);
+ break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f7b7aaa60a35..44cb1367928d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -13779,6 +13779,11 @@ public class AudioService extends IAudioService.Stub
return mDeviceBroker.getDeviceAddresses(device);
}
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ MusicFxHelper getMusicFxHelper() {
+ return mMusicFxHelper;
+ }
+
//======================
// misc
//======================
diff --git a/services/core/java/com/android/server/audio/MusicFxHelper.java b/services/core/java/com/android/server/audio/MusicFxHelper.java
index 5f4e4c3bc4e0..85b3b49ecf78 100644
--- a/services/core/java/com/android/server/audio/MusicFxHelper.java
+++ b/services/core/java/com/android/server/audio/MusicFxHelper.java
@@ -17,9 +17,11 @@
package com.android.server.audio;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
import static com.android.server.audio.AudioService.MUSICFX_HELPER_MSG_START;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.IUidObserver;
import android.app.UidObserver;
@@ -48,7 +50,6 @@ import java.util.List;
/**
* MusicFx management.
- * .
*/
public class MusicFxHelper {
private static final String TAG = "AS.MusicFxHelper";
@@ -60,14 +61,113 @@ public class MusicFxHelper {
// Synchronization UidSessionMap access between UidObserver and AudioServiceBroadcastReceiver.
private final Object mClientUidMapLock = new Object();
+ private final String mPackageName = this.getClass().getPackage().getName();
+
+ private final String mMusicFxPackageName = "com.android.musicfx";
+
+ /*package*/ static final int MSG_EFFECT_CLIENT_GONE = MUSICFX_HELPER_MSG_START + 1;
+
// The binder token identifying the UidObserver registration.
private IBinder mUidObserverToken = null;
+ // Package name and list of open audio sessions for this package
+ private static class PackageSessions {
+ String mPackageName;
+ List<Integer> mSessions;
+ }
+
+ /*
+ * Override of SparseArray class to add bind/unbind and UID observer in the put/remove methods.
+ *
+ * put:
+ * - the first key/value set put into MySparseArray will trigger a procState bump (bindService)
+ * - if no valid observer token exist, will call registerUidObserver for put
+ * - for each new uid put into array, it will be added to uid observer list
+ *
+ * remove:
+ * - for each uid removed from array, it will be removed from uid observer list as well
+ * - if it's the last uid in array, no more MusicFx procState bump (unbindService), uid
+ * observer will also be removed, and observer token reset to null
+ */
+ private class MySparseArray extends SparseArray<PackageSessions> {
+ private final String mMusicFxPackageName = "com.android.musicfx";
+
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES
+ })
+ @Override
+ public void put(int uid, PackageSessions pkgSessions) {
+ if (size() == 0) {
+ int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ try {
+ procState = ActivityManager.getService().getPackageProcessState(
+ mMusicFxPackageName, mPackageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException with getPackageProcessState: " + e);
+ }
+ if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ Intent bindIntent = new Intent().setClassName(mMusicFxPackageName,
+ "com.android.musicfx.KeepAliveService");
+ mContext.bindServiceAsUser(
+ bindIntent, mMusicFxBindConnection, Context.BIND_AUTO_CREATE,
+ UserHandle.of(getCurrentUserId()));
+ Log.i(TAG, "bindService to " + mMusicFxPackageName);
+ }
+
+ Log.i(TAG, mMusicFxPackageName + " procState " + procState);
+ }
+ try {
+ if (mUidObserverToken == null) {
+ mUidObserverToken = ActivityManager.getService().registerUidObserverForUids(
+ mEffectUidObserver, ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, mPackageName,
+ new int[]{uid});
+ Log.i(TAG, "registered to observer with UID " + uid);
+ } else if (get(uid) == null) { // addUidToObserver if this is a new UID
+ ActivityManager.getService().addUidToObserver(mUidObserverToken, mPackageName,
+ uid);
+ Log.i(TAG, " UID " + uid + " add to observer");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException with UID observer add/register: " + e);
+ }
+
+ super.put(uid, pkgSessions);
+ }
+
+ @Override
+ public void remove(int uid) {
+ if (get(uid) != null) {
+ try {
+ ActivityManager.getService().removeUidFromObserver(mUidObserverToken,
+ mPackageName, uid);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException with removeUidFromObserver: " + e);
+ }
+ }
+
+ super.remove(uid);
+
+ // stop foreground service delegate and unregister UID observers with the last UID
+ if (size() == 0) {
+ try {
+ ActivityManager.getService().unregisterUidObserver(mEffectUidObserver);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException with unregisterUidObserver: " + e);
+ }
+ mUidObserverToken = null;
+ mContext.unbindService(mMusicFxBindConnection);
+ Log.i(TAG, "last session closed, unregister UID observer, and unbind "
+ + mMusicFxPackageName);
+ }
+ }
+ }
+
// Hashmap of UID and list of open sessions for this UID.
@GuardedBy("mClientUidMapLock")
- private SparseArray<List<Integer>> mClientUidSessionMap = new SparseArray<>();
-
- /*package*/ static final int MSG_EFFECT_CLIENT_GONE = MUSICFX_HELPER_MSG_START + 1;
+ private MySparseArray mClientUidSessionMap = new MySparseArray();
// UID observer for effect MusicFx clients
private final IUidObserver mEffectUidObserver = new UidObserver() {
@@ -102,23 +202,27 @@ public class MusicFxHelper {
* Handle the broadcast {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
* {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
*
+ * Only intents without target application package {@link android.content.Intent#getPackage}
+ * will be handled by the MusicFxHelper, all intents handled and forwarded by MusicFxHelper
+ * will have the target application package.
+ *
* If the intent is {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION}:
- * - If the MusicFx process is not running, call bindService with AUTO_CREATE to create.
- * - If this is the first audio session in MusicFx, call set foreground service delegate.
+ * - If the MusicFx process is not running, call bindServiceAsUser with AUTO_CREATE to create.
+ * - If this is the first audio session of MusicFx, call set foreground service delegate.
* - If this is the first audio session for a given UID, add the UID into observer.
*
- * If the intent is {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION}:
- * - MusicFx will not be foreground delegated anymore.
- * - The KeepAliveService of MusicFx will be unbound.
- * - The UidObserver will be removed.
+ * If the intent is {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION}
+ * - The KeepAliveService of MusicFx will be unbound, and MusicFx will not be foreground
+ * delegated anymore if the last session of the last package was closed.
+ * - The Uid Observer will be removed when the last session of a package was closed.
*/
+ @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS})
public void handleAudioEffectBroadcast(Context context, Intent intent) {
String target = intent.getPackage();
if (target != null) {
Log.w(TAG, "effect broadcast already targeted to " + target);
return;
}
- intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
final PackageManager pm = context.getPackageManager();
// TODO this should target a user-selected panel
List<ResolveInfo> ril = pm.queryBroadcastReceivers(intent, 0 /* flags */);
@@ -126,14 +230,14 @@ public class MusicFxHelper {
ResolveInfo ri = ril.get(0);
final String senderPackageName = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME);
try {
- final int senderUid = pm.getPackageUidAsUser(senderPackageName,
- PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId());
if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) {
+ final int senderUid = pm.getPackageUidAsUser(senderPackageName,
+ PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId());
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.setPackage(ri.activityInfo.packageName);
- synchronized (mClientUidMapLock) {
- setMusicFxServiceWithObserver(context, intent, senderUid);
+ if (setMusicFxServiceWithObserver(intent, senderUid, senderPackageName)) {
+ context.sendBroadcastAsUser(intent, UserHandle.ALL);
}
- context.sendBroadcastAsUser(intent, UserHandle.ALL);
return;
}
} catch (PackageManager.NameNotFoundException e) {
@@ -144,127 +248,105 @@ public class MusicFxHelper {
Log.w(TAG, "couldn't find receiver package for effect intent");
}
- /**
- * Handle the UidObserver onUidGone callback of MusicFx clients.
- * All open audio sessions of this UID will be closed.
- * If this is the last UID for MusicFx:
- * - MusicFx will not be foreground delegated anymore.
- * - The KeepAliveService of MusicFx will be unbound.
- * - The UidObserver will be removed.
- */
- public void handleEffectClientUidGone(int uid) {
- synchronized (mClientUidMapLock) {
- Log.w(TAG, " inside handle MSG_EFFECT_CLIENT_GONE");
- // Once the uid is no longer running, close all remain audio session(s) for this UID
- if (mClientUidSessionMap.get(Integer.valueOf(uid)) != null) {
- final List<Integer> sessions =
- new ArrayList(mClientUidSessionMap.get(Integer.valueOf(uid)));
- Log.i(TAG, "UID " + uid + " gone, closing " + sessions.size() + " sessions");
- for (Integer session : sessions) {
- Intent intent = new Intent(
- AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
- intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, session);
- setMusicFxServiceWithObserver(mContext, intent, uid);
- Log.i(TAG, "Close session " + session + " of UID " + uid);
- }
- mClientUidSessionMap.remove(Integer.valueOf(uid));
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES
+ })
+ @GuardedBy("mClientUidMapLock")
+ private boolean handleAudioEffectSessionOpen(
+ int senderUid, String senderPackageName, int sessionId) {
+ Log.d(TAG, senderPackageName + " UID " + senderUid + " open MusicFx session " + sessionId);
+
+ PackageSessions pkgSessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
+ if (pkgSessions != null && pkgSessions.mSessions != null) {
+ if (pkgSessions.mSessions.contains(sessionId)) {
+ Log.e(TAG, "Audio session " + sessionId + " already open for UID: "
+ + senderUid + ", package: " + senderPackageName + ", abort");
+ return false;
+ }
+ if (pkgSessions.mPackageName != senderPackageName) {
+ Log.w(TAG, "Inconsistency package names for UID open: " + senderUid + " prev: "
+ + pkgSessions.mPackageName + ", now: " + senderPackageName);
+ return false;
}
+ } else {
+ // first session for this UID, create a new Package/Sessions pair
+ pkgSessions = new PackageSessions();
+ pkgSessions.mSessions = new ArrayList();
+ pkgSessions.mPackageName = senderPackageName;
}
+
+ pkgSessions.mSessions.add(Integer.valueOf(sessionId));
+ mClientUidSessionMap.put(Integer.valueOf(senderUid), pkgSessions);
+ return true;
}
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES
+ })
@GuardedBy("mClientUidMapLock")
- private void setMusicFxServiceWithObserver(Context context, Intent intent, int senderUid) {
- PackageManager pm = context.getPackageManager();
- try {
- final int audioSession = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION,
- AudioManager.AUDIO_SESSION_ID_GENERATE);
- if (AudioManager.AUDIO_SESSION_ID_GENERATE == audioSession) {
- Log.e(TAG, "Intent missing audio session: " + audioSession);
- return;
+ private boolean handleAudioEffectSessionClose(
+ int senderUid, String senderPackageName, int sessionId) {
+ Log.d(TAG, senderPackageName + " UID " + senderUid + " close MusicFx session " + sessionId);
+
+ PackageSessions pkgSessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
+ if (pkgSessions == null) {
+ Log.e(TAG, senderPackageName + " UID " + senderUid + " does not exist in map, abort");
+ return false;
+ }
+ if (pkgSessions.mPackageName != senderPackageName) {
+ Log.w(TAG, "Inconsistency package names for UID " + senderUid + " close, prev: "
+ + pkgSessions.mPackageName + ", now: " + senderPackageName);
+ return false;
+ }
+
+ if (pkgSessions.mSessions != null && pkgSessions.mSessions.size() != 0) {
+ if (!pkgSessions.mSessions.contains(sessionId)) {
+ Log.e(TAG, senderPackageName + " UID " + senderUid + " session " + sessionId
+ + " does not exist in map, abort");
+ return false;
}
- // only apply to com.android.musicfx and KeepAliveService for now
- final String musicFxPackageName = "com.android.musicfx";
- final String musicFxKeepAliveService = "com.android.musicfx.KeepAliveService";
- final int musicFxUid = pm.getPackageUidAsUser(musicFxPackageName,
- PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId());
+ pkgSessions.mSessions.remove(Integer.valueOf(sessionId));
+ }
- if (intent.getAction().equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) {
- List<Integer> sessions = new ArrayList<>();
- Log.d(TAG, "UID " + senderUid + ", open MusicFx session " + audioSession);
- // start foreground service delegate and register UID observer with the first
- // session of first UID open
- if (0 == mClientUidSessionMap.size()) {
- final int procState = ActivityManager.getService().getPackageProcessState(
- musicFxPackageName, this.getClass().getPackage().getName());
- // if musicfx process not in binding state, call bindService with AUTO_CREATE
- if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- Intent bindIntent = new Intent().setClassName(musicFxPackageName,
- musicFxKeepAliveService);
- context.bindServiceAsUser(
- bindIntent, mMusicFxBindConnection, Context.BIND_AUTO_CREATE,
- UserHandle.of(getCurrentUserId()));
- Log.i(TAG, "bindService to " + musicFxPackageName);
- }
+ if (pkgSessions.mSessions == null || pkgSessions.mSessions.size() == 0) {
+ // remove UID from map as well as the UID observer with the last session close
+ mClientUidSessionMap.remove(Integer.valueOf(senderUid));
+ } else {
+ mClientUidSessionMap.put(Integer.valueOf(senderUid), pkgSessions);
+ }
- Log.i(TAG, "Package " + musicFxPackageName + " uid " + musicFxUid
- + " procState " + procState);
- } else if (mClientUidSessionMap.get(Integer.valueOf(senderUid)) != null) {
- sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
- if (sessions.contains(audioSession)) {
- Log.e(TAG, "Audio session " + audioSession + " already exist for UID "
- + senderUid + ", abort");
- return;
- }
- }
- // first session of this UID
- if (sessions.size() == 0) {
- // call registerUidObserverForUids with the first UID and first session
- if (mClientUidSessionMap.size() == 0 || mUidObserverToken == null) {
- mUidObserverToken = ActivityManager.getService().registerUidObserverForUids(
- mEffectUidObserver, ActivityManager.UID_OBSERVER_GONE,
- ActivityManager.PROCESS_STATE_UNKNOWN, null, new int[]{senderUid});
- Log.i(TAG, "UID " + senderUid + " registered to observer");
- } else {
- // add UID to observer for each new UID
- ActivityManager.getService().addUidToObserver(mUidObserverToken, TAG,
- senderUid);
- Log.i(TAG, "UID " + senderUid + " addeded to observer");
- }
- }
+ return true;
+ }
- sessions.add(Integer.valueOf(audioSession));
- mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions);
- } else {
- if (mClientUidSessionMap.get(senderUid) != null) {
- Log.d(TAG, "UID " + senderUid + ", close MusicFx session " + audioSession);
- List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
- sessions.remove(Integer.valueOf(audioSession));
- if (0 == sessions.size()) {
- mClientUidSessionMap.remove(Integer.valueOf(senderUid));
- } else {
- mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions);
- }
+ /**
+ * @return true if the intent is validated and handled successfully, false with any error
+ * (invalid sender/intent for example).
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES
+ })
+ private boolean setMusicFxServiceWithObserver(
+ Intent intent, int senderUid, String packageName) {
+ final int session = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION,
+ AudioManager.AUDIO_SESSION_ID_GENERATE);
+ if (AudioManager.AUDIO_SESSION_ID_GENERATE == session) {
+ Log.e(TAG, packageName + " intent have no invalid audio session");
+ return false;
+ }
- // stop foreground service delegate and unregister UID observer with the
- // last session of last UID close
- if (0 == mClientUidSessionMap.size()) {
- ActivityManager.getService().unregisterUidObserver(mEffectUidObserver);
- mClientUidSessionMap.clear();
- context.unbindService(mMusicFxBindConnection);
- Log.i(TAG, " remove all sessions, unregister UID observer, and unbind "
- + musicFxPackageName);
- }
- } else {
- // if the audio session already closed, print an error
- Log.e(TAG, "UID " + senderUid + " close audio session " + audioSession
- + " which does not exist");
- }
+ synchronized (mClientUidMapLock) {
+ if (intent.getAction().equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) {
+ return handleAudioEffectSessionOpen(senderUid, packageName, session);
+ } else {
+ return handleAudioEffectSessionClose(senderUid, packageName, session);
}
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Not able to find UID from package: " + e);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException " + e + " with handling intent");
}
}
@@ -281,6 +363,39 @@ public class MusicFxHelper {
return UserHandle.USER_SYSTEM;
}
+
+ /**
+ * Handle the UidObserver onUidGone callback of MusicFx clients.
+ * Send close intent for all open audio sessions of this UID. The mClientUidSessionMap will be
+ * updated with the handling of close intent in setMusicFxServiceWithObserver.
+ */
+ @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS})
+ private void handleEffectClientUidGone(int uid) {
+ synchronized (mClientUidMapLock) {
+ Log.d(TAG, "handle MSG_EFFECT_CLIENT_GONE uid: " + uid + " mapSize: "
+ + mClientUidSessionMap.size());
+ // Once the uid is no longer running, close all remain audio session(s) for this UID
+ final PackageSessions pkgSessions = mClientUidSessionMap.get(Integer.valueOf(uid));
+ if (pkgSessions != null) {
+ Log.i(TAG, "UID " + uid + " gone, closing all sessions");
+
+ // send close intent for each open session of the gone UID
+ for (Integer sessionId : pkgSessions.mSessions) {
+ Intent closeIntent =
+ new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
+ closeIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, pkgSessions.mPackageName);
+ closeIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId);
+ closeIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ // set broadcast target
+ closeIntent.setPackage(mMusicFxPackageName);
+ mContext.sendBroadcastAsUser(closeIntent, UserHandle.ALL);
+ }
+ mClientUidSessionMap.remove(Integer.valueOf(uid));
+ }
+ }
+ }
+
+ @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS})
/*package*/ void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EFFECT_CLIENT_GONE:
@@ -292,5 +407,4 @@ public class MusicFxHelper {
break;
}
}
-
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 3b7d80c844de..1e5e147749a2 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1292,7 +1292,11 @@ public class SyncManager {
*/
private boolean isPackageStopped(String packageName, int userId) {
if (android.content.pm.Flags.stayStopped()) {
- return mPackageManagerInternal.isPackageStopped(packageName, userId);
+ try {
+ return mPackageManagerInternal.isPackageStopped(packageName, userId);
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "Couldn't determine stopped state for unknown package: " + packageName);
+ }
}
return false;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 09c388f08c7b..09107c1bb4d3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5626,7 +5626,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void setVirtualDeviceInputMethodForAllUsers(int deviceId, @Nullable String imeId) {
// TODO(b/287269288): validate that id belongs to a valid virtual device instead.
- Preconditions.checkArgument(deviceId == Context.DEVICE_ID_DEFAULT,
+ Preconditions.checkArgument(deviceId != Context.DEVICE_ID_DEFAULT,
"DeviceId " + deviceId + " does not belong to a virtual device.");
synchronized (ImfLock.class) {
if (imeId == null) {
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index d359280c2a6f..bab3cbea108e 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -424,6 +424,7 @@ public abstract class IContextHubWrapper {
// 9a17008d-6bf1-445a-9011-6d21bd985b6c
private static final byte[] UUID = {-102, 23, 0, -115, 107, -15, 68, 90,
-112, 17, 109, 33, -67, -104, 91, 108};
+ private static final String NAME = "ContextHubService";
ContextHubAidlCallback(int contextHubId, ICallback callback) {
mContextHubId = contextHubId;
@@ -466,10 +467,14 @@ public abstract class IContextHubWrapper {
// TODO(271471342): Implement
}
- public byte[] getUuid() throws RemoteException {
+ public byte[] getUuid() {
return UUID;
}
+ public String getName() {
+ return NAME;
+ }
+
@Override
public String getInterfaceHash() {
return android.hardware.contexthub.IContextHubCallback.HASH;
diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
index a00999d08b5b..7cf3983e57f6 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
@@ -39,6 +39,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.media.flags.Flags;
import java.util.ArrayList;
import java.util.HashMap;
@@ -219,7 +220,10 @@ import java.util.stream.Collectors;
BluetoothRouteInfo
newBtRoute = new BluetoothRouteInfo();
newBtRoute.mBtDevice = device;
- String deviceName = device.getName();
+ String deviceName =
+ Flags.enableUseOfBluetoothDeviceGetAliasForMr2infoGetName()
+ ? device.getAlias()
+ : device.getName();
if (TextUtils.isEmpty(deviceName)) {
deviceName = mContext.getResources().getText(R.string.unknownName).toString();
}
diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
index 041fceaf8d3d..ede2d274563e 100644
--- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
@@ -43,6 +43,7 @@ import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.R;
+import com.android.media.flags.Flags;
import java.util.ArrayList;
import java.util.HashMap;
@@ -283,7 +284,10 @@ class LegacyBluetoothRouteController implements BluetoothRouteController {
newBtRoute.mBtDevice = device;
String routeId = device.getAddress();
- String deviceName = device.getName();
+ String deviceName =
+ Flags.enableUseOfBluetoothDeviceGetAliasForMr2infoGetName()
+ ? device.getAlias()
+ : device.getName();
if (TextUtils.isEmpty(deviceName)) {
deviceName = mContext.getResources().getText(R.string.unknownName).toString();
}
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index bdcec3a33221..82622d9a4ea8 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -54,6 +54,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.SparseSetArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -1030,14 +1031,18 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
private void recomputeComponentVisibility(
ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
final WatchedArraySet<String> protectedBroadcasts;
+ final WatchedArraySet<Integer> forceQueryable;
synchronized (mProtectedBroadcastsLock) {
protectedBroadcasts = mProtectedBroadcasts.snapshot();
}
+ synchronized (mForceQueryableLock) {
+ forceQueryable = mForceQueryable.snapshot();
+ }
final ParallelComputeComponentVisibility computer = new ParallelComputeComponentVisibility(
- existingSettings, mForceQueryable, protectedBroadcasts);
+ existingSettings, forceQueryable, protectedBroadcasts);
+ SparseSetArray<Integer> queriesViaComponent = computer.execute();
synchronized (mQueriesViaComponentLock) {
- mQueriesViaComponent.clear();
- computer.execute(mQueriesViaComponent);
+ mQueriesViaComponent.copyFrom(queriesViaComponent);
}
mQueriesViaComponentRequireRecompute.set(false);
diff --git a/services/core/java/com/android/server/pm/AppsFilterUtils.java b/services/core/java/com/android/server/pm/AppsFilterUtils.java
index f3f64c5010ee..200734b37269 100644
--- a/services/core/java/com/android/server/pm/AppsFilterUtils.java
+++ b/services/core/java/com/android/server/pm/AppsFilterUtils.java
@@ -26,6 +26,7 @@ import android.content.IntentFilter;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
+import android.util.SparseSetArray;
import com.android.internal.pm.pkg.component.ParsedComponent;
import com.android.internal.pm.pkg.component.ParsedIntentInfo;
@@ -37,7 +38,6 @@ import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.utils.WatchedArraySet;
-import com.android.server.utils.WatchedSparseSetArray;
import java.util.ArrayList;
import java.util.List;
@@ -213,7 +213,9 @@ final class AppsFilterUtils {
/**
* Computes component visibility of all packages in parallel from a thread pool.
*/
- void execute(@NonNull WatchedSparseSetArray<Integer> outQueriesViaComponent) {
+ @NonNull
+ SparseSetArray<Integer> execute() {
+ final SparseSetArray<Integer> queriesViaComponent = new SparseSetArray<>();
final ExecutorService pool = ConcurrentUtils.newFixedThreadPool(
MAX_THREADS, ParallelComputeComponentVisibility.class.getSimpleName(),
THREAD_PRIORITY_DEFAULT);
@@ -239,7 +241,7 @@ final class AppsFilterUtils {
try {
final ArraySet<Integer> visibleList = future.get();
if (visibleList.size() != 0) {
- outQueriesViaComponent.addAll(appId, visibleList);
+ queriesViaComponent.addAll(appId, visibleList);
}
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException(e);
@@ -248,6 +250,7 @@ final class AppsFilterUtils {
} finally {
pool.shutdownNow();
}
+ return queriesViaComponent;
}
/**
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 92d469ccbfac..c36c8caf8b19 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -405,9 +405,19 @@ public interface Computer extends PackageDataSnapshot {
boolean isInstallDisabledForPackage(@NonNull String packageName, int uid,
@UserIdInt int userId);
- @Nullable
- List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- @PackageManager.PackageInfoFlagsBits long flags, int callingUid, @UserIdInt int userId);
+ /**
+ * Returns a Pair that contains a list of packages that depend on the target library and the
+ * package library dependency information. The List&lt;VersionedPackage&gt; indicates a list of
+ * packages that depend on the target library, it may be null if no package depends on
+ * the target library. The List&lt;Boolean&gt; indicates whether each VersionedPackage in
+ * the List&lt;VersionedPackage&gt; optionally depends on the target library, where true means
+ * optional and false means required. It may be null if no package depends on
+ * the target library or without dependency information, e.g. uses-static-library.
+ */
+ @NonNull
+ Pair<List<VersionedPackage>, List<Boolean>> getPackagesUsingSharedLibrary(
+ @NonNull SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlagsBits long flags,
+ int callingUid, @UserIdInt int userId);
@Nullable
ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 139c7c0849b0..2ae10051182d 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -895,6 +895,9 @@ public class ComputerEngine implements Computer {
@PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
ParsedActivity a = mComponentResolver.getActivity(component);
+ // Allow to match activities of quarantined packages.
+ flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS;
+
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
@@ -3862,11 +3865,13 @@ public class ComputerEngine implements Computer {
Binder.restoreCallingIdentity(identity);
}
+ var usingSharedLibraryPair =
+ getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId);
SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(),
libInfo.getPackageName(), libInfo.getAllCodePaths(),
libInfo.getName(), libInfo.getLongVersion(),
libInfo.getType(), declaringPackage,
- getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId),
+ usingSharedLibraryPair.first,
(libInfo.getDependencies() == null
? null
: new ArrayList<>(libInfo.getDependencies())),
@@ -3935,13 +3940,15 @@ public class ComputerEngine implements Computer {
return false;
}
+
@Override
- public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- @PackageManager.PackageInfoFlagsBits long flags, int callingUid,
- @UserIdInt int userId) {
+ public Pair<List<VersionedPackage>, List<Boolean>> getPackagesUsingSharedLibrary(
+ @NonNull SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlagsBits long flags,
+ int callingUid, @UserIdInt int userId) {
List<VersionedPackage> versionedPackages = null;
final ArrayMap<String, ? extends PackageStateInternal> packageStates = getPackageStates();
final int packageCount = packageStates.size();
+ List<Boolean> usesLibsOptional = null;
for (int i = 0; i < packageCount; i++) {
PackageStateInternal ps = packageStates.valueAt(i);
if (ps == null) {
@@ -3958,12 +3965,15 @@ public class ComputerEngine implements Computer {
libInfo.isStatic() ? ps.getUsesStaticLibraries() : ps.getUsesSdkLibraries();
final long[] libsVersions = libInfo.isStatic() ? ps.getUsesStaticLibrariesVersions()
: ps.getUsesSdkLibrariesVersionsMajor();
+ final boolean[] libsOptional = libInfo.isSdk()
+ ? ps.getUsesSdkLibrariesOptional() : null;
final int libIdx = ArrayUtils.indexOf(libs, libName);
if (libIdx < 0) {
continue;
}
if (libsVersions[libIdx] != libInfo.getLongVersion()) {
+ // Not expected StaticLib/SdkLib version
continue;
}
if (shouldFilterApplication(ps, callingUid, userId)) {
@@ -3972,6 +3982,9 @@ public class ComputerEngine implements Computer {
if (versionedPackages == null) {
versionedPackages = new ArrayList<>();
}
+ if (usesLibsOptional == null) {
+ usesLibsOptional = new ArrayList<>();
+ }
// If the dependent is a static shared lib, use the public package name
String dependentPackageName = ps.getPackageName();
if (ps.getPkg() != null && ps.getPkg().isStaticSharedLibrary()) {
@@ -3979,6 +3992,7 @@ public class ComputerEngine implements Computer {
}
versionedPackages.add(new VersionedPackage(dependentPackageName,
ps.getVersionCode()));
+ usesLibsOptional.add(libsOptional != null && libsOptional[libIdx]);
} else if (ps.getPkg() != null) {
if (ArrayUtils.contains(ps.getPkg().getUsesLibraries(), libName)
|| ArrayUtils.contains(ps.getPkg().getUsesOptionalLibraries(), libName)) {
@@ -3994,7 +4008,7 @@ public class ComputerEngine implements Computer {
}
}
- return versionedPackages;
+ return new Pair<>(versionedPackages, usesLibsOptional);
}
@Nullable
@@ -4053,13 +4067,14 @@ public class ComputerEngine implements Computer {
Binder.restoreCallingIdentity(identity);
}
+ var usingSharedLibraryPair =
+ getPackagesUsingSharedLibrary(libraryInfo, flags, callingUid, userId);
SharedLibraryInfo resultLibraryInfo = new SharedLibraryInfo(
libraryInfo.getPath(), libraryInfo.getPackageName(),
libraryInfo.getAllCodePaths(), libraryInfo.getName(),
libraryInfo.getLongVersion(), libraryInfo.getType(),
libraryInfo.getDeclaringPackage(),
- getPackagesUsingSharedLibrary(
- libraryInfo, flags, callingUid, userId),
+ usingSharedLibraryPair.first,
libraryInfo.getDependencies() == null
? null : new ArrayList<>(libraryInfo.getDependencies()),
libraryInfo.isNative());
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 80e6c833dfb2..93836266d1f4 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -18,7 +18,6 @@ package com.android.server.pm;
import static android.Manifest.permission.CONTROL_KEYGUARD;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
-import static android.content.pm.Flags.sdkLibIndependence;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
@@ -40,6 +39,7 @@ import android.app.ApplicationExitInfo;
import android.app.ApplicationPackageManager;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
@@ -72,8 +72,6 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import dalvik.system.VMRuntime;
-import java.util.List;
-
/**
* Deletes a package. Uninstall if installed, or at least deletes the base directory if it's called
* from a failed installation. Fixes user state after deletion.
@@ -181,16 +179,36 @@ final class DeletePackageHelper {
}
if (libraryInfo != null) {
+ boolean flagSdkLibIndependence = Flags.sdkLibIndependence();
for (int currUserId : allUsers) {
if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
continue;
}
- List<VersionedPackage> libClientPackages =
- computer.getPackagesUsingSharedLibrary(libraryInfo,
- MATCH_KNOWN_PACKAGES, Process.SYSTEM_UID, currUserId);
- boolean allowSdkLibIndependence =
- (pkg.getSdkLibraryName() != null) && sdkLibIndependence();
- if (!ArrayUtils.isEmpty(libClientPackages) && !allowSdkLibIndependence) {
+ var libClientPackagesPair = computer.getPackagesUsingSharedLibrary(
+ libraryInfo, MATCH_KNOWN_PACKAGES, Process.SYSTEM_UID, currUserId);
+ var libClientPackages = libClientPackagesPair.first;
+ var libClientOptional = libClientPackagesPair.second;
+ // We by default don't allow removing a package if the host lib is still be
+ // used by other client packages
+ boolean allowLibIndependence = false;
+ // Only when the sdkLibIndependence flag is enabled we will respect the
+ // "optional" attr in uses-sdk-library. Only allow to remove sdk-lib host
+ // package if no required clients depend on it
+ if ((pkg.getSdkLibraryName() != null)
+ && !ArrayUtils.isEmpty(libClientPackages)
+ && !ArrayUtils.isEmpty(libClientOptional)
+ && (libClientPackages.size() == libClientOptional.size())
+ && flagSdkLibIndependence) {
+ allowLibIndependence = true;
+ for (int i = 0; i < libClientPackages.size(); i++) {
+ boolean usesSdkLibOptional = libClientOptional.get(i);
+ if (!usesSdkLibOptional) {
+ allowLibIndependence = false;
+ break;
+ }
+ }
+ }
+ if (!ArrayUtils.isEmpty(libClientPackages) && !allowLibIndependence) {
Slog.w(TAG, "Not removing package " + pkg.getManifestPackageName()
+ " hosting lib " + libraryInfo.getName() + " version "
+ libraryInfo.getLongVersion() + " used by " + libClientPackages
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f27c462700be..2880f84c6445 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7113,9 +7113,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (info == null) {
continue;
}
- final List<VersionedPackage> dependents =
- computer.getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID,
- userId);
+ var usingSharedLibraryPair = computer.getPackagesUsingSharedLibrary(info, 0,
+ Process.SYSTEM_UID, userId);
+ final List<VersionedPackage> dependents = usingSharedLibraryPair.first;
if (dependents == null) {
continue;
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 174df44c4263..7d0a1f6afe1d 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -123,11 +123,15 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
@Nullable
private Map<String, Set<String>> mimeGroups;
+ // TODO(b/314036181): encapsulate all these fields for usesSdk, instead of having three
+ // separate arrays.
@Nullable
private String[] usesSdkLibraries;
@Nullable
private long[] usesSdkLibrariesVersionsMajor;
+ @Nullable
+ private boolean[] usesSdkLibrariesOptional;
@Nullable
private String[] usesStaticLibraries;
@@ -701,6 +705,9 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
usesSdkLibrariesVersionsMajor = other.usesSdkLibrariesVersionsMajor != null
? Arrays.copyOf(other.usesSdkLibrariesVersionsMajor,
other.usesSdkLibrariesVersionsMajor.length) : null;
+ usesSdkLibrariesOptional = other.usesSdkLibrariesOptional != null
+ ? Arrays.copyOf(other.usesSdkLibrariesOptional,
+ other.usesSdkLibrariesOptional.length) : null;
usesStaticLibraries = other.usesStaticLibraries != null
? Arrays.copyOf(other.usesStaticLibraries,
@@ -1344,6 +1351,12 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
@NonNull
@Override
+ public boolean[] getUsesSdkLibrariesOptional() {
+ return usesSdkLibrariesOptional == null ? EmptyArray.BOOLEAN : usesSdkLibrariesOptional;
+ }
+
+ @NonNull
+ @Override
public String[] getUsesStaticLibraries() {
return usesStaticLibraries == null ? EmptyArray.STRING : usesStaticLibraries;
}
@@ -1444,6 +1457,12 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
return this;
}
+ public PackageSetting setUsesSdkLibrariesOptional(boolean[] usesSdkLibrariesOptional) {
+ this.usesSdkLibrariesOptional = usesSdkLibrariesOptional;
+ onChanged();
+ return this;
+ }
+
public PackageSetting setUsesStaticLibraries(String[] usesStaticLibraries) {
this.usesStaticLibraries = usesStaticLibraries;
onChanged();
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 53b84e66840b..31a63e07b66c 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -218,7 +218,8 @@ final class ScanPackageUtils {
parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
true /*allowInstall*/, instantApp, virtualPreload, isStoppedSystemApp,
UserManagerService.getInstance(), usesSdkLibraries,
- parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
+ parsedPackage.getUsesSdkLibrariesVersionsMajor(),
+ parsedPackage.getUsesSdkLibrariesOptional(), usesStaticLibraries,
parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
newDomainSetId,
parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash());
@@ -240,6 +241,7 @@ final class ScanPackageUtils {
PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
UserManagerService.getInstance(),
usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
+ parsedPackage.getUsesSdkLibrariesOptional(),
usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
parsedPackage.getMimeGroups(), newDomainSetId,
parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash());
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 2cbf714792f7..75d88da059b5 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -106,6 +106,7 @@ import com.android.server.LocalServices;
import com.android.server.backup.PreferredActivityBackupHelper;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.permission.LegacyPermissionState;
@@ -339,6 +340,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
private static final String ATTR_DISTRACTION_FLAGS = "distraction_flags";
private static final String ATTR_SUSPENDED = "suspended";
private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
+
+ private static final String ATTR_OPTIONAL = "optional";
/**
* @deprecated Legacy attribute, kept only for upgrading from P builds.
*/
@@ -942,6 +945,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
ret.setLongVersionCode(p.getVersionCode());
ret.setUsesSdkLibraries(p.getUsesSdkLibraries());
ret.setUsesSdkLibrariesVersionsMajor(p.getUsesSdkLibrariesVersionsMajor());
+ ret.setUsesSdkLibrariesOptional(p.getUsesSdkLibrariesOptional());
ret.setUsesStaticLibraries(p.getUsesStaticLibraries());
ret.setUsesStaticLibrariesVersions(p.getUsesStaticLibrariesVersions());
ret.setMimeGroups(p.getMimeGroups());
@@ -1061,9 +1065,9 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
UserHandle installUser, boolean allowInstall, boolean instantApp,
boolean virtualPreload, boolean isStoppedSystemApp, UserManagerService userManager,
String[] usesSdkLibraries, long[] usesSdkLibrariesVersions,
- String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
- Set<String> mimeGroupNames, @NonNull UUID domainSetId,
- int targetSdkVersion, byte[] restrictUpdatedHash) {
+ boolean[] usesSdkLibrariesOptional, String[] usesStaticLibraries,
+ long[] usesStaticLibrariesVersions, Set<String> mimeGroupNames,
+ @NonNull UUID domainSetId, int targetSdkVersion, byte[] restrictUpdatedHash) {
final PackageSetting pkgSetting;
if (originalPkg != null) {
if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
@@ -1079,6 +1083,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
.setLongVersionCode(versionCode)
.setUsesSdkLibraries(usesSdkLibraries)
.setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions)
+ .setUsesSdkLibrariesOptional(usesSdkLibrariesOptional)
.setUsesStaticLibraries(usesStaticLibraries)
.setUsesStaticLibrariesVersions(usesStaticLibrariesVersions)
// Update new package state.
@@ -1096,6 +1101,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
pkgPrivateFlags, domainSetId)
.setUsesSdkLibraries(usesSdkLibraries)
.setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions)
+ .setUsesSdkLibrariesOptional(usesSdkLibrariesOptional)
.setUsesStaticLibraries(usesStaticLibraries)
.setUsesStaticLibrariesVersions(usesStaticLibrariesVersions)
.setLegacyNativeLibraryPath(legacyNativeLibraryPath)
@@ -1218,6 +1224,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
@Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
int pkgPrivateFlags, @NonNull UserManagerService userManager,
@Nullable String[] usesSdkLibraries, @Nullable long[] usesSdkLibrariesVersions,
+ @Nullable boolean[] usesSdkLibrariesOptional,
@Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
@Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId,
int targetSdkVersion, byte[] restrictUpdatedHash)
@@ -1277,12 +1284,17 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
.setRestrictUpdateHash(restrictUpdatedHash);
// Update SDK library dependencies if needed.
if (usesSdkLibraries != null && usesSdkLibrariesVersions != null
- && usesSdkLibraries.length == usesSdkLibrariesVersions.length) {
+ && usesSdkLibrariesOptional != null
+ && usesSdkLibraries.length == usesSdkLibrariesVersions.length
+ && usesSdkLibraries.length == usesSdkLibrariesOptional.length) {
pkgSetting.setUsesSdkLibraries(usesSdkLibraries)
- .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions);
+ .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions)
+ .setUsesSdkLibrariesOptional(usesSdkLibrariesOptional);
} else {
pkgSetting.setUsesSdkLibraries(null)
- .setUsesSdkLibrariesVersionsMajor(null);
+ .setUsesSdkLibrariesVersionsMajor(null)
+ .setUsesSdkLibrariesOptional(null);
+
}
// Update static shared library dependencies if needed.
@@ -2537,12 +2549,15 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
throws IOException, XmlPullParserException {
String libName = parser.getAttributeValue(null, ATTR_NAME);
long libVersion = parser.getAttributeLong(null, ATTR_VERSION, -1);
+ boolean optional = parser.getAttributeBoolean(null, ATTR_OPTIONAL, true);
if (libName != null && libVersion >= 0) {
outPs.setUsesSdkLibraries(ArrayUtils.appendElement(String.class,
outPs.getUsesSdkLibraries(), libName));
outPs.setUsesSdkLibrariesVersionsMajor(ArrayUtils.appendLong(
outPs.getUsesSdkLibrariesVersionsMajor(), libVersion));
+ outPs.setUsesSdkLibrariesOptional(PackageImpl.appendBoolean(
+ outPs.getUsesSdkLibrariesOptional(), optional));
}
XmlUtils.skipCurrentTag(parser);
@@ -2564,7 +2579,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
void writeUsesSdkLibLPw(TypedXmlSerializer serializer, String[] usesSdkLibraries,
- long[] usesSdkLibraryVersions) throws IOException {
+ long[] usesSdkLibraryVersions, boolean[] usesSdkLibrariesOptional) throws IOException {
if (ArrayUtils.isEmpty(usesSdkLibraries) || ArrayUtils.isEmpty(usesSdkLibraryVersions)
|| usesSdkLibraries.length != usesSdkLibraryVersions.length) {
return;
@@ -2573,9 +2588,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
for (int i = 0; i < libCount; i++) {
final String libName = usesSdkLibraries[i];
final long libVersion = usesSdkLibraryVersions[i];
+ boolean libOptional = usesSdkLibrariesOptional[i];
serializer.startTag(null, TAG_USES_SDK_LIB);
serializer.attribute(null, ATTR_NAME, libName);
serializer.attributeLong(null, ATTR_VERSION, libVersion);
+ serializer.attributeBoolean(null, ATTR_OPTIONAL, libOptional);
serializer.endTag(null, TAG_USES_SDK_LIB);
}
}
@@ -3106,7 +3123,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
- pkg.getUsesSdkLibrariesVersionsMajor());
+ pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesOptional());
+
writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions());
@@ -3206,7 +3224,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
- pkg.getUsesSdkLibrariesVersionsMajor());
+ pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesOptional());
writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions());
@@ -5091,12 +5109,14 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
List<String> usesSdkLibraries = pkg.getUsesSdkLibraries();
long[] usesSdkLibrariesVersionsMajor = pkg.getUsesSdkLibrariesVersionsMajor();
+ boolean[] usesSdkLibrariesOptional = pkg.getUsesSdkLibrariesOptional();
if (usesSdkLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesSdkLibraries:");
for (int i = 0, size = usesSdkLibraries.size(); i < size; ++i) {
pw.print(prefix); pw.print(" ");
pw.print(usesSdkLibraries.get(i)); pw.print(" version:");
- pw.println(usesSdkLibrariesVersionsMajor[i]);
+ pw.println(usesSdkLibrariesVersionsMajor[i]); pw.print(" optional:");
+ pw.println(usesSdkLibrariesOptional[i]);
}
}
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 9384c13e583b..94495bf462f2 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -16,7 +16,6 @@
package com.android.server.pm;
-import static android.content.pm.Flags.sdkLibIndependence;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST;
@@ -28,6 +27,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
@@ -82,6 +82,8 @@ import java.util.function.BiConsumer;
public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable, Snappable {
private static final boolean DEBUG_SHARED_LIBRARIES = false;
+ private static final String LIBRARY_TYPE_SDK = "sdk";
+
/**
* Apps targeting Android S and above need to declare dependencies to the public native
* shared libraries that are defined by the device maker using {@code uses-native-library} tag
@@ -798,8 +800,9 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
// Remove the shared library overlays from its dependent packages.
for (int currentUserId : mPm.mUserManager.getUserIds()) {
- final List<VersionedPackage> dependents = snapshot.getPackagesUsingSharedLibrary(
- libraryInfo, 0, Process.SYSTEM_UID, currentUserId);
+ var usingSharedLibraryPair = snapshot.getPackagesUsingSharedLibrary(libraryInfo, 0,
+ Process.SYSTEM_UID, currentUserId);
+ final List<VersionedPackage> dependents = usingSharedLibraryPair.first;
if (dependents == null) {
continue;
}
@@ -921,42 +924,43 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
// duplicates.
ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
if (!pkg.getUsesLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null, null,
pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
availablePackages, newLibraries);
}
if (!pkg.getUsesStaticLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
- pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, newLibraries);
+ null, pkg.getPackageName(), "static shared", true,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries);
}
if (!pkg.getUsesOptionalLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
- pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
+ null, pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, newLibraries);
}
if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
pkg.getPackageName(), pkg.getTargetSdkVersion())) {
if (!pkg.getUsesNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
- null, pkg.getPackageName(), "native shared", true,
+ null, null, pkg.getPackageName(), "native shared", true,
pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
newLibraries);
}
if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
- null, null, pkg.getPackageName(), "native shared", false,
+ null, null, null, pkg.getPackageName(), "native shared", false,
pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
newLibraries);
}
}
if (!pkg.getUsesSdkLibraries().isEmpty()) {
// Allow installation even if sdk-library dependency doesn't exist
- boolean required = !sdkLibIndependence();
+ boolean required = !Flags.sdkLibIndependence();
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
- pkg.getPackageName(), "sdk", required, pkg.getTargetSdkVersion(),
+ pkg.getUsesSdkLibrariesOptional(),
+ pkg.getPackageName(), LIBRARY_TYPE_SDK, required, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, newLibraries);
}
return usesLibraryInfos;
@@ -965,6 +969,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
private ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
@NonNull List<String> requestedLibraries,
@Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
+ @Nullable boolean[] libsOptional,
@NonNull String packageName, @NonNull String libraryType, boolean required,
int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
@NonNull final Map<String, AndroidPackage> availablePackages,
@@ -981,7 +986,10 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
libName, libVersion, mSharedLibraries, newLibraries);
}
if (libraryInfo == null) {
- if (required) {
+ // Only allow app be installed if the app specifies the sdk-library dependency is
+ // optional
+ if (required || (LIBRARY_TYPE_SDK.equals(libraryType) && (libsOptional != null
+ && !libsOptional[i]))) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + packageName + " requires unavailable " + libraryType
+ " library " + libName + "; failing!");
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 71f6c0d507d4..fe8c12c8e232 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -33,7 +33,6 @@ import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
@@ -504,10 +503,6 @@ public final class SuspendPackageHelper {
final String requiredPermissionControllerPackage =
getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
userId);
- final AppOpsManager appOpsManager = mInjector.getSystemService(AppOpsManager.class);
- final boolean isSystemExemptFlagEnabled = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
- SYSTEM_EXEMPT_FROM_SUSPENSION, /* defaultValue= */ true);
for (int i = 0; i < packageNames.length; i++) {
canSuspend[i] = false;
final String packageName = packageNames[i];
@@ -581,9 +576,7 @@ public final class SuspendPackageHelper {
+ pkg.getStaticSharedLibraryName());
continue;
}
- if (isSystemExemptFlagEnabled && appOpsManager.checkOpNoThrow(
- AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION, uid, packageName)
- == AppOpsManager.MODE_ALLOWED) {
+ if (exemptFromSuspensionByAppOp(uid, packageName)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": has OP_SYSTEM_EXEMPT_FROM_SUSPENSION set");
continue;
@@ -601,6 +594,13 @@ public final class SuspendPackageHelper {
return canSuspend;
}
+ private boolean exemptFromSuspensionByAppOp(int uid, String packageName) {
+ final AppOpsManager appOpsManager = mInjector.getSystemService(AppOpsManager.class);
+ return appOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION, uid, packageName)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
/**
* Suspends packages on behalf of an admin.
*
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b53a21c9aa1c..a7b52f4e7a58 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1519,7 +1519,7 @@ public class UserManagerService extends IUserManager.Stub {
try {
if (enableQuietMode) {
- ActivityManager.getService().stopUser(userId, /* force= */ true, null);
+ stopUserForQuietMode(userId);
LocalServices.getService(ActivityManagerInternal.class)
.killForegroundAppsForUser(userId);
} else {
@@ -1547,6 +1547,18 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ private void stopUserForQuietMode(int userId) throws RemoteException {
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
+ // Allow delayed locking since some profile types want to be able to unlock again via
+ // biometrics.
+ ActivityManager.getService()
+ .stopUserWithDelayedLocking(userId, /* force= */ true, null);
+ return;
+ }
+ ActivityManager.getService().stopUser(userId, /* force= */ true, null);
+ }
+
private void logQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode,
@Nullable String callingPackage) {
Slogf.i(LOG_TAG,
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 4ef8cb780734..7386301bdd6e 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -160,7 +160,9 @@ public final class UserTypeFactory {
UserProperties.SHOW_IN_SHARING_SURFACES_WITH_PARENT)
.setMediaSharedWithParent(true)
.setCredentialShareableWithParent(true)
- .setDeleteAppWithParent(true));
+ .setDeleteAppWithParent(true)
+ .setCrossProfileContentSharingStrategy(UserProperties
+ .CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT));
}
/**
@@ -309,6 +311,7 @@ public final class UserTypeFactory {
.setStartWithParent(true)
.setCredentialShareableWithParent(true)
.setAuthAlwaysRequiredToDisableQuietMode(true)
+ .setAllowStoppingUserWithDelayedLocking(true)
.setMediaSharedWithParent(false)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
.setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
@@ -318,7 +321,9 @@ public final class UserTypeFactory {
UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
.setCrossProfileIntentFilterAccessControl(
UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
- .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT));
+ .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
+ .setCrossProfileContentSharingStrategy(
+ UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT));
}
/**
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index d64201893400..b23dbee5e973 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -266,17 +266,20 @@ public class PackageInfoUtils {
if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
final int N = pkg.getActivities().size();
if (N > 0) {
+ // Allow to match activities of quarantined packages.
+ long aflags = flags | PackageManager.MATCH_QUARANTINED_COMPONENTS;
+
int num = 0;
final ActivityInfo[] res = new ActivityInfo[N];
for (int i = 0; i < N; i++) {
final ParsedActivity a = pkg.getActivities().get(i);
if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a,
- flags)) {
+ aflags)) {
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
a.getName())) {
continue;
}
- res[num++] = generateActivityInfo(pkg, a, flags, state,
+ res[num++] = generateActivityInfo(pkg, a, aflags, state,
applicationInfo, userId, pkgSetting);
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 85d95eab2958..da58d47edbfe 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -267,6 +267,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@Nullable
private String[][] usesSdkLibrariesCertDigests;
@Nullable
+ private boolean[] usesSdkLibrariesOptional;
+ @Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String sharedUserId;
private int sharedUserLabel;
@@ -718,16 +720,33 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@Override
public PackageImpl addUsesSdkLibrary(String libraryName, long versionMajor,
- String[] certSha256Digests) {
+ String[] certSha256Digests, boolean usesSdkLibrariesOptional) {
this.usesSdkLibraries = CollectionUtils.add(this.usesSdkLibraries,
TextUtils.safeIntern(libraryName));
this.usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
this.usesSdkLibrariesVersionsMajor, versionMajor, true);
this.usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
this.usesSdkLibrariesCertDigests, certSha256Digests, true);
+ this.usesSdkLibrariesOptional = appendBoolean(this.usesSdkLibrariesOptional,
+ usesSdkLibrariesOptional);
return this;
}
+ /**
+ * Adds value to given array if not already present, providing set-like
+ * behavior.
+ */
+ public static boolean[] appendBoolean(@Nullable boolean[] cur, boolean val) {
+ if (cur == null) {
+ return new boolean[] { val };
+ }
+ final int N = cur.length;
+ boolean[] ret = new boolean[N + 1];
+ System.arraycopy(cur, 0, ret, 0, N);
+ ret[N] = val;
+ return ret;
+ }
+
@Override
public PackageImpl addUsesStaticLibrary(String libraryName, long version,
String[] certSha256Digests) {
@@ -1468,6 +1487,12 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@Override
public long[] getUsesSdkLibrariesVersionsMajor() { return usesSdkLibrariesVersionsMajor; }
+ @Nullable
+ @Override
+ public boolean[] getUsesSdkLibrariesOptional() {
+ return usesSdkLibrariesOptional;
+ }
+
@NonNull
@Override
public List<String> getUsesStaticLibraries() {
@@ -3126,6 +3151,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
dest.writeStringArray(this.usesSdkLibrariesCertDigests[index]);
}
}
+ dest.writeBooleanArray(this.usesSdkLibrariesOptional);
sForInternedString.parcel(this.sharedUserId, dest, flags);
dest.writeInt(this.sharedUserLabel);
@@ -3278,6 +3304,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
}
}
+ this.usesSdkLibrariesOptional = in.createBooleanArray();
this.sharedUserId = sForInternedString.unparcel(in);
this.sharedUserLabel = in.readInt();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 10b59c7230f6..a7ae4ebcb2eb 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -322,6 +322,14 @@ public interface PackageState {
long[] getUsesSdkLibrariesVersionsMajor();
/**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary_optional
+ * @hide
+ */
+ @Immutable.Ignore
+ @NonNull
+ boolean[] getUsesSdkLibrariesOptional();
+
+ /**
* @see R.styleable#AndroidManifestUsesStaticLibrary
* @hide
*/
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 722350a0d7fb..aa0fb2734382 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -2598,6 +2598,8 @@ public class ParsingPackageUtils {
R.styleable.AndroidManifestUsesSdkLibrary_versionMajor, -1);
String certSha256Digest = sa.getNonResourceString(R.styleable
.AndroidManifestUsesSdkLibrary_certDigest);
+ boolean optional =
+ sa.getBoolean(R.styleable.AndroidManifestUsesSdkLibrary_optional, false);
// Since an APK providing a static shared lib can only provide the lib - fail if
// malformed
@@ -2641,7 +2643,8 @@ public class ParsingPackageUtils {
System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
1, additionalCertSha256Digests.length);
- return input.success(pkg.addUsesSdkLibrary(lname, versionMajor, certSha256Digests));
+ return input.success(
+ pkg.addUsesSdkLibrary(lname, versionMajor, certSha256Digests, optional));
} finally {
sa.recycle();
}
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 99653ae1cd72..24d7acd772c1 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -759,6 +759,36 @@ public class ThermalManagerService extends SystemService {
case "NPU":
type = Temperature.TYPE_NPU;
break;
+ case "TPU":
+ type = Temperature.TYPE_TPU;
+ break;
+ case "DISPLAY":
+ type = Temperature.TYPE_DISPLAY;
+ break;
+ case "MODEM":
+ type = Temperature.TYPE_MODEM;
+ break;
+ case "SOC":
+ type = Temperature.TYPE_SOC;
+ break;
+ case "WIFI":
+ type = Temperature.TYPE_WIFI;
+ break;
+ case "CAMERA":
+ type = Temperature.TYPE_CAMERA;
+ break;
+ case "FLASHLIGHT":
+ type = Temperature.TYPE_FLASHLIGHT;
+ break;
+ case "SPEAKER":
+ type = Temperature.TYPE_SPEAKER;
+ break;
+ case "AMBIENT":
+ type = Temperature.TYPE_AMBIENT;
+ break;
+ case "POGO":
+ type = Temperature.TYPE_POGO;
+ break;
default:
pw.println("Invalid temperature type: " + typeName);
return -1;
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index 4442845f83b2..1af127175f80 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -78,7 +78,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
private boolean mIsPerUidTimeInStateSupported;
private PowerStatsInternal mPowerStatsInternal;
- private int[] mCpuEnergyConsumerIds;
+ private int[] mCpuEnergyConsumerIds = new int[0];
private PowerStats.Descriptor mPowerStatsDescriptor;
// Reusable instance
private PowerStats mCpuPowerStats;
@@ -286,8 +286,6 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
if (mPowerStatsInternal != null) {
readCpuEnergyConsumerIds();
- } else {
- mCpuEnergyConsumerIds = new int[0];
}
int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount();
@@ -320,7 +318,6 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
private void readCpuEnergyConsumerIds() {
EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo();
if (energyConsumerInfo == null) {
- mCpuEnergyConsumerIds = new int[0];
return;
}
diff --git a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
index 0386e66aaf3d..b8850afdf3ad 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
@@ -142,6 +142,20 @@ public class WatchedSparseSetArray<T> extends WatchableImpl implements Snappable
return (T) mStorage.valueAt(intIndex, valueIndex);
}
+ /**
+ * Copy from another SparseSetArray.
+ */
+ public void copyFrom(@NonNull SparseSetArray<T> c) {
+ clear();
+ final int end = c.size();
+ for (int i = 0; i < end; i++) {
+ final int key = c.keyAt(i);
+ final ArraySet<T> set = c.get(key);
+ mStorage.addAll(key, set);
+ }
+ onChanged();
+ }
+
@NonNull
@Override
public Object snapshot() {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e5794a1f7efd..dfb2a5fcf9fa 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1121,6 +1121,7 @@ class ActivityStarter {
callerApp,
request.originatingPendingIntent,
request.forcedBalByPiSender,
+ resultRecord,
intent,
checkedOptions);
request.logMessage.append(" (").append(balVerdict).append(")");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a04513f407b8..73edb4bed6ca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -103,6 +103,7 @@ import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORD
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
@@ -118,6 +119,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -2242,7 +2244,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final BackgroundActivityStartController balController =
mTaskSupervisor.getBackgroundActivityLaunchController();
- if (balController.shouldAbortBackgroundActivityStart(
+ final BalVerdict balVerdict = balController.checkBackgroundActivityStart(
callingUid,
callingPid,
callingPackage,
@@ -2252,10 +2254,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
null,
BackgroundStartPrivileges.NONE,
null,
- null)) {
- if (!isBackgroundActivityStartsEnabled()) {
- return;
- }
+ null,
+ null);
+ if (balVerdict.blocks() && !isBackgroundActivityStartsEnabled()) {
+ Slog.w(TAG, "moveTaskToFront blocked: " + balVerdict);
+ return;
+ }
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "moveTaskToFront allowed: " + balVerdict);
}
try {
final Task task = mRootWindowContainer.anyTaskForId(taskId);
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 761b0a8f0b39..50de0b08f3b0 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
+import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
@@ -31,6 +33,7 @@ import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.Slog;
/**
* An implementation of IAppTask, that allows an app to manage its own tasks via
@@ -123,7 +126,7 @@ class AppTaskImpl extends IAppTask.Stub {
}
final BackgroundActivityStartController balController =
mService.mTaskSupervisor.getBackgroundActivityLaunchController();
- if (balController.shouldAbortBackgroundActivityStart(
+ BalVerdict balVerdict = balController.checkBackgroundActivityStart(
callingUid,
callingPid,
callingPackage,
@@ -133,10 +136,14 @@ class AppTaskImpl extends IAppTask.Stub {
null,
BackgroundStartPrivileges.NONE,
null,
- null)) {
- if (!mService.isBackgroundActivityStartsEnabled()) {
- return;
- }
+ null,
+ null);
+ if (balVerdict.blocks() && !mService.isBackgroundActivityStartsEnabled()) {
+ Slog.w(TAG, "moveTaskToFront blocked: : " + balVerdict);
+ return;
+ }
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "moveTaskToFront allowed: " + balVerdict);
}
}
mService.mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId,
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 07dac5481b79..92665af1075b 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -205,27 +205,6 @@ public class BackgroundActivityStartController {
return activity != null && packageName.equals(activity.getPackageName());
}
- /**
- * @see #checkBackgroundActivityStart(int, int, String, int, int, WindowProcessController,
- * PendingIntentRecord, BackgroundStartPrivileges, Intent, ActivityOptions)
- */
- boolean shouldAbortBackgroundActivityStart(
- int callingUid,
- int callingPid,
- final String callingPackage,
- int realCallingUid,
- int realCallingPid,
- WindowProcessController callerApp,
- PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender,
- Intent intent,
- ActivityOptions checkedOptions) {
- return checkBackgroundActivityStart(callingUid, callingPid, callingPackage,
- realCallingUid, realCallingPid,
- callerApp, originatingPendingIntent,
- forcedBalByPiSender, intent, checkedOptions).blocks();
- }
-
private class BalState {
private final String mCallingPackage;
@@ -255,6 +234,7 @@ public class BackgroundActivityStartController {
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
BackgroundStartPrivileges forcedBalByPiSender,
+ ActivityRecord resultRecord,
Intent intent,
ActivityOptions checkedOptions) {
this.mCallingPackage = callingPackage;
@@ -267,7 +247,9 @@ public class BackgroundActivityStartController {
mOriginatingPendingIntent = originatingPendingIntent;
mIntent = intent;
mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
- if (originatingPendingIntent == null) {
+ if (originatingPendingIntent == null // not a PendingIntent
+ || resultRecord != null // sent for result
+ ) {
// grant BAL privileges unless explicitly opted out
mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator =
checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
@@ -535,6 +517,7 @@ public class BackgroundActivityStartController {
* @param forcedBalByPiSender If set to allow, the
* PendingIntent's sender will try to force allow background activity starts.
* This is only possible if the sender of the PendingIntent is a system process.
+ * @param resultRecord If not null, this indicates that the caller expects a result.
* @param intent Intent that should be started.
* @param checkedOptions ActivityOptions to allow specific opt-ins/opt outs.
*
@@ -550,6 +533,7 @@ public class BackgroundActivityStartController {
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
BackgroundStartPrivileges forcedBalByPiSender,
+ ActivityRecord resultRecord,
Intent intent,
ActivityOptions checkedOptions) {
@@ -560,7 +544,7 @@ public class BackgroundActivityStartController {
BalState state = new BalState(callingUid, callingPid, callingPackage,
realCallingUid, realCallingPid, callerApp, originatingPendingIntent,
- forcedBalByPiSender, intent, checkedOptions);
+ forcedBalByPiSender, resultRecord, intent, checkedOptions);
// In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
// visible window.
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
new file mode 100644
index 000000000000..975fdc0ade5d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS;
+import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
+import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS;
+import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.DisplayInfo;
+import android.window.DisplayAreaInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.wm.utils.DisplayInfoOverrides.DisplayInfoFieldsUpdater;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A DisplayUpdater that could defer and queue display updates coming from DisplayManager to
+ * WindowManager. It allows to defer pending display updates if WindowManager is currently not
+ * ready to apply them.
+ * For example, this might happen if there is a Shell transition running and physical display
+ * changed. We can't immediately apply the display updates because we want to start a separate
+ * display change transition. In this case, we will queue all display updates until the current
+ * transition's collection finishes and then apply them afterwards.
+ */
+public class DeferredDisplayUpdater implements DisplayUpdater {
+
+ /**
+ * List of fields that could be deferred before applying to DisplayContent.
+ * This should be kept in sync with {@link DeferredDisplayUpdater#calculateDisplayInfoDiff}
+ */
+ @VisibleForTesting
+ static final DisplayInfoFieldsUpdater DEFERRABLE_FIELDS = (out, override) -> {
+ // Treat unique id and address change as WM-specific display change as we re-query display
+ // settings and parameters based on it which could cause window changes
+ out.uniqueId = override.uniqueId;
+ out.address = override.address;
+
+ // Also apply WM-override fields, since they might produce differences in window hierarchy
+ WM_OVERRIDE_FIELDS.setFields(out, override);
+ };
+
+ private final DisplayContent mDisplayContent;
+
+ @NonNull
+ private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo();
+
+ /**
+ * The last known display parameters from DisplayManager, some WM-specific fields in this object
+ * might not be applied to the DisplayContent yet
+ */
+ @Nullable
+ private DisplayInfo mLastDisplayInfo;
+
+ /**
+ * The last DisplayInfo that was applied to DisplayContent, only WM-specific parameters must be
+ * used from this object. This object is used to store old values of DisplayInfo while these
+ * fields are pending to be applied to DisplayContent.
+ */
+ @Nullable
+ private DisplayInfo mLastWmDisplayInfo;
+
+ @NonNull
+ private final DisplayInfo mOutputDisplayInfo = new DisplayInfo();
+
+ public DeferredDisplayUpdater(@NonNull DisplayContent displayContent) {
+ mDisplayContent = displayContent;
+ mNonOverrideDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
+ }
+
+ /**
+ * Reads the latest display parameters from the display manager and returns them in a callback.
+ * If there are pending display updates, it will wait for them to finish first and only then it
+ * will call the callback with the latest display parameters.
+ *
+ * @param finishCallback is called when all pending display updates are finished
+ */
+ @Override
+ public void updateDisplayInfo(@NonNull Runnable finishCallback) {
+ // Get the latest display parameters from the DisplayManager
+ final DisplayInfo displayInfo = getCurrentDisplayInfo();
+
+ final int displayInfoDiff = calculateDisplayInfoDiff(mLastDisplayInfo, displayInfo);
+ final boolean physicalDisplayUpdated = isPhysicalDisplayUpdated(mLastDisplayInfo,
+ displayInfo);
+
+ mLastDisplayInfo = displayInfo;
+
+ // Apply whole display info immediately as is if either:
+ // * it is the first display update
+ // * shell transitions are disabled or temporary unavailable
+ if (displayInfoDiff == DIFF_EVERYTHING
+ || !mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
+ ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS,
+ "DeferredDisplayUpdater: applying DisplayInfo immediately");
+
+ mLastWmDisplayInfo = displayInfo;
+ applyLatestDisplayInfo();
+ finishCallback.run();
+ return;
+ }
+
+ // If there are non WM-specific display info changes, apply only these fields immediately
+ if ((displayInfoDiff & DIFF_NOT_WM_DEFERRABLE) > 0) {
+ ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS,
+ "DeferredDisplayUpdater: partially applying DisplayInfo immediately");
+ applyLatestDisplayInfo();
+ }
+
+ // If there are WM-specific display info changes, apply them through a Shell transition
+ if ((displayInfoDiff & DIFF_WM_DEFERRABLE) > 0) {
+ ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS,
+ "DeferredDisplayUpdater: deferring DisplayInfo update");
+
+ requestDisplayChangeTransition(physicalDisplayUpdated, () -> {
+ // Apply deferrable fields to DisplayContent only when the transition
+ // starts collecting, non-deferrable fields are ignored in mLastWmDisplayInfo
+ mLastWmDisplayInfo = displayInfo;
+ applyLatestDisplayInfo();
+ finishCallback.run();
+ });
+ } else {
+ // There are no WM-specific updates, so we can immediately notify that all display
+ // info changes are applied
+ finishCallback.run();
+ }
+ }
+
+ /**
+ * Requests a display change Shell transition
+ *
+ * @param physicalDisplayUpdated if true also starts remote display change
+ * @param onStartCollect called when the Shell transition starts collecting
+ */
+ private void requestDisplayChangeTransition(boolean physicalDisplayUpdated,
+ @NonNull Runnable onStartCollect) {
+
+ final Transition transition = new Transition(TRANSIT_CHANGE, /* flags= */ 0,
+ mDisplayContent.mTransitionController,
+ mDisplayContent.mTransitionController.mSyncEngine);
+
+ mDisplayContent.mAtmService.startPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
+
+ mDisplayContent.mTransitionController.startCollectOrQueue(transition, deferred -> {
+ final Rect startBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth,
+ mDisplayContent.mInitialDisplayHeight);
+ final int fromRotation = mDisplayContent.getRotation();
+
+ onStartCollect.run();
+
+ ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS,
+ "DeferredDisplayUpdater: applied DisplayInfo after deferring");
+
+ if (physicalDisplayUpdated) {
+ onDisplayUpdated(transition, fromRotation, startBounds);
+ } else {
+ transition.setAllReady();
+ }
+ });
+ }
+
+ /**
+ * Applies current DisplayInfo to DisplayContent, DisplayContent is merged from two parts:
+ * - non-deferrable fields are set from the most recent values received from DisplayManager
+ * (uses {@link mLastDisplayInfo} field)
+ * - deferrable fields are set from the latest values that we could apply to WM
+ * (uses {@link mLastWmDisplayInfo} field)
+ */
+ private void applyLatestDisplayInfo() {
+ copyDisplayInfoFields(mOutputDisplayInfo, /* base= */ mLastDisplayInfo,
+ /* override= */ mLastWmDisplayInfo, /* fields= */ DEFERRABLE_FIELDS);
+ mDisplayContent.onDisplayInfoUpdated(mOutputDisplayInfo);
+ }
+
+ @NonNull
+ private DisplayInfo getCurrentDisplayInfo() {
+ mDisplayContent.mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(
+ mDisplayContent.mDisplayId, mNonOverrideDisplayInfo);
+ return new DisplayInfo(mNonOverrideDisplayInfo);
+ }
+
+ /**
+ * Called when physical display is updated, this could happen e.g. on foldable
+ * devices when the physical underlying display is replaced. This method should be called
+ * when the new display info is already applied to the WM hierarchy.
+ *
+ * @param fromRotation rotation before the display change
+ * @param startBounds display bounds before the display change
+ */
+ private void onDisplayUpdated(@NonNull Transition transition, int fromRotation,
+ @NonNull Rect startBounds) {
+ final Rect endBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth,
+ mDisplayContent.mInitialDisplayHeight);
+ final int toRotation = mDisplayContent.getRotation();
+
+ final TransitionRequestInfo.DisplayChange displayChange =
+ new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId());
+ displayChange.setStartAbsBounds(startBounds);
+ displayChange.setEndAbsBounds(endBounds);
+ displayChange.setStartRotation(fromRotation);
+ displayChange.setEndRotation(toRotation);
+ displayChange.setPhysicalDisplayChanged(true);
+
+ mDisplayContent.mTransitionController.requestStartTransition(transition,
+ /* startTask= */ null, /* remoteTransition= */ null, displayChange);
+
+ final DisplayAreaInfo newDisplayAreaInfo = mDisplayContent.getDisplayAreaInfo();
+
+ final boolean startedRemoteChange = mDisplayContent.mRemoteDisplayChangeController
+ .performRemoteDisplayChange(fromRotation, toRotation, newDisplayAreaInfo,
+ transaction -> finishDisplayUpdate(transaction, transition));
+
+ if (!startedRemoteChange) {
+ finishDisplayUpdate(/* wct= */ null, transition);
+ }
+ }
+
+ private void finishDisplayUpdate(@Nullable WindowContainerTransaction wct,
+ @NonNull Transition transition) {
+ if (wct != null) {
+ mDisplayContent.mAtmService.mWindowOrganizerController.applyTransaction(
+ wct);
+ }
+ transition.setAllReady();
+ }
+
+ private boolean isPhysicalDisplayUpdated(@Nullable DisplayInfo first,
+ @Nullable DisplayInfo second) {
+ if (first == null || second == null) return true;
+ return !Objects.equals(first.uniqueId, second.uniqueId);
+ }
+
+ /**
+ * Diff result: fields are the same
+ */
+ static final int DIFF_NONE = 0;
+
+ /**
+ * Diff result: fields that could be deferred in WM are different
+ */
+ static final int DIFF_WM_DEFERRABLE = 1 << 0;
+
+ /**
+ * Diff result: fields that could not be deferred in WM are different
+ */
+ static final int DIFF_NOT_WM_DEFERRABLE = 1 << 1;
+
+ /**
+ * Diff result: everything is different
+ */
+ static final int DIFF_EVERYTHING = 0XFFFFFFFF;
+
+ @VisibleForTesting
+ static int calculateDisplayInfoDiff(@Nullable DisplayInfo first, @Nullable DisplayInfo second) {
+ int diff = DIFF_NONE;
+
+ if (Objects.equals(first, second)) return diff;
+ if (first == null || second == null) return DIFF_EVERYTHING;
+
+ if (first.layerStack != second.layerStack
+ || first.flags != second.flags
+ || first.type != second.type
+ || first.displayId != second.displayId
+ || first.displayGroupId != second.displayGroupId
+ || !Objects.equals(first.deviceProductInfo, second.deviceProductInfo)
+ || first.modeId != second.modeId
+ || first.renderFrameRate != second.renderFrameRate
+ || first.defaultModeId != second.defaultModeId
+ || first.userPreferredModeId != second.userPreferredModeId
+ || !Arrays.equals(first.supportedModes, second.supportedModes)
+ || first.colorMode != second.colorMode
+ || !Arrays.equals(first.supportedColorModes, second.supportedColorModes)
+ || !Objects.equals(first.hdrCapabilities, second.hdrCapabilities)
+ || !Arrays.equals(first.userDisabledHdrTypes, second.userDisabledHdrTypes)
+ || first.minimalPostProcessingSupported != second.minimalPostProcessingSupported
+ || first.appVsyncOffsetNanos != second.appVsyncOffsetNanos
+ || first.presentationDeadlineNanos != second.presentationDeadlineNanos
+ || first.state != second.state
+ || first.committedState != second.committedState
+ || first.ownerUid != second.ownerUid
+ || !Objects.equals(first.ownerPackageName, second.ownerPackageName)
+ || first.removeMode != second.removeMode
+ || first.getRefreshRate() != second.getRefreshRate()
+ || first.brightnessMinimum != second.brightnessMinimum
+ || first.brightnessMaximum != second.brightnessMaximum
+ || first.brightnessDefault != second.brightnessDefault
+ || first.installOrientation != second.installOrientation
+ || !Objects.equals(first.layoutLimitedRefreshRate, second.layoutLimitedRefreshRate)
+ || !BrightnessSynchronizer.floatEquals(first.hdrSdrRatio, second.hdrSdrRatio)
+ || !first.thermalRefreshRateThrottling.contentEquals(
+ second.thermalRefreshRateThrottling)
+ || !Objects.equals(first.thermalBrightnessThrottlingDataId,
+ second.thermalBrightnessThrottlingDataId)) {
+ diff |= DIFF_NOT_WM_DEFERRABLE;
+ }
+
+ if (first.appWidth != second.appWidth
+ || first.appHeight != second.appHeight
+ || first.smallestNominalAppWidth != second.smallestNominalAppWidth
+ || first.smallestNominalAppHeight != second.smallestNominalAppHeight
+ || first.largestNominalAppWidth != second.largestNominalAppWidth
+ || first.largestNominalAppHeight != second.largestNominalAppHeight
+ || first.logicalWidth != second.logicalWidth
+ || first.logicalHeight != second.logicalHeight
+ || first.physicalXDpi != second.physicalXDpi
+ || first.physicalYDpi != second.physicalYDpi
+ || first.rotation != second.rotation
+ || !Objects.equals(first.displayCutout, second.displayCutout)
+ || first.logicalDensityDpi != second.logicalDensityDpi
+ || !Objects.equals(first.roundedCorners, second.roundedCorners)
+ || !Objects.equals(first.displayShape, second.displayShape)
+ || !Objects.equals(first.uniqueId, second.uniqueId)
+ || !Objects.equals(first.address, second.address)
+ ) {
+ diff |= DIFF_WM_DEFERRABLE;
+ }
+
+ return diff;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a840973a941e..4cc1c0d4df9e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -276,6 +276,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import static com.android.window.flags.Flags.deferDisplayUpdates;
/**
* Utility class for keeping track of the WindowStates and other pertinent contents of a
@@ -1158,7 +1159,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWallpaperController.resetLargestDisplay(display);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
- mDisplayUpdater = new ImmediateDisplayUpdater(this);
+ if (deferDisplayUpdates()) {
+ mDisplayUpdater = new DeferredDisplayUpdater(this);
+ } else {
+ mDisplayUpdater = new ImmediateDisplayUpdater(this);
+ }
mSystemGestureExclusionLimit = mWmService.mConstants.mSystemGestureExclusionLimitDp
* mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index b862d7c28b52..460a68f48ff6 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -39,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCRE
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
@@ -272,6 +273,8 @@ public class DisplayPolicy {
private @InsetsType int mForciblyShownTypes;
+ private boolean mImeInsetsConsumed;
+
private boolean mIsImmersiveMode;
// The windows we were told about in focusChanged.
@@ -1420,6 +1423,7 @@ public class DisplayPolicy {
mShowingDream = false;
mIsFreeformWindowOverlappingWithNavBar = false;
mForciblyShownTypes = 0;
+ mImeInsetsConsumed = false;
}
/**
@@ -1481,6 +1485,17 @@ public class DisplayPolicy {
mForciblyShownTypes |= win.mAttrs.forciblyShownTypes;
}
+ if (win.mImeInsetsConsumed != mImeInsetsConsumed) {
+ win.mImeInsetsConsumed = mImeInsetsConsumed;
+ final WindowState imeWin = mDisplayContent.mInputMethodWindow;
+ if (win.isReadyToDispatchInsetsState() && imeWin != null && imeWin.isVisible()) {
+ win.notifyInsetsChanged();
+ }
+ }
+ if ((attrs.privateFlags & PRIVATE_FLAG_CONSUME_IME_INSETS) != 0 && win.isVisible()) {
+ mImeInsetsConsumed = true;
+ }
+
if (!affectsSystemUi) {
return;
}
@@ -2828,6 +2843,7 @@ public class DisplayPolicy {
}
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
+ pw.print(prefix); pw.print("mImeInsetsConsumed="); pw.println(mImeInsetsConsumed);
pw.print(prefix); pw.print("mForceShowNavigationBarEnabled=");
pw.print(mForceShowNavigationBarEnabled);
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index c089d107d07d..781567990235 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -391,13 +391,26 @@ class InsetsPolicy {
if (originalImeSource != null) {
final boolean imeVisibility = w.isRequestedVisible(Type.ime());
- final InsetsState state = copyState ? new InsetsState(originalState)
+ final InsetsState state = copyState
+ ? new InsetsState(originalState)
: originalState;
final InsetsSource imeSource = new InsetsSource(originalImeSource);
imeSource.setVisible(imeVisibility);
state.addSource(imeSource);
return state;
}
+ } else if (w.mImeInsetsConsumed) {
+ // Set the IME source (if there is one) to be invisible if it has been consumed.
+ final InsetsSource originalImeSource = originalState.peekSource(ID_IME);
+ if (originalImeSource != null && originalImeSource.isVisible()) {
+ final InsetsState state = copyState
+ ? new InsetsState(originalState)
+ : originalState;
+ final InsetsSource imeSource = new InsetsSource(originalImeSource);
+ imeSource.setVisible(false);
+ state.addSource(imeSource);
+ return state;
+ }
}
return originalState;
}
diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
new file mode 100644
index 000000000000..1688a1a91114
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.graphics.Matrix.MSCALE_X;
+import static android.graphics.Matrix.MSCALE_Y;
+import static android.graphics.Matrix.MSKEW_X;
+import static android.graphics.Matrix.MSKEW_Y;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TPL;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.Pair;
+import android.util.Size;
+import android.view.InputWindowHandle;
+import android.window.ITrustedPresentationListener;
+import android.window.TrustedPresentationThresholds;
+import android.window.WindowInfosListener;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.wm.utils.RegionUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Optional;
+
+/**
+ * Class to handle TrustedPresentationListener registrations in a thread safe manner. This class
+ * also takes care of cleaning up listeners when the remote process dies.
+ */
+public class TrustedPresentationListenerController {
+
+ // Should only be accessed by the posting to the handler
+ private class Listeners {
+ private final class ListenerDeathRecipient implements IBinder.DeathRecipient {
+ IBinder mListenerBinder;
+ int mInstances;
+
+ ListenerDeathRecipient(IBinder listenerBinder) {
+ mListenerBinder = listenerBinder;
+ mInstances = 0;
+ try {
+ mListenerBinder.linkToDeath(this, 0);
+ } catch (RemoteException ignore) {
+ }
+ }
+
+ void addInstance() {
+ mInstances++;
+ }
+
+ // return true if there are no instances alive
+ boolean removeInstance() {
+ mInstances--;
+ if (mInstances > 0) {
+ return false;
+ }
+ mListenerBinder.unlinkToDeath(this, 0);
+ return true;
+ }
+
+ public void binderDied() {
+ mHandler.post(() -> {
+ mUniqueListeners.remove(mListenerBinder);
+ removeListeners(mListenerBinder, Optional.empty());
+ });
+ }
+ }
+
+ // tracks binder deaths for cleanup
+ ArrayMap<IBinder, ListenerDeathRecipient> mUniqueListeners = new ArrayMap<>();
+ ArrayMap<IBinder /*window*/, ArrayList<TrustedPresentationInfo>> mWindowToListeners =
+ new ArrayMap<>();
+
+ void register(IBinder window, ITrustedPresentationListener listener,
+ TrustedPresentationThresholds thresholds, int id) {
+ var listenersForWindow = mWindowToListeners.computeIfAbsent(window,
+ iBinder -> new ArrayList<>());
+ listenersForWindow.add(new TrustedPresentationInfo(thresholds, id, listener));
+
+ // register death listener
+ var listenerBinder = listener.asBinder();
+ var deathRecipient = mUniqueListeners.computeIfAbsent(listenerBinder,
+ ListenerDeathRecipient::new);
+ deathRecipient.addInstance();
+ }
+
+ void unregister(ITrustedPresentationListener trustedPresentationListener, int id) {
+ var listenerBinder = trustedPresentationListener.asBinder();
+ var deathRecipient = mUniqueListeners.get(listenerBinder);
+ if (deathRecipient == null) {
+ ProtoLog.e(WM_DEBUG_TPL, "unregister failed, couldn't find"
+ + " deathRecipient for %s with id=%d", trustedPresentationListener, id);
+ return;
+ }
+
+ if (deathRecipient.removeInstance()) {
+ mUniqueListeners.remove(listenerBinder);
+ }
+ removeListeners(listenerBinder, Optional.of(id));
+ }
+
+ boolean isEmpty() {
+ return mWindowToListeners.isEmpty();
+ }
+
+ ArrayList<TrustedPresentationInfo> get(IBinder windowToken) {
+ return mWindowToListeners.get(windowToken);
+ }
+
+ private void removeListeners(IBinder listenerBinder, Optional<Integer> id) {
+ for (int i = mWindowToListeners.size() - 1; i >= 0; i--) {
+ var listeners = mWindowToListeners.valueAt(i);
+ for (int j = listeners.size() - 1; j >= 0; j--) {
+ var listener = listeners.get(j);
+ if (listener.mListener.asBinder() == listenerBinder && (id.isEmpty()
+ || listener.mId == id.get())) {
+ listeners.remove(j);
+ }
+ }
+ if (listeners.isEmpty()) {
+ mWindowToListeners.removeAt(i);
+ }
+ }
+ }
+ }
+
+ private final Object mHandlerThreadLock = new Object();
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+
+ private WindowInfosListener mWindowInfosListener;
+
+ Listeners mRegisteredListeners = new Listeners();
+
+ private InputWindowHandle[] mLastWindowHandles;
+
+ private final Object mIgnoredWindowTokensLock = new Object();
+
+ private final ArraySet<IBinder> mIgnoredWindowTokens = new ArraySet<>();
+
+ private void startHandlerThreadIfNeeded() {
+ synchronized (mHandlerThreadLock) {
+ if (mHandler == null) {
+ mHandlerThread = new HandlerThread("WindowInfosListenerForTpl");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+ }
+ }
+
+ void addIgnoredWindowTokens(IBinder token) {
+ synchronized (mIgnoredWindowTokensLock) {
+ mIgnoredWindowTokens.add(token);
+ }
+ }
+
+ void removeIgnoredWindowTokens(IBinder token) {
+ synchronized (mIgnoredWindowTokensLock) {
+ mIgnoredWindowTokens.remove(token);
+ }
+ }
+
+ void registerListener(IBinder window, ITrustedPresentationListener listener,
+ TrustedPresentationThresholds thresholds, int id) {
+ startHandlerThreadIfNeeded();
+ mHandler.post(() -> {
+ ProtoLog.d(WM_DEBUG_TPL, "Registering listener=%s with id=%d for window=%s with %s",
+ listener, id, window, thresholds);
+
+ mRegisteredListeners.register(window, listener, thresholds, id);
+ registerWindowInfosListener();
+ // Update the initial state for the new registered listener
+ computeTpl(mLastWindowHandles);
+ });
+ }
+
+ void unregisterListener(ITrustedPresentationListener listener, int id) {
+ startHandlerThreadIfNeeded();
+ mHandler.post(() -> {
+ ProtoLog.d(WM_DEBUG_TPL, "Unregistering listener=%s with id=%d",
+ listener, id);
+
+ mRegisteredListeners.unregister(listener, id);
+ if (mRegisteredListeners.isEmpty()) {
+ unregisterWindowInfosListener();
+ }
+ });
+ }
+
+ void dump(PrintWriter pw) {
+ final String innerPrefix = " ";
+ pw.println("TrustedPresentationListenerController:");
+ pw.println(innerPrefix + "Active unique listeners ("
+ + mRegisteredListeners.mUniqueListeners.size() + "):");
+ for (int i = 0; i < mRegisteredListeners.mWindowToListeners.size(); i++) {
+ pw.println(
+ innerPrefix + " window=" + mRegisteredListeners.mWindowToListeners.keyAt(i));
+ final var listeners = mRegisteredListeners.mWindowToListeners.valueAt(i);
+ for (int j = 0; j < listeners.size(); j++) {
+ final var listener = listeners.get(j);
+ pw.println(innerPrefix + innerPrefix + " listener=" + listener.mListener.asBinder()
+ + " id=" + listener.mId
+ + " thresholds=" + listener.mThresholds);
+ }
+ }
+ }
+
+ private void registerWindowInfosListener() {
+ if (mWindowInfosListener != null) {
+ return;
+ }
+
+ mWindowInfosListener = new WindowInfosListener() {
+ @Override
+ public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
+ DisplayInfo[] displayInfos) {
+ mHandler.post(() -> computeTpl(windowHandles));
+ }
+ };
+ mLastWindowHandles = mWindowInfosListener.register().first;
+ }
+
+ private void unregisterWindowInfosListener() {
+ if (mWindowInfosListener == null) {
+ return;
+ }
+
+ mWindowInfosListener.unregister();
+ mWindowInfosListener = null;
+ mLastWindowHandles = null;
+ }
+
+ private void computeTpl(InputWindowHandle[] windowHandles) {
+ mLastWindowHandles = windowHandles;
+ if (mLastWindowHandles == null || mLastWindowHandles.length == 0
+ || mRegisteredListeners.isEmpty()) {
+ return;
+ }
+
+ Rect tmpRect = new Rect();
+ Matrix tmpInverseMatrix = new Matrix();
+ float[] tmpMatrix = new float[9];
+ Region coveredRegionsAbove = new Region();
+ long currTimeMs = System.currentTimeMillis();
+ ProtoLog.v(WM_DEBUG_TPL, "Checking %d windows", mLastWindowHandles.length);
+
+ ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates =
+ new ArrayMap<>();
+ ArraySet<IBinder> ignoredWindowTokens;
+ synchronized (mIgnoredWindowTokensLock) {
+ ignoredWindowTokens = new ArraySet<>(mIgnoredWindowTokens);
+ }
+ for (var windowHandle : mLastWindowHandles) {
+ if (ignoredWindowTokens.contains(windowHandle.getWindowToken())) {
+ ProtoLog.v(WM_DEBUG_TPL, "Skipping %s", windowHandle.name);
+ continue;
+ }
+ tmpRect.set(windowHandle.frame);
+ var listeners = mRegisteredListeners.get(windowHandle.getWindowToken());
+ if (listeners != null) {
+ Region region = new Region();
+ region.op(tmpRect, coveredRegionsAbove, Region.Op.DIFFERENCE);
+ windowHandle.transform.invert(tmpInverseMatrix);
+ tmpInverseMatrix.getValues(tmpMatrix);
+ float scaleX = (float) Math.sqrt(tmpMatrix[MSCALE_X] * tmpMatrix[MSCALE_X]
+ + tmpMatrix[MSKEW_X] * tmpMatrix[MSKEW_X]);
+ float scaleY = (float) Math.sqrt(tmpMatrix[MSCALE_Y] * tmpMatrix[MSCALE_Y]
+ + tmpMatrix[MSKEW_Y] * tmpMatrix[MSKEW_Y]);
+
+ float fractionRendered = computeFractionRendered(region, new RectF(tmpRect),
+ windowHandle.contentSize,
+ scaleX, scaleY);
+
+ checkIfInThreshold(listeners, listenerUpdates, fractionRendered, windowHandle.alpha,
+ currTimeMs);
+ }
+
+ coveredRegionsAbove.op(tmpRect, Region.Op.UNION);
+ ProtoLog.v(WM_DEBUG_TPL, "coveredRegionsAbove updated with %s frame:%s region:%s",
+ windowHandle.name, tmpRect.toShortString(), coveredRegionsAbove);
+ }
+
+ for (int i = 0; i < listenerUpdates.size(); i++) {
+ var updates = listenerUpdates.valueAt(i);
+ var listener = listenerUpdates.keyAt(i);
+ try {
+ listener.onTrustedPresentationChanged(updates.first.toArray(),
+ updates.second.toArray());
+ } catch (RemoteException ignore) {
+ }
+ }
+ }
+
+ private void addListenerUpdate(
+ ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates,
+ ITrustedPresentationListener listener, int id, boolean presentationState) {
+ var updates = listenerUpdates.get(listener);
+ if (updates == null) {
+ updates = new Pair<>(new IntArray(), new IntArray());
+ listenerUpdates.put(listener, updates);
+ }
+ if (presentationState) {
+ updates.first.add(id);
+ } else {
+ updates.second.add(id);
+ }
+ }
+
+
+ private void checkIfInThreshold(
+ ArrayList<TrustedPresentationInfo> listeners,
+ ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates,
+ float fractionRendered, float alpha, long currTimeMs) {
+ ProtoLog.v(WM_DEBUG_TPL, "checkIfInThreshold fractionRendered=%f alpha=%f currTimeMs=%d",
+ fractionRendered, alpha, currTimeMs);
+ for (int i = 0; i < listeners.size(); i++) {
+ var trustedPresentationInfo = listeners.get(i);
+ var listener = trustedPresentationInfo.mListener;
+ boolean lastState = trustedPresentationInfo.mLastComputedTrustedPresentationState;
+ boolean newState =
+ (alpha >= trustedPresentationInfo.mThresholds.minAlpha) && (fractionRendered
+ >= trustedPresentationInfo.mThresholds.minFractionRendered);
+ trustedPresentationInfo.mLastComputedTrustedPresentationState = newState;
+
+ ProtoLog.v(WM_DEBUG_TPL,
+ "lastState=%s newState=%s alpha=%f minAlpha=%f fractionRendered=%f "
+ + "minFractionRendered=%f",
+ lastState, newState, alpha, trustedPresentationInfo.mThresholds.minAlpha,
+ fractionRendered, trustedPresentationInfo.mThresholds.minFractionRendered);
+
+ if (lastState && !newState) {
+ // We were in the trusted presentation state, but now we left it,
+ // emit the callback if needed
+ if (trustedPresentationInfo.mLastReportedTrustedPresentationState) {
+ trustedPresentationInfo.mLastReportedTrustedPresentationState = false;
+ addListenerUpdate(listenerUpdates, listener,
+ trustedPresentationInfo.mId, /*presentationState*/ false);
+ ProtoLog.d(WM_DEBUG_TPL, "Adding untrusted state listener=%s with id=%d",
+ listener, trustedPresentationInfo.mId);
+ }
+ // Reset the timer
+ trustedPresentationInfo.mEnteredTrustedPresentationStateTime = -1;
+ } else if (!lastState && newState) {
+ // We were not in the trusted presentation state, but we entered it, begin the timer
+ // and make sure this gets called at least once more!
+ trustedPresentationInfo.mEnteredTrustedPresentationStateTime = currTimeMs;
+ mHandler.postDelayed(() -> {
+ computeTpl(mLastWindowHandles);
+ }, (long) (trustedPresentationInfo.mThresholds.stabilityRequirementMs * 1.5));
+ }
+
+ // Has the timer elapsed, but we are still in the state? Emit a callback if needed
+ if (!trustedPresentationInfo.mLastReportedTrustedPresentationState && newState && (
+ currTimeMs - trustedPresentationInfo.mEnteredTrustedPresentationStateTime
+ > trustedPresentationInfo.mThresholds.stabilityRequirementMs)) {
+ trustedPresentationInfo.mLastReportedTrustedPresentationState = true;
+ addListenerUpdate(listenerUpdates, listener,
+ trustedPresentationInfo.mId, /*presentationState*/ true);
+ ProtoLog.d(WM_DEBUG_TPL, "Adding trusted state listener=%s with id=%d",
+ listener, trustedPresentationInfo.mId);
+ }
+ }
+ }
+
+ private float computeFractionRendered(Region visibleRegion, RectF screenBounds,
+ Size contentSize,
+ float sx, float sy) {
+ ProtoLog.v(WM_DEBUG_TPL,
+ "computeFractionRendered: visibleRegion=%s screenBounds=%s contentSize=%s "
+ + "scale=%f,%f",
+ visibleRegion, screenBounds, contentSize, sx, sy);
+
+ if (contentSize.getWidth() == 0 || contentSize.getHeight() == 0) {
+ return -1;
+ }
+ if (screenBounds.width() == 0 || screenBounds.height() == 0) {
+ return -1;
+ }
+
+ float fractionRendered = Math.min(sx * sy, 1.0f);
+ ProtoLog.v(WM_DEBUG_TPL, "fractionRendered scale=%f", fractionRendered);
+
+ float boundsOverSourceW = screenBounds.width() / (float) contentSize.getWidth();
+ float boundsOverSourceH = screenBounds.height() / (float) contentSize.getHeight();
+ fractionRendered *= boundsOverSourceW * boundsOverSourceH;
+ ProtoLog.v(WM_DEBUG_TPL, "fractionRendered boundsOverSource=%f", fractionRendered);
+ // Compute the size of all the rects since they may be disconnected.
+ float[] visibleSize = new float[1];
+ RegionUtils.forEachRect(visibleRegion, rect -> {
+ float size = rect.width() * rect.height();
+ visibleSize[0] += size;
+ });
+
+ fractionRendered *= visibleSize[0] / (screenBounds.width() * screenBounds.height());
+ return fractionRendered;
+ }
+
+ private static class TrustedPresentationInfo {
+ boolean mLastComputedTrustedPresentationState = false;
+ boolean mLastReportedTrustedPresentationState = false;
+ long mEnteredTrustedPresentationStateTime = -1;
+ final TrustedPresentationThresholds mThresholds;
+
+ final ITrustedPresentationListener mListener;
+ final int mId;
+
+ private TrustedPresentationInfo(TrustedPresentationThresholds thresholds, int id,
+ ITrustedPresentationListener listener) {
+ mThresholds = thresholds;
+ mId = id;
+ mListener = listener;
+ checkValid(thresholds);
+ }
+
+ private void checkValid(TrustedPresentationThresholds thresholds) {
+ if (thresholds.minAlpha <= 0 || thresholds.minFractionRendered <= 0
+ || thresholds.stabilityRequirementMs < 1) {
+ throw new IllegalArgumentException(
+ "TrustedPresentationThresholds values are invalid");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 72632dce2507..2b8e541685cc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -303,9 +303,11 @@ import android.window.AddToSurfaceSyncGroupResult;
import android.window.ClientWindowFrames;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
+import android.window.ITrustedPresentationListener;
import android.window.ScreenCapture;
import android.window.SystemPerformanceHinter;
import android.window.TaskSnapshot;
+import android.window.TrustedPresentationThresholds;
import android.window.WindowContainerToken;
import android.window.WindowContextInfo;
@@ -764,6 +766,9 @@ public class WindowManagerService extends IWindowManager.Stub
private final SurfaceSyncGroupController mSurfaceSyncGroupController =
new SurfaceSyncGroupController();
+ final TrustedPresentationListenerController mTrustedPresentationListenerController =
+ new TrustedPresentationListenerController();
+
@VisibleForTesting
final class SettingsObserver extends ContentObserver {
private final Uri mDisplayInversionEnabledUri =
@@ -7171,6 +7176,7 @@ public class WindowManagerService extends IWindowManager.Stub
pw.println(separator);
}
mSystemPerformanceHinter.dump(pw, "");
+ mTrustedPresentationListenerController.dump(pw);
}
}
@@ -9771,4 +9777,17 @@ public class WindowManagerService extends IWindowManager.Stub
Binder.restoreCallingIdentity(origId);
}
}
+
+ @Override
+ public void registerTrustedPresentationListener(IBinder window,
+ ITrustedPresentationListener listener,
+ TrustedPresentationThresholds thresholds, int id) {
+ mTrustedPresentationListenerController.registerListener(window, listener, thresholds, id);
+ }
+
+ @Override
+ public void unregisterTrustedPresentationListener(ITrustedPresentationListener listener,
+ int id) {
+ mTrustedPresentationListenerController.unregisterListener(listener, id);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b890a9e9bd04..9c4ad5cf3217 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -668,6 +668,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean mSeamlesslyRotated = false;
/**
+ * Whether the IME insets have been consumed. If {@code true}, this window won't be able to
+ * receive visible IME insets; {@code false}, otherwise.
+ */
+ boolean mImeInsetsConsumed = false;
+
+ /**
* The insets state of sources provided by windows above the current window.
*/
final InsetsState mAboveInsetsState = new InsetsState();
@@ -1189,6 +1195,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", this, parentWindow);
parentWindow.addChild(this, sWindowSubLayerComparator);
}
+
+ if (token.mRoundedCornerOverlay) {
+ mWmService.mTrustedPresentationListenerController.addIgnoredWindowTokens(
+ getWindowToken());
+ }
}
@Override
@@ -1487,7 +1498,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (insetsChanged) {
mWindowFrames.setInsetsChanged(false);
- mWmService.mWindowsInsetsChanged--;
+ if (mWmService.mWindowsInsetsChanged > 0) {
+ mWmService.mWindowsInsetsChanged--;
+ }
if (mWmService.mWindowsInsetsChanged == 0) {
mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
}
@@ -2393,6 +2406,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
mWmService.postWindowRemoveCleanupLocked(this);
+
+ mWmService.mTrustedPresentationListenerController.removeIgnoredWindowTokens(
+ getWindowToken());
}
@Override
@@ -3796,13 +3812,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
void notifyInsetsChanged() {
ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsChanged for %s ", this);
- mWindowFrames.setInsetsChanged(true);
+ if (!mWindowFrames.hasInsetsChanged()) {
+ mWindowFrames.setInsetsChanged(true);
- // If the new InsetsState won't be dispatched before releasing WM lock, the following
- // message will be executed.
- mWmService.mWindowsInsetsChanged++;
- mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
- mWmService.mH.sendEmptyMessage(WindowManagerService.H.INSETS_CHANGED);
+ // If the new InsetsState won't be dispatched before releasing WM lock, the following
+ // message will be executed.
+ mWmService.mWindowsInsetsChanged++;
+ mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
+ mWmService.mH.sendEmptyMessage(WindowManagerService.H.INSETS_CHANGED);
+ }
final WindowContainer p = getParent();
if (p != null) {
@@ -4192,6 +4210,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} else {
pw.print("null");
}
+ pw.println();
if (mXOffset != 0 || mYOffset != 0) {
pw.println(prefix + "mXOffset=" + mXOffset + " mYOffset=" + mYOffset);
@@ -4225,6 +4244,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (computeDragResizing()) {
pw.println(prefix + "computeDragResizing=" + computeDragResizing());
}
+ if (mImeInsetsConsumed) {
+ pw.println(prefix + "mImeInsetsConsumed=true");
+ }
pw.println(prefix + "isOnScreen=" + isOnScreen());
pw.println(prefix + "isVisible=" + isVisible());
pw.println(prefix + "keepClearAreas: restricted=" + mKeepClearAreas
diff --git a/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java b/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java
index 8c8f6a6cb386..193a0c8be60b 100644
--- a/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java
+++ b/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java
@@ -30,7 +30,7 @@ public class DisplayInfoOverrides {
* Set of DisplayInfo fields that are overridden in DisplayManager using values from
* WindowManager
*/
- public static final DisplayInfoFields WM_OVERRIDE_FIELDS = (out, source) -> {
+ public static final DisplayInfoFieldsUpdater WM_OVERRIDE_FIELDS = (out, source) -> {
out.appWidth = source.appWidth;
out.appHeight = source.appHeight;
out.smallestNominalAppWidth = source.smallestNominalAppWidth;
@@ -55,7 +55,7 @@ public class DisplayInfoOverrides {
public static void copyDisplayInfoFields(@NonNull DisplayInfo out,
@NonNull DisplayInfo base,
@Nullable DisplayInfo override,
- @NonNull DisplayInfoFields fields) {
+ @NonNull DisplayInfoFieldsUpdater fields) {
out.copyFrom(base);
if (override != null) {
@@ -66,7 +66,7 @@ public class DisplayInfoOverrides {
/**
* Callback interface that allows to specify a subset of fields of DisplayInfo object
*/
- public interface DisplayInfoFields {
+ public interface DisplayInfoFieldsUpdater {
/**
* Copies a subset of fields from {@param source} to {@param out}
*
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 24ee16389fd2..b19f3d813985 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -187,7 +187,7 @@ cc_defaults {
"android.hardware.power.stats@1.0",
"android.hardware.power.stats-V1-ndk",
"android.hardware.thermal@1.0",
- "android.hardware.thermal-V1-ndk",
+ "android.hardware.thermal-V2-ndk",
"android.hardware.tv.input@1.0",
"android.hardware.tv.input-V2-ndk",
"android.hardware.vibrator-V2-cpp",
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index a0dc2b68415c..f07e820bf8b3 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -817,12 +817,14 @@ public class PackageManagerSettingsTests {
ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" });
ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 });
+ ps1.setUsesSdkLibrariesOptional(new boolean[] {true});
ps1.setFlags(ps1.getFlags() | ApplicationInfo.FLAG_SYSTEM);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
assertThat(settingsUnderTest.disableSystemPackageLPw(PACKAGE_NAME_1, false), is(true));
ps2.setUsesSdkLibraries(new String[] { "com.example.sdk.two" });
ps2.setUsesSdkLibrariesVersionsMajor(new long[] { 34 });
+ ps2.setUsesSdkLibrariesOptional(new boolean[] {false});
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
settingsUnderTest.writeLPr(computer, /*sync=*/true);
@@ -838,19 +840,30 @@ public class PackageManagerSettingsTests {
Truth.assertThat(readPs1).isNotNull();
Truth.assertThat(readPs1.getUsesSdkLibraries()).isNotNull();
Truth.assertThat(readPs1.getUsesSdkLibrariesVersionsMajor()).isNotNull();
+ Truth.assertThat(readPs1.getUsesSdkLibrariesOptional()).isNotNull();
Truth.assertThat(readPs2).isNotNull();
Truth.assertThat(readPs2.getUsesSdkLibraries()).isNotNull();
Truth.assertThat(readPs2.getUsesSdkLibrariesVersionsMajor()).isNotNull();
+ Truth.assertThat(readPs2.getUsesSdkLibrariesOptional()).isNotNull();
List<Long> ps1VersionsAsList = new ArrayList<>();
for (long version : ps1.getUsesSdkLibrariesVersionsMajor()) {
ps1VersionsAsList.add(version);
}
+ List<Boolean> ps1RequireAsList = new ArrayList<>();
+ for (boolean optional : ps1.getUsesSdkLibrariesOptional()) {
+ ps1RequireAsList.add(optional);
+ }
+
List<Long> ps2VersionsAsList = new ArrayList<>();
for (long version : ps2.getUsesSdkLibrariesVersionsMajor()) {
ps2VersionsAsList.add(version);
}
+ List<Boolean> ps2RequireAsList = new ArrayList<>();
+ for (boolean optional : ps2.getUsesSdkLibrariesOptional()) {
+ ps2RequireAsList.add(optional);
+ }
Truth.assertThat(readPs1.getUsesSdkLibraries()).asList()
.containsExactlyElementsIn(ps1.getUsesSdkLibraries()).inOrder();
@@ -858,11 +871,17 @@ public class PackageManagerSettingsTests {
Truth.assertThat(readPs1.getUsesSdkLibrariesVersionsMajor()).asList()
.containsExactlyElementsIn(ps1VersionsAsList).inOrder();
+ Truth.assertThat(readPs1.getUsesSdkLibrariesOptional()).asList()
+ .containsExactlyElementsIn(ps1RequireAsList).inOrder();
+
Truth.assertThat(readPs2.getUsesSdkLibraries()).asList()
.containsExactlyElementsIn(ps2.getUsesSdkLibraries()).inOrder();
Truth.assertThat(readPs2.getUsesSdkLibrariesVersionsMajor()).asList()
.containsExactlyElementsIn(ps2VersionsAsList).inOrder();
+
+ Truth.assertThat(readPs2.getUsesSdkLibrariesOptional()).asList()
+ .containsExactlyElementsIn(ps2RequireAsList).inOrder();
}
@Test
@@ -1047,6 +1066,7 @@ public class PackageManagerSettingsTests {
UserManagerService.getInstance(),
null /*usesSdkLibraries*/,
null /*usesSdkLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -1087,6 +1107,7 @@ public class PackageManagerSettingsTests {
UserManagerService.getInstance(),
null /*usesSdkLibraries*/,
null /*usesSdkLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -1129,6 +1150,7 @@ public class PackageManagerSettingsTests {
UserManagerService.getInstance(),
null /*usesSdkLibraries*/,
null /*usesSdkLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -1167,6 +1189,7 @@ public class PackageManagerSettingsTests {
UserManagerService.getInstance(),
null /*usesSdkLibraries*/,
null /*usesSdkLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -1214,6 +1237,7 @@ public class PackageManagerSettingsTests {
UserManagerService.getInstance(),
null /*usesSdkLibraries*/,
null /*usesSdkLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -1263,6 +1287,7 @@ public class PackageManagerSettingsTests {
null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
null /*mimeGroups*/,
UUID.randomUUID(),
34 /*targetSdkVersion*/,
@@ -1311,6 +1336,7 @@ public class PackageManagerSettingsTests {
null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
null /*mimeGroups*/,
UUID.randomUUID(),
34 /*targetSdkVersion*/,
@@ -1356,6 +1382,7 @@ public class PackageManagerSettingsTests {
null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
null /*mimeGroups*/,
UUID.randomUUID(),
34 /*targetSdkVersion*/,
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index ea88ec25b26e..a62cd4fc5fc2 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -1062,7 +1062,7 @@ public class PackageParserTest {
.addProtectedBroadcast("foo8")
.setSdkLibraryName("sdk12")
.setSdkLibVersionMajor(42)
- .addUsesSdkLibrary("sdk23", 200, new String[]{"digest2"})
+ .addUsesSdkLibrary("sdk23", 200, new String[]{"digest2"}, true)
.setStaticSharedLibraryName("foo23")
.setStaticSharedLibraryVersion(100)
.addUsesStaticLibrary("foo23", 100, new String[]{"digest"})
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
index decb44c2cd9b..6202908cdfbf 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
@@ -599,8 +599,8 @@ public class ScanTests {
.setVolumeUuid(UUID_ONE.toString())
.addUsesStaticLibrary("some.static.library", 234L, new String[]{"testCert1"})
.addUsesStaticLibrary("some.other.static.library", 456L, new String[]{"testCert2"})
- .addUsesSdkLibrary("some.sdk.library", 123L, new String[]{"testCert3"})
- .addUsesSdkLibrary("some.other.sdk.library", 789L, new String[]{"testCert4"})
+ .addUsesSdkLibrary("some.sdk.library", 123L, new String[]{"testCert3"}, false)
+ .addUsesSdkLibrary("some.other.sdk.library", 789L, new String[]{"testCert4"}, true)
.hideAsParsed())
.setNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib")
.setVersionCodeMajor(1)
@@ -628,6 +628,7 @@ public class ScanTests {
assertThat(pkgSetting.getUsesSdkLibraries(),
arrayContaining("some.sdk.library", "some.other.sdk.library"));
assertThat(pkgSetting.getUsesSdkLibrariesVersionsMajor(), is(new long[]{123L, 789L}));
+ assertThat(pkgSetting.getUsesSdkLibrariesOptional(), is(new boolean[]{false, true}));
assertThat(pkgSetting.getPkg(), is(scanResult.mRequest.mParsedPackage));
assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName))));
assertThat(pkgSetting.getVersionCode(),
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 170faf61858b..09b66c11d200 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -127,6 +127,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
"getUsesSdkLibraries",
"getUsesSdkLibrariesVersionsMajor",
"getUsesSdkLibrariesCertDigests",
+ "getUsesSdkLibrariesOptional",
// Tested through addUsesStaticLibrary
"addUsesStaticLibrary",
"getUsesStaticLibraries",
@@ -608,7 +609,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
.setSplitHasCode(1, false)
.setSplitClassLoaderName(0, "testSplitClassLoaderNameZero")
.setSplitClassLoaderName(1, "testSplitClassLoaderNameOne")
- .addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1"))
+ .addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1"), true)
.addUsesStaticLibrary("testStatic", 3L, arrayOf("testCertDigest2"))
override fun finalizeObject(parcelable: Parcelable) {
@@ -661,6 +662,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
expect.that(after.usesSdkLibrariesCertDigests!!.size).isEqualTo(1)
expect.that(after.usesSdkLibrariesCertDigests!![0]).asList()
.containsExactly("testCertDigest1")
+ expect.that(after.usesSdkLibrariesOptional).asList().containsExactly(true)
expect.that(after.usesStaticLibraries).containsExactly("testStatic")
expect.that(after.usesStaticLibrariesVersions).asList().containsExactly(3L)
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 4fb9472021c5..650c473533ed 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -30,15 +30,16 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_APPLIED_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_DEADLINE_PROXIMITY_LIMIT;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE;
-import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FLEXIBILITY_ENABLED;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS;
-import static com.android.server.job.controllers.FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS;
+import static com.android.server.job.controllers.FlexibilityController.FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
+import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONTENT_TRIGGER;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
import static com.android.server.job.controllers.JobStatus.MIN_WINDOW_FOR_FLEXIBILITY_MS;
@@ -66,6 +67,7 @@ import android.os.Looper;
import android.provider.DeviceConfig;
import android.util.ArraySet;
+import com.android.server.AppSchedulingModuleThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
@@ -129,6 +131,7 @@ public class FlexibilityControllerTest {
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(false);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED)).thenReturn(false);
// Used in FlexibilityController.FcConstants.
doAnswer((Answer<Void>) invocationOnMock -> null)
.when(() -> DeviceConfig.addOnPropertiesChangedListener(
@@ -161,7 +164,8 @@ public class FlexibilityControllerTest {
setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "50,60,70,80");
setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, 0L);
- setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS);
+ waitForQuietModuleThread();
}
@After
@@ -171,26 +175,22 @@ public class FlexibilityControllerTest {
}
}
- private void setDeviceConfigBoolean(String key, boolean val) {
- mDeviceConfigPropertiesBuilder.setBoolean(key, val);
- synchronized (mFlexibilityController.mLock) {
- mFlexibilityController.prepareForUpdatedConstantsLocked();
- mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
- mFlexibilityController.onConstantsUpdatedLocked();
- }
+ private void setDeviceConfigInt(String key, int val) {
+ mDeviceConfigPropertiesBuilder.setInt(key, val);
+ updateDeviceConfig(key);
}
private void setDeviceConfigLong(String key, Long val) {
mDeviceConfigPropertiesBuilder.setLong(key, val);
- synchronized (mFlexibilityController.mLock) {
- mFlexibilityController.prepareForUpdatedConstantsLocked();
- mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
- mFlexibilityController.onConstantsUpdatedLocked();
- }
+ updateDeviceConfig(key);
}
private void setDeviceConfigString(String key, String val) {
mDeviceConfigPropertiesBuilder.setString(key, val);
+ updateDeviceConfig(key);
+ }
+
+ private void updateDeviceConfig(String key) {
synchronized (mFlexibilityController.mLock) {
mFlexibilityController.prepareForUpdatedConstantsLocked();
mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
@@ -198,6 +198,11 @@ public class FlexibilityControllerTest {
}
}
+ private void waitForQuietModuleThread() {
+ assertTrue("Failed to wait for quiet module thread",
+ AppSchedulingModuleThread.getHandler().runWithScissors(() -> {}, 10_000L));
+ }
+
private static JobInfo.Builder createJob(int id) {
return new JobInfo.Builder(id, new ComponentName("foo", "bar"));
}
@@ -207,6 +212,10 @@ public class FlexibilityControllerTest {
JobStatus js = JobStatus.createFromJobInfo(
jobInfo, 1000, SOURCE_PACKAGE, SOURCE_USER_ID, "FCTest", testTag);
js.enqueueTime = FROZEN_TIME;
+ if (js.hasFlexibilityConstraint()) {
+ js.setNumAppliedFlexibleConstraints(Integer.bitCount(
+ mFlexibilityController.getRelevantAppliedConstraintsLocked(js)));
+ }
return js;
}
@@ -215,18 +224,120 @@ public class FlexibilityControllerTest {
*/
@Test
public void testDefaultVariableValues() {
- assertEquals(NUM_FLEXIBLE_CONSTRAINTS,
+ assertEquals(Integer.bitCount(FLEXIBLE_CONSTRAINTS),
mFlexibilityController.mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS.length
);
}
@Test
- public void testOnConstantsUpdated_DefaultFlexibility() {
+ public void testAppliedConstraints() {
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS);
+
+ // Add connectivity to require 4 constraints
+ JobStatus connJs = createJobStatus("testAppliedConstraints",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY));
+ JobStatus nonConnJs = createJobStatus("testAppliedConstraints",
+ createJob(1).setRequiredNetworkType(NETWORK_TYPE_NONE));
+
+ mFlexibilityController.maybeStartTrackingJobLocked(connJs, null);
+ mFlexibilityController.maybeStartTrackingJobLocked(nonConnJs, null);
+
+ assertEquals(4, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(3, nonConnJs.getNumAppliedFlexibleConstraints());
+
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_BATTERY_NOT_LOW, true,
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CHARGING, false,
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_IDLE, false,
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, true,
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS);
+ connJs.setTransportAffinitiesSatisfied(true);
+
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS,
+ CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_CONNECTIVITY);
+ waitForQuietModuleThread();
+
+ // Only battery-not-low (which is satisfied) applies to the non-connectivity job, so it
+ // should be able to run.
+ assertEquals(2, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_BATTERY_NOT_LOW);
+ waitForQuietModuleThread();
+
+ assertEquals(1, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CONNECTIVITY);
+ waitForQuietModuleThread();
+
+ // No constraints apply to the non-connectivity job, so it should be able to run.
+ assertEquals(1, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CHARGING);
+ waitForQuietModuleThread();
+
+ assertEquals(1, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, 0);
+ waitForQuietModuleThread();
+
+ // No constraints apply, so they should be able to run.
+ assertEquals(0, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // Invalid constraint to apply.
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CONTENT_TRIGGER);
+ waitForQuietModuleThread();
+
+ // No constraints apply, so they should be able to run.
+ assertEquals(0, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+ }
+
+ @Test
+ public void testOnConstantsUpdated_AppliedConstraints() {
JobStatus js = createJobStatus("testDefaultFlexibilityConfig", createJob(0));
- assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
- setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, false);
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, 0);
assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
- setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS);
assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
}
@@ -261,10 +372,10 @@ public class FlexibilityControllerTest {
new int[] {10, 20, 30, 40});
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10,
mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 2,
mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 3,
mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
}
@@ -303,12 +414,12 @@ public class FlexibilityControllerTest {
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5,
nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6,
nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7,
@@ -321,11 +432,11 @@ public class FlexibilityControllerTest {
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(130400100, nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(156320100L, nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(182240100L, nextTimeToDropNumConstraints);
@@ -337,11 +448,11 @@ public class FlexibilityControllerTest {
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(129600100, nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(155520100L, nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(181440100L, nextTimeToDropNumConstraints);
@@ -357,12 +468,12 @@ public class FlexibilityControllerTest {
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5,
nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6,
nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7,
@@ -580,10 +691,10 @@ public class FlexibilityControllerTest {
}
@Test
- public void testWontStopJobFromRunning() {
- JobStatus js = createJobStatus("testWontStopJobFromRunning", createJob(101));
+ public void testWontStopAlreadyRunningJob() {
+ JobStatus js = createJobStatus("testWontStopAlreadyRunningJob", createJob(101));
// Stop satisfied constraints from causing a false positive.
- js.adjustNumRequiredFlexibleConstraints(100);
+ js.setNumAppliedFlexibleConstraints(100);
synchronized (mFlexibilityController.mLock) {
when(mJobSchedulerService.isCurrentlyRunningLocked(js)).thenReturn(true);
assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
@@ -593,10 +704,9 @@ public class FlexibilityControllerTest {
@Test
public void testFlexibilityTracker() {
FlexibilityController.FlexibilityTracker flexTracker =
- mFlexibilityController.new
- FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS);
+ mFlexibilityController.new FlexibilityTracker(4);
// Plus one for jobs with 0 required constraint.
- assertEquals(NUM_FLEXIBLE_CONSTRAINTS + 1, flexTracker.size());
+ assertEquals(4 + 1, flexTracker.size());
JobStatus[] jobs = new JobStatus[4];
JobInfo.Builder jb;
for (int i = 0; i < jobs.length; i++) {
@@ -622,21 +732,21 @@ public class FlexibilityControllerTest {
assertEquals(3, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
+ flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 1);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
assertEquals(2, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
+ flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 2);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(1, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
assertEquals(2, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
+ flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 3);
assertEquals(2, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
@@ -650,14 +760,14 @@ public class FlexibilityControllerTest {
assertEquals(1, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.resetJobNumDroppedConstraints(jobs[0], FROZEN_TIME);
+ flexTracker.calculateNumDroppedConstraints(jobs[0], FROZEN_TIME);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
assertEquals(2, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -2, FROZEN_TIME);
+ flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 2);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(1, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
@@ -669,7 +779,7 @@ public class FlexibilityControllerTest {
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- flexTracker.resetJobNumDroppedConstraints(jobs[0], nowElapsed);
+ flexTracker.calculateNumDroppedConstraints(jobs[0], nowElapsed);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
@@ -779,9 +889,9 @@ public class FlexibilityControllerTest {
mFlexibilityController.setConstraintSatisfied(
SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS, false, FROZEN_TIME);
// Require only a single constraint
- jsAny.adjustNumRequiredFlexibleConstraints(-3);
- jsCell.adjustNumRequiredFlexibleConstraints(-2);
- jsWifi.adjustNumRequiredFlexibleConstraints(-2);
+ jsAny.setNumAppliedFlexibleConstraints(1);
+ jsCell.setNumAppliedFlexibleConstraints(1);
+ jsWifi.setNumAppliedFlexibleConstraints(1);
synchronized (mFlexibilityController.mLock) {
jsAny.setTransportAffinitiesSatisfied(false);
jsCell.setTransportAffinitiesSatisfied(false);
@@ -1008,9 +1118,9 @@ public class FlexibilityControllerTest {
}
@Test
- public void testResetJobNumDroppedConstraints() {
+ public void testCalculateNumDroppedConstraints() {
JobInfo.Builder jb = createJob(22);
- JobStatus js = createJobStatus("testResetJobNumDroppedConstraints", jb);
+ JobStatus js = createJobStatus("testCalculateNumDroppedConstraints", jb);
long nowElapsed = FROZEN_TIME;
mFlexibilityController.mFlexibilityTracker.add(js);
@@ -1025,14 +1135,14 @@ public class FlexibilityControllerTest {
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
mFlexibilityController.mFlexibilityTracker
- .adjustJobsRequiredConstraints(js, -1, nowElapsed);
+ .setNumDroppedFlexibleConstraints(js, 1);
assertEquals(2, js.getNumRequiredFlexibleConstraints());
assertEquals(1, js.getNumDroppedFlexibleConstraints());
assertEquals(1, mFlexibilityController
.mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size());
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
assertEquals(2, js.getNumRequiredFlexibleConstraints());
assertEquals(1, js.getNumDroppedFlexibleConstraints());
@@ -1043,7 +1153,7 @@ public class FlexibilityControllerTest {
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
assertEquals(3, js.getNumRequiredFlexibleConstraints());
assertEquals(0, js.getNumDroppedFlexibleConstraints());
@@ -1054,7 +1164,7 @@ public class FlexibilityControllerTest {
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
assertEquals(0, js.getNumRequiredFlexibleConstraints());
assertEquals(3, js.getNumDroppedFlexibleConstraints());
@@ -1063,7 +1173,7 @@ public class FlexibilityControllerTest {
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
assertEquals(1, js.getNumRequiredFlexibleConstraints());
assertEquals(2, js.getNumDroppedFlexibleConstraints());
@@ -1139,20 +1249,28 @@ public class FlexibilityControllerTest {
}
@Test
- public void testDeviceDisabledFlexibility_Auto() {
- when(mPackageManager.hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(true);
+ public void testUnsupportedDevice_Auto() {
+ runTestUnsupportedDevice(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
+ @Test
+ public void testUnsupportedDevice_Embedded() {
+ runTestUnsupportedDevice(PackageManager.FEATURE_EMBEDDED);
+ }
+
+ private void runTestUnsupportedDevice(String feature) {
+ when(mPackageManager.hasSystemFeature(feature)).thenReturn(true);
mFlexibilityController =
new FlexibilityController(mJobSchedulerService, mPrefetchController);
- assertFalse(mFlexibilityController.mFlexibilityEnabled);
+ assertFalse(mFlexibilityController.isEnabled());
- JobStatus js = createJobStatus("testIsAuto", createJob(0));
+ JobStatus js = createJobStatus("testUnsupportedDevice", createJob(0));
mFlexibilityController.maybeStartTrackingJobLocked(js, null);
assertTrue(js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE));
- setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
- assertFalse(mFlexibilityController.mFlexibilityEnabled);
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS);
+ assertFalse(mFlexibilityController.isEnabled());
ArrayList<ArraySet<JobStatus>> jobs =
mFlexibilityController.mFlexibilityTracker.getArrayList();
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 8397b87706d6..293391f43828 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -232,6 +232,19 @@ public class JobStatusTest {
}
@Test
+ public void testFlexibleConstraintCounts() {
+ JobStatus js = createJobStatus(new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setUserInitiated(false)
+ .build());
+
+ js.setNumAppliedFlexibleConstraints(3);
+ js.setNumDroppedFlexibleConstraints(2);
+ assertEquals(3, js.getNumAppliedFlexibleConstraints());
+ assertEquals(2, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, js.getNumRequiredFlexibleConstraints());
+ }
+
+ @Test
public void testIsUserVisibleJob() {
JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
.setUserInitiated(false)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
index cf81f0a77702..e131a98b52d0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
@@ -16,12 +16,14 @@
package com.android.server.pm
+import android.app.AppOpsManager
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import com.android.server.testutils.any
import com.android.server.testutils.eq
import com.android.server.testutils.nullable
+import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -31,6 +33,7 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+
@RunWith(JUnit4::class)
class DistractingPackageHelperTest : PackageHelperTestBase() {
@@ -40,6 +43,9 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
super.setup()
distractingPackageHelper = DistractingPackageHelper(
pms, broadcastHelper, suspendPackageHelper)
+ whenever(rule.mocks().appOpsManager.checkOpNoThrow(
+ eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION), any(), any()))
+ .thenReturn(AppOpsManager.MODE_DEFAULT)
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 46806f334a27..28bd98781ced 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -15,6 +15,7 @@
*/
package com.android.server.pm
+import android.app.AppOpsManager
import android.app.PropertyInvalidatedCache
import android.content.Context
import android.content.Intent
@@ -151,7 +152,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
.mockStatic(EventLog::class.java)
.mockStatic(LocalServices::class.java)
.mockStatic(LocalManagerRegistry::class.java)
- .mockStatic(DeviceConfig::class.java)
+ .mockStatic(DeviceConfig::class.java, Mockito.CALLS_REAL_METHODS)
.mockStatic(HexEncoding::class.java)
.apply(withSession)
session = apply.startMocking()
@@ -192,6 +193,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
val userManagerService: UserManagerService = mock()
val componentResolver: ComponentResolver = mock()
val permissionManagerInternal: PermissionManagerServiceInternal = mock()
+ val appOpsManager: AppOpsManager = mock()
val incrementalManager: IncrementalManager = mock()
val platformCompat: PlatformCompat = mock()
val settings: Settings = mock()
@@ -304,6 +306,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
whenever(mocks.injector.defaultAppProvider) { mocks.defaultAppProvider }
whenever(mocks.injector.backgroundHandler) { mocks.backgroundHandler }
whenever(mocks.injector.updateOwnershipHelper) { mocks.updateOwnershipHelper }
+ whenever(mocks.injector.getSystemService(AppOpsManager::class.java)) { mocks.appOpsManager }
wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig)
whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP)
whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 7b381ce443e1..147303363200 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -16,12 +16,14 @@
package com.android.server.pm
+import android.app.AppOpsManager
import android.content.Intent
import android.content.pm.SuspendDialogInfo
import android.os.Binder
import android.os.PersistableBundle
import com.android.server.testutils.any
import com.android.server.testutils.eq
+import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,6 +34,12 @@ import org.mockito.Mockito.verify
@RunWith(JUnit4::class)
class SuspendPackageHelperTest : PackageHelperTestBase() {
+ override fun setup() {
+ super.setup()
+ whenever(rule.mocks().appOpsManager.checkOpNoThrow(
+ eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION), any(), any()))
+ .thenReturn(AppOpsManager.MODE_DEFAULT)
+ }
@Test
fun setPackagesSuspended() {
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index e89199dc9278..6537d47106a9 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -40,11 +40,13 @@
mediaSharedWithParent='true'
credentialShareableWithParent='false'
authAlwaysRequiredToDisableQuietMode='true'
+ allowStoppingUserWithDelayedLocking='true'
showInSettings='23'
hideInSettingsInQuietMode='true'
inheritDevicePolicy='450'
deleteAppWithParent='false'
alwaysVisible='true'
+ crossProfileContentSharingStrategy='0'
/>
</profile-type>
<profile-type name='custom.test.1' max-allowed-per-parent='14' />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 91140276cde0..a9967f63b3b3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -717,6 +717,45 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testSecondFingerSwipe_twoPointerDownAndActivatedState_shouldInPanningState() {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ PointF pointer1 = DEFAULT_POINT;
+ PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
+
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
+ //The minimum movement to transit to panningState.
+ final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ pointer2.offset(sWipeMinDistance + 1, 0);
+ send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 1));
+ fastForward(ViewConfiguration.getTapTimeout());
+ assertIn(STATE_PANNING);
+
+ returnToNormalFrom(STATE_PANNING);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTowFingerSwipe_twoPointerDownAndShortcutTriggeredState_shouldInPanningState() {
+ goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
+ PointF pointer1 = DEFAULT_POINT;
+ PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
+
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
+ //The minimum movement to transit to panningState.
+ final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ pointer2.offset(sWipeMinDistance + 1, 0);
+ send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 1));
+ fastForward(ViewConfiguration.getTapTimeout());
+ assertIn(STATE_PANNING);
+
+ returnToNormalFrom(STATE_PANNING);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testSecondFingerSwipe_twoPointerDownAndActivatedState_panningState() {
goFromStateIdleTo(STATE_ACTIVATED);
PointF pointer1 = DEFAULT_POINT;
@@ -734,6 +773,7 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testSecondFingerSwipe_twoPointerDownAndShortcutTriggeredState_panningState() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
PointF pointer1 = DEFAULT_POINT;
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 d26d67107001..77b1455a2ecc 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -92,6 +92,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
import android.view.Display;
@@ -104,11 +105,14 @@ import com.android.server.am.UserState.KeyEvictedCallback;
import com.android.server.pm.UserJourneyLogger;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
+import com.android.server.pm.UserTypeDetails;
+import com.android.server.pm.UserTypeFactory;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerService;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -175,6 +179,9 @@ public class UserControllerTest {
USER_START_MSG,
REPORT_LOCKED_BOOT_COMPLETE_MSG);
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() throws Exception {
runWithDexmakerShareClassLoader(() -> {
@@ -789,28 +796,99 @@ public class UserControllerTest {
}
@Test
- public void testStartProfile() throws Exception {
- setUpAndStartProfileInBackground(TEST_USER_ID1);
+ public void testStartManagedProfile() throws Exception {
+ setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED);
startBackgroundUserAssertions();
verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY);
}
@Test
- public void testStartProfile_whenUsersOnSecondaryDisplaysIsEnabled() throws Exception {
+ public void testStartManagedProfile_whenUsersOnSecondaryDisplaysIsEnabled() throws Exception {
mockIsUsersOnSecondaryDisplaysEnabled(true);
- setUpAndStartProfileInBackground(TEST_USER_ID1);
+ setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED);
startBackgroundUserAssertions();
verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY);
}
@Test
- public void testStopProfile() throws Exception {
- setUpAndStartProfileInBackground(TEST_USER_ID1);
+ public void testStopManagedProfile() throws Exception {
+ setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED);
+ assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true);
+ verifyUserUnassignedFromDisplay(TEST_USER_ID1);
+ }
+
+ @Test
+ public void testStopPrivateProfile() throws Exception {
+ mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+ setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE);
assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true);
verifyUserUnassignedFromDisplay(TEST_USER_ID1);
+
+ mSetFlagsRule.disableFlags(
+ android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+ setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_PRIVATE);
+ assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* expectLocking= */ true);
+ verifyUserUnassignedFromDisplay(TEST_USER_ID2);
+ }
+
+ @Test
+ public void testStopPrivateProfileWithDelayedLocking() throws Exception {
+ mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+ setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true,
+ /* keyEvictedCallback */ null, /* expectLocking= */ false);
+ }
+
+ @Test
+ public void testStopPrivateProfileWithDelayedLocking_flagDisabled() throws Exception {
+ mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+ mSetFlagsRule.disableFlags(
+ android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+ setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true,
+ /* keyEvictedCallback */ null, /* expectLocking= */ true);
+
+ mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+ mSetFlagsRule.enableFlags(
+ android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+ setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_PRIVATE);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* delayedLocking= */ true,
+ /* keyEvictedCallback */ null, /* expectLocking= */ true);
+ }
+
+ @Test
+ public void testStopPrivateProfileWithDelayedLocking_maxRunningUsersBreached()
+ throws Exception {
+ mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
+ /* maxRunningUsers= */ 1, /* delayUserDataLocking= */ false);
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+ setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE);
+ setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_MANAGED);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true,
+ /* keyEvictedCallback */ null, /* expectLocking= */ true);
+ }
+
+ @Test
+ public void testStopManagedProfileWithDelayedLocking() throws Exception {
+ mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+ setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true,
+ /* keyEvictedCallback */ null, /* expectLocking= */ true);
}
/** Tests handleIncomingUser() for a variety of permissions and situations. */
@@ -1001,8 +1079,8 @@ public class UserControllerTest {
mUserStates.put(userId, mUserController.getStartedUserState(userId));
}
- private void setUpAndStartProfileInBackground(int userId) throws Exception {
- setUpUser(userId, UserInfo.FLAG_PROFILE, false, UserManager.USER_TYPE_PROFILE_MANAGED);
+ private void setUpAndStartProfileInBackground(int userId, String userType) throws Exception {
+ setUpUser(userId, UserInfo.FLAG_PROFILE, false, userType);
assertThat(mUserController.startProfile(userId, /* evenWhenDisabled=*/ false,
/* unlockListener= */ null)).isTrue();
@@ -1070,6 +1148,11 @@ public class UserControllerTest {
userInfo.preCreated = preCreated;
when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated);
+
+ UserTypeDetails userTypeDetails = UserTypeFactory.getUserTypes().get(userType);
+ assertThat(userTypeDetails).isNotNull();
+ when(mInjector.mUserManagerInternalMock.getUserProperties(eq(userId)))
+ .thenReturn(userTypeDetails.getDefaultUserPropertiesReference());
}
private static List<String> getActions(List<Intent> intents) {
diff --git a/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
new file mode 100644
index 000000000000..472a82c02937
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.audio;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.audiofx.AudioEffect;
+import android.os.Message;
+import android.util.Log;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class MusicFxHelperTest {
+ private static final String TAG = "MusicFxHelperTest";
+
+ @Mock private AudioService mMockAudioService;
+ @Mock private Context mMockContext;
+ @Mock private PackageManager mMockPackageManager;
+
+ private ResolveInfo mResolveInfo1 = new ResolveInfo();
+ private ResolveInfo mResolveInfo2 = new ResolveInfo();
+ private final String mTestPkg1 = "testPkg1", mTestPkg2 = "testPkg2", mTestPkg3 = "testPkg3";
+ private final String mMusicFxPkgName = "com.android.musicfx";
+ private final int mTestUid1 = 1, mTestUid2 = 2, mTestUid3 = 3, mMusicFxUid = 78;
+ private final int mTestSession1 = 11, mTestSession2 = 22, mTestSession3 = 33;
+
+ private List<ResolveInfo> mEmptyList = new ArrayList<>();
+ private List<ResolveInfo> mSingleList = new ArrayList<>();
+ private List<ResolveInfo> mDoubleList = new ArrayList<>();
+
+ // the class being unit-tested here
+ @InjectMocks private MusicFxHelper mMusicFxHelper;
+
+ @Before
+ @SuppressWarnings("DirectInvocationOnMock")
+ public void setUp() throws Exception {
+ mMockAudioService = mock(AudioService.class);
+ mMusicFxHelper = mMockAudioService.getMusicFxHelper();
+ MockitoAnnotations.initMocks(this);
+
+ mResolveInfo1.activityInfo = new ActivityInfo();
+ mResolveInfo1.activityInfo.packageName = mTestPkg1;
+ mResolveInfo2.activityInfo = new ActivityInfo();
+ mResolveInfo2.activityInfo.packageName = mTestPkg2;
+
+ mSingleList.add(mResolveInfo1);
+ mDoubleList.add(mResolveInfo1);
+ mDoubleList.add(mResolveInfo2);
+
+ Assert.assertNotNull(mMusicFxHelper);
+ }
+
+ private Intent newIntent(String action, String packageName, int sessionId) {
+ Intent intent = new Intent(action);
+ if (packageName != null) {
+ intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, packageName);
+ }
+ intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId);
+ return intent;
+ }
+
+ /**
+ * Helper function to send ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION intent with verification.
+ *
+ * @throws NameNotFoundException if no such package is available to the caller.
+ */
+ private void openSessionWithResList(
+ List<ResolveInfo> list, int bind, int broadcast, String packageName, int audioSession,
+ int uid) {
+ doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+ doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt());
+ if (list != null && list.size() != 0) {
+ try {
+ doReturn(uid).when(mMockPackageManager)
+ .getPackageUidAsUser(eq(packageName), anyObject(), anyInt());
+ doReturn(mMusicFxUid).when(mMockPackageManager)
+ .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt());
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "NameNotFoundException: " + e);
+ }
+ }
+
+ Intent intent = newIntent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION,
+ packageName, audioSession);
+ mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
+ verify(mMockContext, times(bind))
+ .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
+ verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject());
+ }
+
+ /**
+ * Helper function to send ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION intent with verification.
+ *
+ * @throws NameNotFoundException if no such package is available to the caller.
+ */
+ private void closeSessionWithResList(
+ List<ResolveInfo> list, int unBind, int broadcast, String packageName,
+ int audioSession, int uid) {
+ doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+ doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt());
+ if (list != null && list.size() != 0) {
+ try {
+ doReturn(uid).when(mMockPackageManager)
+ .getPackageUidAsUser(eq(packageName), anyObject(), anyInt());
+ doReturn(mMusicFxUid).when(mMockPackageManager)
+ .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt());
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "NameNotFoundException: " + e);
+ }
+ }
+
+ Intent intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION,
+ packageName, audioSession);
+ mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
+ verify(mMockContext, times(unBind)).unbindService(anyObject());
+ verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject());
+ }
+
+ /**
+ * Helper function to send MSG_EFFECT_CLIENT_GONE message with verification.
+ */
+ private void sendMessage(int msgId, int uid, int unBinds, int broadcasts) {
+ mMusicFxHelper.handleMessage(Message.obtain(null, msgId, uid /* arg1 */, 0 /* arg2 */));
+ verify(mMockContext, times(broadcasts)).sendBroadcastAsUser(anyObject(), anyObject());
+ verify(mMockContext, times(unBinds)).unbindService(anyObject());
+ }
+
+ /**
+ * Send invalid message to MusicFxHelper.
+ */
+ @Test
+ public void testInvalidMessage() {
+ Log.i(TAG, "running testInvalidMessage");
+
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE - 1, 0, 0, 0);
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE + 1, 0, 0, 0);
+ }
+
+ /**
+ * Send client gone message to MusicFxHelper when no client exist.
+ */
+ @Test
+ public void testGoneMessageWhenNoClient() {
+ Log.i(TAG, "running testGoneMessageWhenNoClient");
+
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, 0, 0, 0);
+ }
+
+ /**
+ * Send ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION intent to MusicFxHelper when no session exist.
+ */
+ @Test
+ public void testCloseBroadcastIntent() {
+ Log.i(TAG, "running testCloseBroadcastIntent");
+
+ closeSessionWithResList(null, 0, 0, null, mTestSession1, mTestUid1);
+ }
+
+ /**
+ * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION intent when target application package was set.
+ * When the target application package was set for an intent, it means this intent is limited
+ * to a specific target application, as a result MusicFxHelper will not handle this intent.
+ */
+ @Test
+ public void testBroadcastIntentWithPackage() {
+ Log.i(TAG, "running testBroadcastIntentWithPackage");
+
+ Intent intent = newIntent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION, null, 1);
+ intent.setPackage(mTestPkg1);
+ mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
+ verify(mMockContext, times(0))
+ .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
+ verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject());
+
+ intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION, null, 1);
+ intent.setPackage(mTestPkg2);
+ mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
+ verify(mMockContext, times(0))
+ .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
+ verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject());
+ }
+
+ /**
+ * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION with no broadcast receiver.
+ */
+ @Test
+ public void testBroadcastIntentWithNoPackageAndNoBroadcastReceiver() {
+ Log.i(TAG, "running testBroadcastIntentWithNoPackageAndNoBroadcastReceiver");
+
+ openSessionWithResList(mEmptyList, 0, 0, null, mTestSession1, mTestUid1);
+ closeSessionWithResList(mEmptyList, 0, 0, null, mTestSession1, mTestUid1);
+ }
+
+ /**
+ * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION with one broadcast receiver.
+ */
+ @Test
+ public void testBroadcastIntentWithNoPackageAndOneBroadcastReceiver() {
+ Log.i(TAG, "running testBroadcastIntentWithNoPackageAndOneBroadcastReceiver");
+
+ int broadcasts = 1, bind = 1, unbind = 1;
+ openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession1, mTestUid1);
+
+ // repeat with different session ID
+ broadcasts = broadcasts + 1;
+ bind = bind + 1;
+ unbind = unbind + 1;
+ openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession2, mTestUid1);
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession2, mTestUid1);
+
+ // repeat with different UID
+ broadcasts = broadcasts + 1;
+ bind = bind + 1;
+ unbind = unbind + 1;
+ openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession1, mTestUid2);
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession1, mTestUid2);
+ }
+
+ /**
+ * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION with two broadcast receivers.
+ */
+ @Test
+ public void testBroadcastIntentWithNoPackageAndTwoBroadcastReceivers() {
+ Log.i(TAG, "running testBroadcastIntentWithNoPackageAndTwoBroadcastReceivers");
+
+ openSessionWithResList(mDoubleList, 1, 1, null, mTestSession1, mTestUid1);
+ closeSessionWithResList(mDoubleList, 1, 2, null, mTestSession1, mTestUid1);
+ }
+
+ /**
+ * Open/close session UID not matching.
+ * No broadcast for mismatching sessionID/UID/packageName.
+ */
+ @Test
+ public void testBroadcastBadIntents() {
+ Log.i(TAG, "running testBroadcastBadIntents");
+
+ int broadcasts = 1;
+ openSessionWithResList(mSingleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ // mismatch UID
+ closeSessionWithResList(mSingleList, 0, broadcasts, mTestPkg1, mTestSession1, mTestUid2);
+ // mismatch AudioSession
+ closeSessionWithResList(mSingleList, 0, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+ // mismatch packageName
+ closeSessionWithResList(mSingleList, 0, broadcasts, mTestPkg2, mTestSession1, mTestUid1);
+
+ // cleanup with correct UID and session ID
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mSingleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ }
+
+ /**
+ * Open/close sessions with one UID, some with correct intents some with illegal intents.
+ * No broadcast for mismatching sessionID/UID/packageName.
+ */
+ @Test
+ public void testBroadcastGoodAndBadIntents() {
+ Log.i(TAG, "running testBroadcastGoodAndBadIntents");
+
+ int broadcasts = 1, bind = 1, unbind = 0;
+ openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ // mismatch packageName, session ID and UID
+ closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+ mTestUid2);
+ // mismatch session ID and mismatch UID
+ closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+ mTestUid2);
+ // mismatch packageName and mismatch UID
+ closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession1,
+ mTestUid2);
+ // mismatch packageName and sessionID
+ closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+ mTestUid1);
+ // inconsistency package name for same UID
+ openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid1);
+ // open session2 with good intent
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+
+ // cleanup with correct UID and session ID
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+ broadcasts = broadcasts + 1;
+ unbind = unbind + 1;
+ closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+ mTestUid1);
+ }
+
+ /**
+ * Send ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION when there is no listener.
+ */
+ @Test
+ public void testBroadcastOpenSessionWithValidPackageNameAndNoListener() {
+ Log.i(TAG, "running testBroadcastOpenSessionWithValidPackageNameAndNoListener");
+
+ // null listener list should not trigger any action
+ openSessionWithResList(null, 0, 0, mTestPkg1, mTestSession1, mTestUid1);
+ // empty listener list should not trigger any action
+ openSessionWithResList(mEmptyList, 0, 0, mTestPkg1, mTestSession1, mTestUid1);
+ }
+
+ /**
+ * One MusicFx client, open session and close.
+ */
+ @Test
+ public void testOpenCloseAudioSession() {
+ Log.i(TAG, "running testOpenCloseAudioSession");
+
+ int broadcasts = 1;
+ openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ }
+
+ /**
+ * One MusicFx client, open session and close, then gone.
+ */
+ @Test
+ public void testOpenCloseAudioSessionAndGone() {
+ Log.i(TAG, "running testOpenCloseAudioSessionAndGone");
+
+ int broadcasts = 1;
+ openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, 0, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+
+ broadcasts = broadcasts + 1; // 1 open session left
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, 1, broadcasts);
+ }
+
+ /**
+ * One MusicFx client, open session, then UID gone without close.
+ */
+ @Test
+ public void testOpenOneSessionAndGo() {
+ Log.i(TAG, "running testOpenOneSessionAndGo");
+
+ int broadcasts = 1;
+ openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+
+ broadcasts = broadcasts + 1;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, 1, broadcasts);
+ }
+
+ /**
+ * Two MusicFx clients open and close sessions.
+ */
+ @Test
+ public void testOpenTwoSessionsAndClose() {
+ Log.i(TAG, "running testOpenTwoSessionsAndClose");
+
+ int broadcasts = 1, bind = 1, unbind = 0;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+ mTestUid2);
+ broadcasts = broadcasts + 1;
+ unbind = unbind + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+
+ broadcasts = broadcasts + 1;
+ bind = bind + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+ broadcasts = broadcasts + 1;
+ unbind = unbind + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+ mTestUid2);
+ }
+
+ /**
+ * Two MusicFx clients open sessions, then both UID gone without close.
+ */
+ @Test
+ public void testOpenTwoSessionsAndGo() {
+ Log.i(TAG, "running testOpenTwoSessionsAndGo");
+
+ int broadcasts = 1, bind = 1, unbind = 0;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+
+ broadcasts = broadcasts + 1;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, unbind, broadcasts);
+
+ broadcasts = broadcasts + 1;
+ unbind = unbind + 1;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+ }
+
+ /**
+ * Two MusicFx clients open sessions, one close but not gone, the other one gone without close.
+ */
+ @Test
+ public void testTwoSessionsOpenOneCloseOneGo() {
+ Log.i(TAG, "running testTwoSessionsOpneAndOneCloseOneGo");
+
+ int broadcasts = 1, bind = 1, unbind = 0;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+
+ broadcasts = broadcasts + 1;
+ unbind = unbind + 1;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+ }
+
+ /**
+ * One MusicFx client, open multiple audio sessions, and close all sessions.
+ */
+ @Test
+ public void testTwoSessionsInSameUidOpenClose() {
+ Log.i(TAG, "running testTwoSessionsOpneAndOneCloseOneGo");
+
+ int broadcasts = 1, bind = 1, unbind = 0;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+ broadcasts = broadcasts + 1;
+ unbind = unbind + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+ mTestUid1);
+ }
+
+ /**
+ * Three MusicFx clients, each with multiple audio sessions, and close all sessions.
+ */
+ @Test
+ public void testThreeSessionsInThreeUidOpenClose() {
+ Log.i(TAG, "running testThreeSessionsInThreeUidOpenClose");
+
+ int broadcasts = 1, bind = 1, unbind = 0;
+ //client1
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+ // client2
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession3, mTestUid2);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+ // client3
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid3);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession3, mTestUid3);
+
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg3, mTestSession3,
+ mTestUid3);
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+ mTestUid2);
+ // all sessions of client1 closed
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+ mTestUid1);
+ // all sessions of client3 closed
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg3, mTestSession1,
+ mTestUid3);
+ // all sessions of client2 closed
+ broadcasts = broadcasts + 1;
+ // now expect unbind to happen
+ unbind = unbind + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession3,
+ mTestUid2);
+ }
+
+ /**
+ * Two MusicFx clients, with multiple audio sessions, one close all sessions, and other gone.
+ */
+ @Test
+ public void testTwoUidOneCloseOneGo() {
+ Log.i(TAG, "running testTwoUidOneCloseOneGo");
+
+ int broadcasts = 1, bind = 1, unbind = 0;
+ //client1
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+ // client2
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession1, mTestUid2);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+ // client2 gone
+ broadcasts = broadcasts + 2;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+ // client 1 close all sessions
+ broadcasts = broadcasts + 1;
+ unbind = unbind + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+ mTestUid1);
+ }
+
+ /**
+ * Three MusicFx clients, with multiple audio sessions, all UID gone.
+ */
+ @Test
+ public void testThreeUidAllGo() {
+ Log.i(TAG, "running testThreeUidAllGo");
+
+ int broadcasts = 1, bind = 1, unbind = 0;
+ //client1
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+ // client2
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession3, mTestUid2);
+ // client3
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession3, mTestUid3);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid3);
+
+ // client2 gone
+ broadcasts = broadcasts + 2;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+ // client3 gone
+ broadcasts = broadcasts + 2;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid3, unbind, broadcasts);
+ // client 1 gone
+ broadcasts = broadcasts + 2;
+ // now expect unbindService to happen
+ unbind = unbind + 1;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, unbind, broadcasts);
+ }
+
+ /**
+ * Three MusicFx clients, multiple audio sessions, open and UID gone in difference sequence.
+ */
+ @Test
+ public void testThreeUidDiffSequence() {
+ Log.i(TAG, "running testThreeUidDiffSequence");
+
+ int broadcasts = 1, bind = 1, unbind = 0;
+ //client1
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+ // client2
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+ // client1 close one session
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+ // client2 open another session
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession3, mTestUid2);
+ // client3 open one session
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession3, mTestUid3);
+ // client2 gone
+ broadcasts = broadcasts + 2;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+ // client3 open another session
+ broadcasts = broadcasts + 1;
+ openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid3);
+ // client1 close another session, and gone
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+ mTestUid1);
+ // last UID client3 gone, unbind
+ broadcasts = broadcasts + 2;
+ unbind = unbind + 1;
+ sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid3, unbind, broadcasts);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index 33559107dfbb..18e6f0a2cc57 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -151,6 +151,19 @@ public class GenericWindowPolicyControllerTest {
}
@Test
+ public void userNotAllowlisted_launchIsBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithNoAllowedUsers();
+ gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
public void openNonBlockedAppOnVirtualDisplay_isNotBlocked() {
GenericWindowPolicyController gwpc = createGwpc();
gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
@@ -702,6 +715,26 @@ public class GenericWindowPolicyControllerTest {
/* customHomeComponent= */ null);
}
+ private GenericWindowPolicyController createGwpcWithNoAllowedUsers() {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ mSecureWindowCallback,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
private GenericWindowPolicyController createGwpcWithCustomHomeComponent(
ComponentName homeComponent) {
return new GenericWindowPolicyController(
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index d70a4fd555ec..d7ed7c2d6469 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -69,8 +69,10 @@ public class UserManagerServiceUserPropertiesTest {
.setMediaSharedWithParent(false)
.setCredentialShareableWithParent(true)
.setAuthAlwaysRequiredToDisableQuietMode(false)
+ .setAllowStoppingUserWithDelayedLocking(false)
.setDeleteAppWithParent(false)
.setAlwaysVisible(false)
+ .setCrossProfileContentSharingStrategy(0)
.build();
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
@@ -84,8 +86,10 @@ public class UserManagerServiceUserPropertiesTest {
actualProps.setMediaSharedWithParent(true);
actualProps.setCredentialShareableWithParent(false);
actualProps.setAuthAlwaysRequiredToDisableQuietMode(true);
+ actualProps.setAllowStoppingUserWithDelayedLocking(true);
actualProps.setDeleteAppWithParent(true);
actualProps.setAlwaysVisible(true);
+ actualProps.setCrossProfileContentSharingStrategy(1);
// Write the properties to xml.
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -128,6 +132,7 @@ public class UserManagerServiceUserPropertiesTest {
.setMediaSharedWithParent(true)
.setDeleteAppWithParent(true)
.setAuthAlwaysRequiredToDisableQuietMode(false)
+ .setAllowStoppingUserWithDelayedLocking(false)
.setAlwaysVisible(true)
.build();
final UserProperties orig = new UserProperties(defaultProps);
@@ -137,6 +142,7 @@ public class UserManagerServiceUserPropertiesTest {
orig.setInheritDevicePolicy(9456);
orig.setDeleteAppWithParent(false);
orig.setAuthAlwaysRequiredToDisableQuietMode(true);
+ orig.setAllowStoppingUserWithDelayedLocking(true);
orig.setAlwaysVisible(false);
// Test every permission level. (Currently, it's linear so it's easy.)
@@ -182,6 +188,8 @@ public class UserManagerServiceUserPropertiesTest {
assertEqualGetterOrThrows(orig::getDeleteAppWithParent,
copy::getDeleteAppWithParent, exposeAll);
assertEqualGetterOrThrows(orig::getAlwaysVisible, copy::getAlwaysVisible, exposeAll);
+ assertEqualGetterOrThrows(orig::getAllowStoppingUserWithDelayedLocking,
+ copy::getAllowStoppingUserWithDelayedLocking, exposeAll);
// Items requiring hasManagePermission - put them here using hasManagePermission.
assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
@@ -199,6 +207,8 @@ public class UserManagerServiceUserPropertiesTest {
copy::isMediaSharedWithParent, true);
assertEqualGetterOrThrows(orig::isCredentialShareableWithParent,
copy::isCredentialShareableWithParent, true);
+ assertEqualGetterOrThrows(orig::getCrossProfileContentSharingStrategy,
+ copy::getCrossProfileContentSharingStrategy, true);
}
/**
@@ -254,7 +264,11 @@ public class UserManagerServiceUserPropertiesTest {
.isEqualTo(actual.isCredentialShareableWithParent());
assertThat(expected.isAuthAlwaysRequiredToDisableQuietMode())
.isEqualTo(actual.isAuthAlwaysRequiredToDisableQuietMode());
+ assertThat(expected.getAllowStoppingUserWithDelayedLocking())
+ .isEqualTo(actual.getAllowStoppingUserWithDelayedLocking());
assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent());
assertThat(expected.getAlwaysVisible()).isEqualTo(actual.getAlwaysVisible());
+ assertThat(expected.getCrossProfileContentSharingStrategy())
+ .isEqualTo(actual.getCrossProfileContentSharingStrategy());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 77f693917574..70837061b0bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -29,7 +29,6 @@ import static com.android.server.pm.UserTypeDetails.UNLIMITED_NUMBER_OF_USERS;
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.assertNotSame;
import static org.junit.Assert.assertTrue;
@@ -91,12 +90,14 @@ public class UserManagerServiceUserTypeTest {
.setMediaSharedWithParent(true)
.setCredentialShareableWithParent(false)
.setAuthAlwaysRequiredToDisableQuietMode(true)
+ .setAllowStoppingUserWithDelayedLocking(true)
.setShowInSettings(900)
.setShowInSharingSurfaces(20)
.setShowInQuietMode(30)
.setInheritDevicePolicy(340)
.setDeleteAppWithParent(true)
- .setAlwaysVisible(true);
+ .setAlwaysVisible(true)
+ .setCrossProfileContentSharingStrategy(1);
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
@@ -167,6 +168,8 @@ public class UserManagerServiceUserTypeTest {
assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent());
assertTrue(type.getDefaultUserPropertiesReference()
.isAuthAlwaysRequiredToDisableQuietMode());
+ assertTrue(type.getDefaultUserPropertiesReference()
+ .getAllowStoppingUserWithDelayedLocking());
assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings());
assertEquals(20, type.getDefaultUserPropertiesReference().getShowInSharingSurfaces());
assertEquals(30,
@@ -175,6 +178,8 @@ public class UserManagerServiceUserTypeTest {
.getInheritDevicePolicy());
assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertTrue(type.getDefaultUserPropertiesReference().getAlwaysVisible());
+ assertEquals(1, type.getDefaultUserPropertiesReference()
+ .getCrossProfileContentSharingStrategy());
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -231,6 +236,8 @@ public class UserManagerServiceUserTypeTest {
assertEquals(UserProperties.SHOW_IN_LAUNCHER_SEPARATE, props.getShowInSharingSurfaces());
assertEquals(UserProperties.SHOW_IN_QUIET_MODE_PAUSED,
props.getShowInQuietMode());
+ assertEquals(UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION,
+ props.getCrossProfileContentSharingStrategy());
assertFalse(type.hasBadge());
}
@@ -318,12 +325,14 @@ public class UserManagerServiceUserTypeTest {
.setMediaSharedWithParent(false)
.setCredentialShareableWithParent(true)
.setAuthAlwaysRequiredToDisableQuietMode(false)
+ .setAllowStoppingUserWithDelayedLocking(false)
.setShowInSettings(20)
.setInheritDevicePolicy(21)
.setShowInSharingSurfaces(22)
.setShowInQuietMode(24)
.setDeleteAppWithParent(true)
- .setAlwaysVisible(false);
+ .setAlwaysVisible(false)
+ .setCrossProfileContentSharingStrategy(1);
final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
builders.put(userTypeAosp1, new UserTypeDetails.Builder()
@@ -362,6 +371,8 @@ public class UserManagerServiceUserTypeTest {
.isCredentialShareableWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference()
.isAuthAlwaysRequiredToDisableQuietMode());
+ assertFalse(aospType.getDefaultUserPropertiesReference()
+ .getAllowStoppingUserWithDelayedLocking());
assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings());
assertEquals(21, aospType.getDefaultUserPropertiesReference()
.getInheritDevicePolicy());
@@ -370,6 +381,8 @@ public class UserManagerServiceUserTypeTest {
aospType.getDefaultUserPropertiesReference().getShowInQuietMode());
assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
+ assertEquals(1, aospType.getDefaultUserPropertiesReference()
+ .getCrossProfileContentSharingStrategy());
// userTypeAosp2 should be modified.
aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -413,6 +426,8 @@ public class UserManagerServiceUserTypeTest {
.isCredentialShareableWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference()
.isAuthAlwaysRequiredToDisableQuietMode());
+ assertTrue(aospType.getDefaultUserPropertiesReference()
+ .getAllowStoppingUserWithDelayedLocking());
assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings());
assertEquals(22,
aospType.getDefaultUserPropertiesReference().getShowInSharingSurfaces());
@@ -422,6 +437,8 @@ public class UserManagerServiceUserTypeTest {
.getInheritDevicePolicy());
assertFalse(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
+ assertEquals(0, aospType.getDefaultUserPropertiesReference()
+ .getCrossProfileContentSharingStrategy());
// userTypeOem1 should be created.
UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 8933c6c56797..a743fff5d2ea 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.assertTrue;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -219,6 +218,8 @@ public final class UserManagerTest {
.isEqualTo(cloneUserProperties.isMediaSharedWithParent());
assertThat(typeProps.isCredentialShareableWithParent())
.isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
+ assertThat(typeProps.getCrossProfileContentSharingStrategy())
+ .isEqualTo(cloneUserProperties.getCrossProfileContentSharingStrategy());
assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
assertThrows(SecurityException.class, cloneUserProperties::getAlwaysVisible);
compareDrawables(mUserManager.getUserBadge(),
@@ -338,9 +339,15 @@ public final class UserManagerTest {
assertThat(typeProps.isAuthAlwaysRequiredToDisableQuietMode())
.isEqualTo(privateProfileUserProperties
.isAuthAlwaysRequiredToDisableQuietMode());
+ assertThat(typeProps.getCrossProfileContentSharingStrategy())
+ .isEqualTo(privateProfileUserProperties.getCrossProfileContentSharingStrategy());
assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent);
+ assertThrows(SecurityException.class,
+ privateProfileUserProperties::getAllowStoppingUserWithDelayedLocking);
+
compareDrawables(mUserManager.getUserBadge(),
Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
+
// Verify private profile parent
assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id);
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index c3074bb0fee8..ef197918deff 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -99,11 +99,6 @@
android:theme="@style/WhiteBackgroundTheme"
android:exported="true"/>
- <activity android:name="com.android.server.wm.TrustedPresentationCallbackTest$TestActivity"
- android:exported="true"
- android:showWhenLocked="true"
- android:turnScreenOn="true" />
-
<activity android:name="android.app.Activity"
android:exported="true"
android:showWhenLocked="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java
new file mode 100644
index 000000000000..44b69f18eb04
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.hardware.display.DeviceProductInfo.CONNECTION_TO_SINK_UNKNOWN;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorners.NO_ROUNDED_CORNERS;
+
+import static com.android.server.wm.DeferredDisplayUpdater.DEFERRABLE_FIELDS;
+import static com.android.server.wm.DeferredDisplayUpdater.DIFF_NOT_WM_DEFERRABLE;
+import static com.android.server.wm.DeferredDisplayUpdater.DIFF_WM_DEFERRABLE;
+import static com.android.server.wm.DeferredDisplayUpdater.calculateDisplayInfoDiff;
+import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.NonNull;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.hardware.display.DeviceProductInfo;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayAddress;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.DisplayShape;
+import android.view.RoundedCorner;
+import android.view.RoundedCorners;
+import android.view.SurfaceControl.RefreshRateRange;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * Build/Install/Run:
+ * atest WmTests:DeferredDisplayUpdaterDiffTest
+ */
+@SmallTest
+@Presubmit
+public class DeferredDisplayUpdaterDiffTest {
+
+ private static final Set<String> IGNORED_FIELDS = new HashSet<>(Arrays.asList(
+ "name" // human-readable name is ignored in equals() checks
+ ));
+
+ private static final DisplayInfo EMPTY = new DisplayInfo();
+
+ @Test
+ public void testCalculateDisplayInfoDiff_allDifferent_returnsChanges() {
+ final DisplayInfo first = new DisplayInfo();
+ final DisplayInfo second = new DisplayInfo();
+ makeAllFieldsDifferent(first, second);
+
+ int diff = calculateDisplayInfoDiff(first, second);
+
+ assertWithMessage("Expected to receive a non-zero difference when "
+ + "there are changes in all fields of DisplayInfo\n"
+ + "Make sure that you have updated calculateDisplayInfoDiff function after "
+ + "changing DisplayInfo fields").that(diff).isGreaterThan(0);
+ }
+
+ @Test
+ public void testCalculateDisplayInfoDiff_forEveryDifferentField_returnsChanges() {
+ generateWithSingleDifferentField((first, second, field) -> {
+ int diff = calculateDisplayInfoDiff(first, second);
+
+ assertWithMessage("Expected to receive a non-zero difference when "
+ + "there are changes in " + field + "\n"
+ + "Make sure that you have updated calculateDisplayInfoDiff function after "
+ + "changing DisplayInfo fields").that(diff).isGreaterThan(0);
+ });
+ }
+
+ @Test
+ public void testCalculateDisplayInfoDiff_forEveryDifferentField_returnsMatchingChange() {
+ generateWithSingleDifferentField((first, second, field) -> {
+ boolean hasDeferrableFieldChange = hasDeferrableFieldChange(first, second);
+ int expectedDiff =
+ hasDeferrableFieldChange ? DIFF_WM_DEFERRABLE : DIFF_NOT_WM_DEFERRABLE;
+
+ int diff = calculateDisplayInfoDiff(first, second);
+
+ assertWithMessage("Expected to have diff = " + expectedDiff
+ + ", for field = " + field + "\n"
+ + "Make sure that you have updated calculateDisplayInfoDiff function after "
+ + "changing DisplayInfo fields").that(
+ diff).isEqualTo(expectedDiff);
+ });
+ }
+
+ /**
+ * Sets each field of the objects to different values using reflection
+ */
+ private static void makeAllFieldsDifferent(@NonNull DisplayInfo first,
+ @NonNull DisplayInfo second) {
+ forEachDisplayInfoField(field -> {
+ try {
+ setDifferentFieldValues(first, second, field);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ private static boolean hasDeferrableFieldChange(@NonNull DisplayInfo first,
+ @NonNull DisplayInfo second) {
+ final DisplayInfo firstDeferrableFieldsOnly = new DisplayInfo();
+ final DisplayInfo secondDeferrableFieldsOnly = new DisplayInfo();
+
+ copyDisplayInfoFields(/* out= */ firstDeferrableFieldsOnly, /* base= */
+ EMPTY, /* override= */ first, DEFERRABLE_FIELDS);
+ copyDisplayInfoFields(/* out= */ secondDeferrableFieldsOnly, /* base= */
+ EMPTY, /* override= */ second, DEFERRABLE_FIELDS);
+
+ return !firstDeferrableFieldsOnly.equals(secondDeferrableFieldsOnly);
+ }
+
+ /**
+ * Creates pairs of DisplayInfos where only one field is different, the callback is called for
+ * each field
+ */
+ private static void generateWithSingleDifferentField(DisplayInfoConsumer consumer) {
+ forEachDisplayInfoField(field -> {
+ final DisplayInfo first = new DisplayInfo();
+ final DisplayInfo second = new DisplayInfo();
+
+ try {
+ setDifferentFieldValues(first, second, field);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+
+ consumer.consume(first, second, field);
+ });
+ }
+
+ private static void setDifferentFieldValues(@NonNull DisplayInfo first,
+ @NonNull DisplayInfo second,
+ @NonNull Field field) throws IllegalAccessException {
+ final Class<?> type = field.getType();
+ if (type.equals(int.class)) {
+ field.setInt(first, 1);
+ field.setInt(second, 2);
+ } else if (type.equals(double.class)) {
+ field.setDouble(first, 1.0);
+ field.setDouble(second, 2.0);
+ } else if (type.equals(short.class)) {
+ field.setShort(first, (short) 1);
+ field.setShort(second, (short) 2);
+ } else if (type.equals(long.class)) {
+ field.setLong(first, 1L);
+ field.setLong(second, 2L);
+ } else if (type.equals(char.class)) {
+ field.setChar(first, 'a');
+ field.setChar(second, 'b');
+ } else if (type.equals(byte.class)) {
+ field.setByte(first, (byte) 1);
+ field.setByte(second, (byte) 2);
+ } else if (type.equals(float.class)) {
+ field.setFloat(first, 1.0f);
+ field.setFloat(second, 2.0f);
+ } else if (type == boolean.class) {
+ field.setBoolean(first, true);
+ field.setBoolean(second, false);
+ } else if (type.equals(String.class)) {
+ field.set(first, "one");
+ field.set(second, "two");
+ } else if (type.equals(DisplayAddress.class)) {
+ field.set(first, DisplayAddress.fromPhysicalDisplayId(0));
+ field.set(second, DisplayAddress.fromPhysicalDisplayId(1));
+ } else if (type.equals(DeviceProductInfo.class)) {
+ field.set(first, new DeviceProductInfo("name", "pnp_id", "product_id1", 2023,
+ CONNECTION_TO_SINK_UNKNOWN));
+ field.set(second, new DeviceProductInfo("name", "pnp_id", "product_id2", 2023,
+ CONNECTION_TO_SINK_UNKNOWN));
+ } else if (type.equals(DisplayCutout.class)) {
+ field.set(first,
+ new DisplayCutout(Insets.NONE, new Rect(0, 0, 100, 100), null, null,
+ null));
+ field.set(second,
+ new DisplayCutout(Insets.NONE, new Rect(0, 0, 200, 200), null, null,
+ null));
+ } else if (type.equals(RoundedCorners.class)) {
+ field.set(first, NO_ROUNDED_CORNERS);
+
+ final RoundedCorners other = new RoundedCorners(NO_ROUNDED_CORNERS);
+ other.setRoundedCorner(POSITION_TOP_LEFT,
+ new RoundedCorner(POSITION_TOP_LEFT, 1, 2, 3));
+ field.set(second, other);
+ } else if (type.equals(DisplayShape.class)) {
+ field.set(first, DisplayShape.createDefaultDisplayShape(100, 200, false));
+ field.set(second, DisplayShape.createDefaultDisplayShape(50, 100, false));
+ } else if (type.equals(RefreshRateRange.class)) {
+ field.set(first, new RefreshRateRange(0, 100));
+ field.set(second, new RefreshRateRange(20, 80));
+ } else if (type.equals(Display.HdrCapabilities.class)) {
+ field.set(first, new Display.HdrCapabilities(new int[]{0}, 100, 50, 25));
+ field.set(second, new Display.HdrCapabilities(new int[]{1}, 100, 50, 25));
+ } else if (type.equals(SparseArray.class)
+ && ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(
+ RefreshRateRange.class)) {
+ final SparseArray<RefreshRateRange> array1 = new SparseArray<>();
+ array1.set(0, new RefreshRateRange(0, 100));
+ final SparseArray<RefreshRateRange> array2 = new SparseArray<>();
+ array2.set(0, new RefreshRateRange(20, 80));
+ field.set(first, array1);
+ field.set(second, array2);
+ } else if (type.isArray() && type.getComponentType().equals(int.class)) {
+ field.set(first, new int[]{0});
+ field.set(second, new int[]{1});
+ } else if (type.isArray() && type.getComponentType().equals(Display.Mode.class)) {
+ field.set(first, new Display.Mode[]{new Display.Mode(100, 200, 300)});
+ field.set(second, new Display.Mode[]{new Display.Mode(10, 20, 30)});
+ } else {
+ throw new IllegalArgumentException("Field " + field
+ + " is not supported by this test, please add implementation of setting "
+ + "different values for this field");
+ }
+ }
+
+ private interface DisplayInfoConsumer {
+ void consume(DisplayInfo first, DisplayInfo second, Field field);
+ }
+
+ /**
+ * Iterates over every non-static field of DisplayInfo class except IGNORED_FIELDS
+ */
+ private static void forEachDisplayInfoField(Consumer<Field> consumer) {
+ for (Field field : DisplayInfo.class.getDeclaredFields()) {
+ field.setAccessible(true);
+
+ if (Modifier.isStatic(field.getModifiers())) {
+ continue;
+ }
+
+ if (IGNORED_FIELDS.contains(field.getName())) {
+ continue;
+ }
+
+ consumer.accept(field);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
new file mode 100644
index 000000000000..dfa595c23e44
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.TransitionController.OnStartCollect;
+import com.android.window.flags.Flags;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Tests for the {@link DisplayContent} class when FLAG_DEFER_DISPLAY_UPDATES is enabled.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplayContentDeferredUpdateTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
+
+ @Override
+ protected void onBeforeSystemServicesCreated() {
+ // Set other flags to their default values
+ mSetFlagsRule.initAllFlagsToReleaseConfigDefault();
+
+ mSetFlagsRule.enableFlags(Flags.FLAG_DEFER_DISPLAY_UPDATES);
+ }
+
+ @Before
+ public void before() {
+ mockTransitionsController(/* enabled= */ true);
+ mockRemoteDisplayChangeController();
+ }
+
+ @Test
+ public void testUpdate_deferrableFieldChangedTransitionStarted_deferrableFieldUpdated() {
+ performInitialDisplayUpdate();
+
+ givenDisplayInfo(/* uniqueId= */ "old");
+ Runnable onUpdated = mock(Runnable.class);
+ mDisplayContent.requestDisplayUpdate(onUpdated);
+
+ // Emulate that collection has started
+ captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
+ verify(onUpdated).run();
+ clearInvocations(mDisplayContent.mTransitionController, onUpdated);
+
+ givenDisplayInfo(/* uniqueId= */ "new");
+ mDisplayContent.requestDisplayUpdate(onUpdated);
+ captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
+ verify(onUpdated).run();
+ assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new");
+ }
+
+ @Test
+ public void testUpdate_nonDeferrableUpdateAndTransitionDeferred_nonDeferrableFieldUpdated() {
+ performInitialDisplayUpdate();
+
+ // Update only color mode (non-deferrable field) and keep the same unique id
+ givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 123);
+ Runnable onUpdated = mock(Runnable.class);
+ mDisplayContent.requestDisplayUpdate(onUpdated);
+
+ verify(onUpdated).run();
+ assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123);
+ }
+
+ @Test
+ public void testUpdate_nonDeferrableUpdateTwiceAndTransitionDeferred_fieldHasLatestValue() {
+ performInitialDisplayUpdate();
+
+ // Update only color mode (non-deferrable field) and keep the same unique id
+ givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 123);
+ mDisplayContent.requestDisplayUpdate(mock(Runnable.class));
+
+ assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123);
+ assertThat(mDisplayContent.getDisplayInfo().uniqueId)
+ .isEqualTo("initial_unique_id");
+
+ // Update unique id (deferrable field), keep the same color mode,
+ // this update should be deferred
+ givenDisplayInfo(/* uniqueId= */ "new_unique_id", /* colorMode= */ 123);
+ mDisplayContent.requestDisplayUpdate(mock(Runnable.class));
+
+ assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123);
+ assertThat(mDisplayContent.getDisplayInfo().uniqueId)
+ .isEqualTo("initial_unique_id");
+
+ // Update color mode again and keep the same unique id, color mode update
+ // should not be deferred, unique id update is still deferred as transition
+ // has not started collecting yet
+ givenDisplayInfo(/* uniqueId= */ "new_unique_id", /* colorMode= */ 456);
+ Runnable onUpdated = mock(Runnable.class);
+ mDisplayContent.requestDisplayUpdate(onUpdated);
+
+ assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(456);
+ assertThat(mDisplayContent.getDisplayInfo().uniqueId)
+ .isEqualTo("initial_unique_id");
+
+ // Mark transition as started collected, so pending changes are applied
+ captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
+
+ // Verify that all fields have the latest values
+ verify(onUpdated).run();
+ assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(456);
+ assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new_unique_id");
+ }
+
+ @Test
+ public void testUpdate_deferrableFieldUpdatedTransitionPending_fieldNotUpdated() {
+ performInitialDisplayUpdate();
+ givenDisplayInfo(/* uniqueId= */ "old");
+ Runnable onUpdated = mock(Runnable.class);
+ mDisplayContent.requestDisplayUpdate(onUpdated);
+ captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
+ verify(onUpdated).run();
+ clearInvocations(mDisplayContent.mTransitionController, onUpdated);
+
+ givenDisplayInfo(/* uniqueId= */ "new");
+ mDisplayContent.requestDisplayUpdate(onUpdated);
+
+ captureStartTransitionCollection(); // do not continue by not starting the collection
+ verify(onUpdated, never()).run();
+ assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("old");
+ }
+
+ @Test
+ public void testTwoDisplayUpdates_transitionStarted_displayUpdated() {
+ performInitialDisplayUpdate();
+ givenDisplayInfo(/* uniqueId= */ "old");
+ Runnable onUpdated = mock(Runnable.class);
+ mDisplayContent.requestDisplayUpdate(onUpdated);
+ captureStartTransitionCollection().getValue()
+ .onCollectStarted(/* deferred= */ true);
+ verify(onUpdated).run();
+ clearInvocations(mDisplayContent.mTransitionController, onUpdated);
+
+ // Perform two display updates while WM is 'busy'
+ givenDisplayInfo(/* uniqueId= */ "new1");
+ Runnable onUpdated1 = mock(Runnable.class);
+ mDisplayContent.requestDisplayUpdate(onUpdated1);
+ givenDisplayInfo(/* uniqueId= */ "new2");
+ Runnable onUpdated2 = mock(Runnable.class);
+ mDisplayContent.requestDisplayUpdate(onUpdated2);
+
+ // Continue with the first update
+ captureStartTransitionCollection().getAllValues().get(0)
+ .onCollectStarted(/* deferred= */ true);
+ verify(onUpdated1).run();
+ verify(onUpdated2, never()).run();
+ assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new1");
+
+ // Continue with the second update
+ captureStartTransitionCollection().getAllValues().get(1)
+ .onCollectStarted(/* deferred= */ true);
+ verify(onUpdated2).run();
+ assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new2");
+ }
+
+ private void mockTransitionsController(boolean enabled) {
+ spyOn(mDisplayContent.mTransitionController);
+ when(mDisplayContent.mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled);
+ doReturn(true).when(mDisplayContent.mTransitionController).startCollectOrQueue(any(),
+ any());
+ }
+
+ private void mockRemoteDisplayChangeController() {
+ spyOn(mDisplayContent.mRemoteDisplayChangeController);
+ doReturn(true).when(mDisplayContent.mRemoteDisplayChangeController)
+ .performRemoteDisplayChange(anyInt(), anyInt(), any(), any());
+ }
+
+ private ArgumentCaptor<OnStartCollect> captureStartTransitionCollection() {
+ ArgumentCaptor<OnStartCollect> callbackCaptor =
+ ArgumentCaptor.forClass(OnStartCollect.class);
+ verify(mDisplayContent.mTransitionController, atLeast(1)).startCollectOrQueue(any(),
+ callbackCaptor.capture());
+ return callbackCaptor;
+ }
+
+ private void givenDisplayInfo(String uniqueId) {
+ givenDisplayInfo(uniqueId, /* colorMode= */ 0);
+ }
+
+ private void givenDisplayInfo(String uniqueId, int colorMode) {
+ spyOn(mDisplayContent.mDisplay);
+ doAnswer(invocation -> {
+ DisplayInfo info = invocation.getArgument(0);
+ info.uniqueId = uniqueId;
+ info.colorMode = colorMode;
+ return null;
+ }).when(mDisplayContent.mDisplay).getDisplayInfo(any());
+ }
+
+ private void performInitialDisplayUpdate() {
+ givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 0);
+ Runnable onUpdated = mock(Runnable.class);
+ mDisplayContent.requestDisplayUpdate(onUpdated);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index ffa1ed926766..38a66a9d5486 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -19,10 +19,13 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.InsetsSource.ID_IME;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
@@ -432,6 +435,56 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
+ @SetupWindows(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testConsumeImeInsets() {
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+ final InsetsSource imeSource = new InsetsSource(ID_IME, ime());
+ imeSource.setVisible(true);
+ mImeWindow.mHasSurface = true;
+
+ final WindowState win1 = addWindow(TYPE_APPLICATION, "win1");
+ final WindowState win2 = addWindow(TYPE_APPLICATION, "win2");
+
+ win1.mAboveInsetsState.addSource(imeSource);
+ win1.mHasSurface = true;
+ win2.mAboveInsetsState.addSource(imeSource);
+ win2.mHasSurface = true;
+
+ assertTrue(mImeWindow.isVisible());
+ assertTrue(win1.isVisible());
+ assertTrue(win2.isVisible());
+
+ // Make sure both windows have visible IME insets.
+ assertTrue(win1.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
+ assertTrue(win2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
+
+ win2.mAttrs.privateFlags |= PRIVATE_FLAG_CONSUME_IME_INSETS;
+
+ displayPolicy.beginPostLayoutPolicyLw();
+ displayPolicy.applyPostLayoutPolicyLw(win2, win2.mAttrs, null, null);
+ displayPolicy.applyPostLayoutPolicyLw(win1, win1.mAttrs, null, null);
+ displayPolicy.finishPostLayoutPolicyLw();
+
+ // Make sure win2 doesn't have visible IME insets, but win1 still does.
+ assertTrue(win2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
+ assertFalse(win1.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
+ assertTrue(win1.getWindowFrames().hasInsetsChanged());
+
+ win2.mAttrs.privateFlags &= ~PRIVATE_FLAG_CONSUME_IME_INSETS;
+ win2.getWindowFrames().setInsetsChanged(false);
+
+ displayPolicy.beginPostLayoutPolicyLw();
+ displayPolicy.applyPostLayoutPolicyLw(win2, win2.mAttrs, null, null);
+ displayPolicy.applyPostLayoutPolicyLw(win1, win1.mAttrs, null, null);
+ displayPolicy.finishPostLayoutPolicyLw();
+
+ // Make sure both windows have visible IME insets.
+ assertTrue(win1.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
+ assertTrue(win2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
+ assertTrue(win1.getWindowFrames().hasInsetsChanged());
+ }
+
private WindowState addNavigationBar() {
final Binder owner = new Binder();
final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
deleted file mode 100644
index c5dd447b5b0c..000000000000
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule;
-import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.app.Activity;
-import android.platform.test.annotations.Presubmit;
-import android.server.wm.CtsWindowInfoUtils;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.TrustedPresentationThresholds;
-
-import androidx.annotation.GuardedBy;
-import androidx.test.ext.junit.rules.ActivityScenarioRule;
-
-import com.android.server.wm.utils.CommonUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestName;
-
-import java.util.function.Consumer;
-
-/**
- * TODO (b/287076178): Move these tests to
- * {@link android.view.surfacecontrol.cts.TrustedPresentationCallbackTest} when API is made public
- */
-@Presubmit
-public class TrustedPresentationCallbackTest {
- private static final String TAG = "TrustedPresentationCallbackTest";
- private static final int STABILITY_REQUIREMENT_MS = 500;
- private static final long WAIT_TIME_MS = HW_TIMEOUT_MULTIPLIER * 2000L;
-
- private static final float FRACTION_VISIBLE = 0.1f;
-
- private final Object mResultsLock = new Object();
- @GuardedBy("mResultsLock")
- private boolean mResult;
- @GuardedBy("mResultsLock")
- private boolean mReceivedResults;
-
- @Rule
- public TestName mName = new TestName();
-
- @Rule
- public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule(
- TestActivity.class);
-
- private TestActivity mActivity;
-
- @Before
- public void setup() {
- mActivityRule.getScenario().onActivity(activity -> mActivity = activity);
- }
-
- @After
- public void tearDown() {
- CommonUtils.waitUntilActivityRemoved(mActivity);
- }
-
- @Test
- public void testAddTrustedPresentationListenerOnWindow() throws InterruptedException {
- TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds(
- 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS);
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds,
- Runnable::run, inTrustedPresentationState -> {
- synchronized (mResultsLock) {
- mResult = inTrustedPresentationState;
- mReceivedResults = true;
- mResultsLock.notify();
- }
- });
- t.apply();
- synchronized (mResultsLock) {
- assertResults();
- }
- }
-
- @Test
- public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException {
- TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds(
- 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS);
- Consumer<Boolean> trustedPresentationCallback = inTrustedPresentationState -> {
- synchronized (mResultsLock) {
- mResult = inTrustedPresentationState;
- mReceivedResults = true;
- mResultsLock.notify();
- }
- };
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds,
- Runnable::run, trustedPresentationCallback);
- t.apply();
-
- synchronized (mResultsLock) {
- if (!mReceivedResults) {
- mResultsLock.wait(WAIT_TIME_MS);
- }
- assertResults();
- // reset the state
- mReceivedResults = false;
- }
-
- mActivity.getWindow().getRootSurfaceControl().removeTrustedPresentationCallback(t,
- trustedPresentationCallback);
- t.apply();
-
- synchronized (mResultsLock) {
- if (!mReceivedResults) {
- mResultsLock.wait(WAIT_TIME_MS);
- }
- // Ensure we waited the full time and never received a notify on the result from the
- // callback.
- assertFalse("Should never have received a callback", mReceivedResults);
- // results shouldn't have changed.
- assertTrue(mResult);
- }
- }
-
- @GuardedBy("mResultsLock")
- private void assertResults() throws InterruptedException {
- mResultsLock.wait(WAIT_TIME_MS);
-
- if (!mReceivedResults) {
- CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, "test " + mName.getMethodName());
- }
- // Make sure we received the results and not just timed out
- assertTrue("Timed out waiting for results", mReceivedResults);
- assertTrue(mResult);
- }
-
- public static class TestActivity extends Activity {
- }
-}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1e686873c1b0..6a77b983f963 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9593,6 +9593,84 @@ public class CarrierConfigManager {
"satellite_connection_hysteresis_sec_int";
/**
+ * This threshold is used when connected to a non-terrestrial LTE network.
+ * A list of 4 NTN LTE RSRP thresholds above which a signal level is considered POOR,
+ * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+ *
+ * Note that the min and max thresholds are fixed at -140 and -44, as explained in
+ * TS 136.133 9.1.4 - RSRP Measurement Report Mapping.
+ * <p>
+ * See SignalStrength#MAX_LTE_RSRP and SignalStrength#MIN_LTE_RSRP. Any signal level outside
+ * these boundaries is considered invalid.
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public static final String KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY =
+ "ntn_lte_rsrp_thresholds_int_array";
+
+ /**
+ * This threshold is used when connected to a non-terrestrial LTE network.
+ * A list of 4 customized NTN LTE Reference Signal Received Quality (RSRQ) thresholds.
+ *
+ * Reference: TS 136.133 v12.6.0 section 9.1.7 - RSRQ Measurement Report Mapping.
+ *
+ * 4 threshold integers must be within the boundaries [-34 dB, 3 dB], and the levels are:
+ * "NONE: [-34, threshold1)"
+ * "POOR: [threshold1, threshold2)"
+ * "MODERATE: [threshold2, threshold3)"
+ * "GOOD: [threshold3, threshold4)"
+ * "EXCELLENT: [threshold4, 3]"
+ *
+ * This key is considered invalid if the format is violated. If the key is invalid or
+ * not configured, a default value set will apply.
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public static final String KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY =
+ "ntn_lte_rsrq_thresholds_int_array";
+
+ /**
+ * This threshold is used when connected to a non-terrestrial LTE network.
+ * A list of 4 customized NTN LTE Reference Signal Signal to Noise Ratio (RSSNR) thresholds.
+ *
+ * 4 threshold integers must be within the boundaries [-20 dB, 30 dB], and the levels are:
+ * "NONE: [-20, threshold1)"
+ * "POOR: [threshold1, threshold2)"
+ * "MODERATE: [threshold2, threshold3)"
+ * "GOOD: [threshold3, threshold4)"
+ * "EXCELLENT: [threshold4, 30]"
+ *
+ * This key is considered invalid if the format is violated. If the key is invalid or
+ * not configured, a default value set will apply.
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public static final String KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY =
+ "ntn_lte_rssnr_thresholds_int_array";
+
+ /**
+ * This threshold is used when connected to a non-terrestrial LTE network.
+ * Bit-field integer to determine whether to use Reference Signal Received Power (RSRP),
+ * Reference Signal Received Quality (RSRQ), or/and Reference Signal Signal to Noise Ratio
+ * (RSSNR) for the number of NTN LTE signal bars and signal criteria reporting enabling.
+ *
+ * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
+ * not be used for calculating signal level. If multiple measures are set bit, the parameter
+ * whose value is smallest is used to indicate the signal level.
+ * <UL>
+ * <LI>RSRP = 1 << 0</LI>
+ * <LI>RSRQ = 1 << 1</LI>
+ * <LI>RSSNR = 1 << 2</LI>
+ * </UL>
+ * <p> The value of this key must be bitwise OR of CellSignalStrengthLte#USE_RSRP,
+ * CellSignalStrengthLte#USE_RSRQ, CellSignalStrengthLte#USE_RSSNR.
+ *
+ * <p> For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
+ * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply.
+ *
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public static final String KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT =
+ "parameters_used_for_ntn_lte_signal_bar_int";
+
+ /**
* Indicating whether DUN APN should be disabled when the device is roaming. In that case,
* the default APN (i.e. internet) will be used for tethering.
*
@@ -10628,6 +10706,32 @@ public class CarrierConfigManager {
PersistableBundle.EMPTY);
sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);
sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 300);
+ sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+ // Boundaries: [-140 dBm, -44 dBm]
+ new int[]{
+ -128, /* SIGNAL_STRENGTH_POOR */
+ -118, /* SIGNAL_STRENGTH_MODERATE */
+ -108, /* SIGNAL_STRENGTH_GOOD */
+ -98 /* SIGNAL_STRENGTH_GREAT */
+ });
+ sDefaults.putIntArray(KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+ // Boundaries: [-34 dB, 3 dB]
+ new int[]{
+ -20, /* SIGNAL_STRENGTH_POOR */
+ -17, /* SIGNAL_STRENGTH_MODERATE */
+ -14, /* SIGNAL_STRENGTH_GOOD */
+ -11 /* SIGNAL_STRENGTH_GREAT */
+ });
+ sDefaults.putIntArray(KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+ // Boundaries: [-20 dBm, 30 dBm]
+ new int[] {
+ -3, /* SIGNAL_STRENGTH_POOR */
+ 1, /* SIGNAL_STRENGTH_MODERATE */
+ 5, /* SIGNAL_STRENGTH_GOOD */
+ 13 /* SIGNAL_STRENGTH_GREAT */
+ });
+ sDefaults.putInt(KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT,
+ CellSignalStrengthLte.USE_RSRP);
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 5e902613a654..f5282639ae6c 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -263,29 +263,35 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
rssnrThresholds = sRssnrThresholds;
rsrpOnly = false;
} else {
- mParametersUseForLevel = cc.getInt(
- CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT);
- if (DBG) {
- Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
+ if (ss != null && ss.isUsingNonTerrestrialNetwork()) {
+ if (DBG) log("updateLevel: from NTN_LTE");
+ mParametersUseForLevel = cc.getInt(
+ CarrierConfigManager.KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT);
+ rsrpThresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY);
+ rsrqThresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
+ rssnrThresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
+ } else {
+ mParametersUseForLevel = cc.getInt(
+ CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT);
+ rsrpThresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
+ rsrqThresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
+ rssnrThresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
}
- rsrpThresholds = cc.getIntArray(
- CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
if (rsrpThresholds == null) rsrpThresholds = sRsrpThresholds;
+ if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds;
+ if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds;
if (DBG) {
+ Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: "
+ Arrays.toString(rsrpThresholds));
- }
- rsrqThresholds = cc.getIntArray(
- CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
- if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds;
- if (DBG) {
Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: "
+ Arrays.toString(rsrqThresholds));
- }
- rssnrThresholds = cc.getIntArray(
- CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
- if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds;
- if (DBG) {
Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: "
+ Arrays.toString(rssnrThresholds));
}