summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--INPUT_OWNERS2
-rw-r--r--OWNERS1
-rw-r--r--Ravenwood.bp11
-rw-r--r--boot/Android.bp14
-rw-r--r--core/api/current.txt104
-rw-r--r--core/api/system-current.txt22
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/Android.bp7
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java38
-rw-r--r--core/java/android/app/ActivityThread.java30
-rw-r--r--core/java/android/app/AppOpsManager.java30
-rw-r--r--core/java/android/app/AutomaticZenRule.java9
-rw-r--r--core/java/android/app/ClientTransactionHandler.java12
-rw-r--r--core/java/android/app/Notification.java1
-rw-r--r--core/java/android/app/SystemServiceRegistry.java10
-rw-r--r--core/java/android/app/WallpaperManager.java40
-rw-r--r--core/java/android/app/ambient_context.aconfig8
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEvent.java86
-rw-r--r--core/java/android/app/servertransaction/ActivityRelaunchItem.java18
-rw-r--r--core/java/android/content/ClipData.java34
-rw-r--r--core/java/android/content/ClipboardManager.java3
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/pm/PackageManager.java4
-rw-r--r--core/java/android/content/pm/multiuser.aconfig7
-rw-r--r--core/java/android/credentials/GetCandidateCredentialsResponse.java32
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarView.java6
-rw-r--r--core/java/android/net/flags.aconfig6
-rw-r--r--core/java/android/os/HandlerThread.java17
-rw-r--r--core/java/android/os/UserManager.java4
-rw-r--r--core/java/android/os/storage/OWNERS4
-rw-r--r--core/java/android/permission/PermissionUsageHelper.java3
-rw-r--r--core/java/android/provider/E2eeContactKeysManager.java (renamed from core/java/android/provider/ContactKeysManager.java)392
-rw-r--r--core/java/android/service/chooser/flags.aconfig11
-rw-r--r--core/java/android/view/OWNERS1
-rw-r--r--core/java/android/view/PointerIcon.java19
-rw-r--r--core/java/android/view/RoundedCorners.java6
-rw-r--r--core/java/android/view/View.java4
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java19
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java3
-rw-r--r--core/java/android/widget/RemoteViews.java21
-rw-r--r--core/java/android/window/flags/OWNERS1
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig7
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java8
-rw-r--r--core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java15
-rw-r--r--core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java3
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java3
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java11
-rw-r--r--core/jni/LayoutlibLoader.cpp18
-rw-r--r--core/jni/android_view_MotionEvent.cpp19
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp2
-rw-r--r--core/res/Android.bp2
-rw-r--r--core/res/AndroidManifest.xml10
-rw-r--r--core/res/res/drawable/pointer_wait_vector.xml91
-rw-r--r--core/res/res/drawable/pointer_wait_vector_0.xml27
-rw-r--r--core/res/res/drawable/pointer_wait_vector_1.xml21
-rw-r--r--core/res/res/drawable/pointer_wait_vector_10.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_11.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_12.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_13.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_14.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_15.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_16.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_17.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_18.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_19.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_2.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_20.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_21.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_22.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_23.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_24.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_25.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_26.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_27.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_28.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_29.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_3.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_30.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_31.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_32.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_33.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_34.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_35.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_36.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_37.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_38.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_39.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_4.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_40.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_41.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_42.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_43.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_44.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_45.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_46.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_47.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_48.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_49.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_5.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_50.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_51.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_52.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_53.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_54.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_55.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_56.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_57.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_58.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_59.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_6.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_60.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_61.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_62.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_63.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_64.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_65.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_66.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_67.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_68.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_69.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_7.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_70.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_71.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_72.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_73.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_74.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_75.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_76.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_77.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_78.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_79.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_8.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_80.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_81.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_82.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_83.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_84.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_85.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_86.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_87.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_9.xml30
-rw-r--r--core/res/res/drawable/pointer_wait_vector_icon.xml5
-rw-r--r--core/res/res/values/styles.xml2
-rw-r--r--core/tests/coretests/Android.bp38
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java3
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java4
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java3
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java5
-rw-r--r--core/tests/coretests/src/android/view/RoundedCornersTest.java86
-rw-r--r--keystore/java/android/security/KeyStore.java63
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt218
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java75
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java85
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt169
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java113
-rw-r--r--libs/hwui/DisplayListOps.in1
-rw-r--r--libs/hwui/RecordingCanvas.cpp15
-rw-r--r--libs/hwui/RecordingCanvas.h2
-rw-r--r--libs/input/PointerController.cpp4
-rw-r--r--media/java/android/media/AudioFormat.java40
-rw-r--r--media/java/android/media/tv/ITvInputSessionWrapper.java2
-rw-r--r--packages/CrashRecovery/framework/Android.bp22
-rw-r--r--packages/CrashRecovery/services/Android.bp24
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java2
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java11
-rw-r--r--packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java103
-rw-r--r--packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java46
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt12
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt45
-rw-r--r--packages/SettingsLib/Spa/build.gradle.kts2
-rw-r--r--packages/SettingsLib/Spa/gradle/libs.versions.toml2
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.pngbin57340 -> 58496 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.pngbin48422 -> 49467 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.pngbin28871 -> 29537 bytes
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle.kts2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt73
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt4
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt13
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt150
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig9
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig10
-rw-r--r--packages/SystemUI/animation/Android.bp1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java (renamed from packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java)6
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationTargetCompat.java (renamed from packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java)4
-rw-r--r--packages/SystemUI/compose/features/Android.bp1
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt32
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt26
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt66
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt19
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt91
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt46
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt58
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt43
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt115
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt163
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt26
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt116
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java208
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java)28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java70
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java135
-rw-r--r--packages/SystemUI/res/drawable/bg_shutdown_finder_message.xml5
-rw-r--r--packages/SystemUI/res/drawable/ic_finder_active.xml14
-rw-r--r--packages/SystemUI/res/layout/shutdown_dialog_finder_active.xml72
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUiModule.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt164
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt110
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt141
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt238
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt8
-rw-r--r--ravenwood/framework-minus-apex-ravenwood-policies.txt2
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java83
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java7
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java6
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java11
-rw-r--r--ravenwood/ravenwood-annotation-allowed-classes.txt1
-rw-r--r--rs/java/android/renderscript/ScriptC.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java10
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java10
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java3
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java12
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java53
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java26
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessEvent.java36
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessReason.java5
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java17
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java140
-rw-r--r--services/core/java/com/android/server/input/PointerIconCache.java207
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java15
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java17
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java32
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java34
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java13
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java2
-rw-r--r--services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java5
-rw-r--r--services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java15
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java4
-rw-r--r--services/core/java/com/android/server/wm/DragState.java14
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerUi.java50
-rw-r--r--services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java6
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderGetSession.java18
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java12
-rw-r--r--services/fakes/Android.bp20
-rw-r--r--services/fakes/java/com/android/server/FakeClipboardService.java165
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt1
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java31
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java29
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java23
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java102
-rw-r--r--services/tests/servicestests/Android.bp1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt25
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java1247
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java41
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java10
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java14
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java21
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java224
-rw-r--r--services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java46
-rw-r--r--tools/hoststubgen/TEST_MAPPING3
319 files changed, 9155 insertions, 1782 deletions
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
index 44b2f3805495..06ead06fc13a 100644
--- a/INPUT_OWNERS
+++ b/INPUT_OWNERS
@@ -1,4 +1,6 @@
# Bug component: 136048
+arpitks@google.com
+asmitapoddar@google.com
hcutts@google.com
joseprio@google.com
michaelwr@google.com
diff --git a/OWNERS b/OWNERS
index 6bab92be1582..7ceca321283b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -32,6 +32,7 @@ per-file **.bp,**.mk = hansson@google.com, joeo@google.com, lamontjones@google.c
per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
+per-file INPUT_OWNERS = file:/INPUT_OWNERS
per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS
per-file SQLITE_OWNERS = file:/SQLITE_OWNERS
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 4e360759c137..c73e04896173 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -145,6 +145,16 @@ java_library {
}
java_library {
+ name: "services.fakes.ravenwood-jarjar",
+ installable: false,
+ srcs: [":services.fakes-sources"],
+ libs: [
+ "services.core.ravenwood",
+ ],
+ jarjar_rules: ":ravenwood-services-jarjar-rules",
+}
+
+java_library {
name: "mockito-ravenwood-prebuilt",
installable: false,
static_libs: [
@@ -189,6 +199,7 @@ android_ravenwood_libgroup {
"ravenwood-helper-runtime",
"hoststubgen-helper-runtime.ravenwood",
"services.core.ravenwood-jarjar",
+ "services.fakes.ravenwood-jarjar",
// Provide runtime versions of utils linked in below
"junit",
diff --git a/boot/Android.bp b/boot/Android.bp
index cdfa7c80bc93..f60bb9e7149a 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -31,6 +31,7 @@ soong_config_module_type {
"car_bootclasspath_fragment",
"nfc_apex_bootclasspath_fragment",
"release_crashrecovery_module",
+ "release_package_profiling_module",
],
properties: [
"fragments",
@@ -122,10 +123,6 @@ custom_platform_bootclasspath {
module: "com.android.permission-bootclasspath-fragment",
},
{
- apex: "com.android.profiling",
- module: "com.android.profiling-bootclasspath-fragment",
- },
- {
apex: "com.android.scheduling",
module: "com.android.scheduling-bootclasspath-fragment",
},
@@ -179,6 +176,15 @@ custom_platform_bootclasspath {
},
],
},
+ release_package_profiling_module: {
+ fragments: [
+ // only used if profiling is enabled.
+ {
+ apex: "com.android.profiling",
+ module: "com.android.profiling-bootclasspath-fragment",
+ },
+ ],
+ },
},
// Additional information needed by hidden api processing.
diff --git a/core/api/current.txt b/core/api/current.txt
index 386396c67c71..5781e9507160 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -203,7 +203,7 @@ package android {
field public static final String MANAGE_DEVICE_POLICY_SYSTEM_APPS = "android.permission.MANAGE_DEVICE_POLICY_SYSTEM_APPS";
field public static final String MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS = "android.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS";
field public static final String MANAGE_DEVICE_POLICY_SYSTEM_UPDATES = "android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES";
- field @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled") public static final String MANAGE_DEVICE_POLICY_THREAD_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK";
+ field @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") public static final String MANAGE_DEVICE_POLICY_THREAD_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK";
field public static final String MANAGE_DEVICE_POLICY_TIME = "android.permission.MANAGE_DEVICE_POLICY_TIME";
field public static final String MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING = "android.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING";
field public static final String MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER = "android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER";
@@ -3350,7 +3350,6 @@ package android.accessibilityservice {
method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method public boolean clearCache();
method public boolean clearCachedSubtree(@NonNull android.view.accessibility.AccessibilityNodeInfo);
- method @FlaggedApi("android.view.accessibility.braille_display_hid") public void clearTestBrailleDisplayController();
method public final void disableSelf();
method public final boolean dispatchGesture(@NonNull android.accessibilityservice.GestureDescription, @Nullable android.accessibilityservice.AccessibilityService.GestureResultCallback, @Nullable android.os.Handler);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
@@ -3386,7 +3385,6 @@ package android.accessibilityservice {
method public boolean setCacheEnabled(boolean);
method public void setGestureDetectionPassthroughRegion(int, @NonNull android.graphics.Region);
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
- method @FlaggedApi("android.view.accessibility.braille_display_hid") public void setTestBrailleDisplayController(@NonNull android.accessibilityservice.BrailleDisplayController);
method public void setTouchExplorationPassthroughRegion(int, @NonNull android.graphics.Region);
method public void takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull android.accessibilityservice.AccessibilityService.TakeScreenshotCallback);
method public void takeScreenshotOfWindow(int, @NonNull java.util.concurrent.Executor, @NonNull android.accessibilityservice.AccessibilityService.TakeScreenshotCallback);
@@ -13218,7 +13216,7 @@ package android.content.pm {
field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio.access";
field public static final String FEATURE_TELEPHONY_SUBSCRIPTION = "android.hardware.telephony.subscription";
field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television";
- field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
+ field @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
@@ -34086,7 +34084,7 @@ package android.os {
field @FlaggedApi("android.app.admin.flags.esim_management_enabled") public static final String DISALLOW_SIM_GLOBALLY = "no_sim_globally";
field public static final String DISALLOW_SMS = "no_sms";
field public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
- field @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled") public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
+ field @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
field public static final String DISALLOW_ULTRA_WIDEBAND_RADIO = "no_ultra_wideband_radio";
field public static final String DISALLOW_UNIFIED_PASSWORD = "no_unified_password";
field public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
@@ -35817,54 +35815,6 @@ package android.provider {
field public static final String LONGITUDE = "longitude";
}
- @FlaggedApi("android.provider.user_keys") public final class ContactKeysManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.ContactKeysManager.ContactKey> getAllContactKeys(@NonNull String);
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.ContactKeysManager.SelfKey> getAllSelfKeys();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public android.provider.ContactKeysManager.ContactKey getContactKey(@NonNull String, @NonNull String, @NonNull String);
- method public static int getMaxKeySizeBytes();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.ContactKeysManager.ContactKey> getOwnerContactKeys(@NonNull String);
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.ContactKeysManager.SelfKey> getOwnerSelfKeys();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public android.provider.ContactKeysManager.SelfKey getSelfKey(@NonNull String, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean removeContactKey(@NonNull String, @NonNull String, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean removeSelfKey(@NonNull String, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateContactKeyLocalVerificationState(@NonNull String, @NonNull String, @NonNull String, int);
- method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateContactKeyRemoteVerificationState(@NonNull String, @NonNull String, @NonNull String, int);
- method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public void updateOrInsertContactKey(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
- method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateOrInsertSelfKey(@NonNull String, @NonNull String, @NonNull byte[]);
- method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateSelfKeyRemoteVerificationState(@NonNull String, @NonNull String, int);
- field public static final int VERIFICATION_STATE_UNVERIFIED = 0; // 0x0
- field public static final int VERIFICATION_STATE_VERIFICATION_FAILED = 1; // 0x1
- field public static final int VERIFICATION_STATE_VERIFIED = 2; // 0x2
- }
-
- public static final class ContactKeysManager.ContactKey implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public String getAccountId();
- method @Nullable public String getDeviceId();
- method @Nullable public String getDisplayName();
- method @Nullable public String getEmailAddress();
- method @Nullable public byte[] getKeyValue();
- method public int getLocalVerificationState();
- method @NonNull public String getOwnerPackageName();
- method @Nullable public String getPhoneNumber();
- method public int getRemoteVerificationState();
- method public long getTimeUpdated();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.provider.ContactKeysManager.ContactKey> CREATOR;
- }
-
- public static final class ContactKeysManager.SelfKey implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public String getAccountId();
- method @Nullable public String getDeviceId();
- method @Nullable public byte[] getKeyValue();
- method @NonNull public String getOwnerPackageName();
- method public int getRemoteVerificationState();
- method public long getTimeUpdated();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.provider.ContactKeysManager.SelfKey> CREATOR;
- }
-
@Deprecated public class Contacts {
field @Deprecated public static final String AUTHORITY = "contacts";
field @Deprecated public static final android.net.Uri CONTENT_URI;
@@ -37165,6 +37115,54 @@ package android.provider {
method public final int update(android.net.Uri, android.content.ContentValues, String, String[]);
}
+ @FlaggedApi("android.provider.user_keys") public final class E2eeContactKeysManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.E2eeContactKeysManager.E2eeContactKey> getAllE2eeContactKeys(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.E2eeContactKeysManager.E2eeSelfKey> getAllE2eeSelfKeys();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public android.provider.E2eeContactKeysManager.E2eeContactKey getE2eeContactKey(@NonNull String, @NonNull String, @NonNull String);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public android.provider.E2eeContactKeysManager.E2eeSelfKey getE2eeSelfKey(@NonNull String, @NonNull String);
+ method public static int getMaxKeySizeBytes();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.E2eeContactKeysManager.E2eeContactKey> getOwnerE2eeContactKeys(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.E2eeContactKeysManager.E2eeSelfKey> getOwnerE2eeSelfKeys();
+ method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean removeE2eeContactKey(@NonNull String, @NonNull String, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean removeE2eeSelfKey(@NonNull String, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateE2eeContactKeyLocalVerificationState(@NonNull String, @NonNull String, @NonNull String, int);
+ method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateE2eeContactKeyRemoteVerificationState(@NonNull String, @NonNull String, @NonNull String, int);
+ method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateE2eeSelfKeyRemoteVerificationState(@NonNull String, @NonNull String, int);
+ method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public void updateOrInsertE2eeContactKey(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
+ method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateOrInsertE2eeSelfKey(@NonNull String, @NonNull String, @NonNull byte[]);
+ field public static final int VERIFICATION_STATE_UNVERIFIED = 0; // 0x0
+ field public static final int VERIFICATION_STATE_VERIFICATION_FAILED = 1; // 0x1
+ field public static final int VERIFICATION_STATE_VERIFIED = 2; // 0x2
+ }
+
+ public static final class E2eeContactKeysManager.E2eeContactKey implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getAccountId();
+ method @Nullable public String getDeviceId();
+ method @Nullable public String getDisplayName();
+ method @Nullable public String getEmailAddress();
+ method @Nullable public byte[] getKeyValue();
+ method public int getLocalVerificationState();
+ method @NonNull public String getOwnerPackageName();
+ method @Nullable public String getPhoneNumber();
+ method public int getRemoteVerificationState();
+ method public long getTimeUpdated();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.provider.E2eeContactKeysManager.E2eeContactKey> CREATOR;
+ }
+
+ public static final class E2eeContactKeysManager.E2eeSelfKey implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getAccountId();
+ method @Nullable public String getDeviceId();
+ method @Nullable public byte[] getKeyValue();
+ method @NonNull public String getOwnerPackageName();
+ method public int getRemoteVerificationState();
+ method public long getTimeUpdated();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.provider.E2eeContactKeysManager.E2eeSelfKey> CREATOR;
+ }
+
@Deprecated public final class FontRequest {
ctor @Deprecated public FontRequest(@NonNull String, @NonNull String, @NonNull String);
ctor @Deprecated public FontRequest(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.List<java.util.List<byte[]>>);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d9b5d56544d6..67ccd9d86c83 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -327,7 +327,7 @@ package android {
field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
- field @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
+ field @FlaggedApi("android.net.platform.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
@@ -388,7 +388,7 @@ package android {
field public static final String SYSTEM_APPLICATION_OVERLAY = "android.permission.SYSTEM_APPLICATION_OVERLAY";
field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
- field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED";
+ field @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED";
field public static final String TIS_EXTENSION_INTERFACE = "android.permission.TIS_EXTENSION_INTERFACE";
field public static final String TOGGLE_AUTOMOTIVE_PROJECTION = "android.permission.TOGGLE_AUTOMOTIVE_PROJECTION";
field public static final String TRIGGER_LOST_MODE = "android.permission.TRIGGER_LOST_MODE";
@@ -1647,14 +1647,12 @@ package android.app.ambientcontext {
method public int getDensityLevel();
method @NonNull public java.time.Instant getEndTime();
method public int getEventType();
- method @FlaggedApi("android.app.ambient_heart_rate") @IntRange(from=0xffffffff) public int getRatePerMinute();
method @NonNull public java.time.Instant getStartTime();
method @NonNull public android.os.PersistableBundle getVendorData();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR;
field public static final int EVENT_BACK_DOUBLE_TAP = 3; // 0x3
field public static final int EVENT_COUGH = 1; // 0x1
- field @FlaggedApi("android.app.ambient_heart_rate") public static final int EVENT_HEART_RATE = 4; // 0x4
field public static final int EVENT_SNORE = 2; // 0x2
field public static final int EVENT_UNKNOWN = 0; // 0x0
field public static final int EVENT_VENDOR_WEARABLE_START = 100000; // 0x186a0
@@ -1665,7 +1663,6 @@ package android.app.ambientcontext {
field public static final int LEVEL_MEDIUM_HIGH = 4; // 0x4
field public static final int LEVEL_MEDIUM_LOW = 2; // 0x2
field public static final int LEVEL_UNKNOWN = 0; // 0x0
- field @FlaggedApi("android.app.ambient_heart_rate") public static final int RATE_PER_MINUTE_UNKNOWN = -1; // 0xffffffff
}
public static final class AmbientContextEvent.Builder {
@@ -1675,7 +1672,6 @@ package android.app.ambientcontext {
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setDensityLevel(int);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEndTime(@NonNull java.time.Instant);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEventType(int);
- method @FlaggedApi("android.app.ambient_heart_rate") @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setRatePerMinute(@IntRange(from=0xffffffff) int);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setStartTime(@NonNull java.time.Instant);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setVendorData(@NonNull android.os.PersistableBundle);
}
@@ -3787,7 +3783,7 @@ package android.content {
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
- field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String THREAD_NETWORK_SERVICE = "thread_network";
+ field @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") public static final String THREAD_NETWORK_SERVICE = "thread_network";
field public static final String TIME_MANAGER_SERVICE = "time_manager";
field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
@@ -11708,12 +11704,6 @@ package android.provider {
field public static final int ERROR_UNKNOWN = 0; // 0x0
}
- @FlaggedApi("android.provider.user_keys") public final class ContactKeysManager {
- method @RequiresPermission(allOf={android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, android.Manifest.permission.WRITE_CONTACTS}) public boolean updateContactKeyLocalVerificationState(@NonNull String, @NonNull String, @NonNull String, @NonNull String, int);
- method @RequiresPermission(allOf={android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, android.Manifest.permission.WRITE_CONTACTS}) public boolean updateContactKeyRemoteVerificationState(@NonNull String, @NonNull String, @NonNull String, @NonNull String, int);
- method @RequiresPermission(allOf={android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, android.Manifest.permission.WRITE_CONTACTS}) public boolean updateSelfKeyRemoteVerificationState(@NonNull String, @NonNull String, @NonNull String, int);
- }
-
@Deprecated public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
@@ -11771,6 +11761,12 @@ package android.provider {
field public static final int FLAG_REMOVABLE_USB = 524288; // 0x80000
}
+ @FlaggedApi("android.provider.user_keys") public final class E2eeContactKeysManager {
+ method @RequiresPermission(allOf={android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, android.Manifest.permission.WRITE_CONTACTS}) public boolean updateE2eeContactKeyLocalVerificationState(@NonNull String, @NonNull String, @NonNull String, @NonNull String, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, android.Manifest.permission.WRITE_CONTACTS}) public boolean updateE2eeContactKeyRemoteVerificationState(@NonNull String, @NonNull String, @NonNull String, @NonNull String, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, android.Manifest.permission.WRITE_CONTACTS}) public boolean updateE2eeSelfKeyRemoteVerificationState(@NonNull String, @NonNull String, @NonNull String, int);
+ }
+
public abstract class SearchIndexableData {
ctor public SearchIndexableData();
ctor public SearchIndexableData(android.content.Context);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index cdf232cd71ee..b8c32a4aa7ef 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -251,6 +251,7 @@ package android.app {
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method public static int getNumOps();
method public boolean isOperationActive(int, int, String);
+ method public int noteOpNoThrow(int, @NonNull android.content.AttributionSource, @Nullable String);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void offsetHistory(long);
method public static String opToPermission(int);
method public static int permissionToOpCode(String);
@@ -3966,6 +3967,7 @@ package android.view.inputmethod {
}
public final class InputMethodInfo implements android.os.Parcelable {
+ ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, @NonNull String, boolean, @NonNull String);
ctor @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, @NonNull String, boolean, boolean, @NonNull String);
ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
field public static final int COMPONENT_NAME_MAX_LENGTH = 1000; // 0x3e8
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 184421518d32..ab1c9a4ef48d 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -528,6 +528,13 @@ filegroup {
],
}
+java_library {
+ name: "protolog-group",
+ srcs: [
+ "com/android/internal/protolog/common/IProtoLogGroup.java",
+ ],
+}
+
// PackageManager common
filegroup {
name: "framework-pm-common-shared-srcs",
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 42c32723fd72..d70fa19a4468 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -81,7 +81,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
-import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -853,7 +852,6 @@ public abstract class AccessibilityService extends Service {
private final SparseArray<AccessibilityButtonController> mAccessibilityButtonControllers =
new SparseArray<>(0);
private BrailleDisplayController mBrailleDisplayController;
- private BrailleDisplayController mTestBrailleDisplayController;
private int mGestureStatusCallbackSequence;
@@ -3650,46 +3648,10 @@ public abstract class AccessibilityService extends Service {
public BrailleDisplayController getBrailleDisplayController() {
BrailleDisplayController.checkApiFlagIsEnabled();
synchronized (mLock) {
- if (mTestBrailleDisplayController != null) {
- return mTestBrailleDisplayController;
- }
-
if (mBrailleDisplayController == null) {
mBrailleDisplayController = new BrailleDisplayControllerImpl(this, mLock);
}
return mBrailleDisplayController;
}
}
-
- /**
- * Set the {@link BrailleDisplayController} implementation that will be returned by
- * {@link #getBrailleDisplayController}, to allow this accessibility service to test its
- * interaction with BrailleDisplayController without requiring a real Braille display.
- *
- * <p>For full test fidelity, ensure that this test-only implementation follows the same
- * behavior specified in the documentation for {@link BrailleDisplayController}, including
- * thrown exceptions.
- *
- * @param controller A test-only implementation of {@link BrailleDisplayController}.
- */
- @FlaggedApi(android.view.accessibility.Flags.FLAG_BRAILLE_DISPLAY_HID)
- public void setTestBrailleDisplayController(@NonNull BrailleDisplayController controller) {
- BrailleDisplayController.checkApiFlagIsEnabled();
- Objects.requireNonNull(controller);
- synchronized (mLock) {
- mTestBrailleDisplayController = controller;
- }
- }
-
- /**
- * Clears the {@link BrailleDisplayController} previously set by
- * {@link #setTestBrailleDisplayController}.
- */
- @FlaggedApi(android.view.accessibility.Flags.FLAG_BRAILLE_DISPLAY_HID)
- public void clearTestBrailleDisplayController() {
- BrailleDisplayController.checkApiFlagIsEnabled();
- synchronized (mLock) {
- mTestBrailleDisplayController = null;
- }
- }
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 22b8d92b507c..074f7e993eb4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5998,9 +5998,11 @@ public final class ActivityThread extends ClientTransactionHandler
}
@Override
- public ActivityClientRecord prepareRelaunchActivity(IBinder token,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, MergedConfiguration config, boolean preserveWindow) {
+ public ActivityClientRecord prepareRelaunchActivity(@NonNull IBinder token,
+ @Nullable List<ResultInfo> pendingResults,
+ @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges,
+ @NonNull MergedConfiguration config, boolean preserveWindow,
+ @NonNull ActivityWindowInfo activityWindowInfo) {
ActivityClientRecord target = null;
boolean scheduleRelaunch = false;
@@ -6041,14 +6043,15 @@ public final class ActivityThread extends ClientTransactionHandler
target.createdConfig = config.getGlobalConfiguration();
target.overrideConfig = config.getOverrideConfiguration();
target.pendingConfigChanges |= configChanges;
+ target.mActivityWindowInfo = activityWindowInfo;
}
return scheduleRelaunch ? target : null;
}
@Override
- public void handleRelaunchActivity(ActivityClientRecord tmp,
- PendingTransactionActions pendingActions) {
+ public void handleRelaunchActivity(@NonNull ActivityClientRecord tmp,
+ @NonNull PendingTransactionActions pendingActions) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
@@ -6128,7 +6131,8 @@ public final class ActivityThread extends ClientTransactionHandler
r.activity.mChangingConfigurations = true;
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
- pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
+ pendingActions, tmp.startsNotResumed, tmp.overrideConfig, tmp.mActivityWindowInfo,
+ "handleRelaunchActivity");
}
void scheduleRelaunchActivity(IBinder token) {
@@ -6183,7 +6187,8 @@ public final class ActivityThread extends ClientTransactionHandler
r.overrideConfig);
final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
r.token, null /* pendingResults */, null /* pendingIntents */,
- 0 /* configChanges */, mergedConfiguration, r.mPreserveWindow);
+ 0 /* configChanges */, mergedConfiguration, r.mPreserveWindow,
+ r.getActivityWindowInfo());
// Make sure to match the existing lifecycle state in the end of the transaction.
final ActivityLifecycleItem lifecycleRequest =
TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
@@ -6194,10 +6199,12 @@ public final class ActivityThread extends ClientTransactionHandler
executeTransaction(transaction);
}
- private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
- PendingTransactionActions pendingActions, boolean startsNotResumed,
- Configuration overrideConfig, String reason) {
+ private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r, int configChanges,
+ @Nullable List<ResultInfo> pendingResults,
+ @Nullable List<ReferrerIntent> pendingIntents,
+ @NonNull PendingTransactionActions pendingActions, boolean startsNotResumed,
+ @NonNull Configuration overrideConfig, @NonNull ActivityWindowInfo activityWindowInfo,
+ @NonNull String reason) {
// Preserve last used intent, it may be set from Activity#setIntent().
final Intent customIntent = r.activity.mIntent;
// Need to ensure state is saved.
@@ -6230,6 +6237,7 @@ public final class ActivityThread extends ClientTransactionHandler
}
r.startsNotResumed = startsNotResumed;
r.overrideConfig = overrideConfig;
+ r.mActivityWindowInfo = activityWindowInfo;
handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 39900a03e560..2afc78cb90e6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -7800,7 +7800,7 @@ public class AppOpsManager {
}
final List<AppOpsManager.PackageOps> result;
try {
- result = mService.getPackagesForOpsForDevice(opCodes, persistentDeviceId);
+ result = mService.getPackagesForOpsForDevice(opCodes, persistentDeviceId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8272,14 +8272,24 @@ public class AppOpsManager {
cb = new IAppOpsCallback.Stub() {
public void opChanged(int op, int uid, String packageName,
String persistentDeviceId) {
- if (callback instanceof OnOpChangedInternalListener) {
- ((OnOpChangedInternalListener)callback).onOpChanged(op, packageName,
- persistentDeviceId);
- }
- if (sAppOpInfos[op].name != null) {
-
- callback.onOpChanged(sAppOpInfos[op].name, packageName,
- UserHandle.getUserId(uid), persistentDeviceId);
+ if (Flags.deviceAwarePermissionApisEnabled()) {
+ if (callback instanceof OnOpChangedInternalListener) {
+ ((OnOpChangedInternalListener)callback).onOpChanged(op, packageName,
+ persistentDeviceId);
+ }
+ if (sAppOpInfos[op].name != null) {
+ callback.onOpChanged(sAppOpInfos[op].name, packageName,
+ UserHandle.getUserId(uid), persistentDeviceId);
+ }
+ } else {
+ if (callback instanceof OnOpChangedInternalListener) {
+ ((OnOpChangedInternalListener) callback).onOpChanged(op,
+ packageName);
+ }
+ if (sAppOpInfos[op].name != null) {
+ callback.onOpChanged(sAppOpInfos[op].name, packageName,
+ UserHandle.getUserId(uid));
+ }
}
}
};
@@ -8940,6 +8950,8 @@ public class AppOpsManager {
*
* @hide
*/
+ @TestApi
+ @SuppressLint("UnflaggedApi")
public int noteOpNoThrow(int op, @NonNull AttributionSource attributionSource,
@Nullable String message) {
return noteOpNoThrow(op, attributionSource.getUid(), attributionSource.getPackageName(),
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index f6373d690793..6ad03135ea02 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -377,6 +377,9 @@ public final class AutomaticZenRule implements Parcelable {
* Sets the {@link ZenDeviceEffects} associated to this rule. Device effects specify changes to
* the device behavior that should apply while the rule is active, but are not directly related
* to suppressing notifications (for example: disabling always-on display).
+ *
+ * <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule},
+ * a {@code null} value here means the previous set of effects is retained.
*/
@FlaggedApi(Flags.FLAG_MODES_API)
public void setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) {
@@ -749,6 +752,9 @@ public final class AutomaticZenRule implements Parcelable {
/**
* Sets the zen policy.
+ *
+ * <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule},
+ * a {@code null} value here means the previous policy is retained.
*/
public @NonNull Builder setZenPolicy(@Nullable ZenPolicy policy) {
mPolicy = policy;
@@ -759,6 +765,9 @@ public final class AutomaticZenRule implements Parcelable {
* Sets the {@link ZenDeviceEffects} associated to this rule. Device effects specify changes
* to the device behavior that should apply while the rule is active, but are not directly
* related to suppressing notifications (for example: disabling always-on display).
+ *
+ * <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule},
+ * a {@code null} value here means the previous set of effects is retained.
*/
@NonNull
public Builder setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) {
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index b30067491a84..4c92dee6ff17 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -30,6 +30,7 @@ import android.content.res.Configuration;
import android.os.IBinder;
import android.util.MergedConfiguration;
import android.view.SurfaceControl;
+import android.window.ActivityWindowInfo;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.WindowContext;
import android.window.WindowContextInfo;
@@ -232,12 +233,15 @@ public abstract class ClientTransactionHandler {
* @param config New configuration applied to the activity.
* @param preserveWindow Whether the activity should try to reuse the window it created,
* including the decor view after the relaunch.
+ * @param activityWindowInfo Window information about the relaunched Activity.
* @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during
* relaunch, or {@code null} if relaunch cancelled.
*/
- public abstract ActivityClientRecord prepareRelaunchActivity(IBinder token,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, MergedConfiguration config, boolean preserveWindow);
+ public abstract ActivityClientRecord prepareRelaunchActivity(@NonNull IBinder token,
+ @Nullable List<ResultInfo> pendingResults,
+ @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges,
+ @NonNull MergedConfiguration config, boolean preserveWindow,
+ @NonNull ActivityWindowInfo activityWindowInfo);
/**
* Perform activity relaunch.
@@ -245,7 +249,7 @@ public abstract class ClientTransactionHandler {
* @param pendingActions Pending actions to be used on later stages of activity transaction.
* */
public abstract void handleRelaunchActivity(@NonNull ActivityClientRecord r,
- PendingTransactionActions pendingActions);
+ @NonNull PendingTransactionActions pendingActions);
/**
* Report that relaunch request was handled.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 26f85f723bd9..1129f9dbdfeb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7543,6 +7543,7 @@ public class Notification implements Parcelable
/**
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract boolean areNotificationsVisiblyDifferent(Style other);
/**
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 08c193f06286..d01626e17f2d 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -212,7 +212,7 @@ import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
import android.print.IPrintManager;
import android.print.PrintManager;
-import android.provider.ContactKeysManager;
+import android.provider.E2eeContactKeysManager;
import android.safetycenter.SafetyCenterFrameworkInitializer;
import android.scheduling.SchedulingFrameworkInitializer;
import android.security.FileIntegrityManager;
@@ -1608,16 +1608,16 @@ public final class SystemServiceRegistry {
}
});
- registerService(Context.CONTACT_KEYS_SERVICE, ContactKeysManager.class,
- new CachedServiceFetcher<ContactKeysManager>() {
+ registerService(Context.CONTACT_KEYS_SERVICE, E2eeContactKeysManager.class,
+ new CachedServiceFetcher<E2eeContactKeysManager>() {
@Override
- public ContactKeysManager createService(ContextImpl ctx)
+ public E2eeContactKeysManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
if (!android.provider.Flags.userKeys()) {
throw new ServiceNotFoundException(
"ContactKeysManager is not supported");
}
- return new ContactKeysManager(ctx);
+ return new E2eeContactKeysManager(ctx);
}});
// DO NOT do a flag check like this unless the flag is read-only.
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 83fb393da125..02d694436bfe 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -981,12 +981,12 @@ public class WallpaperManager {
/**
* <strong> Important note: </strong>
* <ul>
- * <li>Up to version S, this method requires the
+ * <li>Up to Android 12, this method requires the
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+ * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore,
* instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
+ * (some versions of Android 13 may throw a {@code SecurityException}).</li>
+ * <li>From Android 14, this method should not be used
* and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
@@ -1265,12 +1265,12 @@ public class WallpaperManager {
/**
* <strong> Important note: </strong>
* <ul>
- * <li>Up to version S, this method requires the
+ * <li>Up to Android 12, this method requires the
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+ * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore,
* instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
+ * (some versions of Android 13 may throw a {@code SecurityException}).</li>
+ * <li>From Android 14, this method should not be used
* and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
@@ -1318,12 +1318,12 @@ public class WallpaperManager {
/**
* <strong> Important note: </strong>
* <ul>
- * <li>Up to version S, this method requires the
+ * <li>Up to Android 12, this method requires the
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+ * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore,
* instead the default wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
+ * (some versions of Android 13 may throw a {@code SecurityException}).</li>
+ * <li>From Android 14, this method should not be used
* and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
@@ -1677,12 +1677,12 @@ public class WallpaperManager {
/**
* <strong> Important note: </strong>
* <ul>
- * <li>Up to version S, this method requires the
+ * <li>Up to Android 12, this method requires the
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+ * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore,
* instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
+ * (some versions of Android 13 may throw a {@code SecurityException}).</li>
+ * <li>From Android 14, this method should not be used
* and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
@@ -1904,14 +1904,14 @@ public class WallpaperManager {
* caller doesn't have the appropriate permissions, this returns {@code null}.
*
* <p>
- * Before Android U, this method requires the
+ * For devices running Android 13 or earlier, this method requires the
* {@link android.Manifest.permission#QUERY_ALL_PACKAGES} permission.
* </p>
*
* <p>
- * Starting from Android U, in order to use this, apps should declare a {@code <queries>} tag
- * with the action {@code "android.service.wallpaper.WallpaperService"}. Otherwise,
- * this method will return {@code null} if the caller doesn't otherwise have
+ * For devices running Android 14 or later, in order to use this, apps should declare a
+ * {@code <queries>} tag with the action {@code "android.service.wallpaper.WallpaperService"}.
+ * Otherwise, this method will return {@code null} if the caller doesn't otherwise have
* <a href="{@docRoot}training/package-visibility">visibility</a> of the wallpaper package.
* </p>
*/
diff --git a/core/java/android/app/ambient_context.aconfig b/core/java/android/app/ambient_context.aconfig
deleted file mode 100644
index 3f73da216b9f..000000000000
--- a/core/java/android/app/ambient_context.aconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-package: "android.app"
-
-flag {
- namespace: "biometrics_integration"
- name: "ambient_heart_rate"
- description: "Feature flag for adding heart rate api to ambient context."
- bug: "318309481"
-}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index 5ab7991c6326..13d959c79cd2 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -16,9 +16,7 @@
package android.app.ambientcontext;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
-import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcelable;
@@ -70,14 +68,6 @@ public final class AmbientContextEvent implements Parcelable {
public static final int EVENT_BACK_DOUBLE_TAP = 3;
/**
- * The integer indicating a heart rate measurement was done.
- *
- * @see #getRatePerMinute
- */
- @Event @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
- public static final int EVENT_HEART_RATE = 4;
-
- /**
* Integer indicating the start of wearable vendor defined events that can be detected.
* These depend on the vendor implementation.
*/
@@ -89,19 +79,12 @@ public final class AmbientContextEvent implements Parcelable {
*/
public static final String KEY_VENDOR_WEARABLE_EVENT_NAME = "wearable_event_name";
- /**
- * Default value for {@link #getRatePerMinute}. Indicates that the rate of the event is unknown.
- */
- @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
- public static final int RATE_PER_MINUTE_UNKNOWN = -1;
-
/** @hide */
@IntDef(prefix = { "EVENT_" }, value = {
EVENT_UNKNOWN,
EVENT_COUGH,
EVENT_SNORE,
EVENT_BACK_DOUBLE_TAP,
- EVENT_HEART_RATE,
EVENT_VENDOR_WEARABLE_START,
})
@Retention(RetentionPolicy.SOURCE)
@@ -187,16 +170,6 @@ public final class AmbientContextEvent implements Parcelable {
return new PersistableBundle();
}
- /**
- * Rate per minute of the event during the start to end time.
- *
- * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown.
- */
- private final @IntRange(from = -1) int mRatePerMinute;
- private static int defaultRatePerMinute() {
- return RATE_PER_MINUTE_UNKNOWN;
- }
-
// Code below generated by codegen v1.0.23.
@@ -206,8 +179,6 @@ public final class AmbientContextEvent implements Parcelable {
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java
- // then manually add @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) back to flagged
- // APIs.
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -220,7 +191,6 @@ public final class AmbientContextEvent implements Parcelable {
EVENT_COUGH,
EVENT_SNORE,
EVENT_BACK_DOUBLE_TAP,
- EVENT_HEART_RATE,
EVENT_VENDOR_WEARABLE_START
})
@Retention(RetentionPolicy.SOURCE)
@@ -239,8 +209,6 @@ public final class AmbientContextEvent implements Parcelable {
return "EVENT_SNORE";
case EVENT_BACK_DOUBLE_TAP:
return "EVENT_BACK_DOUBLE_TAP";
- case EVENT_HEART_RATE:
- return "EVENT_HEART_RATE";
case EVENT_VENDOR_WEARABLE_START:
return "EVENT_VENDOR_WEARABLE_START";
default: return Integer.toHexString(value);
@@ -287,8 +255,7 @@ public final class AmbientContextEvent implements Parcelable {
@NonNull Instant endTime,
@LevelValue int confidenceLevel,
@LevelValue int densityLevel,
- @NonNull PersistableBundle vendorData,
- @IntRange(from = -1) int ratePerMinute) {
+ @NonNull PersistableBundle vendorData) {
this.mEventType = eventType;
com.android.internal.util.AnnotationValidations.validate(
EventCode.class, null, mEventType);
@@ -307,10 +274,6 @@ public final class AmbientContextEvent implements Parcelable {
this.mVendorData = vendorData;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mVendorData);
- this.mRatePerMinute = ratePerMinute;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mRatePerMinute,
- "from", -1);
// onConstructed(); // You can define this method to get a callback
}
@@ -367,17 +330,6 @@ public final class AmbientContextEvent implements Parcelable {
return mVendorData;
}
- /**
- * Rate per minute of the event during the start to end time.
- *
- * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown.
- */
- @DataClass.Generated.Member
- @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
- public @IntRange(from = -1) int getRatePerMinute() {
- return mRatePerMinute;
- }
-
@Override
@DataClass.Generated.Member
public String toString() {
@@ -390,8 +342,7 @@ public final class AmbientContextEvent implements Parcelable {
"endTime = " + mEndTime + ", " +
"confidenceLevel = " + mConfidenceLevel + ", " +
"densityLevel = " + mDensityLevel + ", " +
- "vendorData = " + mVendorData + ", " +
- "ratePerMinute = " + mRatePerMinute +
+ "vendorData = " + mVendorData +
" }";
}
@@ -429,7 +380,6 @@ public final class AmbientContextEvent implements Parcelable {
dest.writeInt(mConfidenceLevel);
dest.writeInt(mDensityLevel);
dest.writeTypedObject(mVendorData, flags);
- dest.writeInt(mRatePerMinute);
}
@Override
@@ -449,7 +399,6 @@ public final class AmbientContextEvent implements Parcelable {
int confidenceLevel = in.readInt();
int densityLevel = in.readInt();
PersistableBundle vendorData = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
- int ratePerMinute = in.readInt();
this.mEventType = eventType;
com.android.internal.util.AnnotationValidations.validate(
@@ -469,10 +418,6 @@ public final class AmbientContextEvent implements Parcelable {
this.mVendorData = vendorData;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mVendorData);
- this.mRatePerMinute = ratePerMinute;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mRatePerMinute,
- "from", -1);
// onConstructed(); // You can define this method to get a callback
}
@@ -504,7 +449,6 @@ public final class AmbientContextEvent implements Parcelable {
private @LevelValue int mConfidenceLevel;
private @LevelValue int mDensityLevel;
private @NonNull PersistableBundle mVendorData;
- private @IntRange(from = -1) int mRatePerMinute;
private long mBuilderFieldsSet = 0L;
@@ -581,22 +525,10 @@ public final class AmbientContextEvent implements Parcelable {
return this;
}
- /**
- * Rate per minute of the event during the start to end time.
- */
- @DataClass.Generated.Member
- @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
- public @NonNull Builder setRatePerMinute(@IntRange(from = -1) int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x40;
- mRatePerMinute = value;
- return this;
- }
-
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull AmbientContextEvent build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x80; // Mark builder used
+ mBuilderFieldsSet |= 0x40; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mEventType = defaultEventType();
@@ -616,22 +548,18 @@ public final class AmbientContextEvent implements Parcelable {
if ((mBuilderFieldsSet & 0x20) == 0) {
mVendorData = defaultVendorData();
}
- if ((mBuilderFieldsSet & 0x40) == 0) {
- mRatePerMinute = defaultRatePerMinute();
- }
AmbientContextEvent o = new AmbientContextEvent(
mEventType,
mStartTime,
mEndTime,
mConfidenceLevel,
mDensityLevel,
- mVendorData,
- mRatePerMinute);
+ mVendorData);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x80) != 0) {
+ if ((mBuilderFieldsSet & 0x40) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -639,10 +567,10 @@ public final class AmbientContextEvent implements Parcelable {
}
@DataClass.Generated(
- time = 1705575046107L,
+ time = 1709014715064L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java",
- inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final @android.app.ambientcontext.AmbientContextEvent.Event @android.annotation.FlaggedApi int EVENT_HEART_RATE\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\npublic static final @android.annotation.FlaggedApi int RATE_PER_MINUTE_UNKNOWN\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate final @android.annotation.IntRange int mRatePerMinute\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nprivate static int defaultRatePerMinute()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index cbb0ae784f82..6da871a74383 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -30,6 +30,7 @@ import android.os.Parcel;
import android.os.Trace;
import android.util.MergedConfiguration;
import android.util.Slog;
+import android.window.ActivityWindowInfo;
import com.android.internal.content.ReferrerIntent;
@@ -50,6 +51,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
private int mConfigChanges;
private MergedConfiguration mConfig;
private boolean mPreserveWindow;
+ private ActivityWindowInfo mActivityWindowInfo;
/**
* A record that was properly configured for relaunch. Execution will be cancelled if not
@@ -64,7 +66,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
CompatibilityInfo.applyOverrideScaleIfNeeded(mConfig);
}
mActivityClientRecord = client.prepareRelaunchActivity(getActivityToken(), mPendingResults,
- mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow);
+ mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow, mActivityWindowInfo);
}
@Override
@@ -101,7 +103,8 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
public static ActivityRelaunchItem obtain(@NonNull IBinder activityToken,
@Nullable List<ResultInfo> pendingResults,
@Nullable List<ReferrerIntent> pendingNewIntents, int configChanges,
- @NonNull MergedConfiguration config, boolean preserveWindow) {
+ @NonNull MergedConfiguration config, boolean preserveWindow,
+ @NonNull ActivityWindowInfo activityWindowInfo) {
ActivityRelaunchItem instance = ObjectPool.obtain(ActivityRelaunchItem.class);
if (instance == null) {
instance = new ActivityRelaunchItem();
@@ -113,6 +116,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
instance.mConfigChanges = configChanges;
instance.mConfig = new MergedConfiguration(config);
instance.mPreserveWindow = preserveWindow;
+ instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
return instance;
}
@@ -126,6 +130,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
mConfig = null;
mPreserveWindow = false;
mActivityClientRecord = null;
+ mActivityWindowInfo = null;
ObjectPool.recycle(this);
}
@@ -141,6 +146,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
dest.writeInt(mConfigChanges);
dest.writeTypedObject(mConfig, flags);
dest.writeBoolean(mPreserveWindow);
+ dest.writeTypedObject(mActivityWindowInfo, flags);
}
/** Read from Parcel. */
@@ -151,6 +157,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
mConfigChanges = in.readInt();
mConfig = in.readTypedObject(MergedConfiguration.CREATOR);
mPreserveWindow = in.readBoolean();
+ mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR);
}
public static final @NonNull Creator<ActivityRelaunchItem> CREATOR =
@@ -176,7 +183,8 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
return Objects.equals(mPendingResults, other.mPendingResults)
&& Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
&& mConfigChanges == other.mConfigChanges && Objects.equals(mConfig, other.mConfig)
- && mPreserveWindow == other.mPreserveWindow;
+ && mPreserveWindow == other.mPreserveWindow
+ && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo);
}
@Override
@@ -188,6 +196,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
result = 31 * result + mConfigChanges;
result = 31 * result + Objects.hashCode(mConfig);
result = 31 * result + (mPreserveWindow ? 1 : 0);
+ result = 31 * result + Objects.hashCode(mActivityWindowInfo);
return result;
}
@@ -198,6 +207,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
+ ",pendingNewIntents=" + mPendingNewIntents
+ ",configChanges=" + mConfigChanges
+ ",config=" + mConfig
- + ",preserveWindow" + mPreserveWindow + "}";
+ + ",preserveWindow=" + mPreserveWindow
+ + ",activityWindowInfo=" + mActivityWindowInfo + "}";
}
}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 728c350bfb51..b42133939f28 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -169,6 +169,8 @@ import java.util.List;
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ClipData implements Parcelable {
+ private static final String TAG = "ClipData";
+
static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
ClipDescription.MIMETYPE_TEXT_PLAIN };
static final String[] MIMETYPES_TEXT_HTML = new String[] {
@@ -476,7 +478,6 @@ public class ClipData implements Parcelable {
* @return Returns the item's textual representation.
*/
//BEGIN_INCLUDE(coerceToText)
- @android.ravenwood.annotation.RavenwoodThrow
public CharSequence coerceToText(Context context) {
// If this Item has an explicit textual value, simply return that.
CharSequence text = getText();
@@ -484,13 +485,20 @@ public class ClipData implements Parcelable {
return text;
}
+ // Gracefully handle cases where resolver isn't available
+ ContentResolver resolver = null;
+ try {
+ resolver = context.getContentResolver();
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to obtain ContentResolver: " + e);
+ }
+
// If this Item has a URI value, try using that.
Uri uri = getUri();
- if (uri != null) {
+ if (uri != null && resolver != null) {
// First see if the URI can be opened as a plain text stream
// (of any sub-type). If so, this is the best textual
// representation for it.
- final ContentResolver resolver = context.getContentResolver();
AssetFileDescriptor descr = null;
FileInputStream stream = null;
InputStreamReader reader = null;
@@ -499,7 +507,7 @@ public class ClipData implements Parcelable {
// Ask for a stream of the desired type.
descr = resolver.openTypedAssetFileDescriptor(uri, "text/*", null);
} catch (SecurityException e) {
- Log.w("ClipData", "Failure opening stream", e);
+ Log.w(TAG, "Failure opening stream", e);
} catch (FileNotFoundException|RuntimeException e) {
// Unable to open content URI as text... not really an
// error, just something to ignore.
@@ -519,7 +527,7 @@ public class ClipData implements Parcelable {
return builder.toString();
} catch (IOException e) {
// Something bad has happened.
- Log.w("ClipData", "Failure loading text", e);
+ Log.w(TAG, "Failure loading text", e);
return e.toString();
}
}
@@ -528,7 +536,8 @@ public class ClipData implements Parcelable {
IoUtils.closeQuietly(stream);
IoUtils.closeQuietly(reader);
}
-
+ }
+ if (uri != null) {
// If we couldn't open the URI as a stream, use the URI itself as a textual
// representation (but not for "content", "android.resource" or "file" schemes).
final String scheme = uri.getScheme();
@@ -704,7 +713,7 @@ public class ClipData implements Parcelable {
}
} catch (SecurityException e) {
- Log.w("ClipData", "Failure opening stream", e);
+ Log.w(TAG, "Failure opening stream", e);
} catch (FileNotFoundException e) {
// Unable to open content URI as text... not really an
@@ -712,7 +721,7 @@ public class ClipData implements Parcelable {
} catch (IOException e) {
// Something bad has happened.
- Log.w("ClipData", "Failure loading text", e);
+ Log.w(TAG, "Failure loading text", e);
return Html.escapeHtml(e.toString());
} finally {
@@ -1123,7 +1132,7 @@ public class ClipData implements Parcelable {
*
* @hide
*/
- @android.ravenwood.annotation.RavenwoodThrow
+ @android.ravenwood.annotation.RavenwoodKeep
public void prepareToLeaveProcess(boolean leavingPackage) {
// Assume that callers are going to be granting permissions
prepareToLeaveProcess(leavingPackage, Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -1134,7 +1143,7 @@ public class ClipData implements Parcelable {
*
* @hide
*/
- @android.ravenwood.annotation.RavenwoodThrow
+ @android.ravenwood.annotation.RavenwoodReplace
public void prepareToLeaveProcess(boolean leavingPackage, int intentFlags) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
@@ -1154,6 +1163,11 @@ public class ClipData implements Parcelable {
}
}
+ /** @hide */
+ public void prepareToLeaveProcess$ravenwood(boolean leavingPackage, int intentFlags) {
+ // No process boundaries on Ravenwood; ignored
+ }
+
/** {@hide} */
@android.ravenwood.annotation.RavenwoodThrow
public void prepareToEnterProcess(AttributionSource source) {
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 107f1078b11e..2fabcbae9bbb 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -50,6 +50,7 @@ import java.util.Objects;
* </div>
*/
@SystemService(Context.CLIPBOARD_SERVICE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ClipboardManager extends android.text.ClipboardManager {
/**
@@ -143,6 +144,7 @@ public class ClipboardManager extends android.text.ClipboardManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION)
+ @android.ravenwood.annotation.RavenwoodThrow
public boolean areClipboardAccessNotificationsEnabled() {
try {
return mService.areClipboardAccessNotificationsEnabledForUser(mContext.getUserId());
@@ -159,6 +161,7 @@ public class ClipboardManager extends android.text.ClipboardManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION)
+ @android.ravenwood.annotation.RavenwoodThrow
public void setClipboardAccessNotificationsEnabled(boolean enable) {
try {
mService.setClipboardAccessNotificationsEnabledForUser(enable, mContext.getUserId());
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f77ebc148070..70d2c7a3a41a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -83,6 +83,7 @@ import android.os.StatFs;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
+import android.provider.E2eeContactKeysManager;
import android.provider.MediaStore;
import android.telephony.TelephonyRegistryManager;
import android.util.AttributeSet;
@@ -4815,9 +4816,7 @@ public abstract class Context {
* @see android.net.thread.ThreadNetworkManager
* @hide
*/
- // TODO (b/325886480): update the flag to
- // "com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM"
- @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform")
+ @FlaggedApi(com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM)
@SystemApi
public static final String THREAD_NETWORK_SERVICE = "thread_network";
@@ -6566,10 +6565,10 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a
- * {@link android.provider.ContactKeysManager} to managing contact keys.
+ * {@link E2eeContactKeysManager} to managing contact keys.
*
* @see #getSystemService(String)
- * @see android.provider.ContactKeysManager
+ * @see E2eeContactKeysManager
*/
@FlaggedApi(android.provider.Flags.FLAG_USER_KEYS)
public static final String CONTACT_KEYS_SERVICE = "contact_keys";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7a015cd187ca..240cff34ef36 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3974,9 +3974,7 @@ public abstract class PackageManager {
* The device is capable of communicating with other devices via
* <a href="https://www.threadgroup.org">Thread</a> networking protocol.
*/
- // TODO (b/325886480): update the flag to
- // "com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM"
- @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform")
+ @FlaggedApi(com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM)
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 22e233aebf36..ac80561c3b50 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -177,3 +177,10 @@ flag {
description: "Enable the sensitive notifications toggle to be visible in the Private space settings page"
bug: "317067050"
}
+
+flag {
+ name: "enable_private_space_intent_redirection"
+ namespace: "profile_experiences"
+ description: "Enable Private Space telephony and SMS intent redirection to the main user"
+ bug: "325576602"
+}
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
index 6e53fd99873b..3d8ccaaa45cc 100644
--- a/core/java/android/credentials/GetCandidateCredentialsResponse.java
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -18,7 +18,7 @@ package android.credentials;
import android.annotation.Hide;
import android.annotation.NonNull;
-import android.app.PendingIntent;
+import android.content.Intent;
import android.credentials.selection.GetCredentialProviderData;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,32 +39,22 @@ public final class GetCandidateCredentialsResponse implements Parcelable {
@NonNull
private final List<GetCredentialProviderData> mCandidateProviderDataList;
- private final PendingIntent mPendingIntent;
-
- /**
- * @hide
- */
- @Hide
- public GetCandidateCredentialsResponse(
- GetCredentialResponse getCredentialResponse
- ) {
- mCandidateProviderDataList = null;
- mPendingIntent = null;
- }
+ @NonNull
+ private final Intent mIntent;
/**
* @hide
*/
@Hide
public GetCandidateCredentialsResponse(
- List<GetCredentialProviderData> candidateProviderDataList,
- PendingIntent pendingIntent
+ @NonNull List<GetCredentialProviderData> candidateProviderDataList,
+ @NonNull Intent intent
) {
Preconditions.checkCollectionNotEmpty(
candidateProviderDataList,
/*valueName=*/ "candidateProviderDataList");
mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
- mPendingIntent = pendingIntent;
+ mIntent = intent;
}
/**
@@ -81,8 +71,9 @@ public final class GetCandidateCredentialsResponse implements Parcelable {
*
* @hide
*/
- public PendingIntent getPendingIntent() {
- return mPendingIntent;
+ @NonNull
+ public Intent getIntent() {
+ return mIntent;
}
protected GetCandidateCredentialsResponse(Parcel in) {
@@ -91,14 +82,13 @@ public final class GetCandidateCredentialsResponse implements Parcelable {
mCandidateProviderDataList = candidateProviderDataList;
AnnotationValidations.validate(NonNull.class, null, mCandidateProviderDataList);
-
- mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+ mIntent = in.readTypedObject(Intent.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(mCandidateProviderDataList);
- dest.writeTypedObject(mPendingIntent, flags);
+ dest.writeTypedObject(mIntent, flags);
}
@Override
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
index 510b14e7acd4..e28f34528f42 100644
--- a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
@@ -99,7 +99,11 @@ public final class NavigationBarView extends FrameLayout {
mDeadZone = new android.inputmethodservice.navigationbar.DeadZone(this);
- getImeSwitchButton().setOnClickListener(view -> view.getContext()
+ getBackButton().setLongClickable(false);
+
+ final ButtonDispatcher imeSwitchButton = getImeSwitchButton();
+ imeSwitchButton.setLongClickable(false);
+ imeSwitchButton.setOnClickListener(view -> view.getContext()
.getSystemService(InputMethodManager.class).showInputMethodPicker());
}
diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig
index 6efb87225183..9f9aef8acc6a 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -17,3 +17,9 @@ flag {
bug: "307898240"
}
+flag {
+ name: "register_nsd_offload_engine"
+ namespace: "android_core_networking"
+ description: "Flag for registerOffloadEngine API in NsdManager"
+ bug: "294777050"
+}
diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java
index 36730cb07344..f852d3cd69b2 100644
--- a/core/java/android/os/HandlerThread.java
+++ b/core/java/android/os/HandlerThread.java
@@ -19,6 +19,8 @@ package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import java.util.concurrent.Executor;
+
/**
* A {@link Thread} that has a {@link Looper}.
* The {@link Looper} can then be used to create {@link Handler}s.
@@ -30,7 +32,8 @@ public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
- private @Nullable Handler mHandler;
+ private volatile @Nullable Handler mHandler;
+ private volatile @Nullable Executor mExecutor;
public HandlerThread(String name) {
super(name);
@@ -131,6 +134,18 @@ public class HandlerThread extends Thread {
}
/**
+ * @return a shared {@link Executor} associated with this thread
+ * @hide
+ */
+ @NonNull
+ public Executor getThreadExecutor() {
+ if (mExecutor == null) {
+ mExecutor = new HandlerExecutor(getThreadHandler());
+ }
+ return mExecutor;
+ }
+
+ /**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index be17d7c0f0db..ccb534eb1019 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1920,9 +1920,7 @@ public class UserManager {
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- // TODO (b/325886480): update the flag to
- // "com.android.net.thread.platform.flags.Flags.FLAG_THREAD_USER_RESTRICTION_ENABLED"
- @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled")
+ @FlaggedApi(com.android.net.thread.platform.flags.Flags.FLAG_THREAD_USER_RESTRICTION_ENABLED)
public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
/**
diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS
index 2cb16d337bba..c21895a2407c 100644
--- a/core/java/android/os/storage/OWNERS
+++ b/core/java/android/os/storage/OWNERS
@@ -3,11 +3,15 @@
# PLEASE ASSIGN NEW BUGS TO android-storage-triage@, NOT TO INDIVIDUAL PEOPLE
# Android Storage Team
+aibra@google.com
+akgaurav@google.com
alukin@google.com
ankitavyas@google.com
dipankarb@google.com
gargshivam@google.com
+ishneet@google.com
krishang@google.com
+oeissa@google.com
riyaghai@google.com
sahanas@google.com
shikhamalhotra@google.com
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 460b4dd9b86c..141ffc9a491a 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -359,8 +359,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
new PermissionGroupUsage(usage.packageName, usage.uid, usage.lastAccessTime,
permGroup,
usage.isRunning, isPhone, usage.attributionTag, attributionLabel,
- usagesWithLabels.valueAt(usageNum),
- VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT));
+ usagesWithLabels.valueAt(usageNum), deviceId));
}
}
diff --git a/core/java/android/provider/ContactKeysManager.java b/core/java/android/provider/E2eeContactKeysManager.java
index 01aaa3d9647e..b69441700b0a 100644
--- a/core/java/android/provider/ContactKeysManager.java
+++ b/core/java/android/provider/E2eeContactKeysManager.java
@@ -39,18 +39,18 @@ import java.util.List;
import java.util.Objects;
/**
- * ContactKeysManager provides access to the provider of end-to-end encryption contact keys.
- * It manages two types of keys - {@link ContactKey} and {@link SelfKey}.
+ * E2eeContactKeysManager provides access to the provider of end-to-end encryption contact keys.
+ * It manages two types of keys - {@link E2eeContactKey} and {@link E2eeSelfKey}.
* <ul>
* <li>
- * A {@link ContactKey} is a public key associated with a contact. It's used to end-to-end
+ * A {@link E2eeContactKey} is a public key associated with a contact. It's used to end-to-end
* encrypt the communications between a user and the contact. This API allows operations on
- * {@link ContactKey}s to insert/update, remove, change the verification state, and retrieving
+ * {@link E2eeContactKey}s to insert/update, remove, change the verification state, and retrieving
* keys (either created by or visible to the caller app).
* </li>
* <li>
- * A {@link SelfKey} is a key for this device, so the key represents the owner of the device.
- * This API allows operations on {@link SelfKey}s to insert/update, remove, and retrieving
+ * A {@link E2eeSelfKey} is a key for this device, so the key represents the owner of the device.
+ * This API allows operations on {@link E2eeSelfKey}s to insert/update, remove, and retrieving
* self keys (either created by or visible to the caller app).
* </li>
* </ul>
@@ -65,29 +65,30 @@ import java.util.Objects;
* </li>
* <li>
* accountId - the app-specified identifier for the account for which the contact key can be used.
- * Usually a phone number.
+ * Using different account IDs allows for multiple key entries representing the same user.
+ * For most apps this would be a phone number.
* </li>
* </ul>
* Contact keys also use lookupKey which is an opaque value used to identify a contact in
* ContactsProvider.
*/
@FlaggedApi(Flags.FLAG_USER_KEYS)
-public final class ContactKeysManager {
+public final class E2eeContactKeysManager {
/**
- * The authority for the contact keys provider.
+ * The authority for the end-to-end encryption contact keys provider.
* @hide
*/
public static final String AUTHORITY = "com.android.contactkeys.contactkeysprovider";
/**
- * A content:// style uri to the authority for the contact keys provider.
+ * A content:// style uri to the authority for the end-to-end encryption contact keys provider.
* @hide
*/
@NonNull
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
/**
- * Maximum size of a contact key.
+ * Maximum size of an end-to-end encryption contact key.
*/
private static final int MAX_KEY_SIZE_BYTES = 5000;
@@ -100,14 +101,15 @@ public final class ContactKeysManager {
private final ContentResolver mContentResolver;
/** @hide */
- public ContactKeysManager(@NonNull Context context) {
+ public E2eeContactKeysManager(@NonNull Context context) {
Objects.requireNonNull(context);
mContentResolver = context.getContentResolver();
}
/**
- * Inserts a new entry into the contact keys table or updates one if it already exists.
- * The inserted/updated contact key is owned by the caller app.
+ * Inserts a new entry into the end-to-end encryption contact keys table or updates one if it
+ * already exists.
+ * The inserted/updated end-to-end encryption contact key is owned by the caller app.
*
* @param lookupKey value that references the contact
* @param deviceId an app-specified identifier for the device
@@ -115,76 +117,77 @@ public final class ContactKeysManager {
* @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes)
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
- public void updateOrInsertContactKey(@NonNull String lookupKey,
+ public void updateOrInsertE2eeContactKey(@NonNull String lookupKey,
@NonNull String deviceId,
@NonNull String accountId,
@NonNull byte[] keyValue) {
validateKeyLength(keyValue);
Bundle extras = new Bundle();
- extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
- extras.putByteArray(ContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue));
+ extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putByteArray(E2eeContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue));
nullSafeCall(mContentResolver,
- ContactKeys.UPDATE_OR_INSERT_CONTACT_KEY_METHOD, extras);
+ E2eeContactKeys.UPDATE_OR_INSERT_CONTACT_KEY_METHOD, extras);
}
/**
- * Retrieves a contact key entry given the lookup key, device ID, accountId and inferred
- * caller package name.
+ * Retrieves an end-to-end encryption contact key entry given the lookup key, device ID,
+ * accountId and inferred caller package name.
*
* @param lookupKey the value that references the contact
* @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
*
- * @return a {@link ContactKey} object containing the contact key information,
+ * @return a {@link E2eeContactKey} object containing the contact key information,
* or null if no contact key is found.
*/
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
@Nullable
- public ContactKey getContactKey(
+ public E2eeContactKey getE2eeContactKey(
@NonNull String lookupKey,
@NonNull String deviceId,
@NonNull String accountId) {
Bundle extras = new Bundle();
- extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.GET_CONTACT_KEY_METHOD, extras);
+ E2eeContactKeys.GET_CONTACT_KEY_METHOD, extras);
if (response == null) {
return null;
}
- return response.getParcelable(ContactKeys.KEY_CONTACT_KEY, ContactKey.class);
+ return response.getParcelable(E2eeContactKeys.KEY_CONTACT_KEY, E2eeContactKey.class);
}
/**
- * Retrieves all contact key entries that belong to apps visible to the caller.
+ * Retrieves all end-to-end encryption contact key entries that belong to apps visible to
+ * the caller.
* The keys will be stripped of deviceId, timeUpdated and keyValue data.
*
* @param lookupKey the value that references the contact
*
- * @return a list of {@link ContactKey} objects containing the contact key
+ * @return a list of {@link E2eeContactKey} objects containing the contact key
* information, or an empty list if no keys are found.
*/
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
@NonNull
- public List<ContactKey> getAllContactKeys(@NonNull String lookupKey) {
+ public List<E2eeContactKey> getAllE2eeContactKeys(@NonNull String lookupKey) {
Bundle extras = new Bundle();
- extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+ extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.GET_ALL_CONTACT_KEYS_METHOD, extras);
+ E2eeContactKeys.GET_ALL_CONTACT_KEYS_METHOD, extras);
if (response == null) {
return new ArrayList<>();
}
- List<ContactKey> value = response.getParcelableArrayList(ContactKeys.KEY_CONTACT_KEYS,
- ContactKey.class);
+ List<E2eeContactKey> value = response.getParcelableArrayList(
+ E2eeContactKeys.KEY_CONTACT_KEYS, E2eeContactKey.class);
if (value == null) {
return new ArrayList<>();
}
@@ -192,27 +195,28 @@ public final class ContactKeysManager {
}
/**
- * Retrieves all contact key entries for a given lookupKey that belong to the caller app.
+ * Retrieves all end-to-end encryption contact key entries for a given lookupKey that belong to
+ * the caller app.
*
* @param lookupKey the value that references the contact
*
- * @return a list of {@link ContactKey} objects containing the contact key
- * information, or an empty list if no keys are found.
+ * @return a list of {@link E2eeContactKey} objects containing the end-to-end encryption
+ * contact key information, or an empty list if no keys are found.
*/
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
@NonNull
- public List<ContactKey> getOwnerContactKeys(@NonNull String lookupKey) {
+ public List<E2eeContactKey> getOwnerE2eeContactKeys(@NonNull String lookupKey) {
Bundle extras = new Bundle();
- extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+ extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.GET_OWNER_CONTACT_KEYS_METHOD, extras);
+ E2eeContactKeys.GET_OWNER_CONTACT_KEYS_METHOD, extras);
if (response == null) {
return new ArrayList<>();
}
- List<ContactKey> value = response.getParcelableArrayList(ContactKeys.KEY_CONTACT_KEYS,
- ContactKey.class);
+ List<E2eeContactKey> value = response.getParcelableArrayList(
+ E2eeContactKeys.KEY_CONTACT_KEYS, E2eeContactKey.class);
if (value == null) {
return new ArrayList<>();
}
@@ -220,7 +224,8 @@ public final class ContactKeysManager {
}
/**
- * Updates a contact key entry's local verification state that belongs to the caller app.
+ * Updates an end-to-end encryption contact key entry's local verification state that belongs
+ * to the caller app.
*
* @param lookupKey the value that references the contact
* @param deviceId an app-specified identifier for the device
@@ -230,27 +235,27 @@ public final class ContactKeysManager {
* @return true if the entry was updated, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
- public boolean updateContactKeyLocalVerificationState(@NonNull String lookupKey,
+ public boolean updateE2eeContactKeyLocalVerificationState(@NonNull String lookupKey,
@NonNull String deviceId,
@NonNull String accountId,
@VerificationState int localVerificationState) {
validateVerificationState(localVerificationState);
Bundle extras = new Bundle();
- extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
- extras.putInt(ContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState);
+ extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putInt(E2eeContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState);
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras);
+ E2eeContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras);
- return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+ return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
}
/**
- * Updates a contact key entry's local verification state that belongs to the app identified
- * by ownerPackageName.
+ * Updates an end-to-end encryption contact key entry's local verification state that belongs
+ * to the app identified by ownerPackageName.
*
* @param lookupKey the value that references the contact
* @param deviceId an app-specified identifier for the device
@@ -266,7 +271,7 @@ public final class ContactKeysManager {
@RequiresPermission(allOf = {
android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS,
android.Manifest.permission.WRITE_CONTACTS})
- public boolean updateContactKeyLocalVerificationState(@NonNull String lookupKey,
+ public boolean updateE2eeContactKeyLocalVerificationState(@NonNull String lookupKey,
@NonNull String deviceId,
@NonNull String accountId,
@NonNull String ownerPackageName,
@@ -274,20 +279,22 @@ public final class ContactKeysManager {
validateVerificationState(localVerificationState);
final Bundle extras = new Bundle();
- extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
- extras.putString(ContactKeys.OWNER_PACKAGE_NAME, Objects.requireNonNull(ownerPackageName));
- extras.putInt(ContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState);
+ extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putString(E2eeContactKeys.OWNER_PACKAGE_NAME,
+ Objects.requireNonNull(ownerPackageName));
+ extras.putInt(E2eeContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState);
final Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras);
+ E2eeContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras);
- return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+ return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
}
/**
- * Updates a contact key entry's remote verification state that belongs to the caller app.
+ * Updates an end-to-end encryption contact key entry's remote verification state that belongs
+ * to the caller app.
*
* @param lookupKey the value that references the contact
* @param deviceId an app-specified identifier for the device
@@ -297,27 +304,27 @@ public final class ContactKeysManager {
* @return true if the entry was updated, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
- public boolean updateContactKeyRemoteVerificationState(@NonNull String lookupKey,
+ public boolean updateE2eeContactKeyRemoteVerificationState(@NonNull String lookupKey,
@NonNull String deviceId,
@NonNull String accountId,
@VerificationState int remoteVerificationState) {
validateVerificationState(remoteVerificationState);
Bundle extras = new Bundle();
- extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
- extras.putInt(ContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
+ extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
+ E2eeContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
- return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+ return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
}
/**
- * Updates a contact key entry's remote verification state that belongs to the app identified
- * by ownerPackageName.
+ * Updates an end-to-end encryption contact key entry's remote verification state that belongs
+ * to the app identified by ownerPackageName.
*
* @param lookupKey the value that references the contact
* @param deviceId an app-specified identifier for the device
@@ -333,7 +340,7 @@ public final class ContactKeysManager {
@RequiresPermission(allOf = {
android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS,
android.Manifest.permission.WRITE_CONTACTS})
- public boolean updateContactKeyRemoteVerificationState(@NonNull String lookupKey,
+ public boolean updateE2eeContactKeyRemoteVerificationState(@NonNull String lookupKey,
@NonNull String deviceId,
@NonNull String accountId,
@NonNull String ownerPackageName,
@@ -341,19 +348,19 @@ public final class ContactKeysManager {
validateVerificationState(remoteVerificationState);
final Bundle extras = new Bundle();
- extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
- extras.putString(ContactKeys.OWNER_PACKAGE_NAME, Objects.requireNonNull(ownerPackageName));
- extras.putInt(ContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
+ extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putString(E2eeContactKeys.OWNER_PACKAGE_NAME,
+ Objects.requireNonNull(ownerPackageName));
+ extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
final Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
+ E2eeContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
- return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+ return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
}
-
private static void validateVerificationState(int verificationState) {
if (verificationState != VERIFICATION_STATE_UNVERIFIED
&& verificationState != VERIFICATION_STATE_VERIFICATION_FAILED
@@ -364,7 +371,7 @@ public final class ContactKeysManager {
}
/**
- * Removes a contact key entry that belongs to the caller app.
+ * Removes an end-to-end encryption contact key entry that belongs to the caller app.
*
* @param lookupKey the value that references the contact
* @param deviceId an app-specified identifier for the device
@@ -373,22 +380,23 @@ public final class ContactKeysManager {
* @return true if the entry was removed, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
- public boolean removeContactKey(@NonNull String lookupKey,
+ public boolean removeE2eeContactKey(@NonNull String lookupKey,
@NonNull String deviceId,
@NonNull String accountId) {
final Bundle extras = new Bundle();
- extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
final Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.REMOVE_CONTACT_KEY_METHOD, extras);
+ E2eeContactKeys.REMOVE_CONTACT_KEY_METHOD, extras);
- return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+ return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
}
/**
- * Inserts a new entry into the self keys table or updates one if it already exists.
+ * Inserts a new entry into the end-to-end encryption self keys table or updates one if it
+ * already exists.
* @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
@@ -397,20 +405,20 @@ public final class ContactKeysManager {
* @return true if the entry was added or updated, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
- public boolean updateOrInsertSelfKey(@NonNull String deviceId,
+ public boolean updateOrInsertE2eeSelfKey(@NonNull String deviceId,
@NonNull String accountId,
@NonNull byte[] keyValue) {
validateKeyLength(keyValue);
Bundle extras = new Bundle();
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
- extras.putByteArray(ContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putByteArray(E2eeContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue));
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.UPDATE_OR_INSERT_SELF_KEY_METHOD, extras);
+ E2eeContactKeys.UPDATE_OR_INSERT_SELF_KEY_METHOD, extras);
- return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+ return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
}
private static void validateKeyLength(byte[] keyValue) {
@@ -422,7 +430,7 @@ public final class ContactKeysManager {
}
/**
- * Updates a self key entry's remote verification state.
+ * Updates an end-to-end encryption self key entry's remote verification state.
*
* @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
@@ -431,25 +439,25 @@ public final class ContactKeysManager {
* @return true if the entry was updated, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
- public boolean updateSelfKeyRemoteVerificationState(@NonNull String deviceId,
+ public boolean updateE2eeSelfKeyRemoteVerificationState(@NonNull String deviceId,
@NonNull String accountId,
@VerificationState int remoteVerificationState) {
validateVerificationState(remoteVerificationState);
Bundle extras = new Bundle();
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
- extras.putInt(ContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
+ E2eeContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
- return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+ return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
}
/**
- * Updates a self key entry's remote verification state that belongs to the app identified
- * by ownerPackageName.
+ * Updates an end-to-end encryption self key entry's remote verification state that belongs to
+ * the app identified by ownerPackageName.
*
* @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
@@ -464,77 +472,79 @@ public final class ContactKeysManager {
@RequiresPermission(allOf = {
android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS,
android.Manifest.permission.WRITE_CONTACTS})
- public boolean updateSelfKeyRemoteVerificationState(@NonNull String deviceId,
+ public boolean updateE2eeSelfKeyRemoteVerificationState(@NonNull String deviceId,
@NonNull String accountId,
@NonNull String ownerPackageName,
@VerificationState int remoteVerificationState) {
validateVerificationState(remoteVerificationState);
Bundle extras = new Bundle();
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
- extras.putString(ContactKeys.OWNER_PACKAGE_NAME, Objects.requireNonNull(ownerPackageName));
- extras.putInt(ContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putString(E2eeContactKeys.OWNER_PACKAGE_NAME,
+ Objects.requireNonNull(ownerPackageName));
+ extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
+ E2eeContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
- return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+ return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
}
/**
- * Maximum size of a contact key.
+ * Maximum size of an end-to-end encryption contact key.
*/
public static int getMaxKeySizeBytes() {
return MAX_KEY_SIZE_BYTES;
}
/**
- * Returns a self key entry given the deviceId and the inferred package name of the caller.
+ * Returns an end-to-end encryption self key entry given the deviceId and the inferred package
+ * name of the caller.
*
* @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
*
- * @return a {@link SelfKey} object containing the self key information, or null if no self key
- * is found.
+ * @return a {@link E2eeSelfKey} object containing the end-to-end encryption self key
+ * information, or null if no self key is found.
*/
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
@Nullable
- public SelfKey getSelfKey(@NonNull String deviceId,
+ public E2eeSelfKey getE2eeSelfKey(@NonNull String deviceId,
@NonNull String accountId) {
Bundle extras = new Bundle();
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.GET_SELF_KEY_METHOD, extras);
+ E2eeContactKeys.GET_SELF_KEY_METHOD, extras);
if (response == null) {
return null;
}
- return response.getParcelable(ContactKeys.KEY_CONTACT_KEY, SelfKey.class);
+ return response.getParcelable(E2eeContactKeys.KEY_CONTACT_KEY, E2eeSelfKey.class);
}
/**
- * Returns all self key entries that belong to apps visible to the caller.
+ * Returns all end-to-end encryption self key entries that belong to apps visible to the caller.
* The keys will be stripped of deviceId, timeUpdated and keyValue data.
*
- * @return a list of {@link SelfKey} objects containing the self key information, or
- * an empty list if no keys are found.
+ * @return a list of {@link E2eeSelfKey} objects containing the end-to-end encryption self key
+ * information, or an empty list if no self keys are found.
*/
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
@NonNull
- public List<SelfKey> getAllSelfKeys() {
+ public List<E2eeSelfKey> getAllE2eeSelfKeys() {
Bundle extras = new Bundle();
- Bundle response = nullSafeCall(mContentResolver, ContactKeys.GET_ALL_SELF_KEYS_METHOD,
+ Bundle response = nullSafeCall(mContentResolver, E2eeContactKeys.GET_ALL_SELF_KEYS_METHOD,
extras);
if (response == null) {
return new ArrayList<>();
}
- List<SelfKey> value = response.getParcelableArrayList(ContactKeys.KEY_CONTACT_KEYS,
- SelfKey.class);
+ List<E2eeSelfKey> value = response.getParcelableArrayList(E2eeContactKeys.KEY_CONTACT_KEYS,
+ E2eeSelfKey.class);
if (value == null) {
return new ArrayList<>();
}
@@ -542,24 +552,24 @@ public final class ContactKeysManager {
}
/**
- * Returns all self key entries that are owned by the caller app.
+ * Returns all end-to-end encryption self key entries that are owned by the caller app.
*
- * @return a list of {@link SelfKey} objects containing the self key information, or
- * an empty list if no keys are found.
+ * @return a list of {@link E2eeSelfKey} objects containing the end-to-end encryption self key
+ * information, or an empty list if no self keys are found.
*/
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
@NonNull
- public List<SelfKey> getOwnerSelfKeys() {
+ public List<E2eeSelfKey> getOwnerE2eeSelfKeys() {
Bundle extras = new Bundle();
- Bundle response = nullSafeCall(mContentResolver, ContactKeys.GET_OWNER_SELF_KEYS_METHOD,
+ Bundle response = nullSafeCall(mContentResolver, E2eeContactKeys.GET_OWNER_SELF_KEYS_METHOD,
extras);
if (response == null) {
return new ArrayList<>();
}
- List<SelfKey> value = response.getParcelableArrayList(ContactKeys.KEY_CONTACT_KEYS,
- SelfKey.class);
+ List<E2eeSelfKey> value = response.getParcelableArrayList(E2eeContactKeys.KEY_CONTACT_KEYS,
+ E2eeSelfKey.class);
if (value == null) {
return new ArrayList<>();
}
@@ -567,24 +577,24 @@ public final class ContactKeysManager {
}
/**
- * Removes a self key entry given the deviceId and the inferred package name of the caller.
-
+ * Removes an end-to-end encryption self key entry given the deviceId and the inferred
+ * package name of the caller.
* @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
*
* @return true if the entry was removed, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
- public boolean removeSelfKey(@NonNull String deviceId,
+ public boolean removeE2eeSelfKey(@NonNull String deviceId,
@NonNull String accountId) {
Bundle extras = new Bundle();
- extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
- extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+ extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+ extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
Bundle response = nullSafeCall(mContentResolver,
- ContactKeys.REMOVE_SELF_KEY_METHOD, extras);
+ E2eeContactKeys.REMOVE_SELF_KEY_METHOD, extras);
- return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+ return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
}
private Bundle nullSafeCall(@NonNull ContentResolver resolver, @NonNull String method,
@@ -622,9 +632,9 @@ public final class ContactKeysManager {
public static final int VERIFICATION_STATE_VERIFIED = 2;
/** @hide */
- public static final class ContactKeys {
+ public static final class E2eeContactKeys {
- private ContactKeys() {}
+ private E2eeContactKeys() {}
/**
* <p>
@@ -636,14 +646,16 @@ public final class ContactKeysManager {
/**
* <p>
- * An app-specified identifier for the device for which the contact key can be used.
+ * An app-specified identifier for the device for which the end-to-end encryption
+ * contact key can be used.
* </p>
*/
public static final String DEVICE_ID = "device_id";
/**
* <p>
- * An app-specified identifier for the account for which the contact key can be used.
+ * An app-specified identifier for the account for which the end-to-end encryption
+ * contact key can be used.
* Usually a phone number.
* </p>
*/
@@ -718,29 +730,32 @@ public final class ContactKeysManager {
public static final String GET_CONTACT_KEY_METHOD = "getContactKey";
/**
- * The method to invoke in order to retrieve all contact keys.
+ * The method to invoke in order to retrieve all end-to-end encryption contact keys.
*/
public static final String GET_ALL_CONTACT_KEYS_METHOD = "getAllContactKeys";
/**
- * The method to invoke in order to retrieve contact keys that belong to the caller.
+ * The method to invoke in order to retrieve end-to-end encryption contact keys that belong
+ * to the caller.
*/
public static final String GET_OWNER_CONTACT_KEYS_METHOD = "getOwnerContactKeys";
/**
- * The method to invoke in order to update a contact key local verification state.
+ * The method to invoke in order to update an end-to-end encryption contact key local
+ * verification state.
*/
public static final String UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD =
"updateContactKeyLocalVerificationState";
/**
- * The method to invoke in order to update a contact key remote verification state.
+ * The method to invoke in order to update an end-to-end encryption contact key remote
+ * verification state.
*/
public static final String UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD =
"updateContactKeyRemoteVerificationState";
/**
- * The method to invoke in order to remove a contact key.
+ * The method to invoke in order to remove a end-to-end encryption contact key.
*/
public static final String REMOVE_CONTACT_KEY_METHOD = "removeContactKey";
@@ -776,12 +791,12 @@ public final class ContactKeysManager {
public static final String REMOVE_SELF_KEY_METHOD = "removeSelfKey";
/**
- * Key in the incoming Bundle for all the contact keys.
+ * Key in the incoming Bundle for all the end-to-end encryption contact keys.
*/
public static final String KEY_CONTACT_KEYS = "key_contact_keys";
/**
- * Key in the incoming Bundle for a single contact key.
+ * Key in the incoming Bundle for a single end-to-end encryption contact key.
*/
public static final String KEY_CONTACT_KEY = "key_contact_key";
@@ -790,18 +805,19 @@ public final class ContactKeysManager {
*/
public static final String KEY_UPDATED_ROWS = "key_updated_rows";
}
-
/**
* A parcelable class encapsulating other users' end to end encrypted contact key.
*/
- public static final class ContactKey implements Parcelable {
+ public static final class E2eeContactKey implements Parcelable {
/**
- * An app-specified identifier for the device for which the contact key can be used.
+ * An app-specified identifier for the device for which the end-to-end encryption
+ * contact key can be used.
*/
private final String mDeviceId;
/**
- * An app-specified identifier for the account for which the contact key can be used.
+ * An app-specified identifier for the account for which the end-to-end encryption
+ * contact key can be used.
* Usually a phone number.
*/
private final String mAccountId;
@@ -851,7 +867,7 @@ public final class ContactKeysManager {
/**
* @hide
*/
- public ContactKey(@Nullable String deviceId, @NonNull String accountId,
+ public E2eeContactKey(@Nullable String deviceId, @NonNull String accountId,
@NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue,
@VerificationState int localVerificationState,
@VerificationState int remoteVerificationState,
@@ -870,8 +886,10 @@ public final class ContactKeysManager {
}
/**
- * Gets the app-specified identifier for the device for which the contact key can be used.
- * Returns null if the app doesn't have the required visibility into the contact key.
+ * Gets the app-specified identifier for the device for which the end-to-end encryption
+ * contact key can be used.
+ * Returns null if the app doesn't have the required visibility into
+ * the end-to-end encryption contact key.
*
* @return An app-specified identifier for the device.
*/
@@ -881,7 +899,8 @@ public final class ContactKeysManager {
}
/**
- * Gets the app-specified identifier for the account for which the contact key can be used.
+ * Gets the app-specified identifier for the account for which the end-to-end encryption
+ * contact key can be used.
* Usually a phone number.
*
* @return An app-specified identifier for the account.
@@ -903,7 +922,7 @@ public final class ContactKeysManager {
/**
* Gets the timestamp at which the key was updated. Returns -1 if the app doesn't have the
- * required visibility into the contact key.
+ * required visibility into the end-to-end encryption contact key.
*
* @return The timestamp at which the key was updated in the System.currentTimeMillis()
* base.
@@ -914,7 +933,8 @@ public final class ContactKeysManager {
/**
* Gets the raw bytes for the key.
- * Returns null if the app doesn't have the required visibility into the contact key.
+ * Returns null if the app doesn't have the required visibility into
+ * the end-to-end encryption contact key.
*
* @return A copy of the raw bytes for the key.
*/
@@ -984,7 +1004,7 @@ public final class ContactKeysManager {
if (obj == null) return false;
if (obj == this) return true;
- if (!(obj instanceof ContactKey toCompare)) {
+ if (!(obj instanceof E2eeContactKey toCompare)) {
return false;
}
@@ -1023,10 +1043,10 @@ public final class ContactKeysManager {
}
@NonNull
- public static final Creator<ContactKey> CREATOR =
+ public static final Creator<E2eeContactKey> CREATOR =
new Creator<>() {
@Override
- public ContactKey createFromParcel(Parcel source) {
+ public E2eeContactKey createFromParcel(Parcel source) {
String deviceId = source.readString8();
String accountId = source.readString8();
String ownerPackageName = source.readString8();
@@ -1044,14 +1064,14 @@ public final class ContactKeysManager {
String displayName = source.readString8();
String number = source.readString8();
String address = source.readString8();
- return new ContactKey(deviceId, accountId, ownerPackageName,
+ return new E2eeContactKey(deviceId, accountId, ownerPackageName,
timeUpdated, keyValue, localVerificationState,
remoteVerificationState, displayName, number, address);
}
@Override
- public ContactKey[] newArray(int size) {
- return new ContactKey[size];
+ public E2eeContactKey[] newArray(int size) {
+ return new E2eeContactKey[size];
}
};
}
@@ -1059,14 +1079,16 @@ public final class ContactKeysManager {
/**
* A parcelable class encapsulating self end to end encrypted contact key.
*/
- public static final class SelfKey implements Parcelable {
+ public static final class E2eeSelfKey implements Parcelable {
/**
- * An app-specified identifier for the device for which the contact key can be used.
+ * An app-specified identifier for the device for which the end-to-end encryption
+ * contact key can be used.
*/
private final String mDeviceId;
/**
- * An app-specified identifier for the account for which the contact key can be used.
+ * An app-specified identifier for the account for which the end-to-end encryption
+ * contact key can be used.
* Usually a phone number.
*/
private final String mAccountId;
@@ -1096,7 +1118,7 @@ public final class ContactKeysManager {
/**
* @hide
*/
- public SelfKey(@Nullable String deviceId, @NonNull String accountId,
+ public E2eeSelfKey(@Nullable String deviceId, @NonNull String accountId,
@NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue,
@VerificationState int remoteVerificationState) {
this.mDeviceId = deviceId;
@@ -1108,8 +1130,10 @@ public final class ContactKeysManager {
}
/**
- * Gets the app-specified identifier for the device for which the contact key can be used.
- * Returns null if the app doesn't have the required visibility into the contact key.
+ * Gets the app-specified identifier for the device for which the end-to-end encryption
+ * contact key can be used.
+ * Returns null if the app doesn't have the required visibility into
+ * the end-to-end encryption contact key.
*
* @return An app-specified identifier for the device.
*/
@@ -1119,7 +1143,8 @@ public final class ContactKeysManager {
}
/**
- * Gets the app-specified identifier for the account for which the contact key can be used.
+ * Gets the app-specified identifier for the account for which the end-to-end encryption
+ * contact key can be used.
* Usually a phone number.
*
* @return An app-specified identifier for the device.
@@ -1141,7 +1166,7 @@ public final class ContactKeysManager {
/**
* Gets the timestamp at which the key was updated. Returns -1 if the app doesn't have the
- * required visibility into the contact key.
+ * required visibility into the end-to-end encryption contact key.
*
* @return The timestamp at which the key was updated in the System.currentTimeMillis()
* base.
@@ -1152,7 +1177,8 @@ public final class ContactKeysManager {
/**
* Gets the raw bytes for the key.
- * Returns null if the app doesn't have the required visibility into the contact key.
+ * Returns null if the app doesn't have the required visibility into
+ * the end-to-end encryption contact key.
*
* @return A copy of the raw bytes for the key.
*/
@@ -1182,7 +1208,7 @@ public final class ContactKeysManager {
if (obj == null) return false;
if (obj == this) return true;
- if (!(obj instanceof SelfKey toCompare)) {
+ if (!(obj instanceof E2eeSelfKey toCompare)) {
return false;
}
@@ -1213,10 +1239,10 @@ public final class ContactKeysManager {
}
@NonNull
- public static final Creator<SelfKey> CREATOR =
+ public static final Creator<E2eeSelfKey> CREATOR =
new Creator<>() {
@Override
- public SelfKey createFromParcel(Parcel source) {
+ public E2eeSelfKey createFromParcel(Parcel source) {
String deviceId = source.readString8();
String accountId = source.readString8();
String ownerPackageName = source.readString8();
@@ -1230,13 +1256,13 @@ public final class ContactKeysManager {
keyValue = null;
}
int remoteVerificationState = source.readInt();
- return new SelfKey(deviceId, accountId, ownerPackageName,
+ return new E2eeSelfKey(deviceId, accountId, ownerPackageName,
timeUpdated, keyValue, remoteVerificationState);
}
@Override
- public SelfKey[] newArray(int size) {
- return new SelfKey[size];
+ public E2eeSelfKey[] newArray(int size) {
+ return new E2eeSelfKey[size];
}
};
}
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index 00236dfa7876..d72441f1e4b7 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -27,3 +27,14 @@ flag {
description: "Provides additional callbacks with information about user actions in ChooserResult"
bug: "263474465"
}
+
+flag {
+ name: "legacy_chooser_pinning_removal"
+ namespace: "intentresolver"
+ description: "Removing pinning functionality from the legacy chooser (used by partial screenshare)"
+ bug: "301068735"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index ad326e41a146..a2f767d002f4 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -102,6 +102,7 @@ per-file ISurfaceControlViewHost*.aidl = file:/services/core/java/com/android/se
per-file IWindow*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
per-file RemoteAnimation*.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file RemoteAnimation*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ScreenRecordingCallbacks.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file *SurfaceControl*.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file SurfaceControl*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
per-file SurfaceSession.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 13b9c4518711..f5f4fd9f7042 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -480,11 +480,19 @@ public final class PointerIcon implements Parcelable {
mBitmapFrames = new Bitmap[frames - 1];
final int width = drawable.getIntrinsicWidth();
final int height = drawable.getIntrinsicHeight();
+ final boolean isVectorAnimation = drawable instanceof VectorDrawable;
+ mDrawNativeDropShadow = isVectorAnimation;
for (int i = 1; i < frames; ++i) {
Drawable drawableFrame = animationDrawable.getFrame(i);
- if (!(drawableFrame instanceof BitmapDrawable)) {
+ if (!(drawableFrame instanceof BitmapDrawable)
+ && !(drawableFrame instanceof VectorDrawable)) {
throw new IllegalArgumentException("Frame of an animated pointer icon "
- + "must refer to a bitmap drawable.");
+ + "must refer to a bitmap drawable or vector drawable.");
+ }
+ if (isVectorAnimation != (drawableFrame instanceof VectorDrawable)) {
+ throw new IllegalArgumentException("The drawable of the " + i + "-th frame "
+ + "is a different type from the others. All frames should be the "
+ + "same type.");
}
if (drawableFrame.getIntrinsicWidth() != width ||
drawableFrame.getIntrinsicHeight() != height) {
@@ -492,8 +500,11 @@ public final class PointerIcon implements Parcelable {
+ "is different. All frames should have the exact same size and "
+ "share the same hotspot.");
}
- BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame;
- mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame);
+ if (isVectorAnimation) {
+ drawableFrame = getBitmapDrawableFromVectorDrawable(resources,
+ (VectorDrawable) drawableFrame);
+ }
+ mBitmapFrames[i - 1] = getBitmapFromDrawable((BitmapDrawable) drawableFrame);
}
}
}
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
index f68cc691abeb..1f841bf53364 100644
--- a/core/java/android/view/RoundedCorners.java
+++ b/core/java/android/view/RoundedCorners.java
@@ -198,6 +198,12 @@ public class RoundedCorners implements Parcelable {
radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius);
}
array.recycle();
+ // For devices with round displays (e.g. watches) that don't otherwise provide the rounded
+ // corner radius via resource overlays, we can infer the corner radius directly from the
+ // display size.
+ if (radius == 0 && res.getConfiguration().isScreenRound()) {
+ radius = res.getDisplayMetrics().widthPixels / 2;
+ }
return radius;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cbb3be7c4155..042af1f0fb15 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15314,6 +15314,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link #isLongClickable()}, {@link #isContextClickable()},
* {@link #isScreenReaderFocusable()}, or {@link #isFocusable()}
* <li>Has an {@link AccessibilityDelegate}
+ * <li>Has an {@link AccessibilityNodeProvider}
* <li>Has an interaction listener, e.g. {@link OnTouchListener},
* {@link OnKeyListener}, etc.
* <li>Is an accessibility live region, e.g.
@@ -15346,7 +15347,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
return mode == IMPORTANT_FOR_ACCESSIBILITY_YES || isActionableForAccessibility()
- || hasListenersForAccessibility() || mAccessibilityDelegate != null
+ || hasListenersForAccessibility() || getAccessibilityNodeProvider() != null
+ || getAccessibilityDelegate() != null
|| getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE
|| isAccessibilityPane() || isAccessibilityHeading();
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 7c9678f11e0e..16fecc17d426 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -497,6 +497,25 @@ public final class InputMethodInfo implements Parcelable {
* @hide
*/
@TestApi
+ public InputMethodInfo(@NonNull String packageName, @NonNull String className,
+ @NonNull CharSequence label, @NonNull String settingsActivity,
+ @NonNull String languageSettingsActivity, boolean supportStylusHandwriting,
+ @NonNull String stylusHandwritingSettingsActivityAttr) {
+ this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
+ settingsActivity, languageSettingsActivity, null /* subtypes */,
+ 0 /* isDefaultResId */, false /* forceDefault */,
+ true /* supportsSwitchingToNextInputMethod */,
+ false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
+ false /* isVirtualDeviceOnly */, 0 /* handledConfigChanges */,
+ supportStylusHandwriting, false /* supportConnectionlessStylusHandwriting */,
+ stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */);
+ }
+
+ /**
+ * Test API for creating a built-in input method to verify stylus handwriting.
+ * @hide
+ */
+ @TestApi
@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING)
public InputMethodInfo(@NonNull String packageName, @NonNull String className,
@NonNull CharSequence label, @NonNull String settingsActivity,
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 3bce155049c8..f349ae9d8d79 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -3490,8 +3490,7 @@ public final class InputMethodManager {
return false;
}
mServedView = mNextServedView;
- if (initiationWithoutInputConnection() && mServedView.onCheckIsTextEditor()
- && mServedView.isHandwritingDelegate()) {
+ if (initiationWithoutInputConnection() && mServedView.isHandwritingDelegate()) {
mServedView.getViewRootImpl().getHandwritingInitiator().onDelegateViewFocused(
mServedView);
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 13dc4efb374d..0e5747d0e445 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3874,7 +3874,7 @@ public class RemoteViews implements Parcelable, Filter {
}
}
- private static class SetDrawInstructionAction extends Action {
+ private class SetDrawInstructionAction extends Action {
@Nullable
private final DrawInstructions mInstructions;
@@ -3909,6 +3909,15 @@ public class RemoteViews implements Parcelable, Filter {
}
try (ByteArrayInputStream is = new ByteArrayInputStream(bytes.get(0))) {
player.setDocument(new RemoteComposeDocument(is));
+ player.addClickListener((viewId, metadata) -> {
+ mActions.forEach(action -> {
+ if (viewId == action.mViewId
+ && action instanceof SetOnClickResponse setOnClickResponse) {
+ setOnClickResponse.mResponse.handleViewInteraction(
+ player, params.handler);
+ }
+ });
+ });
} catch (IOException e) {
Log.e(LOG_TAG, "Failed to render draw instructions", e);
}
@@ -6051,16 +6060,6 @@ public class RemoteViews implements Parcelable, Filter {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
View result = inflateView(context, rvToApply, directParent,
params.applyThemeResId, params.colorResources);
- if (result instanceof RemoteComposePlayer player) {
- player.addClickListener((viewId, metadata) -> {
- mActions.forEach(action -> {
- if (viewId == action.mViewId
- && action instanceof SetOnClickResponse setOnClickResponse) {
- setOnClickResponse.mResponse.handleViewInteraction(player, params.handler);
- }
- });
- });
- }
rvToApply.performApply(result, rootParent, params);
return result;
}
diff --git a/core/java/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS
index 3fa376003123..fd73d35e00d2 100644
--- a/core/java/android/window/flags/OWNERS
+++ b/core/java/android/window/flags/OWNERS
@@ -1,2 +1,3 @@
per-file responsible_apis.aconfig = file:/BAL_OWNERS
per-file large_screen_experiences_app_compat.aconfig = file:/LSE_APP_COMPAT_OWNERS
+per-file accessibility.aconfig = file:/core/java/android/view/accessibility/OWNERS
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 4e8661652e85..63a2474e70c1 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -7,3 +7,10 @@ flag {
bug: "320350734"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_desktop_windowing_mode"
+ namespace: "lse_desktop_experience"
+ description: "Enables desktop windowing"
+ bug: "304778354"
+}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 86d3037b1612..29669d312b1b 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -96,6 +96,7 @@ import android.provider.Downloads;
import android.provider.OpenableColumns;
import android.provider.Settings;
import android.service.chooser.ChooserTarget;
+import android.service.chooser.Flags;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.HashedStringCache;
@@ -2543,6 +2544,9 @@ public class ChooserActivity extends ResolverActivity implements
@Override
public boolean isComponentPinned(ComponentName name) {
+ if (Flags.legacyChooserPinningRemoval()) {
+ return false;
+ }
return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
}
@@ -3147,6 +3151,10 @@ public class ChooserActivity extends ResolverActivity implements
}
private boolean shouldShowTargetDetails(TargetInfo ti) {
+ if (Flags.legacyChooserPinningRemoval()) {
+ // Never show the long press menu if we've removed pinning.
+ return false;
+ }
ComponentName nearbyShare = getNearbySharingComponent();
// Suppress target details for nearby share to hide pin/unpin action
boolean isNearbyShare = nearbyShare != null && nearbyShare.equals(
diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index 73df5e8e7b1b..5e89d06facd1 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -421,6 +421,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@NonNull
private String[] mUsesStaticLibrariesSorted;
+ private boolean mAppMetadataFileInApk = false;
+
@NonNull
public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath,
@NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp,
@@ -1063,6 +1065,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
return memtagMode;
}
+ @Override
+ public boolean isAppMetadataFileInApk() {
+ return mAppMetadataFileInApk;
+ }
+
@Nullable
@Override
public Bundle getMetaData() {
@@ -2151,6 +2158,12 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
@Override
+ public PackageImpl setAppMetadataFileInApk(boolean fileInApk) {
+ mAppMetadataFileInApk = fileInApk;
+ return this;
+ }
+
+ @Override
public PackageImpl setMetaData(@Nullable Bundle value) {
metaData = value;
return this;
@@ -3264,6 +3277,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
dest.writeLong(this.mBooleans);
dest.writeLong(this.mBooleans2);
dest.writeBoolean(this.mAllowCrossUidActivitySwitchFromBelow);
+ dest.writeBoolean(this.mAppMetadataFileInApk);
}
public PackageImpl(Parcel in) {
@@ -3426,6 +3440,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
this.mBooleans = in.readLong();
this.mBooleans2 = in.readLong();
this.mAllowCrossUidActivitySwitchFromBelow = in.readBoolean();
+ this.mAppMetadataFileInApk = in.readBoolean();
assignDerivedFields();
assignDerivedFields2();
diff --git a/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
index 66cfb69d7871..3c26a7c3eba3 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
@@ -127,4 +127,7 @@ public interface ParsedPackage extends AndroidPackage {
ParsedPackage setDirectBootAware(boolean directBootAware);
ParsedPackage setPersistent(boolean persistent);
+
+ /** Retrieves whether the apk contains a app metadata file. */
+ boolean isAppMetadataFileInApk();
}
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 5d185af17d48..5ab17a6b2b19 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -133,6 +133,9 @@ public interface ParsingPackage {
@Nullable SparseArray<int[]> splitDependencies
);
+ /** Sets whether the apk contains a app metadata file. */
+ ParsingPackage setAppMetadataFileInApk(boolean fileInApk);
+
ParsingPackage setMetaData(Bundle metaData);
ParsingPackage setForceQueryable(boolean forceQueryable);
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index 2e6053d3d904..00c482722fa3 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -46,6 +46,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
+import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
@@ -163,6 +164,8 @@ public class ParsingPackageUtils {
*/
public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+ public static final String APP_METADATA_FILE_NAME = "app.metadata";
+
/**
* Path prefix for apps on expanded storage
*/
@@ -636,6 +639,11 @@ public class ParsingPackageUtils {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
}
+ if (Flags.aslInApkAppMetadataSource()
+ && ArrayUtils.contains(assets.list(""), APP_METADATA_FILE_NAME)) {
+ pkg.setAppMetadataFileInApk(true);
+ }
+
return input.success(pkg);
} catch (Exception e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
@@ -686,7 +694,8 @@ public class ParsingPackageUtils {
*/
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
String codePath, Resources res, XmlResourceParser parser, int flags,
- boolean shouldSkipComponents) throws XmlPullParserException, IOException {
+ boolean shouldSkipComponents)
+ throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 01e9f437a79f..06d5eb305ff0 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -362,6 +362,22 @@ static void init_keyboard(JNIEnv* env, const vector<string>& keyboardPaths) {
using namespace android;
+// Called right before aborting by LOG_ALWAYS_FATAL. Print the pending exception.
+void abort_handler(const char* abort_message) {
+ ALOGE("Abort to abort the process...");
+
+ JNIEnv* env = NULL;
+ if (javaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("vm->GetEnv() failed");
+ return;
+ }
+ if (env->ExceptionOccurred() != NULL) {
+ ALOGE("Pending exception:");
+ env->ExceptionDescribe();
+ }
+ ALOGE("Aborting because: %s", abort_message);
+}
+
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
javaVM = vm;
JNIEnv* env = nullptr;
@@ -369,6 +385,8 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
return JNI_ERR;
}
+ __android_log_set_aborter(abort_handler);
+
init_android_graphics();
// Configuration is stored as java System properties.
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 285dee364bb0..b39d5cf3842b 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -85,9 +85,24 @@ static void android_view_MotionEvent_setNativePtr(JNIEnv* env, ScopedLocalRef<jo
ScopedLocalRef<jobject> android_view_MotionEvent_obtainAsCopy(JNIEnv* env,
const MotionEvent& event) {
- std::unique_ptr<MotionEvent> destEvent = std::make_unique<MotionEvent>();
+ ScopedLocalRef<jobject> eventObj(env,
+ env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
+ gMotionEventClassInfo.obtain));
+ if (env->ExceptionCheck() || !eventObj.get()) {
+ ALOGE("An exception occurred while obtaining a motion event.");
+ LOGE_EX(env);
+ env->ExceptionClear();
+ return ScopedLocalRef<jobject>(env);
+ }
+
+ MotionEvent* destEvent = android_view_MotionEvent_getNativePtr(env, eventObj.get());
+ if (!destEvent) {
+ destEvent = new MotionEvent();
+ android_view_MotionEvent_setNativePtr(env, eventObj, destEvent);
+ }
+
destEvent->copyFrom(&event, true);
- return android_view_MotionEvent_obtainFromNative(env, std::move(destEvent));
+ return eventObj;
}
ScopedLocalRef<jobject> android_view_MotionEvent_obtainFromNative(
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 0938ce17a4c0..3ed9f49ede26 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1965,7 +1965,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
- if (!is_system_server && getuid() == 0) {
+ if (getuid() == 0) {
const int rc = createProcessGroup(uid, getpid());
if (rc != 0) {
fail_fn(rc == -EROFS ? CREATE_ERROR("createProcessGroup failed, kernel missing "
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 277824cbb4c8..e2e419fcf5b7 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -159,7 +159,7 @@ android_app {
"android.content.pm.flags-aconfig",
"android.provider.flags-aconfig",
"camera_platform_flags",
- "com.android.net.flags-aconfig",
+ "android.net.platform.flags-aconfig",
"com.android.window.flags.window-aconfig",
],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8720f947bb8d..487b5be9ef5c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2281,7 +2281,7 @@
<!-- @SystemApi @hide Allows changing Thread network state and access to Thread network
credentials such as Network Key and PSKc.
<p>Not for use by third-party applications.
- @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") -->
+ @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") -->
<permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"
android:protectionLevel="signature|privileged" />
@@ -2331,12 +2331,12 @@
<!-- Allows system apps to call methods to register itself as a mDNS offload engine.
<p>Not for use by third-party or privileged applications.
@SystemApi
- @FlaggedApi("com.android.net.flags.register_nsd_offload_engine")
+ @FlaggedApi("android.net.platform.flags.register_nsd_offload_engine")
@hide This should only be used by system apps.
-->
<permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE"
android:protectionLevel="signature"
- android:featureFlag="com.android.net.flags.register_nsd_offload_engine" />
+ android:featureFlag="android.net.platform.flags.register_nsd_offload_engine" />
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
@@ -3629,7 +3629,7 @@
<!-- Allows an application to set policy related to <a
href="https://www.threadgroup.org">Thread</a> network.
- @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled")
+ @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK"
android:protectionLevel="internal|role" />
@@ -7986,7 +7986,7 @@
@hide
-->
<permission android:name="android.permission.GET_APP_METADATA"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- @hide @SystemApi Allows an application to stage HealthConnect's remote data so that
HealthConnect can later integrate it. -->
diff --git a/core/res/res/drawable/pointer_wait_vector.xml b/core/res/res/drawable/pointer_wait_vector.xml
new file mode 100644
index 000000000000..4de0690bc26b
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
+ <item android:drawable="@drawable/pointer_wait_vector_0" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_1" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_2" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_3" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_4" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_5" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_6" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_7" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_8" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_9" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_10" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_11" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_12" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_13" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_14" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_15" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_16" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_17" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_18" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_19" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_20" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_21" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_22" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_23" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_24" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_25" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_26" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_27" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_28" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_29" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_30" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_31" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_32" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_33" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_34" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_35" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_36" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_37" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_38" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_39" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_40" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_41" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_42" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_43" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_44" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_45" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_46" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_47" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_48" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_49" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_50" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_51" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_52" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_53" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_54" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_55" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_56" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_57" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_58" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_59" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_60" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_61" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_62" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_63" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_64" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_65" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_66" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_67" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_68" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_69" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_70" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_71" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_72" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_73" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_74" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_75" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_76" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_77" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_78" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_79" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_80" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_81" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_82" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_83" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_84" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_85" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_86" android:duration="16"/>
+ <item android:drawable="@drawable/pointer_wait_vector_87" android:duration="16"/>
+</animation-list>
diff --git a/core/res/res/drawable/pointer_wait_vector_0.xml b/core/res/res/drawable/pointer_wait_vector_0.xml
new file mode 100644
index 000000000000..4bfbc9f08b1e
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_0.xml
@@ -0,0 +1,27 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.116,18.654a1.78,1.78 0,1 0,0.061 3.561,1.78 1.78,0 0,0 -0.061,-3.561m-7.194,-1.73a1.781,1.781 0,1 0,2.594 2.441,1.781 1.781,0 0,0 -2.594,-2.441"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.294,7.294a1.78,1.78 0,1 0,-2.517 -2.519,1.78 1.78,0 0,0 2.517,2.519"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.225,7.294a1.78,1.78 0,1 0,-2.517 -2.519,1.78 1.78,0 0,0 2.517,2.519"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.436,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.747,16.665a1.78,1.78 0,1 0,2.539 2.497,1.78 1.78,0 0,0 -2.539,-2.497"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_1.xml b/core/res/res/drawable/pointer_wait_vector_1.xml
new file mode 100644
index 000000000000..bab91e1d9402
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_1.xml
@@ -0,0 +1,21 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.174,18.653a1.782,1.782 0,1 0,0.094 3.562,1.782 1.782,0 0,0 -0.094,-3.562m-7.187,-1.637a1.782,1.782 0,1 0,2.627 2.407,1.782 1.782,0 0,0 -2.627,-2.407"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.294,7.294a1.78,1.78 0,1 0,-2.517 -2.519,1.78 1.78,0 0,0 2.517,2.519"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.225,7.294a1.78,1.78 0,1 0,-2.517 -2.519,1.78 1.78,0 0,0 2.517,2.519m1.203,2.888a1.78,1.78 0,1 0,0.016 3.562,1.78 1.78,0 1,0 -0.016,-3.562m-3.641,6.441a1.782,1.782 0,1 0,2.564 2.475,1.782 1.782,0 0,0 -2.564,-2.475"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_10.xml b/core/res/res/drawable/pointer_wait_vector_10.xml
new file mode 100644
index 000000000000..ae3e36093aaa
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_10.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.028,20.374m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.952,18.76m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12.066m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.957,6.113m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.779,3.566m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.7,5.78m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.42,11.485m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.462,17.423m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_11.xml b/core/res/res/drawable/pointer_wait_vector_11.xml
new file mode 100644
index 000000000000..d2c014d1ee3d
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_11.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.101,20.364m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.101,18.868m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12.074m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.931,6.139m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.706,3.569m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.646,5.731m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.41,11.338m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.509,17.366m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_12.xml b/core/res/res/drawable/pointer_wait_vector_12.xml
new file mode 100644
index 000000000000..e9a36c033ca6
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_12.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.247,20.344m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.222,18.953m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.564,12.147m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.88,6.193m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.632,3.571m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.591,5.681m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.404,11.265m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.602,17.252m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_13.xml b/core/res/res/drawable/pointer_wait_vector_13.xml
new file mode 100644
index 000000000000..5800f39fa7ef
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_13.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.392,20.321m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.344,19.035m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.565,12.184m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.83,6.246m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.559,3.575m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.535,5.633m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.39,11.118m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.693,17.136m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_14.xml b/core/res/res/drawable/pointer_wait_vector_14.xml
new file mode 100644
index 000000000000..3566aaf358e7
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_14.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.537,20.295m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.467,19.115m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.567,12.258m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.755,6.327m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.485,3.579m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.423,5.537m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.386,11.082m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.759,17.048m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_15.xml b/core/res/res/drawable/pointer_wait_vector_15.xml
new file mode 100644
index 000000000000..c995faf24f33
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_15.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.682,20.267m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.718,19.269m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.568,12.295m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.73,6.355m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.412,3.584m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.367,5.49m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.369,10.935m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.847,16.929m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_16.xml b/core/res/res/drawable/pointer_wait_vector_16.xml
new file mode 100644
index 000000000000..179328205776
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_16.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.826,20.237m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.846,19.343m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.57,12.331m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.706,6.382m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.338,3.589m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.31,5.444m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.354,10.826m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.91,16.839m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_17.xml b/core/res/res/drawable/pointer_wait_vector_17.xml
new file mode 100644
index 000000000000..59772f566644
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_17.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.969,20.204m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.974,19.414m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.571,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.681,6.41m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.191,3.602m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.194,5.352m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.333,10.68m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.994,16.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_18.xml b/core/res/res/drawable/pointer_wait_vector_18.xml
new file mode 100644
index 000000000000..1bbb242d5663
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_18.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M14.184,20.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.236,19.55m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.575,12.442m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.585,6.521m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.118,3.61m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.136,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.315,10.571m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.075,16.595m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_19.xml b/core/res/res/drawable/pointer_wait_vector_19.xml
new file mode 100644
index 000000000000..b8e0dd54708f
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_19.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M14.325,20.11m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.434,19.646m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.581,12.552m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.537,6.577m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.972,3.626m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.959,5.175m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.282,10.39m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.193,16.408m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_2.xml b/core/res/res/drawable/pointer_wait_vector_2.xml
new file mode 100644
index 000000000000..5c4606f36569
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_2.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.331,20.43m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.355,18.269m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.035m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.435,11.89m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.094,17.834m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_20.xml b/core/res/res/drawable/pointer_wait_vector_20.xml
new file mode 100644
index 000000000000..b3ac2dab2537
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_20.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M14.607,20.024m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.703,19.766m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.592,12.699m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.49,6.634m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.899,3.636m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.899,5.132m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.252,10.246m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.269,16.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_21.xml b/core/res/res/drawable/pointer_wait_vector_21.xml
new file mode 100644
index 000000000000..884476e929a6
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_21.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M14.816,19.953m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.908,19.849m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.589,12.662m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.443,6.691m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.753,3.656m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.718,5.006m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.22,10.102m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.414,16.025m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_22.xml b/core/res/res/drawable/pointer_wait_vector_22.xml
new file mode 100644
index 000000000000..20660ca89f3a
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_22.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M15.092,19.849m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.184,19.952m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.617,12.955m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.352,6.806m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.608,3.679m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.595,4.925m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.149,9.816m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.483,15.896m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_23.xml b/core/res/res/drawable/pointer_wait_vector_23.xml
new file mode 100644
index 000000000000..6b594ee78eae
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_23.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M15.296,19.766m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.533,20.068m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.609,12.882m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.262,6.923m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.463,3.705m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.471,4.845m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.11,9.675m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.614,15.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_24.xml b/core/res/res/drawable/pointer_wait_vector_24.xml
new file mode 100644
index 000000000000..a35203e3857b
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_24.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M15.632,19.615m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.816,20.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.626,13.028m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.175,7.041m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.318,3.733m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.346,4.769m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.046,9.463m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.677,15.498m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_25.xml b/core/res/res/drawable/pointer_wait_vector_25.xml
new file mode 100644
index 000000000000..df8549af4006
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_25.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M15.896,19.483m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.174,20.236m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.645,13.174m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.089,7.161m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.174,3.763m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.219,4.694m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.977,9.253m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.794,15.228m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_26.xml b/core/res/res/drawable/pointer_wait_vector_26.xml
new file mode 100644
index 000000000000..0abb7d41a59a
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_26.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M16.154,19.343m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.535,20.308m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.667,13.32m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.006,7.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.959,3.814m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.961,4.551m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.902,9.045m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.902,14.955m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_27.xml b/core/res/res/drawable/pointer_wait_vector_27.xml
new file mode 100644
index 000000000000..bde242548af5
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_27.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M16.471,19.155m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.972,20.374m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.691,13.465m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.885,7.467m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.817,3.851m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.831,4.483m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.85,8.908m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.023,14.607m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_28.xml b/core/res/res/drawable/pointer_wait_vector_28.xml
new file mode 100644
index 000000000000..19cb410b1405
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_28.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M16.778,18.953m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.411,20.416m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.718,13.61m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.807,7.592m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.604,3.911m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.566,4.354m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.677,8.502m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.109,14.325m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_29.xml b/core/res/res/drawable/pointer_wait_vector_29.xml
new file mode 100644
index 000000000000..5d6776fcdb21
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_29.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.136,18.693m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.926,20.436m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.748,13.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.731,7.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.463,3.954m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.365,4.263m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.55,8.236m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.203,13.969m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_3.xml b/core/res/res/drawable/pointer_wait_vector_3.xml
new file mode 100644
index 000000000000..8a19401bd401
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_3.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.368,20.429m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.437,18.343m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.94,6.009m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.435,11.853m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.119,17.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_30.xml b/core/res/res/drawable/pointer_wait_vector_30.xml
new file mode 100644
index 000000000000..56dcdc228401
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_30.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.479,18.415m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12.441,20.425m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.796,13.969m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.586,7.974m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.184,4.047m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.092,4.151m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.414,7.975m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.281,13.61m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_31.xml b/core/res/res/drawable/pointer_wait_vector_31.xml
new file mode 100644
index 000000000000..5687a9b46b67
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_31.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.807,18.12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M13.028,20.373m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.832,14.112m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.517,8.104m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.908,4.15m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.817,4.047m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.269,7.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.344,13.247m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_32.xml b/core/res/res/drawable/pointer_wait_vector_32.xml
new file mode 100644
index 000000000000..cb626e41d1d5
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_32.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18.17,17.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M13.61,20.281m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.87,14.254m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.417,8.302m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.704,4.234m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.537,3.954m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.076,7.405m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.397,12.809m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_33.xml b/core/res/res/drawable/pointer_wait_vector_33.xml
new file mode 100644
index 000000000000..052f973f2ab4
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_33.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18.51,17.366m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.183,20.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.954,14.537m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.323,8.501m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.435,4.354m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.184,3.851m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.868,7.101m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.428,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_34.xml b/core/res/res/drawable/pointer_wait_vector_34.xml
new file mode 100644
index 000000000000..9e665756f4c2
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_34.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18.911,16.839m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.885,19.928m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.999,14.677m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.206,8.771m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.17,4.483m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M13.826,3.764m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.603,6.748m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.433,11.779m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_35.xml b/core/res/res/drawable/pointer_wait_vector_35.xml
new file mode 100644
index 000000000000..1d063fc9fbb9
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_35.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19.232,16.345m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.566,19.646m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.098,14.955m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.098,9.045m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.846,4.657m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M13.465,3.692m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.27,6.355m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.41,11.338m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_36.xml b/core/res/res/drawable/pointer_wait_vector_36.xml
new file mode 100644
index 000000000000..db0ad1c847bc
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_36.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19.583,15.698m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.345,19.232m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.205,15.229m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.999,9.323m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.592,4.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M13.028,3.626m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.035m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.344,10.753m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_37.xml b/core/res/res/drawable/pointer_wait_vector_37.xml
new file mode 100644
index 000000000000..ee944a6ccbe6
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_37.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19.876,15.023m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.077,18.738m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.323,15.499m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.911,9.604m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.221,5.047m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12.589,3.584m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.59,5.681m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.236,10.174m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_38.xml b/core/res/res/drawable/pointer_wait_vector_38.xml
new file mode 100644
index 000000000000..a347a36e34b0
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_38.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20.129,14.255m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.861,18.069m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.45,15.764m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.796,10.031m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.864,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12.147,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.136,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.046,9.463m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_39.xml b/core/res/res/drawable/pointer_wait_vector_39.xml
new file mode 100644
index 000000000000..2c86f0dd7849
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_39.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20.321,13.392m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.557,17.309m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.586,16.026m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.705,10.462m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.521,5.585m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.559,3.575m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.657,4.965m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.766,8.704m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_4.xml b/core/res/res/drawable/pointer_wait_vector_4.xml
new file mode 100644
index 000000000000..a6690c003c4b
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_4.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.441,20.425m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.493,18.391m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.914,5.983m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.433,11.772m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.17,17.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_40.xml b/core/res/res/drawable/pointer_wait_vector_40.xml
new file mode 100644
index 000000000000..953fdc1deb23
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_40.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20.428,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.232,16.345m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.731,16.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.656,10.753m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.193,5.88m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.972,3.626m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.026,4.586m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.449,8.039m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_41.xml b/core/res/res/drawable/pointer_wait_vector_41.xml
new file mode 100644
index 000000000000..a1b17fa2d19d
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_41.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20.41,11.338m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.766,15.296m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.924,16.595m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.602,11.191m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.83,6.246m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.39,3.719m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.364,4.263m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.994,7.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_42.xml b/core/res/res/drawable/pointer_wait_vector_42.xml
new file mode 100644
index 000000000000..4de9b8d14dd8
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_42.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20.244,10.21m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.14,14.219m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.153,16.929m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.571,11.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.49,6.634m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.817,3.851m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.677,4m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.367,6.465m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_43.xml b/core/res/res/drawable/pointer_wait_vector_43.xml
new file mode 100644
index 000000000000..38187fe62ac4
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_43.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19.94,9.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.365,13.101m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.397,17.252m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.566,12.221m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.089,7.161m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.115,4.072m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M13.826,3.763m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.645,5.73m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_44.xml b/core/res/res/drawable/pointer_wait_vector_44.xml
new file mode 100644
index 000000000000..890ba7ee6533
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_44.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19.466,8.072m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.436,12.073m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.73,17.645m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.589,12.662m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.768,7.655m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.368,4.385m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12.882,3.61m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.839,5.089m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_45.xml b/core/res/res/drawable/pointer_wait_vector_45.xml
new file mode 100644
index 000000000000..309ac082edef
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_45.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18.847,7.071m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.382,11.045m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.139,18.069m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.656,13.247m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.417,8.302m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.592,4.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.853,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.83,4.483m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_46.xml b/core/res/res/drawable/pointer_wait_vector_46.xml
new file mode 100644
index 000000000000..5a9bf706064a
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_46.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18.195,6.273m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.237,10.174m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.465,18.367m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.797,13.969m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.124,8.977m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.864,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.826,3.646m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.747,4.023m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_47.xml b/core/res/res/drawable/pointer_wait_vector_47.xml
new file mode 100644
index 000000000000..0068f27c78d5
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_47.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.451,5.561m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.046,9.463m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.922,18.738m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.954,14.537m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.851,9.817m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.087,5.983m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.675,3.89m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M13.61,3.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_48.xml b/core/res/res/drawable/pointer_wait_vector_48.xml
new file mode 100644
index 000000000000..6ece990ad516
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_48.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M16.748,5.026m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.822,8.84m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.405,19.076m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.205,15.229m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.667,10.68m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.397,6.748m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.569,4.293m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12.588,3.584m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_49.xml b/core/res/res/drawable/pointer_wait_vector_49.xml
new file mode 100644
index 000000000000..d44438975224
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_49.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M16.09,4.621m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.583,8.302m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.974,19.414m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.586,16.025m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.571,11.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.768,7.655m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.655,4.768m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.558,3.575m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_5.xml b/core/res/res/drawable/pointer_wait_vector_5.xml
new file mode 100644
index 000000000000..4952fe57397b
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_5.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.515,20.421m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.577,18.463m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.877,5.947m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.433,11.779m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.22,17.7m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_50.xml b/core/res/res/drawable/pointer_wait_vector_50.xml
new file mode 100644
index 000000000000..a85155fc5dd2
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_50.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M15.499,4.323m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.343,7.846m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.636,19.737m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.047,16.778m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.589,12.662m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.234,8.704m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.634,5.49m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.608,3.679m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_51.xml b/core/res/res/drawable/pointer_wait_vector_51.xml
new file mode 100644
index 000000000000..95b4aaa2365a
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_51.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M14.885,4.072m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.155,7.529m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.393,20.024m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.585,17.479m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.748,13.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.851,9.817m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.88,6.193m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.816,3.851m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_52.xml b/core/res/res/drawable/pointer_wait_vector_52.xml
new file mode 100644
index 000000000000..34619b5d4361
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_52.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M14.396,3.911m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.953,7.221m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.246,20.252m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.3,18.22m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.072,14.885m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.636,10.899m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.307,6.864m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.114,4.072m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_53.xml b/core/res/res/drawable/pointer_wait_vector_53.xml
new file mode 100644
index 000000000000..926b63998470
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_53.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.969,3.797m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.782,6.982m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.118,20.39m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.221,18.953m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.551,15.961m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.564,11.927m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.807,7.592m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.568,4.293m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_54.xml b/core/res/res/drawable/pointer_wait_vector_54.xml
new file mode 100644
index 000000000000..c51851fce837
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_54.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.61,3.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.603,6.748m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12.147,20.435m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.17,19.517m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.132,16.899m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.618,12.955m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.45,8.236m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.104,4.517m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_55.xml b/core/res/res/drawable/pointer_wait_vector_55.xml
new file mode 100644
index 000000000000..cdf435fa0637
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_55.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.247,3.656m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.463,6.577m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M13.247,20.344m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.253,19.977m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.83,17.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.748,13.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.178,8.84m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.686,4.749m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_56.xml b/core/res/res/drawable/pointer_wait_vector_56.xml
new file mode 100644
index 000000000000..1528711e2542
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_56.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.028,3.626m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.319,6.41m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.325,20.11m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.39,20.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.521,18.415m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.954,14.537m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.976,9.393m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.405,4.924m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_57.xml b/core/res/res/drawable/pointer_wait_vector_57.xml
new file mode 100644
index 000000000000..1715db1e2bef
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_57.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.772,3.599m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.22,6.3m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.431,19.707m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.411,20.416m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.282,18.994m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.178,15.16m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.851,9.817m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.041,5.175m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_58.xml b/core/res/res/drawable/pointer_wait_vector_58.xml
new file mode 100644
index 000000000000..83aec4b24851
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_58.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.589,3.584m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.12,6.193m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.471,19.155m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12.442,20.425m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M7.974,19.414m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.417,15.698m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.748,10.246m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.864,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_59.xml b/core/res/res/drawable/pointer_wait_vector_59.xml
new file mode 100644
index 000000000000..2e5e4728ec85
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_59.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.442,3.575m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.069,6.139m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.309,18.556m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M13.465,20.308m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M8.568,19.707m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.657,16.154m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.679,10.608m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.634,5.49m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_6.xml b/core/res/res/drawable/pointer_wait_vector_6.xml
new file mode 100644
index 000000000000..d4f57af49b40
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_6.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.588,20.416m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.633,18.51m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.926,3.564m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.861,5.931m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.431,11.706m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.269,17.645m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_60.xml b/core/res/res/drawable/pointer_wait_vector_60.xml
new file mode 100644
index 000000000000..4f257671d25d
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_60.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.294,3.569m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.069,6.139m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.12,17.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.183,20.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.115,19.928m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M4.845,16.471m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.646,10.826m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.465,5.633m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_61.xml b/core/res/res/drawable/pointer_wait_vector_61.xml
new file mode 100644
index 000000000000..7e06db4d1874
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_61.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.147,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.017,6.087m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.738,17.077m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M14.885,19.928m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M9.604,20.089m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.047,16.779m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.61,11.118m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.355,5.73m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_62.xml b/core/res/res/drawable/pointer_wait_vector_62.xml
new file mode 100644
index 000000000000..b3d0541a6417
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_62.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.074,3.564m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.231,16.345m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.432,19.707m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.102,20.22m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.307,17.136m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.584,11.411m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.246,5.83m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_63.xml b/core/res/res/drawable/pointer_wait_vector_63.xml
new file mode 100644
index 000000000000..ee09c2537d3f
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_63.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.583,15.698m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M15.961,19.449m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.39,20.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.397,17.252m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.575,11.558m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.193,5.88m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_64.xml b/core/res/res/drawable/pointer_wait_vector_64.xml
new file mode 100644
index 000000000000..e87f94f33304
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_64.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M19.822,15.16m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.345,19.232m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.826,20.354m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.537,17.423m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.571,11.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.139,5.931m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_65.xml b/core/res/res/drawable/pointer_wait_vector_65.xml
new file mode 100644
index 000000000000..b8407c448db5
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_65.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.023,14.607m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.718,18.994m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M10.972,20.374m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.681,17.59m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.569,11.706m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.087,5.983m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_66.xml b/core/res/res/drawable/pointer_wait_vector_66.xml
new file mode 100644
index 000000000000..7e2448df4d11
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_66.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.149,14.184m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M16.959,18.825m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.265,20.405m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.78,17.7m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.565,11.853m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_67.xml b/core/res/res/drawable/pointer_wait_vector_67.xml
new file mode 100644
index 000000000000..615ac04c40b3
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_67.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.252,13.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.194,18.648m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.412,20.416m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.88,17.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_68.xml b/core/res/res/drawable/pointer_wait_vector_68.xml
new file mode 100644
index 000000000000..0ba4634a131f
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_68.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.308,13.465m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.366,18.51m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.632,20.429m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.931,17.861m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_69.xml b/core/res/res/drawable/pointer_wait_vector_69.xml
new file mode 100644
index 000000000000..efa90bf9df22
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_69.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.354,13.174m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.535,18.367m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.706,20.432m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.931,17.86m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_7.xml b/core/res/res/drawable/pointer_wait_vector_7.xml
new file mode 100644
index 000000000000..86d571a40f73
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_7.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.661,20.411m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.69,18.556m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.024,6.045m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.889,3.564m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.808,5.88m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.428,11.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.318,17.59m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_70.xml b/core/res/res/drawable/pointer_wait_vector_70.xml
new file mode 100644
index 000000000000..4e58af5dfb3b
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_70.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.39,12.882m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.645,18.27m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.853,20.435m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_71.xml b/core/res/res/drawable/pointer_wait_vector_71.xml
new file mode 100644
index 000000000000..5a534342ec8d
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_71.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.416,12.589m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.754,18.17m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.853,20.435m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_72.xml b/core/res/res/drawable/pointer_wait_vector_72.xml
new file mode 100644
index 000000000000..40fb5d313f7c
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_72.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.425,12.442m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.807,18.12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_73.xml b/core/res/res/drawable/pointer_wait_vector_73.xml
new file mode 100644
index 000000000000..933cc6ae4813
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_73.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.429,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.861,18.069m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_74.xml b/core/res/res/drawable/pointer_wait_vector_74.xml
new file mode 100644
index 000000000000..6c7fef264f81
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_74.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.431,12.294m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_75.xml b/core/res/res/drawable/pointer_wait_vector_75.xml
new file mode 100644
index 000000000000..24e64e3491cf
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_75.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.435,12.147m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_76.xml b/core/res/res/drawable/pointer_wait_vector_76.xml
new file mode 100644
index 000000000000..2ede57de05fd
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_76.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.436,12.073m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_77.xml b/core/res/res/drawable/pointer_wait_vector_77.xml
new file mode 100644
index 000000000000..71f811a497a0
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_77.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.436,12.037m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_78.xml b/core/res/res/drawable/pointer_wait_vector_78.xml
new file mode 100644
index 000000000000..e2d56c6f676e
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_78.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.437,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_79.xml b/core/res/res/drawable/pointer_wait_vector_79.xml
new file mode 100644
index 000000000000..e2d56c6f676e
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_79.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.437,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_8.xml b/core/res/res/drawable/pointer_wait_vector_8.xml
new file mode 100644
index 000000000000..ebac74961497
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_8.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.808,20.398m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.806,18.648m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.008,6.061m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.853,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.754,5.83m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.425,11.558m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.367,17.535m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_80.xml b/core/res/res/drawable/pointer_wait_vector_80.xml
new file mode 100644
index 000000000000..40fb5d313f7c
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_80.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.425,12.442m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.807,18.12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_81.xml b/core/res/res/drawable/pointer_wait_vector_81.xml
new file mode 100644
index 000000000000..933cc6ae4813
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_81.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.429,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.861,18.069m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_82.xml b/core/res/res/drawable/pointer_wait_vector_82.xml
new file mode 100644
index 000000000000..6c7fef264f81
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_82.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.431,12.294m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_83.xml b/core/res/res/drawable/pointer_wait_vector_83.xml
new file mode 100644
index 000000000000..24e64e3491cf
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_83.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.435,12.147m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_84.xml b/core/res/res/drawable/pointer_wait_vector_84.xml
new file mode 100644
index 000000000000..2ede57de05fd
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_84.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.436,12.073m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_85.xml b/core/res/res/drawable/pointer_wait_vector_85.xml
new file mode 100644
index 000000000000..71f811a497a0
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_85.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.436,12.037m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_86.xml b/core/res/res/drawable/pointer_wait_vector_86.xml
new file mode 100644
index 000000000000..e2d56c6f676e
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_86.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.437,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_87.xml b/core/res/res/drawable/pointer_wait_vector_87.xml
new file mode 100644
index 000000000000..e2d56c6f676e
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_87.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.437,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_9.xml b/core/res/res/drawable/pointer_wait_vector_9.xml
new file mode 100644
index 000000000000..d46dfa394c01
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_9.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.881,20.39m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M6.864,18.693m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M3.563,12.052m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M5.983,6.087m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M11.816,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M17.754,5.83m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M20.423,11.522m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+ <path
+ android:pathData="M18.415,17.479m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0"
+ android:fillColor="#FFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_wait_vector_icon.xml b/core/res/res/drawable/pointer_wait_vector_icon.xml
new file mode 100644
index 000000000000..45d371e95c5d
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_wait_vector"
+ android:hotSpotX="12dp"
+ android:hotSpotY="12dp" />
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index adf8d9f95b52..5945f81704c5 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1468,7 +1468,7 @@ please see styles_device_defaults.xml.
<item name="pointerIconHand">@drawable/pointer_hand_vector_icon</item>
<item name="pointerIconContextMenu">@drawable/pointer_context_menu_vector_icon</item>
<item name="pointerIconHelp">@drawable/pointer_help_vector_icon</item>
- <item name="pointerIconWait">@drawable/pointer_wait_icon</item>
+ <item name="pointerIconWait">@drawable/pointer_wait_vector_icon</item>
<item name="pointerIconCell">@drawable/pointer_cell_vector_icon</item>
<item name="pointerIconCrosshair">@drawable/pointer_crosshair_vector_icon</item>
<item name="pointerIconText">@drawable/pointer_text_vector_icon</item>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 37e6780a8109..e72beee71e50 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -28,10 +28,19 @@ filegroup {
visibility: ["//visibility:private"],
}
+java_defaults {
+ name: "FrameworksCoreTests-resources",
+ aaptflags: [
+ "-0 .dat",
+ "-0 .gld",
+ "-c fa",
+ ],
+ resource_dirs: ["res"],
+}
+
android_test {
name: "FrameworksCoreTests",
- // FrameworksCoreTestsRavenwood references the .aapt.srcjar
- use_resource_processor: false,
+ defaults: ["FrameworksCoreTests-resources"],
srcs: [
"src/**/*.java",
@@ -117,7 +126,6 @@ android_test {
certificate: "platform",
- resource_dirs: ["res"],
resource_zips: [":FrameworksCoreTests_apks_as_resources"],
java_resources: [":FrameworksCoreTests_unit_test_cert_der"],
@@ -128,6 +136,24 @@ android_test {
],
}
+// FrameworksCoreTestsRavenwood pulls in the R.java class from this one.
+// Note, "FrameworksCoreTests" and "FrameworksCoreTests-resonly" _might_ not have indentical
+// R.java (not sure if there's a guarantee), but that doesn't matter as long as
+// FrameworksCoreTestsRavenwood consistently uses the R definition in this module.
+android_app {
+ name: "FrameworksCoreTests-resonly",
+ defaults: ["FrameworksCoreTests-resources"],
+
+ // FrameworksCoreTestsRavenwood references the .aapt.srcjar
+ use_resource_processor: false,
+ libs: [
+ "framework-res",
+ "android.test.runner",
+ "org.apache.http.legacy",
+ ],
+ sdk_version: "core_platform",
+}
+
// Rules to copy all the test apks to the intermediate raw resource directory
java_genrule {
name: "FrameworksCoreTests_apks_as_resources",
@@ -225,7 +251,11 @@ android_ravenwood_test {
"src/com/android/internal/util/**/*.java",
"src/com/android/internal/power/EnergyConsumerStatsTest.java",
- ":FrameworksCoreTests{.aapt.srcjar}",
+ // Pull in R.java from FrameworksCoreTests-resonly, not from FrameworksCoreTests,
+ // to avoid having a dependency to FrameworksCoreTests.
+ // This way, when updating source files and running this test, we don't need to
+ // rebuild the entire FrameworksCoreTests, which would be slow.
+ ":FrameworksCoreTests-resonly{.aapt.srcjar}",
":FrameworksCoreTests-aidl",
":FrameworksCoreTests-helpers",
":FrameworksCoreTestDoubles-sources",
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index a7d083cbc399..d95834fc0f4a 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -65,6 +65,7 @@ import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
import android.view.Display;
import android.view.View;
+import android.window.ActivityWindowInfo;
import android.window.WindowContextInfo;
import android.window.WindowTokenClientController;
@@ -839,7 +840,7 @@ public class ActivityThreadTest {
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(
activity.getActivityToken(), null, null, 0,
new MergedConfiguration(currentConfig, currentConfig),
- false /* preserveWindow */);
+ false /* preserveWindow */, new ActivityWindowInfo());
final ResumeActivityItem resumeStateRequest =
ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */,
false /* shouldSendCompatFakeFocus*/);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
index b5e8203c51ed..30545f994f01 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -40,6 +40,7 @@ import android.util.ArrayMap;
import android.util.MergedConfiguration;
import android.view.IWindow;
import android.view.InsetsState;
+import android.window.ActivityWindowInfo;
import android.window.ClientWindowFrames;
import android.window.WindowContext;
import android.window.WindowContextInfo;
@@ -116,7 +117,8 @@ public class ClientTransactionItemTest {
public void testActivityRelaunchItem_getContextToUpdate() {
final ActivityRelaunchItem item = ActivityRelaunchItem
.obtain(mActivityToken, null /* pendingResults */, null /* pendingNewIntents */,
- 0 /* configChange */, mMergedConfiguration, false /* preserveWindow */);
+ 0 /* configChange */, mMergedConfiguration, false /* preserveWindow */,
+ new ActivityWindowInfo());
final Context context = item.getContextToUpdate(mHandler);
assertEquals(mActivity, context);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 207fe730aac2..a8466bb092c8 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -151,7 +151,8 @@ public class ObjectPoolTests {
@Test
public void testRecycleActivityRelaunchItem() {
testRecycle(() -> ActivityRelaunchItem.obtain(mActivityToken,
- resultInfoList(), referrerIntentList(), 42, mergedConfig(), true));
+ resultInfoList(), referrerIntentList(), 42, mergedConfig(), true,
+ new ActivityWindowInfo()));
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 03b85dc8b4e2..9743e84b9349 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -220,8 +220,11 @@ public class TransactionParcelTests {
// Write to parcel
Configuration overrideConfig = new Configuration();
overrideConfig.assetsSeq = 5;
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000),
+ new Rect(0, 0, 500, 500));
ActivityRelaunchItem item = ActivityRelaunchItem.obtain(mActivityToken, resultInfoList(),
- referrerIntentList(), 35, mergedConfig(), true);
+ referrerIntentList(), 35, mergedConfig(), true, activityWindowInfo);
writeAndPrepareForReading(item);
// Read from parcel and assert
diff --git a/core/tests/coretests/src/android/view/RoundedCornersTest.java b/core/tests/coretests/src/android/view/RoundedCornersTest.java
index 07ef33af3088..ec665ad042ae 100644
--- a/core/tests/coretests/src/android/view/RoundedCornersTest.java
+++ b/core/tests/coretests/src/android/view/RoundedCornersTest.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.content.res.Configuration.SCREENLAYOUT_ROUND_NO;
+import static android.content.res.Configuration.SCREENLAYOUT_ROUND_YES;
import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
import static android.view.RoundedCorner.POSITION_TOP_LEFT;
@@ -28,22 +30,47 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.platform.test.annotations.Presubmit;
+import android.util.DisplayMetrics;
import android.util.Pair;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
+
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class RoundedCornersTest {
+ @Mock
+ DisplayMetrics mMockDisplayMetrics;
+ @Mock
+ Resources mMockResources;
+ @Mock TypedArray mMockTypedArray;
+ Configuration mConfiguration;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mConfiguration = Configuration.EMPTY;
+ }
+
final RoundedCorners mRoundedCorners = new RoundedCorners(
new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10),
new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10),
@@ -199,4 +226,63 @@ public class RoundedCornersTest {
assertThat(RoundedCorners.fromRadii(radius, 200, 300),
not(sameInstance(cached)));
}
+
+ @Test
+ public void testGetRoundedCornerRadius_withRoundDevice_usesDisplayRadiusAsDefault() {
+ final int displayWidth = 400;
+ mConfiguration.screenLayout = SCREENLAYOUT_ROUND_YES;
+ mMockDisplayMetrics.widthPixels = displayWidth;
+
+ when(mMockResources.getConfiguration()).thenReturn(mConfiguration);
+ when(mMockResources.getDisplayMetrics()).thenReturn(mMockDisplayMetrics);
+ when(mMockResources.getStringArray(R.array.config_displayUniqueIdArray))
+ .thenReturn(new String[]{"0"});
+ when(mMockTypedArray.length()).thenReturn(0);
+ when(mMockResources.obtainTypedArray(anyInt())).thenReturn(mMockTypedArray);
+ when(mMockResources.getDimensionPixelSize(R.dimen.rounded_corner_radius))
+ .thenReturn(0);
+
+
+ int radius = RoundedCorners.getRoundedCornerRadius(mMockResources, "0");
+ assertEquals((displayWidth / 2), radius);
+ }
+
+ @Test
+ public void testGetRoundedCornerRadius_withRoundDevice_usesOverlayIfProvided() {
+ final int displayWidth = 400;
+ final int overlayValue = 199;
+ mConfiguration.screenLayout = SCREENLAYOUT_ROUND_YES;
+ mMockDisplayMetrics.widthPixels = displayWidth;
+
+ when(mMockResources.getConfiguration()).thenReturn(mConfiguration);
+ when(mMockResources.getDisplayMetrics()).thenReturn(mMockDisplayMetrics);
+ when(mMockResources.getStringArray(R.array.config_displayUniqueIdArray))
+ .thenReturn(new String[]{"0"});
+ when(mMockTypedArray.length()).thenReturn(0);
+ when(mMockResources.obtainTypedArray(anyInt())).thenReturn(mMockTypedArray);
+ when(mMockResources.getDimensionPixelSize(R.dimen.rounded_corner_radius))
+ .thenReturn(overlayValue);
+
+ int radius = RoundedCorners.getRoundedCornerRadius(mMockResources, "0");
+ assertEquals(overlayValue, radius);
+ }
+
+ @Test
+ public void testGetRoundedCornerRadius_withNonRoundDevice_noDisplayDefault() {
+ final int displayWidth = 400;
+ mConfiguration.screenLayout = SCREENLAYOUT_ROUND_NO;
+ mMockDisplayMetrics.widthPixels = displayWidth;
+
+ when(mMockResources.getConfiguration()).thenReturn(mConfiguration);
+ when(mMockResources.getDisplayMetrics()).thenReturn(mMockDisplayMetrics);
+ when(mMockResources.getStringArray(R.array.config_displayUniqueIdArray))
+ .thenReturn(new String[]{"0"});
+ when(mMockTypedArray.length()).thenReturn(0);
+ when(mMockResources.obtainTypedArray(anyInt())).thenReturn(mMockTypedArray);
+ when(mMockResources.getDimensionPixelSize(R.dimen.rounded_corner_radius))
+ .thenReturn(0);
+
+ int radius = RoundedCorners.getRoundedCornerRadius(mMockResources, "0");
+ assertEquals(0, radius);
+ }
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 11b827117aa3..bd9abec22325 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -21,12 +21,14 @@ import android.os.Build;
import android.os.StrictMode;
/**
- * @hide This should not be made public in its present form because it
- * assumes that private and secret key bytes are available and would
- * preclude the use of hardware crypto.
+ * This class provides some constants and helper methods related to Android's Keystore service.
+ * This class was originally much larger, but its functionality was superseded by other classes.
+ * It now just contains a few remaining pieces for which the users haven't been updated yet.
+ * You may be looking for {@link java.security.KeyStore} instead.
+ *
+ * @hide
*/
public class KeyStore {
- private static final String TAG = "KeyStore";
// ResponseCodes - see system/security/keystore/include/keystore/keystore.h
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -42,50 +44,6 @@ public class KeyStore {
return KEY_STORE;
}
- /** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public byte[] get(String key) {
- return null;
- }
-
- /** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public boolean delete(String key) {
- return false;
- }
-
- /**
- * List uids of all keys that are auth bound to the current user.
- * Only system is allowed to call this method.
- * @hide
- * @deprecated This function always returns null.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public int[] listUidsOfAuthBoundKeys() {
- return null;
- }
-
-
- /**
- * @hide
- * @deprecated This function has no effect.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public boolean unlock(String password) {
- return false;
- }
-
- /**
- *
- * @return
- * @deprecated This function always returns true.
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public boolean isEmpty() {
- return true;
- }
-
/**
* Add an authentication record to the keystore authorization table.
*
@@ -105,13 +63,4 @@ public class KeyStore {
public void onDeviceOffBody() {
AndroidKeyStoreMaintenance.onDeviceOffBody();
}
-
- /**
- * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
- * code.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static KeyStoreException getKeyStoreException(int errorCode) {
- return new KeyStoreException(-10000, "Should not be called.");
- }
}
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index cbfa74e9332b..48e6428524ae 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -101,6 +101,10 @@
<dimen name="split_divider_bar_width">10dp</dimen>
<dimen name="split_divider_corner_size">42dp</dimen>
+ <!-- The distance from the edge of the screen to invoke splitscreen when the user is dragging
+ an intent that can be launched into split. -->
+ <dimen name="drag_launchable_intent_edge_margin">48dp</dimen>
+
<!-- One-Handed Mode -->
<!-- Threshold for dragging distance to enable one-handed mode -->
<dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
new file mode 100644
index 000000000000..95d47146e834
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import com.android.internal.util.FrameworkStatsLog
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.util.KtProtoLog
+
+/**
+ * Event logger for logging desktop mode session events
+ */
+class DesktopModeEventLogger {
+ /**
+ * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for
+ * entering desktop mode
+ */
+ fun logSessionEnter(sessionId: Int, enterReason: EnterReason) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: Logging session enter, session: %s reason: %s",
+ sessionId, enterReason.name
+ )
+ FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID,
+ /* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER,
+ /* enterReason */ enterReason.reason,
+ /* exitReason */ 0,
+ /* session_id */ sessionId)
+ }
+
+ /**
+ * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for
+ * exiting desktop mode
+ */
+ fun logSessionExit(sessionId: Int, exitReason: ExitReason) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: Logging session exit, session: %s reason: %s",
+ sessionId, exitReason.name
+ )
+ FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID,
+ /* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT,
+ /* enterReason */ 0,
+ /* exitReason */ exitReason.reason,
+ /* session_id */ sessionId)
+ }
+
+ /**
+ * Logs that the task with update [taskUpdate] was added in the desktop mode session having
+ * session id [sessionId]
+ */
+ fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: Logging task added, session: %s taskId: %s",
+ sessionId, taskUpdate.instanceId
+ )
+ FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
+ /* task_event */
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED,
+ /* instance_id */
+ taskUpdate.instanceId,
+ /* uid */
+ taskUpdate.uid,
+ /* task_height */
+ taskUpdate.taskHeight,
+ /* task_width */
+ taskUpdate.taskWidth,
+ /* task_x */
+ taskUpdate.taskX,
+ /* task_y */
+ taskUpdate.taskY,
+ /* session_id */
+ sessionId)
+ }
+
+ /**
+ * Logs that the task with update [taskUpdate] was removed in the desktop mode session having
+ * session id [sessionId]
+ */
+ fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: Logging task remove, session: %s taskId: %s",
+ sessionId, taskUpdate.instanceId
+ )
+ FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
+ /* task_event */
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED,
+ /* instance_id */
+ taskUpdate.instanceId,
+ /* uid */
+ taskUpdate.uid,
+ /* task_height */
+ taskUpdate.taskHeight,
+ /* task_width */
+ taskUpdate.taskWidth,
+ /* task_x */
+ taskUpdate.taskX,
+ /* task_y */
+ taskUpdate.taskY,
+ /* session_id */
+ sessionId)
+ }
+
+ /**
+ * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session
+ * having session id [sessionId]
+ */
+ fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: Logging task info changed, session: %s taskId: %s",
+ sessionId, taskUpdate.instanceId
+ )
+ FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
+ /* task_event */
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
+ /* instance_id */
+ taskUpdate.instanceId,
+ /* uid */
+ taskUpdate.uid,
+ /* task_height */
+ taskUpdate.taskHeight,
+ /* task_width */
+ taskUpdate.taskWidth,
+ /* task_x */
+ taskUpdate.taskX,
+ /* task_y */
+ taskUpdate.taskY,
+ /* session_id */
+ sessionId)
+ }
+
+ companion object {
+ data class TaskUpdate(
+ val instanceId: Int,
+ val uid: Int,
+ val taskHeight: Int = Int.MIN_VALUE,
+ val taskWidth: Int = Int.MIN_VALUE,
+ val taskX: Int = Int.MIN_VALUE,
+ val taskY: Int = Int.MIN_VALUE,
+ )
+
+ /**
+ * Enum EnterReason mapped to the EnterReason definition in
+ * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto
+ */
+ enum class EnterReason(val reason: Int) {
+ UNKNOWN_ENTER(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER
+ ),
+ OVERVIEW(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__OVERVIEW
+ ),
+ APP_HANDLE_DRAG(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_HANDLE_DRAG
+ ),
+ APP_HANDLE_MENU_BUTTON(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_HANDLE_MENU_BUTTON
+ ),
+ APP_FREEFORM_INTENT(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_FREEFORM_INTENT
+ ),
+ KEYBOARD_SHORTCUT_ENTER(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER
+ ),
+ SCREEN_ON(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON
+ );
+ }
+
+ /**
+ * Enum ExitReason mapped to the ExitReason definition in
+ * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto
+ */
+ enum class ExitReason(val reason: Int) {
+ UNKNOWN_EXIT(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT
+ ),
+ DRAG_TO_EXIT(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT
+ ),
+ APP_HANDLE_MENU_BUTTON_EXIT(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__APP_HANDLE_MENU_BUTTON_EXIT
+ ),
+ KEYBOARD_SHORTCUT_EXIT(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__KEYBOARD_SHORTCUT_EXIT
+ ),
+ RETURN_HOME_OR_OVERVIEW(
+ FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME
+ ),
+ TASK_FINISHED(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED
+ ),
+ SCREEN_OFF(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF
+ )
+ }
+
+ private const val DESKTOP_MODE_ATOM_ID = FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED
+ private const val DESKTOP_MODE_TASK_UPDATE_ATOM_ID =
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 1afbdf90eac0..7da1b23dd5b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -59,7 +59,6 @@ import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
@@ -316,12 +315,11 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
return false;
}
// TODO(b/290391688): Also update the session data with task stack changes
- InstanceId loggerSessionId = mLogger.logStart(event);
- pd.activeDragCount++;
- pd.dragSession = new DragSession(mContext, ActivityTaskManager.getInstance(),
+ pd.dragSession = new DragSession(ActivityTaskManager.getInstance(),
mDisplayController.getDisplayLayout(displayId), event.getClipData());
pd.dragSession.update();
- pd.dragLayout.prepare(pd.dragSession, loggerSessionId);
+ pd.activeDragCount++;
+ pd.dragLayout.prepare(pd.dragSession, mLogger.logStart(pd.dragSession));
setDropTargetWindowVisibility(pd, View.VISIBLE);
notifyListeners(l -> {
l.onDragStarted();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java
index 2a7dd5aeb341..75b126c47690 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java
@@ -53,17 +53,21 @@ public class DragAndDropEventLogger {
/**
* Logs the start of a drag.
*/
- public InstanceId logStart(DragEvent event) {
- final ClipDescription description = event.getClipDescription();
- final ClipData data = event.getClipData();
- final ClipData.Item item = data.getItemAt(0);
- mInstanceId = item.getIntent().getParcelableExtra(
- ClipDescription.EXTRA_LOGGING_INSTANCE_ID);
+ public InstanceId logStart(DragSession session) {
+ mInstanceId = session.appData != null
+ ? session.appData.getParcelableExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID,
+ InstanceId.class)
+ : null;
if (mInstanceId == null) {
mInstanceId = mIdSequence.newInstanceId();
}
- mActivityInfo = item.getActivityInfo();
- log(getStartEnum(description), mActivityInfo);
+ mActivityInfo = session.activityInfo;
+ if (session.appData != null) {
+ log(getStartEnum(session.getClipDescription()), mActivityInfo);
+ } else {
+ // TODO(b/255649902): Update this once we have a new enum
+ log(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_ACTIVITY, mActivityInfo);
+ }
return mInstanceId;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index a31a773a76a0..29fe9abbd1d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -29,6 +29,8 @@ import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_SHORTCUT_ID;
import static android.content.Intent.EXTRA_TASK_ID;
import static android.content.Intent.EXTRA_USER;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -52,6 +54,7 @@ import android.content.pm.LauncherApps;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -63,8 +66,10 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.InstanceId;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
@@ -104,7 +109,9 @@ public class DragAndDropPolicy {
void start(DragSession session, InstanceId loggerSessionId) {
mLoggerSessionId = loggerSessionId;
mSession = session;
- RectF disallowHitRegion = (RectF) mSession.dragData.getExtra(EXTRA_DISALLOW_HIT_REGION);
+ RectF disallowHitRegion = mSession.appData != null
+ ? (RectF) mSession.appData.getExtra(EXTRA_DISALLOW_HIT_REGION)
+ : null;
if (disallowHitRegion == null) {
mDisallowHitRegion.setEmpty();
} else {
@@ -223,7 +230,7 @@ public class DragAndDropPolicy {
}
@VisibleForTesting
- void handleDrop(Target target, ClipData data) {
+ void handleDrop(Target target) {
if (target == null || !mTargets.contains(target)) {
return;
}
@@ -238,41 +245,81 @@ public class DragAndDropPolicy {
mSplitScreen.onDroppedToSplit(position, mLoggerSessionId);
}
- final ClipDescription description = data.getDescription();
- final Intent dragData = mSession.dragData;
- startClipDescription(description, dragData, position);
+ if (mSession.appData != null) {
+ launchApp(mSession, position);
+ } else {
+ launchIntent(mSession, position);
+ }
}
- private void startClipDescription(ClipDescription description, Intent intent,
- @SplitPosition int position) {
+ /**
+ * Launches an app provided by SysUI.
+ */
+ private void launchApp(DragSession session, @SplitPosition int position) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching app data at position=%d",
+ position);
+ final ClipDescription description = session.getClipDescription();
final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic();
baseActivityOpts.setDisallowEnterPictureInPictureWhileLaunching(true);
final Bundle opts = baseActivityOpts.toBundle();
- if (intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)) {
- opts.putAll(intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS));
+ if (session.appData.hasExtra(EXTRA_ACTIVITY_OPTIONS)) {
+ opts.putAll(session.appData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS));
}
// Put BAL flags to avoid activity start aborted.
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
- final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
+ final UserHandle user = session.appData.getParcelableExtra(EXTRA_USER);
if (isTask) {
- final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+ final int taskId = session.appData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
mStarter.startTask(taskId, position, opts);
} else if (isShortcut) {
- final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
- final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+ final String packageName = session.appData.getStringExtra(EXTRA_PACKAGE_NAME);
+ final String id = session.appData.getStringExtra(EXTRA_SHORTCUT_ID);
mStarter.startShortcut(packageName, id, position, opts, user);
} else {
- final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
+ final PendingIntent launchIntent =
+ session.appData.getParcelableExtra(EXTRA_PENDING_INTENT);
+ if (Build.IS_DEBUGGABLE) {
+ if (launchIntent.isImmutable()) {
+ throw new IllegalStateException("Expected immutable app pending intent from "
+ + "SysUI");
+ } else if (!user.equals(launchIntent.getCreatorUserHandle())) {
+ throw new IllegalStateException("Expected app intent's EXTRA_USER to match "
+ + "pending intent user");
+ }
+ }
mStarter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */,
position, opts);
}
}
/**
+ * Launches an intent sender provided by an application.
+ */
+ private void launchIntent(DragSession session, @SplitPosition int position) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching intent at position=%d",
+ position);
+ final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic();
+ baseActivityOpts.setDisallowEnterPictureInPictureWhileLaunching(true);
+ // TODO(b/255649902): Rework this so that SplitScreenController can always use the options
+ // instead of a fillInIntent since it's assuming that the PendingIntent is mutable
+ baseActivityOpts.setPendingIntentLaunchFlags(FLAG_ACTIVITY_NEW_TASK
+ | FLAG_ACTIVITY_MULTIPLE_TASK);
+
+ final Bundle opts = baseActivityOpts.toBundle();
+ // Put BAL flags to avoid activity start aborted.
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
+
+ mStarter.startIntent(session.launchableIntent,
+ session.launchableIntent.getCreatorUserHandle().getIdentifier(),
+ null /* fillIntent */, position, opts);
+ }
+
+ /**
* Interface for actually committing the task launches.
*/
public interface Starter {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 619f624ff3bc..ecb53dc17a48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -22,6 +22,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS;
import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -38,12 +40,15 @@ import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.view.DragEvent;
import android.view.SurfaceControl;
+import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.widget.LinearLayout;
@@ -65,7 +70,8 @@ import java.util.ArrayList;
/**
* Coordinates the visible drop targets for the current drag within a single display.
*/
-public class DragLayout extends LinearLayout {
+public class DragLayout extends LinearLayout
+ implements ViewTreeObserver.OnComputeInternalInsetsListener {
// While dragging the status bar is hidden.
private static final int HIDE_STATUS_BAR_FLAGS = StatusBarManager.DISABLE_NOTIFICATION_ICONS
@@ -90,7 +96,9 @@ public class DragLayout extends LinearLayout {
private int mDisplayMargin;
private int mDividerSize;
+ private int mLaunchIntentEdgeMargin;
private Insets mInsets = Insets.NONE;
+ private Region mTouchableRegion;
private boolean mIsShowing;
private boolean mHasDropped;
@@ -106,10 +114,11 @@ public class DragLayout extends LinearLayout {
mStatusBarManager = context.getSystemService(StatusBarManager.class);
mLastConfiguration.setTo(context.getResources().getConfiguration());
- mDisplayMargin = context.getResources().getDimensionPixelSize(
- R.dimen.drop_layout_display_margin);
- mDividerSize = context.getResources().getDimensionPixelSize(
- R.dimen.split_divider_bar_width);
+ final Resources res = context.getResources();
+ mDisplayMargin = res.getDimensionPixelSize(R.dimen.drop_layout_display_margin);
+ mDividerSize = res.getDimensionPixelSize(R.dimen.split_divider_bar_width);
+ mLaunchIntentEdgeMargin =
+ res.getDimensionPixelSize(R.dimen.drag_launchable_intent_edge_margin);
// Always use LTR because we assume dropZoneView1 is on the left and 2 is on the right when
// showing the highlight.
@@ -131,6 +140,66 @@ public class DragLayout extends LinearLayout {
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mTouchableRegion = Region.obtain();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ mTouchableRegion.recycle();
+ }
+
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inOutInfo) {
+ if (mSession != null && mSession.launchableIntent != null) {
+ inOutInfo.touchableRegion.set(mTouchableRegion);
+ inOutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ updateTouchableRegion();
+ }
+
+ /**
+ * Updates the touchable region, this should be called after any configuration changes have
+ * been applied.
+ */
+ private void updateTouchableRegion() {
+ mTouchableRegion.setEmpty();
+ if (mSession != null && mSession.launchableIntent != null) {
+ final int width = getMeasuredWidth();
+ final int height = getMeasuredHeight();
+ if (mIsLeftRightSplit) {
+ mTouchableRegion.union(
+ new Rect(0, 0, mInsets.left + mLaunchIntentEdgeMargin, height));
+ mTouchableRegion.union(
+ new Rect(width - mInsets.right - mLaunchIntentEdgeMargin, 0, width,
+ height));
+ } else {
+ mTouchableRegion.union(
+ new Rect(0, 0, width, mInsets.top + mLaunchIntentEdgeMargin));
+ mTouchableRegion.union(
+ new Rect(0, height - mInsets.bottom - mLaunchIntentEdgeMargin, width,
+ height));
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Updating drag layout width=%d height=%d touchable region=%s",
+ width, height, mTouchableRegion);
+
+ // Reapply insets to update the touchable region
+ requestApplyInsets();
+ }
+ }
+
+
+ @Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mInsets = insets.getInsets(Type.tappableElement() | Type.displayCutout());
recomputeDropTargets();
@@ -164,6 +233,7 @@ public class DragLayout extends LinearLayout {
mDropZoneView2.onThemeChange();
}
mLastConfiguration.setTo(newConfig);
+ requestLayout();
}
private void updateContainerMarginsForSingleTask() {
@@ -242,6 +312,7 @@ public class DragLayout extends LinearLayout {
mSplitScreenController.getStageBounds(topOrLeftBounds, bottomOrRightBounds);
updateDropZoneSizes(topOrLeftBounds, bottomOrRightBounds);
}
+ requestLayout();
}
private void updateDropZoneSizesForSingleTask() {
@@ -392,7 +463,7 @@ public class DragLayout extends LinearLayout {
mHasDropped = true;
// Process the drop
- mPolicy.handleDrop(mCurrentTarget, event.getClipData());
+ mPolicy.handleDrop(mCurrentTarget);
// Start animating the drop UI out with the drag surface
hide(event, dropCompleteCallback);
@@ -505,5 +576,7 @@ public class DragLayout extends LinearLayout {
pw.println(innerPrefix + "mIsShowing=" + mIsShowing);
pw.println(innerPrefix + "mHasDropped=" + mHasDropped);
pw.println(innerPrefix + "mCurrentTarget=" + mCurrentTarget);
+ pw.println(innerPrefix + "mInsets=" + mInsets);
+ pw.println(innerPrefix + "mTouchableRegion=" + mTouchableRegion);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
index 353d702e5bc4..8f1bc59af1ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
@@ -21,12 +21,15 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.ClipData;
-import android.content.Context;
+import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import androidx.annotation.Nullable;
+
import com.android.wm.shell.common.DisplayLayout;
import java.util.List;
@@ -39,7 +42,18 @@ public class DragSession {
private final ClipData mInitialDragData;
final DisplayLayout displayLayout;
- Intent dragData;
+ // The activity info associated with the activity in the appData or the launchableIntent
+ @Nullable
+ ActivityInfo activityInfo;
+ // The intent bundle that includes data about an app-type drag that is started by
+ // Launcher/SysUI. Only one of appDragData OR launchableIntent will be non-null for a session.
+ @Nullable
+ Intent appData;
+ // A launchable intent that is specified in the ClipData directly.
+ // Only one of appDragData OR launchableIntent will be non-null for a session.
+ @Nullable
+ PendingIntent launchableIntent;
+ // Stores the current running task at the time that the drag was initiated
ActivityManager.RunningTaskInfo runningTaskInfo;
@WindowConfiguration.WindowingMode
int runningTaskWinMode = WINDOWING_MODE_UNDEFINED;
@@ -47,7 +61,7 @@ public class DragSession {
int runningTaskActType = ACTIVITY_TYPE_STANDARD;
boolean dragItemSupportsSplitscreen;
- DragSession(Context context, ActivityTaskManager activityTaskManager,
+ DragSession(ActivityTaskManager activityTaskManager,
DisplayLayout dispLayout, ClipData data) {
mActivityTaskManager = activityTaskManager;
mInitialDragData = data;
@@ -55,6 +69,14 @@ public class DragSession {
}
/**
+ * Returns the clip description associated with the drag.
+ * @return
+ */
+ ClipDescription getClipDescription() {
+ return mInitialDragData.getDescription();
+ }
+
+ /**
* Updates the session data based on the current state of the system.
*/
void update() {
@@ -67,9 +89,11 @@ public class DragSession {
runningTaskActType = task.getActivityType();
}
- final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo();
- dragItemSupportsSplitscreen = info == null
- || ActivityInfo.isResizeableMode(info.resizeMode);
- dragData = mInitialDragData.getItemAt(0).getIntent();
+ activityInfo = mInitialDragData.getItemAt(0).getActivityInfo();
+ // TODO: This should technically check & respect config_supportsNonResizableMultiWindow
+ dragItemSupportsSplitscreen = activityInfo == null
+ || ActivityInfo.isResizeableMode(activityInfo.resizeMode);
+ appData = mInitialDragData.getItemAt(0).getIntent();
+ launchableIntent = DragUtils.getLaunchIntent(mInitialDragData);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
index f7bcc9477aa1..24f8e186bf76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
@@ -36,8 +36,21 @@ public class DragUtils {
* Returns whether we can handle this particular drag.
*/
public static boolean canHandleDrag(DragEvent event) {
- return event.getClipData().getItemCount() > 0
- && (isAppDrag(event.getClipDescription()));
+ if (event.getClipData().getItemCount() <= 0) {
+ // No clip data, ignore this drag
+ return false;
+ }
+ if (isAppDrag(event.getClipDescription())) {
+ // Clip data contains an app drag initiated from SysUI, handle it
+ return true;
+ }
+ if (com.android.window.flags.Flags.delegateUnhandledDrags()
+ && getLaunchIntent(event) != null) {
+ // Clip data contains a launchable intent drag, handle it
+ return true;
+ }
+ // Otherwise ignore
+ return false;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 53dd981755d2..32be41924219 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -806,6 +806,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override
public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1,
+ fillInIntent, position);
// Flag this as a no-user-action launch to prevent sending user leaving event to the current
// top activity since it's going to be put into another side of the split. This prevents the
// current top activity from going into pip mode due to user leaving event.
@@ -824,6 +827,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
.map(recentTasks -> recentTasks.findTaskInBackground(component, userId1))
.orElse(null);
if (taskInfo != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Found suitable background task=%s", taskInfo);
if (ENABLE_SHELL_TRANSITIONS) {
mStageCoordinator.startTask(taskInfo.taskId, position, options);
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index fa14b4c64fe0..2933cf48614a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -3396,6 +3396,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(stageType, wct);
+ clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index c70a8219489e..9130edfa9f26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -470,10 +470,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
final float cornerRadius;
- if (a.hasRoundedCorners() && isTask) {
- // hasRoundedCorners is currently only enabled for tasks
+ if (a.hasRoundedCorners()) {
+ final int displayId = isTask ? change.getTaskInfo().displayId
+ : info.getRoot(TransitionUtil.rootIndexFor(change, info))
+ .getDisplayId();
final Context displayContext =
- mDisplayController.getDisplayContext(change.getTaskInfo().displayId);
+ mDisplayController.getDisplayContext(displayId);
cornerRadius = displayContext == null ? 0
: ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
} else {
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 8c4711603904..32c070305e05 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -24,7 +24,10 @@ package {
android_test {
name: "WMShellUnitTests",
-
+ defaults: [
+ // For ExtendedMockito dependencies.
+ "modules-utils-testable-device-config-defaults",
+ ],
srcs: [
"**/*.java",
"**/*.kt",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
new file mode 100644
index 000000000000..4548fcb06c55
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.internal.util.FrameworkStatsLog
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.kotlin.eq
+
+/**
+ * Tests for [DesktopModeEventLogger].
+ */
+class DesktopModeEventLoggerTest {
+
+ private val desktopModeEventLogger = DesktopModeEventLogger()
+
+ @JvmField
+ @Rule
+ val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
+ .mockStatic(FrameworkStatsLog::class.java).build()!!
+
+ @Test
+ fun logSessionEnter_enterReason() = runBlocking {
+ desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER)
+
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
+ /* event */
+ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER),
+ /* enter_reason */
+ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER),
+ /* exit_reason */
+ eq(0),
+ /* sessionId */
+ eq(SESSION_ID)
+ )
+ }
+ }
+
+ @Test
+ fun logSessionExit_exitReason() = runBlocking {
+ desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT)
+
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
+ /* event */
+ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT),
+ /* enter_reason */
+ eq(0),
+ /* exit_reason */
+ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT),
+ /* sessionId */
+ eq(SESSION_ID)
+ )
+ }
+ }
+
+ @Test
+ fun logTaskAdded_taskUpdate() = runBlocking {
+ desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE)
+
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ /* task_event */
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
+ /* instance_id */
+ eq(TASK_UPDATE.instanceId),
+ /* uid */
+ eq(TASK_UPDATE.uid),
+ /* task_height */
+ eq(TASK_UPDATE.taskHeight),
+ /* task_width */
+ eq(TASK_UPDATE.taskWidth),
+ /* task_x */
+ eq(TASK_UPDATE.taskX),
+ /* task_y */
+ eq(TASK_UPDATE.taskY),
+ /* session_id */
+ eq(SESSION_ID))
+ }
+ }
+
+ @Test
+ fun logTaskRemoved_taskUpdate() = runBlocking {
+ desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE)
+
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ /* task_event */
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
+ /* instance_id */
+ eq(TASK_UPDATE.instanceId),
+ /* uid */
+ eq(TASK_UPDATE.uid),
+ /* task_height */
+ eq(TASK_UPDATE.taskHeight),
+ /* task_width */
+ eq(TASK_UPDATE.taskWidth),
+ /* task_x */
+ eq(TASK_UPDATE.taskX),
+ /* task_y */
+ eq(TASK_UPDATE.taskY),
+ /* session_id */
+ eq(SESSION_ID))
+ }
+ }
+
+ @Test
+ fun logTaskInfoChanged_taskUpdate() = runBlocking {
+ desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE)
+
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ /* task_event */
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ /* instance_id */
+ eq(TASK_UPDATE.instanceId),
+ /* uid */
+ eq(TASK_UPDATE.uid),
+ /* task_height */
+ eq(TASK_UPDATE.taskHeight),
+ /* task_width */
+ eq(TASK_UPDATE.taskWidth),
+ /* task_x */
+ eq(TASK_UPDATE.taskX),
+ /* task_y */
+ eq(TASK_UPDATE.taskY),
+ /* session_id */
+ eq(SESSION_ID))
+ }
+ }
+
+ companion object {
+ private const val SESSION_ID = 1
+ private const val TASK_ID = 1
+ private const val TASK_UID = 1
+ private const val TASK_X = 0
+ private const val TASK_Y = 0
+ private const val TASK_HEIGHT = 100
+ private const val TASK_WIDTH = 100
+
+ private val TASK_UPDATE = TaskUpdate(
+ TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y
+ )
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 1b347e01888e..5dd9d8a859d6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -22,9 +22,11 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static android.content.ClipDescription.MIMETYPE_TEXT_INTENT;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -46,6 +48,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.quality.Strictness.LENIENT;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -61,6 +65,7 @@ import android.content.res.Resources;
import android.graphics.Insets;
import android.os.RemoteException;
import android.view.DisplayInfo;
+import android.view.DragEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -70,12 +75,15 @@ import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
import java.util.ArrayList;
import java.util.Collections;
@@ -107,6 +115,7 @@ public class DragAndDropPolicyTest extends ShellTestCase {
private DragAndDropPolicy mPolicy;
private ClipData mActivityClipData;
+ private ClipData mLaunchableIntentClipData;
private ClipData mNonResizeableActivityClipData;
private ClipData mTaskClipData;
private ClipData mShortcutClipData;
@@ -115,9 +124,16 @@ public class DragAndDropPolicyTest extends ShellTestCase {
private ActivityManager.RunningTaskInfo mFullscreenAppTask;
private ActivityManager.RunningTaskInfo mNonResizeableFullscreenAppTask;
+ private MockitoSession mMockitoSession;
+
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
+ mMockitoSession = mockitoSession()
+ .strictness(LENIENT)
+ .mockStatic(DragUtils.class)
+ .startMocking();
+ when(DragUtils.canHandleDrag(any())).thenReturn(true);
Resources res = mock(Resources.class);
Configuration config = new Configuration();
@@ -134,11 +150,12 @@ public class DragAndDropPolicyTest extends ShellTestCase {
mInsets = Insets.of(0, 0, 0, 0);
mPolicy = spy(new DragAndDropPolicy(mContext, mSplitScreenStarter, mSplitScreenStarter));
- mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
- mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
+ mActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
+ mLaunchableIntentClipData = createIntentClipData();
+ mNonResizeableActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
setClipDataResizeable(mNonResizeableActivityClipData, false);
- mTaskClipData = createClipData(MIMETYPE_APPLICATION_TASK);
- mShortcutClipData = createClipData(MIMETYPE_APPLICATION_SHORTCUT);
+ mTaskClipData = createAppClipData(MIMETYPE_APPLICATION_TASK);
+ mShortcutClipData = createAppClipData(MIMETYPE_APPLICATION_SHORTCUT);
mHomeTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
mFullscreenAppTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -149,10 +166,15 @@ public class DragAndDropPolicyTest extends ShellTestCase {
setRunningTask(mFullscreenAppTask);
}
+ @After
+ public void tearDown() {
+ mMockitoSession.finishMocking();
+ }
+
/**
- * Creates a clip data that is by default resizeable.
+ * Creates an app-based clip data that is by default resizeable.
*/
- private ClipData createClipData(String mimeType) {
+ private ClipData createAppClipData(String mimeType) {
ClipDescription clipDescription = new ClipDescription(mimeType, new String[] { mimeType });
Intent i = new Intent();
switch (mimeType) {
@@ -164,7 +186,9 @@ public class DragAndDropPolicyTest extends ShellTestCase {
i.putExtra(Intent.EXTRA_TASK_ID, 12345);
break;
case MIMETYPE_APPLICATION_ACTIVITY:
- i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, mock(PendingIntent.class));
+ final PendingIntent pi = mock(PendingIntent.class);
+ doReturn(android.os.Process.myUserHandle()).when(pi).getCreatorUserHandle();
+ i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, pi);
break;
}
i.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
@@ -175,6 +199,22 @@ public class DragAndDropPolicyTest extends ShellTestCase {
return data;
}
+ /**
+ * Creates an intent-based clip data that is by default resizeable.
+ */
+ private ClipData createIntentClipData() {
+ ClipDescription clipDescription = new ClipDescription("Intent",
+ new String[] { MIMETYPE_TEXT_INTENT });
+ PendingIntent intent = mock(PendingIntent.class);
+ when(intent.getCreatorUserHandle()).thenReturn(android.os.Process.myUserHandle());
+ ClipData.Item item = new ClipData.Item.Builder()
+ .setIntentSender(intent.getIntentSender())
+ .build();
+ ClipData data = new ClipData(clipDescription, item);
+ when(DragUtils.getLaunchIntent((ClipData) any())).thenReturn(intent);
+ return data;
+ }
+
private ActivityManager.RunningTaskInfo createTaskInfo(int winMode, int actType) {
ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.configuration.windowConfiguration.setActivityType(actType);
@@ -204,58 +244,85 @@ public class DragAndDropPolicyTest extends ShellTestCase {
@Test
public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
+ dragOverFullscreenHome_expectOnlyFullscreenTarget(mActivityClipData);
+ }
+
+ @Test
+ public void testDragAppOverFullscreenApp_expectSplitScreenTargets() {
+ dragOverFullscreenApp_expectSplitScreenTargets(mActivityClipData);
+ }
+
+ @Test
+ public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
+ dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(mActivityClipData);
+ }
+
+ @Test
+ public void testDragIntentOverFullscreenHome_expectOnlyFullscreenTarget() {
+ dragOverFullscreenHome_expectOnlyFullscreenTarget(mLaunchableIntentClipData);
+ }
+
+ @Test
+ public void testDragIntentOverFullscreenApp_expectSplitScreenTargets() {
+ dragOverFullscreenApp_expectSplitScreenTargets(mLaunchableIntentClipData);
+ }
+
+ @Test
+ public void testDragIntentOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
+ dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(mLaunchableIntentClipData);
+ }
+
+ private void dragOverFullscreenHome_expectOnlyFullscreenTarget(ClipData data) {
doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
setRunningTask(mHomeTask);
- DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
- mLandscapeDisplayLayout, mActivityClipData);
+ DragSession dragSession = new DragSession(mActivityTaskManager,
+ mLandscapeDisplayLayout, data);
dragSession.update();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_UNDEFINED), any());
}
- @Test
- public void testDragAppOverFullscreenApp_expectSplitScreenTargets() {
+ private void dragOverFullscreenApp_expectSplitScreenTargets(ClipData data) {
doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
setRunningTask(mFullscreenAppTask);
- DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
- mLandscapeDisplayLayout, mActivityClipData);
+ DragSession dragSession = new DragSession(mActivityTaskManager,
+ mLandscapeDisplayLayout, data);
dragSession.update();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
- @Test
- public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
+ private void dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(ClipData data) {
doReturn(false).when(mSplitScreenStarter).isLeftRightSplit();
setRunningTask(mFullscreenAppTask);
- DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
- mPortraitDisplayLayout, mActivityClipData);
+ DragSession dragSession = new DragSession(mActivityTaskManager,
+ mPortraitDisplayLayout, data);
dragSession.update();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -263,7 +330,7 @@ public class DragAndDropPolicyTest extends ShellTestCase {
@Test
public void testTargetHitRects() {
setRunningTask(mFullscreenAppTask);
- DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
+ DragSession dragSession = new DragSession(mActivityTaskManager,
mLandscapeDisplayLayout, mActivityClipData);
dragSession.update();
mPolicy.start(dragSession, mLoggerSessionId);
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index d21f07efe36a..b12486e32bd4 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -26,6 +26,7 @@ X(ClipPath)
X(ClipRect)
X(ClipRRect)
X(ClipRegion)
+X(ClipShader)
X(ResetClip)
X(DrawPaint)
X(DrawBehind)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 3b694c5d399b..54aef55f8b90 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -207,6 +207,13 @@ struct ClipRegion final : Op {
SkClipOp op;
void draw(SkCanvas* c, const SkMatrix&) const { c->clipRegion(region, op); }
};
+struct ClipShader final : Op {
+ static const auto kType = Type::ClipShader;
+ ClipShader(const sk_sp<SkShader>& shader, SkClipOp op) : shader(shader), op(op) {}
+ sk_sp<SkShader> shader;
+ SkClipOp op;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->clipShader(shader, op); }
+};
struct ResetClip final : Op {
static const auto kType = Type::ResetClip;
ResetClip() {}
@@ -822,6 +829,9 @@ void DisplayListData::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
void DisplayListData::clipRegion(const SkRegion& region, SkClipOp op) {
this->push<ClipRegion>(0, region, op);
}
+void DisplayListData::clipShader(const sk_sp<SkShader>& shader, SkClipOp op) {
+ this->push<ClipShader>(0, shader, op);
+}
void DisplayListData::resetClip() {
this->push<ResetClip>(0);
}
@@ -1134,6 +1144,11 @@ void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
fDL->clipRegion(region, op);
this->INHERITED::onClipRegion(region, op);
}
+void RecordingCanvas::onClipShader(sk_sp<SkShader> shader, SkClipOp op) {
+ setClipMayBeComplex();
+ fDL->clipShader(shader, op);
+ this->INHERITED::onClipShader(shader, op);
+}
void RecordingCanvas::onResetClip() {
// This is part of "replace op" emulation, but rely on the following intersection
// clip to potentially mark the clip as complex. If we are already complex, we do
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index afadbfda7471..965264f31119 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -140,6 +140,7 @@ private:
void translateZ(SkScalar);
void clipPath(const SkPath&, SkClipOp, bool aa);
+ void clipShader(const sk_sp<SkShader>& shader, SkClipOp);
void clipRect(const SkRect&, SkClipOp, bool aa);
void clipRRect(const SkRRect&, SkClipOp, bool aa);
void clipRegion(const SkRegion&, SkClipOp);
@@ -216,6 +217,7 @@ public:
void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
+ void onClipShader(sk_sp<SkShader>, SkClipOp) override;
void onClipRegion(const SkRegion&, SkClipOp) override;
void onResetClip() override;
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index f84107e8792c..f9dc5fac7e21 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -41,6 +41,8 @@ namespace android {
namespace {
+static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
+
const ui::Transform kIdentityTransform;
} // namespace
@@ -224,7 +226,7 @@ void PointerController::setPresentation(Presentation presentation) {
mLocked.presentation = presentation;
- if (input_flags::enable_pointer_choreographer()) {
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
// When pointer choreographer is enabled, the presentation mode is only set once when the
// PointerController is constructed, before the display viewport is provided.
// TODO(b/293587049): Clean up the PointerController interface after pointer choreographer
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index ea136edf46be..c90c44152440 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -1079,7 +1079,7 @@ public final class AudioFormat implements Parcelable {
* @return one of the values that can be set in {@link Builder#setEncoding(int)} or
* {@link AudioFormat#ENCODING_INVALID} if not set.
*/
- public @Encoding int getEncoding() {
+ public @EncodingCanBeInvalid int getEncoding() {
return mEncoding;
}
@@ -1486,6 +1486,44 @@ public final class AudioFormat implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
+ /** @hide same as @Encoding, but adding ENCODING_INVALID */
+ @IntDef(flag = false, prefix = "ENCODING", value = {
+ ENCODING_INVALID,
+ ENCODING_DEFAULT,
+ ENCODING_PCM_16BIT,
+ ENCODING_PCM_8BIT,
+ ENCODING_PCM_FLOAT,
+ ENCODING_AC3,
+ ENCODING_E_AC3,
+ ENCODING_DTS,
+ ENCODING_DTS_HD,
+ ENCODING_MP3,
+ ENCODING_AAC_LC,
+ ENCODING_AAC_HE_V1,
+ ENCODING_AAC_HE_V2,
+ ENCODING_IEC61937,
+ ENCODING_DOLBY_TRUEHD,
+ ENCODING_AAC_ELD,
+ ENCODING_AAC_XHE,
+ ENCODING_AC4,
+ ENCODING_E_AC3_JOC,
+ ENCODING_DOLBY_MAT,
+ ENCODING_OPUS,
+ ENCODING_PCM_24BIT_PACKED,
+ ENCODING_PCM_32BIT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4,
+ ENCODING_DTS_UHD_P1,
+ ENCODING_DRA,
+ ENCODING_DTS_HD_MA,
+ ENCODING_DTS_UHD_P2,
+ ENCODING_DSD }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EncodingCanBeInvalid {}
+
/** @hide */
public static final int[] SURROUND_SOUND_ENCODING = {
ENCODING_AC3,
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index d1453975f801..ed543e67fdf1 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -519,7 +519,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
@Override
public void stopPlayback(int mode) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_STOP_PLAYBACK, mode));
+ mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_STOP_PLAYBACK, mode));
}
@Override
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
index c0d93531a1e6..9480327253f4 100644
--- a/packages/CrashRecovery/framework/Android.bp
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -3,7 +3,7 @@ soong_config_module_type {
module_type: "filegroup",
config_namespace: "ANDROID",
bool_variables: [
- "move_crashrecovery_files",
+ "crashrecovery_files_in_platform",
],
properties: [
"srcs",
@@ -12,14 +12,13 @@ soong_config_module_type {
platform_filegroup {
name: "framework-crashrecovery-sources",
- srcs: [
- "java/**/*.java",
- "java/**/*.aidl",
- ],
soong_config_variables: {
- // if the flag is enabled, then files would be moved to module
- move_crashrecovery_files: {
- srcs: [],
+ // if this flag is enabled, then files are part of platform
+ crashrecovery_files_in_platform: {
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
},
},
path: "java",
@@ -31,7 +30,7 @@ soong_config_module_type {
module_type: "filegroup",
config_namespace: "ANDROID",
bool_variables: [
- "move_crashrecovery_files",
+ "crashrecovery_files_in_module",
],
properties: [
"srcs",
@@ -40,10 +39,9 @@ soong_config_module_type {
module_filegroup {
name: "framework-crashrecovery-module-sources",
- srcs: [],
soong_config_variables: {
- // if the flag is enabled, then files would be moved to module
- move_crashrecovery_files: {
+ // if this flag is enabled, then files are part of module
+ crashrecovery_files_in_module: {
srcs: [
"java/**/*.java",
"java/**/*.aidl",
diff --git a/packages/CrashRecovery/services/Android.bp b/packages/CrashRecovery/services/Android.bp
index ab10b5a23676..961b41f4a633 100644
--- a/packages/CrashRecovery/services/Android.bp
+++ b/packages/CrashRecovery/services/Android.bp
@@ -3,7 +3,7 @@ soong_config_module_type {
module_type: "filegroup",
config_namespace: "ANDROID",
bool_variables: [
- "move_crashrecovery_files",
+ "crashrecovery_files_in_platform",
],
properties: [
"srcs",
@@ -12,15 +12,14 @@ soong_config_module_type {
platform_filegroup {
name: "services-crashrecovery-sources",
- srcs: [
- "java/**/*.java",
- "java/**/*.aidl",
- ":statslog-crashrecovery-java-gen",
- ],
soong_config_variables: {
- // if the flag is enabled, then files would be moved to module
- move_crashrecovery_files: {
- srcs: [],
+ // if this flag is enabled, then files are part of platform
+ crashrecovery_files_in_platform: {
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ":statslog-crashrecovery-java-gen",
+ ],
},
},
visibility: ["//frameworks/base:__subpackages__"],
@@ -31,7 +30,7 @@ soong_config_module_type {
module_type: "filegroup",
config_namespace: "ANDROID",
bool_variables: [
- "move_crashrecovery_files",
+ "crashrecovery_files_in_module",
],
properties: [
"srcs",
@@ -40,10 +39,9 @@ soong_config_module_type {
module_filegroup {
name: "services-crashrecovery-module-sources",
- srcs: [],
soong_config_variables: {
- // if the flag is enabled, then files would be moved to module
- move_crashrecovery_files: {
+ // if this flag is enabled, then files are part of module
+ crashrecovery_files_in_module: {
srcs: [
"java/**/*.java",
"java/**/*.aidl",
diff --git a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
index ce8fb6568bd5..5d71b7d98fdc 100644
--- a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
@@ -38,13 +38,13 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.BackgroundThread;
import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 5fb47dd9b95a..0fb932735ab4 100644
--- a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -75,6 +75,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
| ApplicationInfo.FLAG_SYSTEM;
+ private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG =
+ "persist.device_config.configuration.disable_high_impact_rollback";
+
private final Context mContext;
private final Handler mHandler;
private final ApexManager mApexManager;
@@ -605,6 +608,10 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
// Apply all available low impact rollbacks.
mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
} else if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH) {
+ // Check disable_high_impact_rollback device config before performing rollback
+ if (SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) {
+ return;
+ }
// Rollback one package at a time. If that doesn't resolve the issue, rollback
// next with same impact level.
mHandler.post(() -> rollbackHighImpact(availableRollbacks, rollbackReason));
@@ -718,7 +725,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
break;
case PackageManager.ROLLBACK_USER_IMPACT_HIGH:
- impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_90;
+ if (!SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) {
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_90;
+ }
break;
default:
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
diff --git a/packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java b/packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java
new file mode 100644
index 000000000000..a6ae68f62f10
--- /dev/null
+++ b/packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java
@@ -0,0 +1,103 @@
+/*
+ * * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Thread for asynchronous event processing. This thread is configured as
+ * {@link android.os.Process#THREAD_PRIORITY_BACKGROUND}, which means fewer CPU
+ * resources will be dedicated to it, and it will "have less chance of impacting
+ * the responsiveness of the user interface."
+ * <p>
+ * This thread is best suited for tasks that the user is not actively waiting
+ * for, or for tasks that the user expects to be executed eventually.
+ *
+ * @see com.android.internal.os.BackgroundThread
+ *
+ * TODO: b/326916057 depend on modules-utils-backgroundthread instead
+ * @hide
+ */
+public final class BackgroundThread extends HandlerThread {
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static BackgroundThread sInstance;
+ @GuardedBy("sLock")
+ private static Handler sHandler;
+ @GuardedBy("sLock")
+ private static HandlerExecutor sHandlerExecutor;
+
+ private BackgroundThread() {
+ super(BackgroundThread.class.getName(), android.os.Process.THREAD_PRIORITY_BACKGROUND);
+ }
+
+ @GuardedBy("sLock")
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new BackgroundThread();
+ sInstance.start();
+ sHandler = new Handler(sInstance.getLooper());
+ sHandlerExecutor = new HandlerExecutor(sHandler);
+ }
+ }
+
+ /**
+ * Get the singleton instance of this class.
+ *
+ * @return the singleton instance of this class
+ */
+ @NonNull
+ public static BackgroundThread get() {
+ synchronized (sLock) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ /**
+ * Get the singleton {@link Handler} for this class.
+ *
+ * @return the singleton {@link Handler} for this class.
+ */
+ @NonNull
+ public static Handler getHandler() {
+ synchronized (sLock) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+
+ /**
+ * Get the singleton {@link Executor} for this class.
+ *
+ * @return the singleton {@link Executor} for this class.
+ */
+ @NonNull
+ public static Executor getExecutor() {
+ synchronized (sLock) {
+ ensureThreadLocked();
+ return sHandlerExecutor;
+ }
+ }
+}
diff --git a/packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java b/packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java
new file mode 100644
index 000000000000..948ebcca0263
--- /dev/null
+++ b/packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * An adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * TODO: b/326916057 depend on modules-utils-backgroundthread instead
+ * @hide
+ */
+public class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public HandlerExecutor(@NonNull Handler handler) {
+ mHandler = Objects.requireNonNull(handler);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ if (!mHandler.post(command)) {
+ throw new RejectedExecutionException(mHandler + " is shutting down");
+ }
+ }
+}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
index a5f227a3adc9..02320a193602 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -127,10 +127,10 @@ private fun getCredentialOptionInfoList(
userName = credentialEntry.username.toString(),
displayName = credentialEntry.displayName?.toString(),
icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
+ shouldTintIcon = credentialEntry.hasDefaultIcon,
lastUsedTimeMillis = credentialEntry.lastUsedTime,
isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
+ credentialEntry.isAutoSelectAllowedFromOption,
)
)
}
@@ -148,10 +148,10 @@ private fun getCredentialOptionInfoList(
userName = credentialEntry.username.toString(),
displayName = credentialEntry.displayName?.toString(),
icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
+ shouldTintIcon = credentialEntry.hasDefaultIcon,
lastUsedTimeMillis = credentialEntry.lastUsedTime,
isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
+ credentialEntry.isAutoSelectAllowedFromOption,
)
)
}
@@ -170,10 +170,10 @@ private fun getCredentialOptionInfoList(
userName = credentialEntry.title.toString(),
displayName = credentialEntry.subtitle?.toString(),
icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
+ shouldTintIcon = credentialEntry.hasDefaultIcon,
lastUsedTimeMillis = credentialEntry.lastUsedTime,
isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
+ credentialEntry.isAutoSelectAllowedFromOption,
)
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 0f1721790295..28c42fb3f86c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -213,7 +213,7 @@ class CredentialAutofillService : AutofillService() {
autofillIdToProvidersMap.forEach { (autofillId, providers) ->
validFillResponse = processProvidersForAutofillId(
filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder,
- getCredResponse.pendingIntent)
+ getCredResponse.intent)
.or(validFillResponse)
}
if (!validFillResponse) {
@@ -229,7 +229,7 @@ class CredentialAutofillService : AutofillService() {
providerDataList: ArrayList<GetCredentialProviderData>,
entryIconMap: Map<String, Icon>,
fillResponseBuilder: FillResponse.Builder,
- bottomSheetPendingIntent: PendingIntent?
+ bottomSheetIntent: Intent
): Boolean {
val providerList = GetFlowUtils.toProviderList(
providerDataList,
@@ -267,6 +267,8 @@ class CredentialAutofillService : AutofillService() {
}
}
}
+ bottomSheetIntent.putExtra(
+ ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, providerDataList)
providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{
val primaryEntry = it.sortedCredentialEntryList.first()
val pendingIntent = primaryEntry.pendingIntent
@@ -330,16 +332,16 @@ class CredentialAutofillService : AutofillService() {
datasetAdded = true
i++
- if (i == lastDropdownDatasetIndex && bottomSheetPendingIntent != null) {
- addDropdownMoreOptionsPresentation(bottomSheetPendingIntent, autofillId,
+ if (i == lastDropdownDatasetIndex) {
+ addDropdownMoreOptionsPresentation(bottomSheetIntent, autofillId,
fillResponseBuilder)
}
}
val pinnedSpec = getLastInlinePresentationSpec(inlinePresentationSpecs,
inlinePresentationSpecsCount)
- if (datasetAdded && bottomSheetPendingIntent != null && pinnedSpec != null) {
- addPinnedInlineSuggestion(bottomSheetPendingIntent, pinnedSpec, autofillId,
- fillResponseBuilder, providerDataList)
+ if (datasetAdded && pinnedSpec != null) {
+ addPinnedInlineSuggestion(pinnedSpec, autofillId,
+ fillResponseBuilder, bottomSheetIntent)
}
return datasetAdded
}
@@ -370,13 +372,14 @@ class CredentialAutofillService : AutofillService() {
}
private fun addDropdownMoreOptionsPresentation(
- bottomSheetPendingIntent: PendingIntent,
+ bottomSheetIntent: Intent,
autofillId: AutofillId,
fillResponseBuilder: FillResponse.Builder
) {
val presentationBuilder = Presentations.Builder()
.setMenuPresentation(
RemoteViewsFactory.createMoreSignInOptionsPresentation(this))
+ val pendingIntent = setUpBottomSheetPendingIntent(bottomSheetIntent)
fillResponseBuilder.addDataset(
Dataset.Builder()
@@ -385,7 +388,7 @@ class CredentialAutofillService : AutofillService() {
Field.Builder().setPresentations(
presentationBuilder.build())
.build())
- .setAuthentication(bottomSheetPendingIntent.intentSender)
+ .setAuthentication(pendingIntent.intentSender)
.build()
)
}
@@ -401,37 +404,41 @@ class CredentialAutofillService : AutofillService() {
}
private fun addPinnedInlineSuggestion(
- bottomSheetPendingIntent: PendingIntent,
spec: InlinePresentationSpec,
autofillId: AutofillId,
fillResponseBuilder: FillResponse.Builder,
- providerDataList: ArrayList<GetCredentialProviderData>
+ bottomSheetIntent: Intent
) {
+ val pendingIntent = setUpBottomSheetPendingIntent(bottomSheetIntent)
+
val dataSetBuilder = Dataset.Builder()
val sliceBuilder = InlineSuggestionUi
- .newContentBuilder(bottomSheetPendingIntent)
+ .newContentBuilder(pendingIntent)
.setStartIcon(Icon.createWithResource(this,
com.android.credentialmanager.R.drawable.more_horiz_24px))
val presentationBuilder = Presentations.Builder()
.setInlinePresentation(InlinePresentation(
sliceBuilder.build().slice, spec, /* pinned= */ true))
- val extrasIntent = Intent()
- extrasIntent.putExtra(ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, providerDataList)
-
fillResponseBuilder.addDataset(
dataSetBuilder
.setField(
autofillId,
Field.Builder().setPresentations(
- presentationBuilder.build())
- .build())
- .setAuthentication(bottomSheetPendingIntent.intentSender)
- .setCredentialFillInIntent(extrasIntent)
+ presentationBuilder.build()
+ ).build()
+ )
+ .setAuthentication(pendingIntent.intentSender)
.build()
)
}
+ private fun setUpBottomSheetPendingIntent(intent: Intent): PendingIntent {
+ intent.setAction(java.util.UUID.randomUUID().toString())
+ return PendingIntent.getActivity(this, /*requestCode=*/0, intent,
+ PendingIntent.FLAG_MUTABLE, /*options=*/null)
+ }
+
/**
* Maps Autofill Id to provider list. For example, passing in a provider info
*
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 463e9be391b6..335725cdad5e 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.7.0-alpha02"
+ extra["jetpackComposeVersion"] = "1.7.0-alpha03"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index fe378c27523c..609a82e79827 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -16,7 +16,7 @@
[versions]
agp = "8.2.2"
-compose-compiler = "1.5.9"
+compose-compiler = "1.5.10"
dexmaker-mockito = "2.28.3"
jvm = "17"
kotlin = "1.9.22"
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
index a038779b0e79..e1f5c742dab7 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
index 03db688f6799..928e9268292b 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
index 1345c379cfb0..05213688b0a8 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 2259bd74d56e..a193a2f9970d 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")
+ api("androidx.compose.material3:material3:1.3.0-alpha01")
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/widget/preference/RadioPreferences.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt
new file mode 100644
index 000000000000..8300ce855988
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.material3.RadioButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.ui.CategoryTitle
+import com.android.settingslib.spa.widget.ui.SettingsListItem
+
+@Composable
+fun RadioPreferences(model: ListPreferenceModel) {
+ CategoryTitle(title = model.title)
+ Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
+ Column(modifier = Modifier.selectableGroup()) {
+ for (option in model.options) {
+ Radio2(option, model.selectedId.intValue, model.enabled()) {
+ model.onIdSelected(it)
+ }
+ }
+ }
+}
+
+@Composable
+fun Radio2(
+ option: ListPreferenceOption,
+ selectedId: Int,
+ enabled: Boolean,
+ onIdSelected: (id: Int) -> Unit,
+) {
+ val selected = option.id == selectedId
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .selectable(
+ selected = selected,
+ enabled = enabled,
+ onClick = { onIdSelected(option.id) },
+ role = Role.RadioButton,
+ )
+ .padding(SettingsDimension.dialogItemPadding),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ RadioButton(selected = selected, onClick = null, enabled = enabled)
+ Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
+ SettingsListItem(text = option.text, enabled = enabled)
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index 4f6196648634..56534f41c3df 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -180,7 +180,7 @@ private class TopAppBarColors(
* @param colorTransitionFraction a `0.0` to `1.0` value that represents a color transition
* percentage
*/
- @Composable
+ @Stable
fun containerColor(colorTransitionFraction: Float): Color {
return lerp(
containerColor,
@@ -519,7 +519,7 @@ private fun TopAppBarLayout(
0
}
- val layoutHeight = if (heightPx.isNaN()) 0 else heightPx.roundToInt()
+ val layoutHeight = if (heightPx > 0) heightPx.roundToInt() else 0
layout(constraints.maxWidth, layoutHeight) {
// Navigation icon
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index b4a6a0d00720..a59b95a60879 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -66,6 +66,19 @@ fun SettingsDialogItem(text: String, enabled: Boolean = true) {
}
@Composable
+fun SettingsListItem(text: String, enabled: Boolean = true) {
+ Text(
+ text = text,
+ modifier = Modifier
+ .alphaForEnabled(enabled)
+ .padding(vertical = SettingsDimension.paddingTiny),
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.titleMedium,
+ overflow = TextOverflow.Ellipsis,
+ )
+}
+
+@Composable
fun SettingsBody(
body: String,
maxLines: Int = Int.MAX_VALUE,
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt
new file mode 100644
index 000000000000..2f98b02b8dd5
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsNotSelected
+import androidx.compose.ui.test.assertIsSelectable
+import androidx.compose.ui.test.assertIsSelected
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RadioPreferencesTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ RadioPreferences(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = emptyList<ListPreferenceOption>()
+ override val selectedId = mutableIntStateOf(0)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun item_displayed() {
+ val selectedId = mutableIntStateOf(1)
+ composeTestRule.setContent {
+ RadioPreferences(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "A"),
+ ListPreferenceOption(id = 2, text = "B"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected = { id: Int -> selectedId.intValue = id }
+ }
+ })
+ }
+ composeTestRule.onNodeWithText("A").assertIsDisplayed()
+ composeTestRule.onNodeWithText("B").assertIsDisplayed()
+ }
+
+ @Test
+ fun item_selectable() {
+ val selectedId = mutableIntStateOf(1)
+ val enabledState = mutableStateOf(true)
+ composeTestRule.setContent {
+ RadioPreferences(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val enabled = { enabledState.value }
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "A"),
+ ListPreferenceOption(id = 2, text = "B"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected = { id: Int -> selectedId.intValue = id }
+ }
+ })
+ }
+ composeTestRule.onNodeWithText("A").assertIsSelectable()
+ composeTestRule.onNodeWithText("B").assertIsSelectable()
+ }
+
+ @Test
+ fun item_single_selected() {
+ val selectedId = mutableIntStateOf(1)
+ val enabledState = mutableStateOf(true)
+ composeTestRule.setContent {
+ RadioPreferences(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val enabled = { enabledState.value }
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "A"),
+ ListPreferenceOption(id = 2, text = "B"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected = { id: Int -> selectedId.intValue = id }
+ }
+ })
+ }
+ composeTestRule.onNodeWithText("B").assertIsSelectable()
+ composeTestRule.onNodeWithText("B").performClick()
+ composeTestRule.onNodeWithText("B").assertIsSelected()
+ composeTestRule.onNodeWithText("A").assertIsNotSelected()
+ composeTestRule.onNodeWithText("A").performClick()
+ composeTestRule.onNodeWithText("A").assertIsSelected()
+ composeTestRule.onNodeWithText("B").assertIsNotSelected()
+ }
+
+ @Test
+ fun select_itemDisabled() {
+ val selectedId = mutableIntStateOf(1)
+ val enabledState = mutableStateOf(true)
+ composeTestRule.setContent {
+ RadioPreferences(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val enabled = { enabledState.value }
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "A"),
+ ListPreferenceOption(id = 2, text = "B"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected = { id: Int -> selectedId.intValue = id }
+ }
+ })
+ }
+ enabledState.value = false
+ composeTestRule.onNodeWithText("A").assertIsDisplayed().assertIsNotEnabled()
+ composeTestRule.onNodeWithText("B").assertIsDisplayed().assertIsNotEnabled()
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 6a1ee3a3c623..54c5a14702f6 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -43,4 +43,11 @@ flag {
namespace: "pixel_cross_device_control"
description: "Gates whether to enable LE audio private broadcast sharing via QR code"
bug: "308368124"
-} \ No newline at end of file
+}
+
+flag {
+ name: "enable_hide_exclusively_managed_bluetooth_device"
+ namespace: "dck_framework"
+ description: "Hide exclusively managed Bluetooth devices in BT settings menu."
+ bug: "324475542"
+}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9588e502f28e..a4bc235aaa7a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1085,7 +1085,7 @@
<!-- [CHAR_LIMIT=NONE] Label for charging on hold on main page of settings -->
<string name="power_charging_on_hold_settings_home_page"><xliff:g id="level">%1$s</xliff:g> - Charging on hold to protect battery</string>
<!-- [CHAR_LIMIT=NONE] Label for incompatible charging accessory on main page of settings -->
- <string name="power_incompatible_charging_settings_home_page"><xliff:g id="level">%1$s</xliff:g> - Checking charging accessory</string>
+ <string name="power_incompatible_charging_settings_home_page"><xliff:g id="level">%1$s</xliff:g> - Check charging accessory</string>
<!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
<string name="power_remaining_duration_only">About <xliff:g id="time_remaining">%1$s</xliff:g> left</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index bed95a584c27..e63232a87499 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -230,6 +230,7 @@ android_library {
extra_check_modules: ["SystemUILintChecker"],
warning_checks: ["MissingApacheLicenseDetector"],
},
+ skip_jarjar_repackage: true,
}
filegroup {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a69a2a64dccb..f5c4843e1324 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -546,3 +546,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "notify_power_manager_user_activity_background"
+ namespace: "systemui"
+ description: "Decide whether to notify the user activity to power manager in the background thread."
+ bug: "325203885"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index c1125f0b9e92..99b7c36d6fb9 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -45,6 +45,7 @@ android_library {
"androidx.core_core-ktx",
"androidx.annotation_annotation",
"SystemUIShaderLib",
+ "WindowManager-Shell-shared",
"animationlib",
],
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index a78080fdabf2..e20425d4b98c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.shared.system;
+package com.android.systemui.animation;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CLOSE;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationTargetCompat.java
index e4d924323bbd..e251af44727e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationTargetCompat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.system;
+package com.android.systemui.animation;
import android.util.ArrayMap;
import android.view.RemoteAnimationTarget;
diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp
index c12084dac9a9..dfb3a55b0966 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -44,4 +44,5 @@ android_library {
],
kotlincflags: ["-Xjvm-default=all"],
+ skip_jarjar_repackage: true,
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 622a4f04ee0d..609f314bb540 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -274,7 +274,7 @@ private fun BoxScope.CommunalHubLazyGrid(
widgetConfigurator: WidgetConfigurator?,
) {
var gridModifier =
- Modifier.align(Alignment.CenterStart).onGloballyPositioned { setGridCoordinates(it) }
+ Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
var list = communalContent
var dragDropState: GridDragDropState? = null
if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) {
@@ -309,8 +309,8 @@ private fun BoxScope.CommunalHubLazyGrid(
state = gridState,
rows = GridCells.Fixed(CommunalContentSize.FULL.span),
contentPadding = contentPadding,
- horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
- verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
+ horizontalArrangement = Arrangement.spacedBy(32.dp),
+ verticalArrangement = Arrangement.spacedBy(32.dp),
) {
items(
count = list.size,
@@ -681,19 +681,20 @@ private fun WidgetContent(
Box(
modifier = modifier,
) {
- val paddingInPx = with(LocalDensity.current) { CardOutlineWidth.toPx().toInt() }
+ val paddingInPx =
+ if (selected) with(LocalDensity.current) { CardOutlineWidth.toPx().toInt() } else 0
AndroidView(
modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
factory = { context ->
- val view =
- model.appWidgetHost
- .createViewForCommunal(context, model.appWidgetId, model.providerInfo)
- .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
+ model.appWidgetHost
+ .createViewForCommunal(context, model.appWidgetId, model.providerInfo)
+ .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
+ },
+ update = { view ->
// Remove the extra padding applied to AppWidgetHostView to allow widgets to
// occupy the entire box. The added padding is now adjusted to leave only sufficient
// space for displaying the outline around the box when the widget is selected.
view.setPadding(paddingInPx)
- view
},
// For reusing composition in lazy lists.
onReset = {},
@@ -795,7 +796,7 @@ private fun Umo(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier)
@Composable
private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): PaddingValues {
if (!isEditMode || toolbarSize == null) {
- return PaddingValues(horizontal = Dimensions.Spacing)
+ return PaddingValues(start = 48.dp, end = 48.dp, top = Dimensions.GridTopSpacing)
}
val configuration = LocalConfiguration.current
val density = LocalDensity.current
@@ -858,12 +859,13 @@ data class ContentPaddingInPx(val start: Float, val top: Float) {
}
object Dimensions {
- val CardWidth = 464.dp
- val CardHeightFull = 630.dp
- val CardHeightHalf = 307.dp
- val CardHeightThird = 199.dp
+ val CardWidth = 424.dp
+ val CardHeightFull = 596.dp
+ val CardHeightHalf = 282.dp
+ val CardHeightThird = 177.33.dp
val CardOutlineWidth = 3.dp
- val GridHeight = CardHeightFull
+ val GridTopSpacing = 72.dp
+ val GridHeight = CardHeightFull + GridTopSpacing
val Spacing = 16.dp
// The sizing/padding of the toolbar in glanceable hub edit mode
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index be6f022d8d52..31d3fa0be163 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.composable.section
import android.content.Context
import android.util.DisplayMetrics
+import android.view.View
import android.view.WindowManager
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -45,6 +46,7 @@ import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
@@ -63,6 +65,7 @@ constructor(
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
private val vibratorHelper: Lazy<VibratorHelper>,
+ private val notificationPanelView: NotificationPanelView,
) {
@Composable
fun SceneScope.LockIcon(modifier: Modifier = Modifier) {
@@ -70,6 +73,10 @@ constructor(
return
}
+ notificationPanelView.findViewById<View?>(R.id.lock_icon_view)?.let {
+ notificationPanelView.removeView(it)
+ }
+
val context = LocalContext.current
AndroidView(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index a197a4b0fd55..ddc9252a5a4a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -36,6 +36,8 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
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.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
@@ -66,9 +68,9 @@ fun ColumnVolumeSliders(
modifier: Modifier = Modifier,
) {
require(viewModels.isNotEmpty())
- var isExpanded: Boolean by remember { mutableStateOf(false) }
+ var isExpanded: Boolean by remember(isExpandable) { mutableStateOf(!isExpandable) }
val transition = updateTransition(isExpanded, label = "CollapsableSliders")
- Column(modifier = modifier) {
+ Column(modifier = modifier.verticalScroll(rememberScrollState())) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 5925b1482e77..0d94bb06c06f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -41,7 +41,7 @@ fun VolumeSlider(
modifier: Modifier = Modifier,
sliderColors: PlatformSliderColors,
) {
- var value by remember { mutableFloatStateOf(state.value) }
+ var value by remember(state.value) { mutableFloatStateOf(state.value) }
PlatformSlider(
modifier = modifier,
value = value,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
index 6213dc5f63c9..1ca18deeaac2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
@@ -26,6 +26,7 @@ import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.Sl
import com.android.systemui.volume.panel.component.volume.ui.viewmodel.AudioVolumeComponentViewModel
import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import com.android.systemui.volume.panel.ui.composable.isPortrait
import javax.inject.Inject
class VolumeSlidersComponent
@@ -50,7 +51,7 @@ constructor(
ColumnVolumeSliders(
viewModels = sliderViewModels,
sliderColors = PlatformSliderDefaults.defaultPlatformSliderColors(),
- isExpandable = true,
+ isExpandable = isPortrait,
modifier = modifier.fillMaxWidth(),
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
index 0a651c898aba..a838a99524a3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
@@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -56,7 +55,7 @@ fun VolumePanelComposeScope.HorizontalVolumePanelContent(
}
}
Row(
- modifier = Modifier.fillMaxWidth().wrapContentHeight(),
+ modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(spacing),
) {
for (component in layout.footerComponents) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index 8a1e6a8b74f4..8a9ebc918be6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -16,7 +16,6 @@
package com.android.systemui.volume.panel.ui.composable
-import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
@@ -111,15 +110,24 @@ private fun VolumePanelComposeScope.Components(
layout: ComponentsLayout,
modifier: Modifier = Modifier
) {
- var columnModifier = modifier.widthIn(max = 800.dp)
- if (!isLargeScreen && orientation != Configuration.ORIENTATION_PORTRAIT) {
- columnModifier = columnModifier.heightIn(max = 332.dp)
- }
- Column(modifier = columnModifier, verticalArrangement = Arrangement.spacedBy(padding)) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT || isLargeScreen) {
- VerticalVolumePanelContent(layout)
+ val arrangement =
+ if (isLargeScreen) {
+ Arrangement.spacedBy(20.dp)
+ } else {
+ if (isPortrait) Arrangement.spacedBy(padding) else Arrangement.spacedBy(4.dp)
+ }
+ Column(
+ modifier = modifier.widthIn(max = 800.dp),
+ verticalArrangement = arrangement,
+ ) {
+ val contentModifier = Modifier
+ if (isPortrait || isLargeScreen) {
+ VerticalVolumePanelContent(modifier = contentModifier, layout = layout)
} else {
- HorizontalVolumePanelContent(layout)
+ HorizontalVolumePanelContent(
+ modifier = contentModifier.heightIn(max = 212.dp),
+ layout = layout
+ )
}
BottomBar(layout = layout, modifier = Modifier)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 2e781e69bb4a..c7186da6b961 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -39,7 +39,6 @@ import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.round
@@ -204,6 +203,17 @@ internal class ElementNode(
measurable: Measurable,
constraints: Constraints,
): MeasureResult {
+ val overscrollScene = layoutImpl.state.currentOverscrollSpec?.scene
+ if (overscrollScene != null && overscrollScene != scene.key) {
+ // There is an overscroll in progress on another scene
+ // By measuring composable elements, Compose can cache relevant information.
+ // This reduces the need for re-measure when users return from an overscroll animation.
+ val placeable = measurable.measure(constraints)
+ return layout(placeable.width, placeable.height) {
+ // We don't want to draw it, no need to place the element.
+ }
+ }
+
val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints)
return layout(placeable.width, placeable.height) {
place(layoutImpl, scene, element, sceneState, placeable, placementScope = this)
@@ -253,11 +263,13 @@ private fun shouldDrawElement(
): Boolean {
val transition = layoutImpl.state.currentTransition
- // Always draw the element if there is no ongoing transition or if the element is not shared.
+ // Always draw the element if there is no ongoing transition or if the element is not shared or
+ // if the current scene is the one that is currently over scrolling with [OverscrollSpec].
if (
transition == null ||
transition.fromScene !in element.sceneStates ||
- transition.toScene !in element.sceneStates
+ transition.toScene !in element.sceneStates ||
+ layoutImpl.state.currentOverscrollSpec?.scene == scene.key
) {
return true
}
@@ -286,12 +298,14 @@ internal fun shouldDrawOrComposeSharedElement(
val fromScene = transition.fromScene
val toScene = transition.toScene
- return scenePicker.sceneDuringTransition(
- element = element,
- transition = transition,
- fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex,
- toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
- ) == scene
+ val chosenByPicker =
+ scenePicker.sceneDuringTransition(
+ element = element,
+ transition = transition,
+ fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex,
+ toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
+ ) == scene
+ return chosenByPicker || layoutImpl.state.currentOverscrollSpec?.scene == scene
}
private fun isSharedElementEnabled(
@@ -549,6 +563,40 @@ private inline fun <T> computeValue(
return idleValue
}
+ if (transition is TransitionState.HasOverscrollProperties) {
+ val overscroll = layoutImpl.state.currentOverscrollSpec
+ if (overscroll?.scene == scene.key) {
+ val elementSpec = overscroll.transformationSpec.transformations(element.key, scene.key)
+ val propertySpec = transformation(elementSpec) ?: return currentValue()
+ val overscrollState = checkNotNull(if (scene.key == toScene) toState else fromState)
+ val targetValue =
+ propertySpec.transform(
+ layoutImpl,
+ scene,
+ element,
+ overscrollState,
+ transition,
+ idleValue,
+ )
+
+ // Make sure we don't read progress if values are the same and we don't need to
+ // interpolate, so we don't invalidate the phase where this is read.
+ if (targetValue == idleValue) {
+ return targetValue
+ }
+
+ // TODO(b/290184746): Make sure that we don't overflow transformations associated to a
+ // range.
+ val directionSign = if (transition.isUpOrLeft) -1 else 1
+ val overscrollProgress = transition.progress.let { if (it > 1f) it - 1f else it }
+ val progress = directionSign * overscrollProgress
+ val rangeProgress = propertySpec.range?.progress(progress) ?: progress
+
+ // Interpolate between the value at rest and the over scrolled value.
+ return lerp(idleValue, targetValue, rangeProgress)
+ }
+ }
+
// The element is shared: interpolate between the value in fromScene and the value in toScene.
// TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
// elements follow the finger direction.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index c40856061152..187d82a9e626 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -497,9 +497,11 @@ private class SwipeTransition(
val _fromScene: Scene,
val _toScene: Scene,
private val userActionDistanceScope: UserActionDistanceScope,
- private val orientation: Orientation,
- private val isUpOrLeft: Boolean,
-) : TransitionState.Transition(_fromScene.key, _toScene.key) {
+ override val orientation: Orientation,
+ override val isUpOrLeft: Boolean,
+) :
+ TransitionState.Transition(_fromScene.key, _toScene.key),
+ TransitionState.HasOverscrollProperties {
var _currentScene by mutableStateOf(_fromScene)
override val currentScene: SceneKey
get() = _currentScene.key
@@ -789,14 +791,21 @@ internal class SceneNestedScrollHandler(
)
fun hasNextScene(amount: Float): Boolean {
- val fromScene = layoutImpl.scene(layoutState.transitionState.currentScene)
+ val transitionState = layoutState.transitionState
+ val scene = transitionState.currentScene
+ val fromScene = layoutImpl.scene(scene)
val nextScene =
when {
amount < 0f -> fromScene.userActions[actionUpOrLeft]
amount > 0f -> fromScene.userActions[actionDownOrRight]
else -> null
}
- return nextScene != null
+ if (nextScene != null) return true
+
+ if (transitionState !is TransitionState.Idle) return false
+
+ val overscrollSpec = layoutImpl.state.transitions.overscrollSpec(scene, orientation)
+ return overscrollSpec != null
}
val source = this
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 039a5b0c9523..3093d477a24c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -25,8 +25,13 @@ import androidx.compose.runtime.key
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ApproachLayoutModifierNode
+import androidx.compose.ui.layout.ApproachMeasureScope
import androidx.compose.ui.layout.LookaheadScope
-import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEach
@@ -181,7 +186,6 @@ internal class SceneTransitionLayoutImpl(
}
@Composable
- @OptIn(ExperimentalComposeUiApi::class)
internal fun Content(modifier: Modifier) {
Box(
modifier
@@ -190,37 +194,7 @@ internal class SceneTransitionLayoutImpl(
// swipes.
.swipeToScene(horizontalGestureHandler)
.swipeToScene(verticalGestureHandler)
- // Animate the size of this layout.
- .intermediateLayout { measurable, constraints ->
- // Measure content normally.
- val placeable = measurable.measure(constraints)
-
- val width: Int
- val height: Int
- val transition = state.currentTransition
- if (transition == null) {
- width = placeable.width
- height = placeable.height
- } else {
- // Interpolate the size.
- val fromSize = scene(transition.fromScene).targetSize
- val toSize = scene(transition.toScene).targetSize
-
- // Optimization: make sure we don't read state.progress if fromSize ==
- // toSize to avoid running this code every frame when the layout size does
- // not change.
- if (fromSize == toSize) {
- width = fromSize.width
- height = fromSize.height
- } else {
- val size = lerp(fromSize, toSize, transition.progress)
- width = size.width.coerceAtLeast(0)
- height = size.height.coerceAtLeast(0)
- }
- }
-
- layout(width, height) { placeable.place(0, 0) }
- }
+ .then(LayoutElement(layoutImpl = this))
) {
LookaheadScope {
val scenesToCompose =
@@ -263,3 +237,54 @@ internal class SceneTransitionLayoutImpl(
scenes.values.forEach { it.targetSize = size }
}
}
+
+private data class LayoutElement(private val layoutImpl: SceneTransitionLayoutImpl) :
+ ModifierNodeElement<LayoutNode>() {
+ override fun create(): LayoutNode = LayoutNode(layoutImpl)
+
+ override fun update(node: LayoutNode) {
+ node.layoutImpl = layoutImpl
+ }
+}
+
+private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
+ Modifier.Node(), ApproachLayoutModifierNode {
+ override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean {
+ return layoutImpl.state.currentTransition == null
+ }
+
+ @ExperimentalComposeUiApi
+ override fun ApproachMeasureScope.approachMeasure(
+ measurable: Measurable,
+ constraints: Constraints,
+ ): MeasureResult {
+ // Measure content normally.
+ val placeable = measurable.measure(constraints)
+
+ val width: Int
+ val height: Int
+ val transition = layoutImpl.state.currentTransition
+ if (transition == null) {
+ width = placeable.width
+ height = placeable.height
+ } else {
+ // Interpolate the size.
+ val fromSize = layoutImpl.scene(transition.fromScene).targetSize
+ val toSize = layoutImpl.scene(transition.toScene).targetSize
+
+ // Optimization: make sure we don't read state.progress if fromSize ==
+ // toSize to avoid running this code every frame when the layout size does
+ // not change.
+ if (fromSize == toSize) {
+ width = fromSize.width
+ height = fromSize.height
+ } else {
+ val size = lerp(fromSize, toSize, transition.progress)
+ width = size.width.coerceAtLeast(0)
+ height = size.height.coerceAtLeast(0)
+ }
+ }
+
+ return layout(width, height) { placeable.place(0, 0) }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 662f33f3e88b..0fa19bb33818 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
@@ -221,6 +222,23 @@ sealed interface TransitionState {
isTransitioning(from = other, to = scene)
}
}
+
+ interface HasOverscrollProperties {
+ /**
+ * The position of the [TransitionState.Transition.toScene].
+ *
+ * Used to understand the direction of the overscroll.
+ */
+ val isUpOrLeft: Boolean
+
+ /**
+ * The relative orientation between [TransitionState.Transition.fromScene] and
+ * [TransitionState.Transition.toScene].
+ *
+ * Used to understand the orientation of the overscroll.
+ */
+ val orientation: Orientation
+ }
}
internal abstract class BaseSceneTransitionLayoutState(
@@ -237,6 +255,25 @@ internal abstract class BaseSceneTransitionLayoutState(
*/
internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
+ private var fromOverscrollSpec: OverscrollSpecImpl? = null
+ private var toOverscrollSpec: OverscrollSpecImpl? = null
+
+ /**
+ * @return the overscroll [OverscrollSpecImpl] if it is defined for the current
+ * [transitionState] and we are currently over scrolling.
+ */
+ internal val currentOverscrollSpec: OverscrollSpecImpl?
+ get() {
+ val transition = currentTransition ?: return null
+ if (transition !is TransitionState.HasOverscrollProperties) return null
+ val progress = transition.progress
+ return when {
+ progress < 0f -> fromOverscrollSpec
+ progress > 1f -> toOverscrollSpec
+ else -> null
+ }
+ }
+
private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
/** Whether we can transition to the given [scene]. */
@@ -266,10 +303,13 @@ internal abstract class BaseSceneTransitionLayoutState(
transitionKey: TransitionKey?,
) {
// Compute the [TransformationSpec] when the transition starts.
+ val fromScene = transition.fromScene
+ val toScene = transition.toScene
+ val orientation = (transition as? TransitionState.HasOverscrollProperties)?.orientation
transformationSpec =
- transitions
- .transitionSpec(transition.fromScene, transition.toScene, key = transitionKey)
- .transformationSpec()
+ transitions.transitionSpec(fromScene, toScene, key = transitionKey).transformationSpec()
+ fromOverscrollSpec = orientation?.let { transitions.overscrollSpec(fromScene, it) }
+ toOverscrollSpec = orientation?.let { transitions.overscrollSpec(toScene, it) }
cancelActiveTransitionLinks()
setupTransitionLinks(transition)
transitionState = transition
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index 8ee23b6ffe8e..2dd41cd329a2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -21,6 +21,7 @@ import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.spring
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEach
@@ -41,18 +42,22 @@ class SceneTransitions
internal constructor(
internal val defaultSwipeSpec: SpringSpec<Float>,
internal val transitionSpecs: List<TransitionSpecImpl>,
+ internal val overscrollSpecs: List<OverscrollSpecImpl>,
) {
- private val cache =
+ private val transitionCache =
mutableMapOf<
SceneKey, MutableMap<SceneKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
>()
+ private val overscrollCache =
+ mutableMapOf<SceneKey, MutableMap<Orientation, OverscrollSpecImpl?>>()
+
internal fun transitionSpec(
from: SceneKey,
to: SceneKey,
key: TransitionKey?,
): TransitionSpecImpl {
- return cache
+ return transitionCache
.getOrPut(from) { mutableMapOf() }
.getOrPut(to) { mutableMapOf() }
.getOrPut(key) { findSpec(from, to, key) }
@@ -105,6 +110,28 @@ internal constructor(
private fun defaultTransition(from: SceneKey, to: SceneKey) =
TransitionSpecImpl(key = null, from, to, TransformationSpec.EmptyProvider)
+ internal fun overscrollSpec(scene: SceneKey, orientation: Orientation): OverscrollSpecImpl? =
+ overscrollCache
+ .getOrPut(scene) { mutableMapOf() }
+ .getOrPut(orientation) { overscroll(scene, orientation) { it.scene == scene } }
+
+ private fun overscroll(
+ scene: SceneKey,
+ orientation: Orientation,
+ filter: (OverscrollSpecImpl) -> Boolean,
+ ): OverscrollSpecImpl? {
+ var match: OverscrollSpecImpl? = null
+ overscrollSpecs.fastForEach { spec ->
+ if (spec.orientation == orientation && filter(spec)) {
+ if (match != null) {
+ error("Found multiple transition specs for transition $scene")
+ }
+ match = spec
+ }
+ }
+ return match
+ }
+
companion object {
internal val DefaultSwipeSpec =
spring(
@@ -112,7 +139,12 @@ internal constructor(
visibilityThreshold = OffsetVisibilityThreshold,
)
- val Empty = SceneTransitions(DefaultSwipeSpec, transitionSpecs = emptyList())
+ val Empty =
+ SceneTransitions(
+ defaultSwipeSpec = DefaultSwipeSpec,
+ transitionSpecs = emptyList(),
+ overscrollSpecs = emptyList(),
+ )
}
}
@@ -139,7 +171,7 @@ interface TransitionSpec {
*/
fun reversed(): TransitionSpec
- /*
+ /**
* The [TransformationSpec] associated to this [TransitionSpec].
*
* Note that this is called once every a transition associated to this [TransitionSpec] is
@@ -212,6 +244,24 @@ internal class TransitionSpecImpl(
override fun transformationSpec(): TransformationSpecImpl = this.transformationSpec.invoke()
}
+/** The definition of the overscroll behavior of the [scene]. */
+interface OverscrollSpec {
+ /** The scene we are over scrolling. */
+ val scene: SceneKey
+
+ /** The orientation of this [OverscrollSpec]. */
+ val orientation: Orientation
+
+ /** The [TransformationSpec] associated to this [OverscrollSpec]. */
+ val transformationSpec: TransformationSpec
+}
+
+internal class OverscrollSpecImpl(
+ override val scene: SceneKey,
+ override val orientation: Orientation,
+ override val transformationSpec: TransformationSpecImpl,
+) : OverscrollSpec
+
/**
* An implementation of [TransformationSpec] that allows the quick retrieval of an element
* [ElementTransformations].
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 8a09b00a63ae..bc52a28279dc 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -18,6 +18,7 @@ package com.android.compose.animation.scene
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.SpringSpec
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -72,24 +73,23 @@ interface SceneTransitionsBuilder {
key: TransitionKey? = null,
builder: TransitionBuilder.() -> Unit = {},
): TransitionSpec
-}
-
-@TransitionDsl
-interface TransitionBuilder : PropertyTransformationBuilder {
- /**
- * The [AnimationSpec] used to animate the associated transition progress from `0` to `1` when
- * the transition is triggered (i.e. it is not gesture-based).
- */
- var spec: AnimationSpec<Float>
/**
- * The [SpringSpec] used to animate the associated transition progress when the transition was
- * started by a swipe and is now animating back to a scene because the user lifted their finger.
+ * Define the animation to be played when the [scene] is overscrolled in the given
+ * [orientation].
*
- * If `null`, then the [SceneTransitionsBuilder.defaultSwipeSpec] will be used.
+ * The overscroll animation always starts from a progress of 0f, and reaches 1f when moving the
+ * [distance] down/right, -1f when moving in the opposite direction.
*/
- var swipeSpec: SpringSpec<Float>?
+ fun overscroll(
+ scene: SceneKey,
+ orientation: Orientation,
+ builder: OverscrollBuilder.() -> Unit = {},
+ ): OverscrollSpec
+}
+@TransitionDsl
+interface OverscrollBuilder : PropertyTransformationBuilder {
/**
* The distance it takes for this transition to animate from 0% to 100% when it is driven by a
* [UserAction].
@@ -117,6 +117,23 @@ interface TransitionBuilder : PropertyTransformationBuilder {
end: Float? = null,
builder: PropertyTransformationBuilder.() -> Unit,
)
+}
+
+@TransitionDsl
+interface TransitionBuilder : OverscrollBuilder, PropertyTransformationBuilder {
+ /**
+ * The [AnimationSpec] used to animate the associated transition progress from `0` to `1` when
+ * the transition is triggered (i.e. it is not gesture-based).
+ */
+ var spec: AnimationSpec<Float>
+
+ /**
+ * The [SpringSpec] used to animate the associated transition progress when the transition was
+ * started by a swipe and is now animating back to a scene because the user lifted their finger.
+ *
+ * If `null`, then the [SceneTransitionsBuilder.defaultSwipeSpec] will be used.
+ */
+ var swipeSpec: SpringSpec<Float>?
/**
* Define a timestamp-based range for the transformations inside [builder].
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 78289997885f..65e8ea5cc341 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -21,7 +21,9 @@ import androidx.compose.animation.core.DurationBasedAnimationSpec
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.snap
import androidx.compose.animation.core.spring
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.Dp
import com.android.compose.animation.scene.transformation.AnchoredSize
@@ -41,13 +43,18 @@ internal fun transitionsImpl(
builder: SceneTransitionsBuilder.() -> Unit,
): SceneTransitions {
val impl = SceneTransitionsBuilderImpl().apply(builder)
- return SceneTransitions(impl.defaultSwipeSpec, impl.transitionSpecs)
+ return SceneTransitions(
+ impl.defaultSwipeSpec,
+ impl.transitionSpecs,
+ impl.transitionOverscrollSpecs
+ )
}
private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
override var defaultSwipeSpec: SpringSpec<Float> = SceneTransitions.DefaultSwipeSpec
val transitionSpecs = mutableListOf<TransitionSpecImpl>()
+ val transitionOverscrollSpecs = mutableListOf<OverscrollSpecImpl>()
override fun to(
to: SceneKey,
@@ -66,6 +73,25 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
return transition(from = from, to = to, key = key, builder)
}
+ override fun overscroll(
+ scene: SceneKey,
+ orientation: Orientation,
+ builder: OverscrollBuilder.() -> Unit
+ ): OverscrollSpec {
+ fun transformationSpec(): TransformationSpecImpl {
+ val impl = OverscrollBuilderImpl().apply(builder)
+ return TransformationSpecImpl(
+ progressSpec = snap(),
+ swipeSpec = null,
+ distance = impl.distance,
+ transformations = impl.transformations,
+ )
+ }
+ val spec = OverscrollSpecImpl(scene, orientation, transformationSpec())
+ transitionOverscrollSpecs.add(spec)
+ return spec
+ }
+
private fun transition(
from: SceneKey?,
to: SceneKey?,
@@ -88,28 +114,11 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
}
}
-internal class TransitionBuilderImpl : TransitionBuilder {
+internal open class OverscrollBuilderImpl : OverscrollBuilder {
val transformations = mutableListOf<Transformation>()
- override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow)
- override var swipeSpec: SpringSpec<Float>? = null
- override var distance: UserActionDistance? = null
-
private var range: TransformationRange? = null
- private var reversed = false
- private val durationMillis: Int by lazy {
- val spec = spec
- if (spec !is DurationBasedAnimationSpec) {
- error("timestampRange {} can only be used with a DurationBasedAnimationSpec")
- }
-
- spec.vectorize(Float.VectorConverter).durationMillis
- }
-
- override fun reversed(builder: TransitionBuilder.() -> Unit) {
- reversed = true
- builder()
- reversed = false
- }
+ protected var reversed = false
+ override var distance: UserActionDistance? = null
override fun fractionRange(
start: Float?,
@@ -121,28 +130,6 @@ internal class TransitionBuilderImpl : TransitionBuilder {
range = null
}
- override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) {
- transformations.add(SharedElementTransformation(matcher, enabled))
- }
-
- override fun timestampRange(
- startMillis: Int?,
- endMillis: Int?,
- builder: PropertyTransformationBuilder.() -> Unit
- ) {
- if (startMillis != null && (startMillis < 0 || startMillis > durationMillis)) {
- error("invalid start value: startMillis=$startMillis durationMillis=$durationMillis")
- }
-
- if (endMillis != null && (endMillis < 0 || endMillis > durationMillis)) {
- error("invalid end value: endMillis=$startMillis durationMillis=$durationMillis")
- }
-
- val start = startMillis?.let { it.toFloat() / durationMillis }
- val end = endMillis?.let { it.toFloat() / durationMillis }
- fractionRange(start, end, builder)
- }
-
private fun transformation(transformation: PropertyTransformation<*>) {
val transformation =
if (range != null) {
@@ -197,3 +184,45 @@ internal class TransitionBuilderImpl : TransitionBuilder {
transformation(AnchoredSize(matcher, anchor, anchorWidth, anchorHeight))
}
}
+
+internal class TransitionBuilderImpl : OverscrollBuilderImpl(), TransitionBuilder {
+ override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow)
+ override var swipeSpec: SpringSpec<Float>? = null
+ override var distance: UserActionDistance? = null
+ private val durationMillis: Int by lazy {
+ val spec = spec
+ if (spec !is DurationBasedAnimationSpec) {
+ error("timestampRange {} can only be used with a DurationBasedAnimationSpec")
+ }
+
+ spec.vectorize(Float.VectorConverter).durationMillis
+ }
+
+ override fun reversed(builder: TransitionBuilder.() -> Unit) {
+ reversed = true
+ builder()
+ reversed = false
+ }
+
+ override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) {
+ transformations.add(SharedElementTransformation(matcher, enabled))
+ }
+
+ override fun timestampRange(
+ startMillis: Int?,
+ endMillis: Int?,
+ builder: PropertyTransformationBuilder.() -> Unit
+ ) {
+ if (startMillis != null && (startMillis < 0 || startMillis > durationMillis)) {
+ error("invalid start value: startMillis=$startMillis durationMillis=$durationMillis")
+ }
+
+ if (endMillis != null && (endMillis < 0 || endMillis > durationMillis)) {
+ error("invalid end value: endMillis=$startMillis durationMillis=$durationMillis")
+ }
+
+ val start = startMillis?.let { it.toFloat() / durationMillis }
+ val end = endMillis?.let { it.toFloat() / durationMillis }
+ fractionRange(start, end, builder)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 33be1dc83dc8..059a10e23207 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -18,9 +18,13 @@ package com.android.compose.animation.scene
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
@@ -34,8 +38,14 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -528,4 +538,157 @@ class ElementTest {
after { assertThat(fooCompositions).isEqualTo(1) }
}
}
+
+ @Test
+ fun elementTransitionDuringOverscroll() {
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ val overscrollTranslateY = 10.dp
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+
+ val state =
+ MutableSceneTransitionLayoutState(
+ initialScene = TestScenes.SceneA,
+ transitions =
+ transitions {
+ overscroll(TestScenes.SceneB, Orientation.Vertical) {
+ translate(TestElements.Foo, y = overscrollTranslateY)
+ }
+ }
+ )
+ as MutableSceneTransitionLayoutStateImpl
+
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(
+ state = state,
+ modifier = Modifier.size(layoutWidth, layoutHeight)
+ ) {
+ scene(
+ key = TestScenes.SceneA,
+ userActions = mapOf(Swipe.Down to TestScenes.SceneB)
+ ) {
+ Spacer(Modifier.fillMaxSize())
+ }
+ scene(TestScenes.SceneB) {
+ Spacer(Modifier.element(TestElements.Foo).fillMaxSize())
+ }
+ }
+ }
+
+ assertThat(state.currentTransition).isNull()
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ // Swipe by half of verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
+ down(middleTop)
+ // Scroll 50%
+ moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
+ }
+
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
+ fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+ val transition = state.currentTransition
+ assertThat(transition).isNotNull()
+ assertThat(transition!!.progress).isEqualTo(0.5f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 150% (Scene B overscroll by 50%)
+ assertThat(transition.progress).isEqualTo(1.5f)
+ assertThat(state.currentOverscrollSpec).isNotNull()
+ fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 250% (Scene B overscroll by 150%)
+ assertThat(transition.progress).isEqualTo(2.5f)
+ assertThat(state.currentOverscrollSpec).isNotNull()
+ fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f)
+ }
+
+ @Test
+ fun elementTransitionDuringNestedScrollOverscroll() {
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ val overscrollTranslateY = 10.dp
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+
+ val state =
+ MutableSceneTransitionLayoutState(
+ initialScene = TestScenes.SceneB,
+ transitions =
+ transitions {
+ overscroll(TestScenes.SceneB, Orientation.Vertical) {
+ translate(TestElements.Foo, y = overscrollTranslateY)
+ }
+ }
+ )
+ as MutableSceneTransitionLayoutStateImpl
+
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(
+ state = state,
+ modifier = Modifier.size(layoutWidth, layoutHeight)
+ ) {
+ scene(TestScenes.SceneA) { Spacer(Modifier.fillMaxSize()) }
+ scene(TestScenes.SceneB, userActions = mapOf(Swipe.Up to TestScenes.SceneA)) {
+ Box(
+ Modifier
+ // Unconsumed scroll gesture will be intercepted by STL
+ .verticalNestedScrollToScene()
+ // A scrollable that does not consume the scroll gesture
+ .scrollable(
+ rememberScrollableState(consumeScrollDelta = { 0f }),
+ Orientation.Vertical
+ )
+ .fillMaxSize()
+ ) {
+ Spacer(Modifier.element(TestElements.Foo).fillMaxSize())
+ }
+ }
+ }
+ }
+
+ assertThat(state.currentTransition).isNull()
+ assertThat(state.currentOverscrollSpec).isNull()
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
+ fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+
+ // Swipe by half of verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
+ down(middleTop)
+ // Scroll 50%
+ moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
+ }
+
+ val transition = state.currentTransition
+ assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(transition).isNotNull()
+ assertThat(transition!!.progress).isEqualTo(-0.5f)
+ fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 150% (Scene B overscroll by 50%)
+ assertThat(transition.progress).isEqualTo(-1.5f)
+ assertThat(state.currentOverscrollSpec).isNotNull()
+ fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index fe53d5b45420..d28ac6ad546e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -52,7 +52,7 @@ class SceneGestureHandlerTest {
private val testScope: MonotonicClockTestScope,
) {
var canChangeScene: (SceneKey) -> Boolean = { true }
- private val layoutState =
+ val layoutState =
MutableSceneTransitionLayoutStateImpl(
SceneA,
EmptyTestTransitions,
@@ -932,4 +932,28 @@ class SceneGestureHandlerTest {
advanceUntilIdle()
assertIdle(SceneB)
}
+
+ @Test
+ fun scrollFromIdleWithNoTargetScene_shouldUseOverscrollSpecIfAvailable() = runGestureTest {
+ layoutState.transitions = transitions {
+ overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
+ }
+ // Start at scene C.
+ navigateToSceneC()
+
+ val scene = layoutState.transitionState.currentScene
+ // We should have overscroll spec for scene C
+ assertThat(layoutState.transitions.overscrollSpec(scene, Orientation.Vertical)).isNotNull()
+ assertThat(layoutState.currentOverscrollSpec).isNull()
+
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
+ nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f))
+
+ // We scrolled down, under scene C there is nothing, so we can use the overscroll spec
+ assertThat(layoutState.currentOverscrollSpec).isNotNull()
+ assertThat(layoutState.currentOverscrollSpec?.scene).isEqualTo(SceneC)
+ val transition = layoutState.currentTransition
+ assertThat(transition).isNotNull()
+ assertThat(transition!!.progress).isEqualTo(-0.1f)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index f81a7f2908e8..3cbcd73a0adb 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -16,6 +16,8 @@
package com.android.compose.animation.scene
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
@@ -389,4 +391,118 @@ class SceneTransitionLayoutStateTest {
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse()
}
+
+ private fun startOverscrollableTransistionFromAtoB(
+ progress: () -> Float,
+ sceneTransitions: SceneTransitions,
+ ): MutableSceneTransitionLayoutStateImpl {
+ val state =
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ sceneTransitions,
+ )
+ state.startTransition(
+ object :
+ TransitionState.Transition(SceneA, SceneB),
+ TransitionState.HasOverscrollProperties {
+ override val currentScene: SceneKey = SceneA
+ override val progress: Float
+ get() = progress()
+
+ override val isInitiatedByUserInput: Boolean = false
+ override val isUserInputOngoing: Boolean = false
+ override val isUpOrLeft: Boolean = false
+ override val orientation: Orientation = Orientation.Vertical
+ },
+ transitionKey = null
+ )
+ assertThat(state.isTransitioning()).isTrue()
+ return state
+ }
+
+ @Test
+ fun overscrollDsl_definedForToScene() = runMonotonicClockTest {
+ val progress = mutableStateOf(0f)
+ val state =
+ startOverscrollableTransistionFromAtoB(
+ progress = { progress.value },
+ sceneTransitions =
+ transitions {
+ overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
+ }
+ )
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ // overscroll for SceneA is NOT defined
+ progress.value = -0.1f
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ // scroll from SceneA to SceneB
+ progress.value = 0.5f
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ progress.value = 1f
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ // overscroll for SceneB is defined
+ progress.value = 1.1f
+ assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentOverscrollSpec?.scene).isEqualTo(SceneB)
+ }
+
+ @Test
+ fun overscrollDsl_definedForFromScene() = runMonotonicClockTest {
+ val progress = mutableStateOf(0f)
+ val state =
+ startOverscrollableTransistionFromAtoB(
+ progress = { progress.value },
+ sceneTransitions =
+ transitions {
+ overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
+ }
+ )
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ // overscroll for SceneA is defined
+ progress.value = -0.1f
+ assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentOverscrollSpec?.scene).isEqualTo(SceneA)
+
+ // scroll from SceneA to SceneB
+ progress.value = 0.5f
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ progress.value = 1f
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ // overscroll for SceneB is NOT defined
+ progress.value = 1.1f
+ assertThat(state.currentOverscrollSpec).isNull()
+ }
+
+ @Test
+ fun overscrollDsl_notDefinedScenes() = runMonotonicClockTest {
+ val progress = mutableStateOf(0f)
+ val state =
+ startOverscrollableTransistionFromAtoB(
+ progress = { progress.value },
+ sceneTransitions = transitions {}
+ )
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ // overscroll for SceneA is NOT defined
+ progress.value = -0.1f
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ // scroll from SceneA to SceneB
+ progress.value = 0.5f
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ progress.value = 1f
+ assertThat(state.currentOverscrollSpec).isNull()
+
+ // overscroll for SceneB is NOT defined
+ progress.value = 1.1f
+ assertThat(state.currentOverscrollSpec).isNull()
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index 1beafcc2cbb2..c9c3eccdedfc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -20,9 +20,11 @@ import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.TweenSpec
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.TransformationRange
+import com.android.compose.animation.scene.transformation.Translate
import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -223,6 +225,17 @@ class TransitionDslTest {
.isSameInstanceAs(specFromAToC)
}
+ @Test
+ fun overscrollSpec() {
+ val transitions = transitions {
+ overscroll(TestScenes.SceneA, Orientation.Vertical) { translate(TestElements.Bar) }
+ }
+
+ val overscrollSpec = transitions.overscrollSpecs.single()
+ val transformation = overscrollSpec.transformationSpec.transformations.single()
+ assertThat(transformation).isInstanceOf(Translate::class.java)
+ }
+
companion object {
private val TRANSFORMATION_RANGE =
Correspondence.transforming<Transformation, TransformationRange?>(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
index 12611cbd8c63..88f5e1b85840 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
@@ -17,8 +17,10 @@
package com.android.systemui.communal.widgets
import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.content.pm.UserInfo
+import android.os.Bundle
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -33,8 +35,8 @@ import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -43,6 +45,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -91,7 +94,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
any<Int>(),
any<UserHandle>(),
any<ComponentName>(),
- nullable()
+ any<Bundle>(),
)
)
.thenReturn(true)
@@ -100,8 +103,14 @@ class CommunalWidgetHostTest : SysuiTestCase() {
val result = underTest.allocateIdAndBindWidget(provider)
verify(appWidgetHost).allocateAppWidgetId()
- verify(appWidgetManager).bindAppWidgetIdIfAllowed(widgetId, user, provider, null)
+ val bundle =
+ withArgCaptor<Bundle> {
+ verify(appWidgetManager)
+ .bindAppWidgetIdIfAllowed(eq(widgetId), eq(user), eq(provider), capture())
+ }
assertThat(result).isEqualTo(widgetId)
+ assertThat(bundle.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY))
+ .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
}
@Test
@@ -117,7 +126,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
any<Int>(),
any<UserHandle>(),
any<ComponentName>(),
- nullable()
+ any<Bundle>()
)
)
.thenReturn(true)
@@ -126,8 +135,14 @@ class CommunalWidgetHostTest : SysuiTestCase() {
val result = underTest.allocateIdAndBindWidget(provider, user)
verify(appWidgetHost).allocateAppWidgetId()
- verify(appWidgetManager).bindAppWidgetIdIfAllowed(widgetId, user, provider, null)
+ val bundle =
+ withArgCaptor<Bundle> {
+ verify(appWidgetManager)
+ .bindAppWidgetIdIfAllowed(eq(widgetId), eq(user), eq(provider), capture())
+ }
assertThat(result).isEqualTo(widgetId)
+ assertThat(bundle.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY))
+ .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
}
@Test
@@ -144,14 +159,15 @@ class CommunalWidgetHostTest : SysuiTestCase() {
any<Int>(),
any<UserHandle>(),
any<ComponentName>(),
- nullable()
+ any<Bundle>()
)
)
.thenReturn(false)
val result = underTest.allocateIdAndBindWidget(provider, user)
verify(appWidgetHost).allocateAppWidgetId()
- verify(appWidgetManager).bindAppWidgetIdIfAllowed(widgetId, user, provider, null)
+ verify(appWidgetManager)
+ .bindAppWidgetIdIfAllowed(eq(widgetId), eq(user), eq(provider), any())
verify(appWidgetHost).deleteAppWidgetId(widgetId)
assertThat(result).isNull()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 558e7e6564ae..3a28471b5f9a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -33,6 +33,7 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
@@ -215,6 +216,39 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
}
@Test
+ fun onFingerprintFailed_failedAuthenticationStatusWithOtherStatuses() =
+ testScope.runTest {
+ val failStatus by
+ collectLastValue(
+ underTest.authenticationStatus.filterIsInstance<
+ FailFingerprintAuthenticationStatus
+ >()
+ )
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricAcquired(
+ BiometricSourceType.FINGERPRINT,
+ /* acquireInfo */ 0,
+ )
+ updateMonitorCallback.value.onBiometricAuthFailed(
+ BiometricSourceType.FINGERPRINT,
+ )
+ updateMonitorCallback.value.onBiometricHelp(
+ /* msgId */ 7,
+ /* errString */ "Not recognized.",
+ BiometricSourceType.FINGERPRINT,
+ )
+ updateMonitorCallback.value.onBiometricError(
+ /* msgId */ 7,
+ /* errString */ "Too many attempts.",
+ BiometricSourceType.FINGERPRINT,
+ )
+
+ assertThat(failStatus).isNotNull()
+ }
+
+ @Test
fun onFingerprintError_errorAuthenticationStatus() =
testScope.runTest {
val authenticationStatus by collectLastValue(underTest.authenticationStatus)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 87d25ddcc75c..6456669c09b6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -19,8 +19,6 @@ package com.android.systemui.statusbar.policy;
import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
-import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
import static com.google.common.truth.Truth.assertThat;
@@ -41,31 +39,23 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Person;
-import android.content.Context;
import android.content.Intent;
-import android.graphics.Region;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.testing.TestableLooper;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.filters.SmallTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
-import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
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.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.FakeGlobalSettings;
-import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
-import com.android.systemui.util.time.SystemClock;
import org.junit.Rule;
import org.junit.Test;
@@ -83,8 +73,8 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "BaseHeadsUpManagerTest";
- private static final int TEST_TOUCH_ACCEPTANCE_TIME = 200;
- private static final int TEST_A11Y_AUTO_DISMISS_TIME = 1_000;
+ static final int TEST_TOUCH_ACCEPTANCE_TIME = 200;
+ static final int TEST_A11Y_AUTO_DISMISS_TIME = 1_000;
private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
@@ -110,143 +100,6 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME);
}
- private final class TestableHeadsUpManager extends BaseHeadsUpManager {
-
- private HeadsUpEntry mLastCreatedEntry;
-
- TestableHeadsUpManager(Context context,
- HeadsUpManagerLogger logger,
- DelayableExecutor executor,
- GlobalSettings globalSettings,
- SystemClock systemClock,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger) {
- super(context, logger, mockExecutorHandler(executor), globalSettings, systemClock,
- executor, accessibilityManagerWrapper, uiEventLogger);
-
- mTouchAcceptanceDelay = TEST_TOUCH_ACCEPTANCE_TIME;
- mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
- mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
- mStickyForSomeTimeAutoDismissTime = TEST_STICKY_AUTO_DISMISS_TIME;
-
- }
-
- @Override
- protected HeadsUpEntry createHeadsUpEntry() {
- mLastCreatedEntry = spy(super.createHeadsUpEntry());
- return mLastCreatedEntry;
- }
-
- @Override
- public int getContentFlag() {
- return FLAG_CONTENT_VIEW_CONTRACTED;
- }
-
- // The following are only implemented by HeadsUpManagerPhone. If you need them, use that.
- @Override
- public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void addSwipedOutNotification(@NonNull String key) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void extendHeadsUp() {
- throw new UnsupportedOperationException();
- }
-
- @Nullable
- @Override
- public Region getTouchableRegion() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isHeadsUpGoingAway() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void onExpandingFinished() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
- boolean animate) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setAnimationStateHandler(@NonNull AnimationStateHandler handler) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setRemoteInputActive(@NonNull NotificationEntry entry,
- boolean remoteInputActive) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setTrackingHeadsUp(boolean tracking) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean shouldSwallowClick(@NonNull String key) {
- throw new UnsupportedOperationException();
- }
- }
-
- protected StatusBarNotification createSbn(int id, Notification n) {
- return new StatusBarNotification(
- TEST_PACKAGE_NAME /* pkg */,
- TEST_PACKAGE_NAME,
- id,
- null /* tag */,
- TEST_UID,
- 0 /* initialPid */,
- n,
- new UserHandle(ActivityManager.getCurrentUser()),
- null /* overrideGroupKey */,
- 0 /* postTime */);
- }
-
- protected StatusBarNotification createSbn(int id, Notification.Builder n) {
- return createSbn(id, n.build());
- }
-
- protected StatusBarNotification createSbn(int id) {
- final Notification.Builder b = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
- return createSbn(id, b);
- }
-
- protected NotificationEntry createEntry(int id, Notification n) {
- return new NotificationEntryBuilder().setSbn(createSbn(id, n)).build();
- }
-
- protected NotificationEntry createEntry(int id) {
- return new NotificationEntryBuilder().setSbn(createSbn(id)).build();
- }
-
-
private BaseHeadsUpManager createHeadsUpManager() {
return new TestableHeadsUpManager(mContext, mLogger, mExecutor, mGlobalSettings,
mSystemClock, mAccessibilityMgr, mUiEventLoggerFake);
@@ -257,7 +110,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
.setSmallIcon(R.drawable.ic_person)
.setFullScreenIntent(mock(PendingIntent.class), /* highPriority */ true)
.build();
- return createEntry(id, notif);
+ return HeadsUpManagerTestUtil.createEntry(id, notif);
}
private NotificationEntry createStickyForSomeTimeEntry(int id) {
@@ -265,7 +118,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
.setSmallIcon(R.drawable.ic_person)
.setFlag(FLAG_FSI_REQUESTED_BUT_DENIED, true)
.build();
- return createEntry(id, notif);
+ return HeadsUpManagerTestUtil.createEntry(id, notif);
}
private PendingIntent createFullScreenIntent() {
@@ -279,7 +132,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
.setSmallIcon(R.drawable.ic_person)
.setFullScreenIntent(createFullScreenIntent(), /* highPriority */ true)
.build();
- return createEntry(id, notif);
+ return HeadsUpManagerTestUtil.createEntry(id, notif);
}
@@ -296,7 +149,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testShowNotification_addsEntry() {
final BaseHeadsUpManager alm = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
alm.showNotification(entry);
@@ -308,7 +161,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testShowNotification_autoDismisses() {
final BaseHeadsUpManager alm = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
alm.showNotification(entry);
mSystemClock.advanceTime(TEST_AUTO_DISMISS_TIME * 3 / 2);
@@ -319,7 +172,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testRemoveNotification_removeDeferred() {
final BaseHeadsUpManager alm = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
alm.showNotification(entry);
@@ -332,7 +185,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testRemoveNotification_forceRemove() {
final BaseHeadsUpManager alm = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
alm.showNotification(entry);
@@ -346,7 +199,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
public void testReleaseAllImmediately() {
final BaseHeadsUpManager alm = createHeadsUpManager();
for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
- final NotificationEntry entry = createEntry(i);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(i, mContext);
entry.setRow(mRow);
alm.showNotification(entry);
}
@@ -359,7 +212,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testCanRemoveImmediately_notShownLongEnough() {
final BaseHeadsUpManager alm = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
alm.showNotification(entry);
@@ -370,7 +223,8 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testHunRemovedLogging() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry = createEntry(/* id = */ 0);
+ final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+ mContext);
final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = mock(
BaseHeadsUpManager.HeadsUpEntry.class);
headsUpEntry.mEntry = notifEntry;
@@ -413,7 +267,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testShouldHeadsUpBecomePinned_noFSI_false() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
assertFalse(hum.shouldHeadsUpBecomePinned(entry));
}
@@ -422,7 +276,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
useAccessibilityTimeout(false);
hum.showNotification(entry);
@@ -435,7 +289,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testShowNotification_autoDismissesWithDefaultTimeout() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
useAccessibilityTimeout(false);
hum.showNotification(entry);
@@ -476,7 +330,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
useAccessibilityTimeout(true);
hum.showNotification(entry);
@@ -504,7 +358,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testRemoveNotification_beforeMinimumDisplayTime() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
useAccessibilityTimeout(false);
hum.showNotification(entry);
@@ -523,7 +377,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testRemoveNotification_afterMinimumDisplayTime() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
useAccessibilityTimeout(false);
hum.showNotification(entry);
@@ -541,7 +395,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testRemoveNotification_releaseImmediately() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
hum.showNotification(entry);
@@ -555,7 +409,8 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testIsSticky_rowPinnedAndExpanded_true() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry = createEntry(/* id = */ 0);
+ final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+ mContext);
when(mRow.isPinned()).thenReturn(true);
notifEntry.setRow(mRow);
@@ -571,7 +426,8 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testIsSticky_remoteInputActive_true() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry = createEntry(/* id = */ 0);
+ final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+ mContext);
hum.showNotification(notifEntry);
@@ -607,7 +463,8 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testIsSticky_false() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry = createEntry(/* id = */ 0);
+ final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+ mContext);
hum.showNotification(notifEntry);
@@ -653,14 +510,14 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
final BaseHeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry();
ongoingCall.setEntry(new NotificationEntryBuilder()
- .setSbn(createSbn(/* id = */ 0,
+ .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0,
new Notification.Builder(mContext, "")
.setCategory(Notification.CATEGORY_CALL)
.setOngoing(true)))
.build());
final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
- activeRemoteInput.setEntry(createEntry(/* id = */ 1));
+ activeRemoteInput.setEntry(HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext));
activeRemoteInput.mRemoteInputActive = true;
assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0);
@@ -675,14 +532,14 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
final Person person = new Person.Builder().setName("person").build();
final PendingIntent intent = mock(PendingIntent.class);
incomingCall.setEntry(new NotificationEntryBuilder()
- .setSbn(createSbn(/* id = */ 0,
+ .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0,
new Notification.Builder(mContext, "")
.setStyle(Notification.CallStyle
.forIncomingCall(person, intent, intent))))
.build());
final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
- activeRemoteInput.setEntry(createEntry(/* id = */ 1));
+ activeRemoteInput.setEntry(HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext));
activeRemoteInput.mRemoteInputActive = true;
assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0);
@@ -712,7 +569,8 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Test
public void testSetUserActionMayIndirectlyRemove() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry = createEntry(/* id = */ 0);
+ final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+ mContext);
hum.showNotification(notifEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
index c350de21dda5..ec23f76935d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.policy;
import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
@@ -26,10 +26,10 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -39,11 +39,9 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.statusbar.policy.BaseHeadsUpManagerTest;
-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.statusbar.phone.ConfigurationControllerImpl;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
@@ -61,7 +59,7 @@ import org.mockito.junit.MockitoRule;
import kotlinx.coroutines.flow.StateFlowKt;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {
@Rule public MockitoRule rule = MockitoJUnit.rule();
@@ -152,7 +150,7 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {
@Test
public void testSnooze() {
final HeadsUpManager hmp = createHeadsUpManagerPhone();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
hmp.showNotification(entry);
hmp.snooze();
@@ -163,7 +161,7 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {
@Test
public void testSwipedOutNotification() {
final HeadsUpManager hmp = createHeadsUpManagerPhone();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
hmp.showNotification(entry);
hmp.addSwipedOutNotification(entry.getKey());
@@ -179,7 +177,7 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {
@Test
public void testCanRemoveImmediately_swipedOut() {
final HeadsUpManager hmp = createHeadsUpManagerPhone();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
hmp.showNotification(entry);
hmp.addSwipedOutNotification(entry.getKey());
@@ -192,8 +190,10 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {
@Test
public void testCanRemoveImmediately_notTopEntry() {
final HeadsUpManager hmp = createHeadsUpManagerPhone();
- final NotificationEntry earlierEntry = createEntry(/* id = */ 0);
- final NotificationEntry laterEntry = createEntry(/* id = */ 1);
+ final NotificationEntry earlierEntry =
+ HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
+ final NotificationEntry laterEntry =
+ HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext);
laterEntry.setRow(mRow);
hmp.showNotification(earlierEntry);
@@ -206,7 +206,7 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {
@Test
public void testExtendHeadsUp() {
final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
- final NotificationEntry entry = createEntry(/* id = */ 0);
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
hmp.showNotification(entry);
hmp.extendHeadsUp();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
new file mode 100644
index 000000000000..c70b03b08789
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+import android.app.ActivityManager;
+import android.os.UserHandle;
+
+import android.content.Context;
+import android.service.notification.StatusBarNotification;
+import android.app.Notification;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+
+/**
+ * Test helper class for HeadsUpEntry creation.
+ */
+public class HeadsUpManagerTestUtil {
+
+ private static final String TEST_PACKAGE_NAME = "HeadsUpManagerTestUtil";
+ private static final int TEST_UID = 0;
+
+ protected static StatusBarNotification createSbn(int id, Notification.Builder n) {
+ return createSbn(id, n.build());
+ }
+
+ protected static StatusBarNotification createSbn(int id, Context context) {
+ final Notification.Builder b = new Notification.Builder(context, "")
+ .setSmallIcon(com.android.systemui.res.R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+ return createSbn(id, b);
+ }
+
+ protected static StatusBarNotification createSbn(int id, Notification n) {
+ return new StatusBarNotification(
+ TEST_PACKAGE_NAME /* pkg */,
+ TEST_PACKAGE_NAME,
+ id,
+ null /* tag */,
+ TEST_UID,
+ 0 /* initialPid */,
+ n,
+ new UserHandle(ActivityManager.getCurrentUser()),
+ null /* overrideGroupKey */,
+ 0 /* postTime */);
+ }
+
+ protected static NotificationEntry createEntry(int id, Notification n) {
+ return new NotificationEntryBuilder().setSbn(createSbn(id, n)).build();
+ }
+
+ protected static NotificationEntry createEntry(int id, Context context) {
+ return new NotificationEntryBuilder().setSbn(
+ HeadsUpManagerTestUtil.createSbn(id, context)).build();
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
new file mode 100644
index 000000000000..27476299cf18
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.graphics.Region;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
+
+class TestableHeadsUpManager extends BaseHeadsUpManager {
+
+ private HeadsUpEntry mLastCreatedEntry;
+
+ TestableHeadsUpManager(Context context,
+ HeadsUpManagerLogger logger,
+ DelayableExecutor executor,
+ GlobalSettings globalSettings,
+ SystemClock systemClock,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ UiEventLogger uiEventLogger) {
+ super(context, logger, mockExecutorHandler(executor), globalSettings, systemClock,
+ executor, accessibilityManagerWrapper, uiEventLogger);
+
+ mTouchAcceptanceDelay = BaseHeadsUpManagerTest.TEST_TOUCH_ACCEPTANCE_TIME;
+ mMinimumDisplayTime = BaseHeadsUpManagerTest.TEST_MINIMUM_DISPLAY_TIME;
+ mAutoDismissTime = BaseHeadsUpManagerTest.TEST_AUTO_DISMISS_TIME;
+ mStickyForSomeTimeAutoDismissTime = BaseHeadsUpManagerTest.TEST_STICKY_AUTO_DISMISS_TIME;
+ }
+
+ @Override
+ protected HeadsUpEntry createHeadsUpEntry() {
+ mLastCreatedEntry = spy(super.createHeadsUpEntry());
+ return mLastCreatedEntry;
+ }
+
+ @Override
+ public int getContentFlag() {
+ return FLAG_CONTENT_VIEW_CONTRACTED;
+ }
+
+ // The following are only implemented by HeadsUpManagerPhone. If you need them, use that.
+ @Override
+ public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addSwipedOutNotification(@NonNull String key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void extendHeadsUp() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public Region getTouchableRegion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isHeadsUpGoingAway() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void onExpandingFinished() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
+ boolean animate) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setAnimationStateHandler(@NonNull AnimationStateHandler handler) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setRemoteInputActive(@NonNull NotificationEntry entry,
+ boolean remoteInputActive) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setTrackingHeadsUp(boolean tracking) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean shouldSwallowClick(@NonNull String key) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/packages/SystemUI/res/drawable/bg_shutdown_finder_message.xml b/packages/SystemUI/res/drawable/bg_shutdown_finder_message.xml
new file mode 100644
index 000000000000..324ae0c5c1d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bg_shutdown_finder_message.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:radius="28dp" />
+ <solid android:color="@color/global_actions_lite_button_background" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_finder_active.xml b/packages/SystemUI/res/drawable/ic_finder_active.xml
new file mode 100644
index 000000000000..8ca221ab7392
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_finder_active.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,0L12,0A12,12 0,0 1,24 12L24,12A12,12 0,0 1,12 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z"
+ android:fillColor="#00677D"/>
+ <path
+ android:pathData="M12.797,4.005C11.949,3.936 11.203,4.597 11.203,5.467V6.659C8.855,7.001 6.998,8.856 6.653,11.203H5.467C4.597,11.203 3.936,11.948 4.005,12.796L4.006,12.802L4.006,12.809C4.38,16.605 7.399,19.625 11.195,20C12.051,20.087 12.803,19.404 12.803,18.547V17.355C15.154,17.012 17.013,15.154 17.355,12.803H18.54C19.406,12.803 20.079,12.058 19.992,11.196C19.618,7.4 16.606,4.388 12.812,4.006L12.804,4.006L12.797,4.005ZM11.203,9.344V8.283C9.741,8.591 8.588,9.741 8.278,11.203H9.344C9.585,10.4 10.179,9.754 10.942,9.437C11.027,9.402 11.114,9.371 11.203,9.344ZM11.998,13.171C11.358,13.175 10.828,12.651 10.827,12.004H10.827C10.827,11.959 10.83,11.915 10.835,11.871C10.885,11.427 11.185,11.056 11.59,10.902C11.694,10.863 11.806,10.838 11.921,10.83C11.948,10.833 11.976,10.834 12.003,10.834C12.65,10.834 13.177,11.356 13.179,12.007C13.177,12.622 12.695,13.13 12.091,13.175C12.06,13.172 12.029,13.17 11.998,13.171ZM17.353,11.203H18.383C18.028,8.289 15.72,5.979 12.804,5.616V6.658C15.153,7 17.004,8.852 17.353,11.203ZM14.663,11.203C14.395,10.311 13.692,9.611 12.804,9.344V8.283C14.265,8.59 15.414,9.736 15.727,11.203H14.663ZM5.615,12.803H6.654C7.001,15.15 8.855,17.002 11.203,17.346V18.391C8.287,18.034 5.972,15.719 5.615,12.803ZM11.203,14.666C10.316,14.394 9.613,13.692 9.345,12.803H8.279C8.591,14.264 9.741,15.412 11.203,15.721V14.666ZM14.661,12.811H15.729C15.418,14.272 14.266,15.422 12.804,15.73V14.662C13.689,14.396 14.391,13.699 14.661,12.811Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/shutdown_dialog_finder_active.xml b/packages/SystemUI/res/layout/shutdown_dialog_finder_active.xml
new file mode 100644
index 000000000000..b6db7fc8007f
--- /dev/null
+++ b/packages/SystemUI/res/layout/shutdown_dialog_finder_active.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="24dp"
+ android:fontFamily="google-sans"
+ android:gravity="center"
+ android:text="@string/shutdown_progress"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textDirection="locale"
+ android:textSize="18sp"
+ android:visibility="gone"
+ app:layout_constraintBottom_toTopOf="@android:id/text2"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.375"
+ app:layout_constraintVertical_chainStyle="packed" />
+
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="24dp"
+ android:fontFamily="google-sans"
+ android:gravity="center"
+ android:text="@string/shutdown_progress"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textDirection="locale"
+ android:textSize="24sp"
+ app:layout_constraintBottom_toTopOf="@android:id/progress"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@android:id/text1" />
+
+ <ProgressBar
+ android:id="@android:id/progress"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ android:importantForAccessibility="no"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@android:id/text2" />
+
+ <TextView
+ android:id="@+id/finer_hint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="32dp"
+ android:background="@drawable/bg_shutdown_finder_message"
+ android:drawablePadding="16dp"
+ android:drawableStart="@drawable/ic_finder_active"
+ android:fontFamily="google-sans"
+ android:gravity="start"
+ android:padding="20dp"
+ android:text="@string/finder_active"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@android:color/secondary_text_dark"
+ android:textDirection="locale"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@android:id/progress"
+ app:layout_constraintVertical_bias="1" />
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4263d9402d66..ea0e3092a781 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2236,6 +2236,11 @@
<!-- Tuner string -->
<!-- Tuner string -->
+ <!-- Message shown during shutdown when Find My Device with Dead Battery Finder is active [CHAR LIMIT=300] -->
+ <string name="finder_active">You can locate this phone with Find My Device even when powered off</string>
+ <!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. [CHAR LIMIT=60] -->
+ <string name="shutdown_progress">Shutting down\u2026</string>
+
<!-- Text help link for care instructions for overheating devices [CHAR LIMIT=40] -->
<string name="thermal_shutdown_dialog_help_text">See care steps</string>
<!-- URL for care instructions for overheating devices -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 10393cf83247..ca63483f656a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -196,7 +196,7 @@ public class ActivityManagerWrapper {
final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
boolean result = startRecentsActivity(intent, eventTime, animationHandler);
- if (resultCallback != null) {
+ if (resultCallback != null && resultCallbackHandler != null) {
resultCallbackHandler.post(new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
index ae1539ebaf89..59b59bf1e437 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
@@ -50,6 +50,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
@@ -64,13 +65,16 @@ interface FacePropertyRepository {
/** The current face sensor location in current device rotation */
val sensorLocation: StateFlow<Point?>
+
+ /** The info of current available camera. */
+ val cameraInfo: StateFlow<CameraInfo?>
}
/** Describes a biometric sensor */
data class FaceSensorInfo(val id: Int, val strength: SensorStrength)
/** Data class for camera info */
-private data class CameraInfo(
+data class CameraInfo(
/** The logical id of the camera */
val cameraId: String,
/** The physical id of the camera */
@@ -124,7 +128,7 @@ constructor(
private val cameraInfoList: List<CameraInfo> = loadCameraInfoList()
private var currentPhysicalCameraId: String? = null
- private val defaultSensorLocation: StateFlow<Point?> =
+ override val cameraInfo: StateFlow<CameraInfo?> =
ConflatedCallbackFlow.conflatedCallbackFlow {
val callback =
object : CameraManager.AvailabilityCallback() {
@@ -142,7 +146,7 @@ constructor(
physicalCameraId == it.cameraPhysicalId
}
trySendWithFailureLogging(
- cameraInfo?.cameraLocation,
+ cameraInfo,
TAG,
"Update face sensor location to $cameraInfo."
)
@@ -168,7 +172,7 @@ constructor(
}
currentPhysicalCameraId = cameraInfo?.cameraPhysicalId
trySendWithFailureLogging(
- cameraInfo?.cameraLocation,
+ cameraInfo,
TAG,
"Update face sensor location to $cameraInfo."
)
@@ -181,8 +185,16 @@ constructor(
.stateIn(
applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue =
- if (cameraInfoList.isNotEmpty()) cameraInfoList[0].cameraLocation else null
+ initialValue = if (cameraInfoList.isNotEmpty()) cameraInfoList[0] else null
+ )
+
+ private val defaultSensorLocation: StateFlow<Point?> =
+ cameraInfo
+ .map { it?.cameraLocation }
+ .stateIn(
+ applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = null
)
override val sensorLocation: StateFlow<Point?> =
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
index 080dbedcb350..93e2b37cfe87 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
@@ -21,6 +21,7 @@ import android.appwidget.AppWidgetProviderInfo
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
import android.content.ComponentName
+import android.os.Bundle
import android.os.UserHandle
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
@@ -56,6 +57,7 @@ constructor(
return widgetInfo.configure != null && !configurationOptional
}
}
+
private val logger = Logger(logBuffer, TAG)
/**
@@ -84,9 +86,16 @@ constructor(
private fun bindWidget(widgetId: Int, user: UserHandle, provider: ComponentName): Boolean {
if (appWidgetManager.isPresent) {
+ val options =
+ Bundle().apply {
+ putInt(
+ AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+ )
+ }
return appWidgetManager
.get()
- .bindAppWidgetIdIfAllowed(widgetId, user, provider, /* options */ null)
+ .bindAppWidgetIdIfAllowed(widgetId, user, provider, options)
}
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 6d9994fb2205..ce24259bbc1e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -71,6 +71,7 @@ import android.media.MediaRouter2Manager;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.MediaProjectionManager;
import android.media.session.MediaSessionManager;
+import android.nearby.NearbyManager;
import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
@@ -441,6 +442,12 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ static NearbyManager provideNearbyManager(Context context) {
+ return context.getSystemService(NearbyManager.class);
+ }
+
+ @Provides
+ @Singleton
static NetworkScoreManager provideNetworkScoreManager(Context context) {
return context.getSystemService(NetworkScoreManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index a90980fddfb0..a431a59fcef6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -16,7 +16,6 @@
package com.android.systemui.dagger;
-import com.android.systemui.globalactions.ShutdownUiModule;
import com.android.systemui.keyguard.CustomizationProvider;
import com.android.systemui.statusbar.NotificationInsetsModule;
import com.android.systemui.statusbar.QsFrameTranslateModule;
@@ -32,7 +31,6 @@ import dagger.Subcomponent;
DependencyProvider.class,
NotificationInsetsModule.class,
QsFrameTranslateModule.class,
- ShutdownUiModule.class,
SystemUIBinder.class,
SystemUIModule.class,
SystemUICoreStartableModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index cf91e147d1b3..0c9fbc27cc5d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -189,6 +189,18 @@ constructor(
}
}
.launchIn(applicationScope)
+
+ facePropertyRepository.cameraInfo
+ .onEach {
+ if (it != null && isRunning()) {
+ repository.cancel()
+ runFaceAuth(
+ FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED,
+ fallbackToDetect = true
+ )
+ }
+ }
+ .launchIn(applicationScope)
}
private suspend fun resetLockedOutState(currentUserId: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
index ee220d5fb713..08a6166fe557 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
@@ -31,6 +31,7 @@ import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALTERNATE
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.BIOMETRIC_ENABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_AVAILABLE_CHANGED
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_LAUNCHED
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DISPLAY_OFF
@@ -130,6 +131,7 @@ private object InternalFaceAuthReasons {
"Face auth stopped because non strong biometric allowed changed"
const val POSTURE_CHANGED = "Face auth started/stopped due to device posture changed."
const val DISPLAY_OFF = "Face auth stopped due to display state OFF."
+ const val CAMERA_AVAILABLE_CHANGED = "Face auth started due to the available camera changed"
}
/**
@@ -221,7 +223,9 @@ constructor(private val id: Int, val reason: String, var extraInfo: Int = 0) :
@UiEvent(doc = NON_STRONG_BIOMETRIC_ALLOWED_CHANGED)
FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED(1256, NON_STRONG_BIOMETRIC_ALLOWED_CHANGED),
@UiEvent(doc = ACCESSIBILITY_ACTION) FACE_AUTH_ACCESSIBILITY_ACTION(1454, ACCESSIBILITY_ACTION),
- @UiEvent(doc = DISPLAY_OFF) FACE_AUTH_DISPLAY_OFF(1461, DISPLAY_OFF);
+ @UiEvent(doc = DISPLAY_OFF) FACE_AUTH_DISPLAY_OFF(1461, DISPLAY_OFF),
+ @UiEvent(doc = CAMERA_AVAILABLE_CHANGED)
+ FACE_AUTH_CAMERA_AVAILABLE_CHANGED(1623, CAMERA_AVAILABLE_CHANGED);
override fun getId(): Int = this.id
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
index 51978ece14db..ccd69ca55f0c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
@@ -22,7 +22,10 @@ import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.Dialog;
import android.content.Context;
+import android.nearby.NearbyManager;
+import android.net.platform.flags.Flags;
import android.os.PowerManager;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -38,6 +41,8 @@ import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.ScrimController;
+import javax.inject.Inject;
+
/**
* Provides the UI shown during system shutdown.
*/
@@ -45,9 +50,13 @@ public class ShutdownUi {
private Context mContext;
private BlurUtils mBlurUtils;
- public ShutdownUi(Context context, BlurUtils blurUtils) {
+ private NearbyManager mNearbyManager;
+
+ @Inject
+ public ShutdownUi(Context context, BlurUtils blurUtils, NearbyManager nearbyManager) {
mContext = context;
mBlurUtils = blurUtils;
+ mNearbyManager = nearbyManager;
}
/**
@@ -132,12 +141,28 @@ public class ShutdownUi {
/**
* Returns the layout resource to use for UI while shutting down.
* @param isReboot Whether this is a reboot or a shutdown.
- * @return
*/
- public int getShutdownDialogContent(boolean isReboot) {
- return R.layout.shutdown_dialog;
+ @VisibleForTesting int getShutdownDialogContent(boolean isReboot) {
+ if (!Flags.poweredOffFindingPlatform()) {
+ return R.layout.shutdown_dialog;
+ }
+ int finderActive = mNearbyManager.getPoweredOffFindingMode();
+ if (finderActive == NearbyManager.POWERED_OFF_FINDING_MODE_DISABLED
+ || finderActive == NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) {
+ // inactive or unsupported, use regular shutdown dialog
+ return R.layout.shutdown_dialog;
+ } else if (finderActive == NearbyManager.POWERED_OFF_FINDING_MODE_ENABLED) {
+ // active, use dialog with finder info if shutting down
+ return isReboot ? R.layout.shutdown_dialog :
+ com.android.systemui.res.R.layout.shutdown_dialog_finder_active;
+ } else {
+ // that's weird? default to regular dialog
+ Log.w("ShutdownUi", "Unexpected value for finder active: " + finderActive);
+ return R.layout.shutdown_dialog;
+ }
}
+
@StringRes
@VisibleForTesting int getRebootMessage(boolean isReboot, @Nullable String reason) {
if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUiModule.kt b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUiModule.kt
deleted file mode 100644
index b7285da49bb7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUiModule.kt
+++ /dev/null
@@ -1,31 +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.systemui.globalactions
-
-import android.content.Context
-import com.android.systemui.statusbar.BlurUtils
-import dagger.Module
-import dagger.Provides
-
-/** Provides the UI shown during system shutdown. */
-@Module
-class ShutdownUiModule {
- /** Shutdown UI provider. */
- @Provides
- fun provideShutdownUi(context: Context?, blurUtils: BlurUtils?): ShutdownUi {
- return ShutdownUi(context, blurUtils)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c6b99528b2ba..6d917bbde82b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -40,6 +40,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
import static com.android.systemui.Flags.refactorGetCurrentUser;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -1488,7 +1489,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
public void userActivity() {
- mPM.userActivity(mSystemClock.uptimeMillis(), false);
+ if (notifyPowerManagerUserActivityBackground()) {
+ mUiBgExecutor.execute(() -> mPM.userActivity(mSystemClock.uptimeMillis(), false));
+ } else {
+ mPM.userActivity(mSystemClock.uptimeMillis(), false);
+ }
}
private void setupLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index b152eea63028..2bede977c958 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -40,6 +40,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.shareIn
@@ -177,92 +178,99 @@ constructor(
// TODO(b/322555228) Remove after consolidating device entry auth messages with BP auth messages
// in BiometricStatusRepository
+ /**
+ * FingerprintAuthenticationStatus Multiple statuses may arrive in immediate sequence (ie:
+ * acquired, failed, help, error), so we use a buffer to ensure consumers receive each distinct
+ * status.
+ */
override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
- get() = conflatedCallbackFlow {
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onBiometricAuthenticated(
- userId: Int,
- biometricSourceType: BiometricSourceType,
- isStrongBiometric: Boolean,
- ) {
- sendUpdateIfFingerprint(
- biometricSourceType,
- SuccessFingerprintAuthenticationStatus(
- userId,
- isStrongBiometric,
- ),
- )
- }
+ get() =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricAuthenticated(
+ userId: Int,
+ biometricSourceType: BiometricSourceType,
+ isStrongBiometric: Boolean,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ SuccessFingerprintAuthenticationStatus(
+ userId,
+ isStrongBiometric,
+ ),
+ )
+ }
- override fun onBiometricError(
- msgId: Int,
- errString: String?,
- biometricSourceType: BiometricSourceType,
- ) {
- sendUpdateIfFingerprint(
- biometricSourceType,
- ErrorFingerprintAuthenticationStatus(
- msgId,
- errString,
- ),
- )
- }
+ override fun onBiometricError(
+ msgId: Int,
+ errString: String?,
+ biometricSourceType: BiometricSourceType,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ ErrorFingerprintAuthenticationStatus(
+ msgId,
+ errString,
+ ),
+ )
+ }
- override fun onBiometricHelp(
- msgId: Int,
- helpString: String?,
- biometricSourceType: BiometricSourceType,
- ) {
- sendUpdateIfFingerprint(
- biometricSourceType,
- HelpFingerprintAuthenticationStatus(
- msgId,
- helpString,
- ),
- )
- }
+ override fun onBiometricHelp(
+ msgId: Int,
+ helpString: String?,
+ biometricSourceType: BiometricSourceType,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ HelpFingerprintAuthenticationStatus(
+ msgId,
+ helpString,
+ ),
+ )
+ }
- override fun onBiometricAuthFailed(
- biometricSourceType: BiometricSourceType,
- ) {
- sendUpdateIfFingerprint(
- biometricSourceType,
- FailFingerprintAuthenticationStatus,
- )
- }
+ override fun onBiometricAuthFailed(
+ biometricSourceType: BiometricSourceType,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ FailFingerprintAuthenticationStatus,
+ )
+ }
- override fun onBiometricAcquired(
- biometricSourceType: BiometricSourceType,
- acquireInfo: Int,
- ) {
- sendUpdateIfFingerprint(
- biometricSourceType,
- AcquiredFingerprintAuthenticationStatus(
- AuthenticationReason.DeviceEntryAuthentication,
- acquireInfo
- ),
- )
- }
+ override fun onBiometricAcquired(
+ biometricSourceType: BiometricSourceType,
+ acquireInfo: Int,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ AcquiredFingerprintAuthenticationStatus(
+ AuthenticationReason.DeviceEntryAuthentication,
+ acquireInfo
+ ),
+ )
+ }
- private fun sendUpdateIfFingerprint(
- biometricSourceType: BiometricSourceType,
- authenticationStatus: FingerprintAuthenticationStatus
- ) {
- if (biometricSourceType != BiometricSourceType.FINGERPRINT) {
- return
- }
+ private fun sendUpdateIfFingerprint(
+ biometricSourceType: BiometricSourceType,
+ authenticationStatus: FingerprintAuthenticationStatus
+ ) {
+ if (biometricSourceType != BiometricSourceType.FINGERPRINT) {
+ return
+ }
- trySendWithFailureLogging(
- authenticationStatus,
- TAG,
- "new fingerprint authentication status"
- )
- }
+ trySendWithFailureLogging(
+ authenticationStatus,
+ TAG,
+ "new fingerprint authentication status"
+ )
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
- keyguardUpdateMonitor.registerCallback(callback)
- awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
- }
+ .buffer(capacity = 4)
override val shouldUpdateIndicatorVisibility: Flow<Boolean> =
conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
index 59288a155666..0ea53906b7f8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onStart
@@ -111,7 +110,6 @@ constructor(
awaitClose { clockRegistry.unregisterClockChangeListener(listener) }
}
.mapNotNull { it }
- .distinctUntilChanged()
override val currentClock: StateFlow<ClockController?> =
currentClockId
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 873cc847fa60..f46a207b273a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -22,6 +22,7 @@ import android.content.res.ColorStateList
import android.util.StateSet
import android.view.HapticFeedbackConstants
import android.view.View
+import androidx.core.view.isInvisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.common.ui.view.LongPressHandlingView
@@ -82,6 +83,11 @@ object DeviceEntryIconViewBinder {
// of the transition.
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
+ viewModel.isVisible.collect { isVisible ->
+ longPressHandlingView.isInvisible = !isVisible
+ }
+ }
+ launch {
viewModel.isLongPressEnabled.collect { isEnabled ->
longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index d657c24d7ae0..62810974be80 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
@@ -70,12 +71,13 @@ constructor(
// While dozing, the display can show the AOD UI; show the AOD udfps when dozing
private val useAodIconVariant: Flow<Boolean> =
- combine(isShowingAodOrDozing, deviceEntryUdfpsInteractor.isUdfpsSupported) {
- isTransitionToAodOrDozing,
- isUdfps ->
- isTransitionToAodOrDozing && isUdfps
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfspEnrolled ->
+ if (udfspEnrolled) {
+ isShowingAodOrDozing.distinctUntilChanged()
+ } else {
+ flowOf(false)
}
- .distinctUntilChanged()
+ }
private val padding: Flow<Int> =
deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { udfpsSupported ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index c9cf0c31a8fd..31e809356e02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -37,6 +37,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
@@ -206,6 +207,7 @@ constructor(
DeviceEntryIconView.IconType.LOCK
}
}
+ val isVisible: Flow<Boolean> = deviceEntryViewAlpha.map { it > 0f }.distinctUntilChanged()
val isLongPressEnabled: Flow<Boolean> =
combine(
iconType,
@@ -217,6 +219,7 @@ constructor(
DeviceEntryIconView.IconType.FINGERPRINT -> false
}
}
+
val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
combine(iconType, isLongPressEnabled) { deviceEntryStatus, longPressEnabled ->
if (longPressEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
index afe628527833..f1cade7512e2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
@@ -16,9 +16,6 @@
package com.android.systemui.mediaprojection
-import android.compat.annotation.ChangeId
-import android.compat.annotation.Disabled
-import android.compat.annotation.Overridable
import android.content.Context
import android.media.projection.IMediaProjection
import android.media.projection.IMediaProjectionManager
@@ -34,18 +31,6 @@ import android.util.Log
*/
class MediaProjectionServiceHelper {
companion object {
- /**
- * This change id ensures that users are presented with a choice of capturing a single app
- * or the entire screen when initiating a MediaProjection session, overriding the usage of
- * MediaProjectionConfig#createConfigForDefaultDisplay.
- *
- * @hide
- */
- @ChangeId
- @Overridable
- @Disabled
- const val OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION = 316897322L // buganizer id
-
private const val TAG = "MediaProjectionServiceHelper"
private val service =
IMediaProjectionManager.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 0769731bffb1..8b034b293dcb 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -16,26 +16,21 @@
package com.android.systemui.mediaprojection.permission;
-import static android.Manifest.permission.LOG_COMPAT_CHANGE;
-import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.media.projection.IMediaProjectionManager.EXTRA_PACKAGE_REUSING_GRANTED_CONSENT;
import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTED_CONSENT;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import static com.android.systemui.mediaprojection.MediaProjectionServiceHelper.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION;
import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN;
import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AlertDialog;
import android.app.StatusBarManager;
-import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -109,7 +104,6 @@ public class MediaProjectionPermissionActivity extends Activity
}
@Override
- @RequiresPermission(allOf = {READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE})
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -237,9 +231,6 @@ public class MediaProjectionPermissionActivity extends Activity
// the correct screen width when in split screen.
Context dialogContext = getApplicationContext();
if (isPartialScreenSharingEnabled()) {
- final boolean overrideDisableSingleAppOption = CompatChanges.isChangeEnabled(
- OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION,
- mPackageName, getHostUserHandle());
MediaProjectionPermissionDialogDelegate delegate =
new MediaProjectionPermissionDialogDelegate(
dialogContext,
@@ -251,7 +242,6 @@ public class MediaProjectionPermissionActivity extends Activity
},
() -> finish(RECORD_CANCEL, /* projection= */ null),
appName,
- overrideDisableSingleAppOption,
mUid,
mMediaProjectionMetricsLogger);
mDialog =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
index 9ce8070131fa..0f54e934f3cf 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
@@ -30,12 +30,11 @@ class MediaProjectionPermissionDialogDelegate(
private val onStartRecordingClicked: Consumer<MediaProjectionPermissionDialogDelegate>,
private val onCancelClicked: Runnable,
private val appName: String?,
- forceShowPartialScreenshare: Boolean,
hostUid: Int,
mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
BaseMediaProjectionPermissionDialogDelegate<AlertDialog>(
- createOptionList(context, appName, mediaProjectionConfig, forceShowPartialScreenshare),
+ createOptionList(context, appName, mediaProjectionConfig),
appName,
hostUid,
mediaProjectionMetricsLogger
@@ -66,8 +65,7 @@ class MediaProjectionPermissionDialogDelegate(
private fun createOptionList(
context: Context,
appName: String?,
- mediaProjectionConfig: MediaProjectionConfig?,
- overrideDisableSingleAppOption: Boolean = false,
+ mediaProjectionConfig: MediaProjectionConfig?
): List<ScreenShareOption> {
val singleAppWarningText =
if (appName == null) {
@@ -82,13 +80,8 @@ class MediaProjectionPermissionDialogDelegate(
R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
}
- // The single app option should only be disabled if there is an app name provided,
- // the client has setup a MediaProjection with
- // MediaProjectionConfig#createConfigForDefaultDisplay, AND it hasn't been overridden by
- // the OVERRIDE_DISABLE_SINGLE_APP_OPTION per-app override.
val singleAppOptionDisabled =
appName != null &&
- !overrideDisableSingleAppOption &&
mediaProjectionConfig?.regionToCapture ==
MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
index 1c9be0f105b2..f13ecf3e91b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.media.AudioManager
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.flags.Flags
import com.android.systemui.res.R
private val backgroundOn = R.drawable.settingslib_switch_bar_bg_on
@@ -36,6 +37,7 @@ private val actionAccessibilityLabelDisconnect =
/** Factories to create different types of Bluetooth device items from CachedBluetoothDevice. */
internal abstract class DeviceItemFactory {
abstract fun isFilterMatched(
+ context: Context,
cachedDevice: CachedBluetoothDevice,
audioManager: AudioManager?
): Boolean
@@ -45,6 +47,7 @@ internal abstract class DeviceItemFactory {
internal class ActiveMediaDeviceItemFactory : DeviceItemFactory() {
override fun isFilterMatched(
+ context: Context,
cachedDevice: CachedBluetoothDevice,
audioManager: AudioManager?
): Boolean {
@@ -71,6 +74,7 @@ internal class ActiveMediaDeviceItemFactory : DeviceItemFactory() {
internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() {
override fun isFilterMatched(
+ context: Context,
cachedDevice: CachedBluetoothDevice,
audioManager: AudioManager?
): Boolean {
@@ -99,10 +103,18 @@ internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() {
internal class ConnectedDeviceItemFactory : DeviceItemFactory() {
override fun isFilterMatched(
+ context: Context,
cachedDevice: CachedBluetoothDevice,
audioManager: AudioManager?
): Boolean {
- return BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager)
+ return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
+ !BluetoothUtils.isExclusivelyManagedBluetoothDevice(
+ context,
+ cachedDevice.getDevice()
+ ) && BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager)
+ } else {
+ BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager)
+ }
}
override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
@@ -125,10 +137,18 @@ internal class ConnectedDeviceItemFactory : DeviceItemFactory() {
internal class SavedDeviceItemFactory : DeviceItemFactory() {
override fun isFilterMatched(
+ context: Context,
cachedDevice: CachedBluetoothDevice,
audioManager: AudioManager?
): Boolean {
- return cachedDevice.bondState == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected
+ return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
+ !BluetoothUtils.isExclusivelyManagedBluetoothDevice(
+ context,
+ cachedDevice.getDevice()
+ ) && cachedDevice.bondState == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected
+ } else {
+ cachedDevice.bondState == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected
+ }
}
override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
index fcd45a6431bb..1df496b17aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
@@ -133,7 +133,7 @@ constructor(
bluetoothTileDialogRepository.cachedDevices
.mapNotNull { cachedDevice ->
deviceItemFactoryList
- .firstOrNull { it.isFilterMatched(cachedDevice, audioManager) }
+ .firstOrNull { it.isFilterMatched(context, cachedDevice, audioManager) }
?.create(context, cachedDevice)
}
.sort(displayPriority, bluetoothAdapter?.mostRecentlyConnectedDevices)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7068f5fcc32b..d5bbaa5be53c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1539,6 +1539,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@Override
public void setOpenCloseListener(OpenCloseListener openCloseListener) {
+ SceneContainerFlag.assertInLegacyMode();
mOpenCloseListener = openCloseListener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index ea419127d7c1..e6555f2e0993 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -32,6 +32,7 @@ import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.dagger.ShadeTouchLog;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
@@ -98,6 +99,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
statusBarKeyguardViewManager,
notificationShadeWindowController,
assistManagerLazy);
+ SceneContainerFlag.assertInLegacyMode();
mCommandQueue = commandQueue;
mMainExecutor = mainExecutor;
mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 599600d61976..0fd05550dbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -39,7 +39,6 @@ import android.view.ViewGroup
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -54,6 +53,7 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.regionsampling.RegionSampler
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DATE_SMARTSPACE_DATA_PLUGIN
@@ -68,11 +68,14 @@ import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import java.time.Instant
+import java.util.Deque
+import java.util.LinkedList
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Named
+
/** Controller for managing the smartspace view on the lockscreen */
@SysUISingleton
class LockscreenSmartspaceController
@@ -106,6 +109,8 @@ constructor(
) : Dumpable {
companion object {
private const val TAG = "LockscreenSmartspaceController"
+
+ private const val MAX_RECENT_SMARTSPACE_DATA_FOR_DUMP = 5
}
private var session: SmartspaceSession? = null
@@ -114,6 +119,9 @@ constructor(
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
private val configPlugin: BcSmartspaceConfigPlugin? = optionalConfigPlugin.orElse(null)
+ // This stores recently received Smartspace pushes to be included in dumpsys.
+ private val recentSmartspaceData: Deque<List<SmartspaceTarget>> = LinkedList()
+
// Smartspace can be used on multiple displays, such as when the user casts their screen
private var smartspaceViews = mutableSetOf<SmartspaceView>()
private var regionSamplers =
@@ -173,6 +181,7 @@ constructor(
// The weather data plugin takes unfiltered targets and performs the filtering internally.
weatherPlugin?.onTargetsAvailable(targets)
+
val now = Instant.ofEpochMilli(systemClock.currentTimeMillis())
val weatherTarget = targets.find { t ->
t.featureType == SmartspaceTarget.FEATURE_WEATHER &&
@@ -201,6 +210,14 @@ constructor(
}
val filteredTargets = targets.filter(::filterSmartspaceTarget)
+
+ synchronized(recentSmartspaceData) {
+ recentSmartspaceData.offerLast(filteredTargets)
+ if (recentSmartspaceData.size > MAX_RECENT_SMARTSPACE_DATA_FOR_DUMP) {
+ recentSmartspaceData.pollFirst()
+ }
+ }
+
plugin?.onTargetsAvailable(filteredTargets)
}
@@ -588,9 +605,26 @@ constructor(
return null
}
- override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run {
- printCollection("Region Samplers", regionSamplers.values) {
- it.dump(this)
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.asIndenting().run {
+ printCollection("Region Samplers", regionSamplers.values) {
+ it.dump(this)
+ }
+ }
+
+ pw.println("Recent BC Smartspace Targets (most recent first)")
+ synchronized(recentSmartspaceData) {
+ if (recentSmartspaceData.size === 0) {
+ pw.println(" No data\n")
+ return
+ }
+ recentSmartspaceData.descendingIterator().forEachRemaining { smartspaceTargets ->
+ pw.println(" Number of targets: ${smartspaceTargets.size}")
+ for (target in smartspaceTargets) {
+ pw.println(" $target")
+ }
+ pw.println()
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 9052409b4d8d..a1fae03a2090 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -39,11 +39,11 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityTransitionAnimator;
+import com.android.systemui.animation.RemoteAnimationRunnerCompat;
import com.android.systemui.display.data.repository.DisplayMetricsRepository;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Compile;
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 b07ba3c5b326..a155e94584e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -67,7 +67,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp
private static final String TAG = "HeadsUpManagerPhone";
@VisibleForTesting
- final int mExtensionTime;
+ public final int mExtensionTime;
private final KeyguardBypassController mBypassController;
private final GroupMembershipManager mGroupMembershipManager;
private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 5a8b636e54fc..d975009017e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.AlarmManager.AlarmClockInfo;
+import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -379,7 +380,7 @@ public class PhoneStatusBarPolicy
}
@Override
- public void onConfigChanged(ZenModeConfig config) {
+ public void onConsolidatedPolicyChanged(NotificationManager.Policy policy) {
updateVolumeZen();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
index 9f24d5dfea79..f5e96c93271a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
@@ -237,6 +237,35 @@ class FacePropertyRepositoryImplTest : SysuiTestCase() {
}
}
+ @Test
+ fun providesTheCameraInfoOnCameraAvailableChange() {
+ testScope.runTest {
+ runCurrent()
+ collectLastValue(underTest.cameraInfo)
+
+ verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
+ callback.value.onAllAuthenticatorsRegistered(
+ listOf(createSensorProperties(1, SensorProperties.STRENGTH_STRONG))
+ )
+ runCurrent()
+ verify(cameraManager)
+ .registerAvailabilityCallback(any(Executor::class.java), cameraCallback.capture())
+
+ cameraCallback.value.onPhysicalCameraAvailable("0", PHYSICAL_CAMERA_ID_OUTER_FRONT)
+ runCurrent()
+
+ val cameraInfo by collectLastValue(underTest.cameraInfo)
+ assertThat(cameraInfo)
+ .isEqualTo(
+ CameraInfo(
+ "0",
+ PHYSICAL_CAMERA_ID_OUTER_FRONT,
+ Point(OUTER_FRONT_SENSOR_LOCATION[0], OUTER_FRONT_SENSOR_LOCATION[1])
+ )
+ )
+ }
+ }
+
private fun createSensorProperties(id: Int, strength: Int) =
FaceSensorPropertiesInternal(id, strength, 0, emptyList(), 1, false, false, false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index e30dd35d74c1..e1e9fcb2d059 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest
import com.android.keyguard.keyguardUpdateMonitor
import com.android.keyguard.trustManager
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.CameraInfo
import com.android.systemui.biometrics.data.repository.FaceSensorInfo
import com.android.systemui.biometrics.data.repository.facePropertyRepository
import com.android.systemui.biometrics.shared.model.LockoutMode
@@ -490,6 +491,47 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
verify(trustManager).clearAllBiometricRecognized(eq(BiometricSourceType.FACE), anyInt())
}
+ @Test
+ fun faceAuthIsRequestedWhenAuthIsRunningWhileCameraInfoChanged() =
+ testScope.runTest {
+ facePropertyRepository.setCameraIno(null)
+ underTest.start()
+
+ faceAuthRepository.requestAuthenticate(
+ FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED,
+ true
+ )
+ facePropertyRepository.setCameraIno(CameraInfo("0", "1", null))
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED, true))
+ }
+
+ @Test
+ fun faceAuthIsNotRequestedWhenNoAuthRunningWhileCameraInfoChanged() =
+ testScope.runTest {
+ facePropertyRepository.setCameraIno(null)
+ underTest.start()
+
+ facePropertyRepository.setCameraIno(CameraInfo("0", "1", null))
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
+ }
+
+ @Test
+ fun faceAuthIsNotRequestedWhenAuthIsRunningWhileCameraInfoIsNull() =
+ testScope.runTest {
+ facePropertyRepository.setCameraIno(null)
+ underTest.start()
+
+ facePropertyRepository.setCameraIno(null)
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
+ }
+
companion object {
private const val primaryUserId = 1
private val primaryUser = UserInfo(primaryUserId, "test user", UserInfo.FLAG_PRIMARY)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
index e97edcbdc68a..9b8cf592d496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
@@ -27,7 +27,7 @@ import com.android.systemui.deviceentry.data.ui.viewmodel.deviceEntryUdfpsAccess
import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -39,6 +39,7 @@ import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.runner.RunWith
@@ -52,7 +53,7 @@ class UdfpsAccessibilityOverlayViewModelTest : SysuiTestCase() {
}
private val deviceEntryIconTransition = kosmos.fakeDeviceEntryIconViewModelTransition
private val testScope = kosmos.testScope
- private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val biometricSettingsRepository = kosmos.fakeBiometricSettingsRepository
private val accessibilityRepository = kosmos.fakeAccessibilityRepository
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
@@ -85,22 +86,12 @@ class UdfpsAccessibilityOverlayViewModelTest : SysuiTestCase() {
setupVisibleStateOnLockscreen()
// AOD
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = 0f,
- transitionState = TransitionState.STARTED,
- )
- )
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- )
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ this,
)
+ runCurrent()
assertThat(visible).isFalse()
}
fun fpNotRunning_overlayNotVisible() =
@@ -129,6 +120,7 @@ class UdfpsAccessibilityOverlayViewModelTest : SysuiTestCase() {
// Listening for UDFPS
fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
deviceEntryFingerprintAuthRepository.setIsRunning(true)
deviceEntryRepository.setUnlocked(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
index 9d9b263c5df5..2d3ca6095835 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
@@ -19,7 +19,13 @@ package com.android.systemui.globalactions;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
+import static org.mockito.Mockito.when;
+
+import android.nearby.NearbyManager;
+import android.net.platform.flags.Flags;
import android.os.PowerManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
@@ -32,6 +38,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@@ -41,10 +48,13 @@ public class ShutdownUiTest extends SysuiTestCase {
ShutdownUi mShutdownUi;
@Mock
BlurUtils mBlurUtils;
+ @Mock
+ NearbyManager mNearbyManager;
@Before
public void setUp() throws Exception {
- mShutdownUi = new ShutdownUi(getContext(), mBlurUtils);
+ MockitoAnnotations.initMocks(this);
+ mShutdownUi = new ShutdownUi(getContext(), mBlurUtils, mNearbyManager);
}
@Test
@@ -82,4 +92,53 @@ public class ShutdownUiTest extends SysuiTestCase {
String message = mShutdownUi.getReasonMessage("anything-else");
assertNull(message);
}
+
+ @EnableFlags(Flags.FLAG_POWERED_OFF_FINDING_PLATFORM)
+ @Test
+ public void getDialog_whenPowerOffFindingModeEnabled_returnsFinderDialog() {
+ when(mNearbyManager.getPoweredOffFindingMode()).thenReturn(
+ NearbyManager.POWERED_OFF_FINDING_MODE_ENABLED);
+
+ int actualLayout = mShutdownUi.getShutdownDialogContent(false);
+
+ int expectedLayout = com.android.systemui.res.R.layout.shutdown_dialog_finder_active;
+ assertEquals(actualLayout, expectedLayout);
+ }
+
+ @DisableFlags(Flags.FLAG_POWERED_OFF_FINDING_PLATFORM)
+ @Test
+ public void getDialog_whenPowerOffFindingModeEnabledFlagDisabled_returnsFinderDialog() {
+ when(mNearbyManager.getPoweredOffFindingMode()).thenReturn(
+ NearbyManager.POWERED_OFF_FINDING_MODE_ENABLED);
+
+ int actualLayout = mShutdownUi.getShutdownDialogContent(false);
+
+ int expectedLayout = R.layout.shutdown_dialog;
+ assertEquals(actualLayout, expectedLayout);
+ }
+
+ @EnableFlags(Flags.FLAG_POWERED_OFF_FINDING_PLATFORM)
+ @Test
+ public void getDialog_whenPowerOffFindingModeDisabled_returnsDefaultDialog() {
+ when(mNearbyManager.getPoweredOffFindingMode()).thenReturn(
+ NearbyManager.POWERED_OFF_FINDING_MODE_DISABLED);
+
+ int actualLayout = mShutdownUi.getShutdownDialogContent(false);
+
+ int expectedLayout = R.layout.shutdown_dialog;
+ assertEquals(actualLayout, expectedLayout);
+ }
+
+ @EnableFlags(Flags.FLAG_POWERED_OFF_FINDING_PLATFORM)
+ @Test
+ public void getDialog_whenPowerOffFindingModeEnabledAndIsReboot_returnsDefaultDialog() {
+ when(mNearbyManager.getPoweredOffFindingMode()).thenReturn(
+ NearbyManager.POWERED_OFF_FINDING_MODE_ENABLED);
+
+ int actualLayout = mShutdownUi.getShutdownDialogContent(true);
+
+ int expectedLayout = R.layout.shutdown_dialog;
+ assertEquals(actualLayout, expectedLayout);
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
new file mode 100644
index 000000000000..02bd810a43d5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryIconViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var fingerprintAuthRepository: FakeDeviceEntryFingerprintAuthRepository
+ private lateinit var deviceEntryIconTransition: FakeDeviceEntryIconTransition
+ private lateinit var underTest: DeviceEntryIconViewModel
+
+ @Before
+ fun setUp() {
+ keyguardRepository = kosmos.fakeKeyguardRepository
+ fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
+ fingerprintAuthRepository = kosmos.fakeDeviceEntryFingerprintAuthRepository
+ deviceEntryIconTransition = kosmos.fakeDeviceEntryIconViewModelTransition
+ underTest = kosmos.deviceEntryIconViewModel
+ }
+
+ @Test
+ fun isLongPressEnabled_udfpsRunning() =
+ testScope.runTest {
+ val isLongPressEnabled by collectLastValue(underTest.isLongPressEnabled)
+ fingerprintPropertyRepository.supportsUdfps()
+ fingerprintAuthRepository.setIsRunning(true)
+ keyguardRepository.setKeyguardDismissible(false)
+ assertThat(isLongPressEnabled).isFalse()
+ }
+
+ @Test
+ fun isLongPressEnabled_unlocked() =
+ testScope.runTest {
+ val isLongPressEnabled by collectLastValue(underTest.isLongPressEnabled)
+ keyguardRepository.setKeyguardDismissible(true)
+ assertThat(isLongPressEnabled).isTrue()
+ }
+
+ @Test
+ fun isLongPressEnabled_lock() =
+ testScope.runTest {
+ val isLongPressEnabled by collectLastValue(underTest.isLongPressEnabled)
+ keyguardRepository.setKeyguardDismissible(false)
+
+ // udfps supported
+ fingerprintPropertyRepository.supportsUdfps()
+ assertThat(isLongPressEnabled).isTrue()
+
+ // udfps isn't supported
+ fingerprintPropertyRepository.supportsRearFps()
+ assertThat(isLongPressEnabled).isFalse()
+ }
+
+ @Test
+ fun isVisible() =
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ deviceEntryIconTransitionAlpha(1f)
+ assertThat(isVisible).isTrue()
+
+ deviceEntryIconTransitionAlpha(0f)
+ assertThat(isVisible).isFalse()
+
+ deviceEntryIconTransitionAlpha(.5f)
+ assertThat(isVisible).isTrue()
+ }
+
+ private fun deviceEntryIconTransitionAlpha(alpha: Float) {
+ deviceEntryIconTransition.setDeviceEntryParentViewAlpha(alpha)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
deleted file mode 100644
index e044eeca8303..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
+++ /dev/null
@@ -1,141 +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.mediaprojection.permission
-
-import android.app.AlertDialog
-import android.media.projection.MediaProjectionConfig
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.WindowManager
-import android.widget.Spinner
-import android.widget.TextView
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
-import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.phone.AlertDialogWithDelegate
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.mockito.mock
-import junit.framework.Assert.assertEquals
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() {
-
- private lateinit var dialog: AlertDialog
-
- private val flags = mock<FeatureFlagsClassic>()
- private val onStartRecordingClicked = mock<Runnable>()
- private val mediaProjectionMetricsLogger = mock<MediaProjectionMetricsLogger>()
-
- private val mediaProjectionConfig: MediaProjectionConfig =
- MediaProjectionConfig.createConfigForDefaultDisplay()
- private val appName: String = "testApp"
- private val hostUid: Int = 12345
-
- private val resIdSingleApp = R.string.screen_share_permission_dialog_option_single_app
- private val resIdFullScreen = R.string.screen_share_permission_dialog_option_entire_screen
- private val resIdSingleAppDisabled =
- R.string.media_projection_entry_app_permission_dialog_single_app_disabled
-
- @Before
- fun setUp() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
- }
-
- @After
- fun teardown() {
- if (::dialog.isInitialized) {
- dialog.dismiss()
- }
- }
-
- @Test
- fun showDialog_forceShowPartialScreenShareFalse() {
- // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and
- // overrideDisableSingleAppOption = false
- val overrideDisableSingleAppOption = false
- setUpAndShowDialog(overrideDisableSingleAppOption)
-
- val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
- val secondOptionText =
- spinner.adapter
- .getDropDownView(1, null, spinner)
- .findViewById<TextView>(android.R.id.text2)
- ?.text
-
- // check that the first option is full screen and enabled
- assertEquals(context.getString(resIdFullScreen), spinner.selectedItem)
-
- // check that the second option is single app and disabled
- assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionText)
- }
-
- @Test
- fun showDialog_forceShowPartialScreenShareTrue() {
- // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and
- // overrideDisableSingleAppOption = true
- val overrideDisableSingleAppOption = true
- setUpAndShowDialog(overrideDisableSingleAppOption)
-
- val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
- val secondOptionText =
- spinner.adapter
- .getDropDownView(1, null, spinner)
- .findViewById<TextView>(android.R.id.text1)
- ?.text
-
- // check that the first option is single app and enabled
- assertEquals(context.getString(resIdSingleApp), spinner.selectedItem)
-
- // check that the second option is full screen and enabled
- assertEquals(context.getString(resIdFullScreen), secondOptionText)
- }
-
- private fun setUpAndShowDialog(overrideDisableSingleAppOption: Boolean) {
- val delegate =
- MediaProjectionPermissionDialogDelegate(
- context,
- mediaProjectionConfig,
- {},
- onStartRecordingClicked,
- appName,
- overrideDisableSingleAppOption,
- hostUid,
- mediaProjectionMetricsLogger
- )
-
- dialog = AlertDialogWithDelegate(context, R.style.Theme_SystemUI_Dialog, delegate)
- SystemUIDialog.applyFlags(dialog)
- SystemUIDialog.setDialogSize(dialog)
-
- dialog.window?.addSystemFlags(
- WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
- )
-
- delegate.onCreate(dialog, savedInstanceState = null)
- dialog.show()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt
index 92c73261a95e..a8cd8c801a95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt
@@ -16,10 +16,18 @@
package com.android.systemui.qs.tiles.dialog.bluetooth
+import android.bluetooth.BluetoothDevice
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.media.AudioManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.flags.Flags
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -35,19 +43,26 @@ import org.mockito.junit.MockitoRule
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class DeviceItemFactoryTest : SysuiTestCase() {
-
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var cachedDevice: CachedBluetoothDevice
+ @Mock private lateinit var bluetoothDevice: BluetoothDevice
+ @Mock private lateinit var packageManager: PackageManager
private val availableMediaDeviceItemFactory = AvailableMediaDeviceItemFactory()
private val connectedDeviceItemFactory = ConnectedDeviceItemFactory()
private val savedDeviceItemFactory = SavedDeviceItemFactory()
+ private val audioManager = context.getSystemService(AudioManager::class.java)!!
+
@Before
fun setup() {
`when`(cachedDevice.name).thenReturn(DEVICE_NAME)
+ `when`(cachedDevice.address).thenReturn(DEVICE_ADDRESS)
+ `when`(cachedDevice.device).thenReturn(bluetoothDevice)
`when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
+
+ context.setMockPackageManager(packageManager)
}
@Test
@@ -72,6 +87,225 @@ class DeviceItemFactoryTest : SysuiTestCase() {
assertThat(deviceItem.background).isNotNull()
}
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testSavedFactory_isFilterMatched_bondedAndNotConnected_returnsTrue() {
+ `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(cachedDevice.isConnected).thenReturn(false)
+
+ assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isTrue()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testSavedFactory_isFilterMatched_connected_returnsFalse() {
+ `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(cachedDevice.isConnected).thenReturn(true)
+
+ assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testSavedFactory_isFilterMatched_notBonded_returnsFalse() {
+ `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
+ `when`(cachedDevice.isConnected).thenReturn(false)
+
+ assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testSavedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
+ val exclusiveManagerName =
+ BluetoothUtils.getExclusiveManagers().firstOrNull() ?: FAKE_EXCLUSIVE_MANAGER_NAME
+ `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+ .thenReturn(exclusiveManagerName.toByteArray())
+ `when`(packageManager.getPackageInfo(exclusiveManagerName, 0)).thenReturn(PackageInfo())
+ `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(cachedDevice.isConnected).thenReturn(false)
+
+ assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testSavedFactory_isFilterMatched_noExclusiveManager_returnsTrue() {
+ `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(cachedDevice.isConnected).thenReturn(false)
+
+ assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testSavedFactory_isFilterMatched_notAllowedExclusiveManager_returnsTrue() {
+ `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+ .thenReturn(FAKE_EXCLUSIVE_MANAGER_NAME.toByteArray())
+ `when`(packageManager.getPackageInfo(FAKE_EXCLUSIVE_MANAGER_NAME, 0))
+ .thenReturn(PackageInfo())
+ `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(cachedDevice.isConnected).thenReturn(false)
+
+ assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testSavedFactory_isFilterMatched_uninstalledExclusiveManager_returnsTrue() {
+ val exclusiveManagerName =
+ BluetoothUtils.getExclusiveManagers().firstOrNull() ?: FAKE_EXCLUSIVE_MANAGER_NAME
+ `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+ .thenReturn(exclusiveManagerName.toByteArray())
+ `when`(packageManager.getPackageInfo(exclusiveManagerName, 0))
+ .thenThrow(PackageManager.NameNotFoundException("Test!"))
+ `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(cachedDevice.isConnected).thenReturn(false)
+
+ assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testSavedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalse() {
+ `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
+ `when`(cachedDevice.isConnected).thenReturn(false)
+
+ assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testSavedFactory_isFilterMatched_notExclusivelyManaged_connected_returnsFalse() {
+ `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(cachedDevice.isConnected).thenReturn(true)
+
+ assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTrue() {
+ `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(bluetoothDevice.isConnected).thenReturn(true)
+ audioManager.setMode(AudioManager.MODE_NORMAL)
+
+ assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isTrue()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testConnectedFactory_isFilterMatched_notConnected_returnsFalse() {
+ `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(bluetoothDevice.isConnected).thenReturn(false)
+ audioManager.setMode(AudioManager.MODE_NORMAL)
+
+ assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testConnectedFactory_isFilterMatched_notBonded_returnsFalse() {
+ `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
+ `when`(bluetoothDevice.isConnected).thenReturn(true)
+ audioManager.setMode(AudioManager.MODE_NORMAL)
+
+ assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testConnectedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
+ val exclusiveManagerName =
+ BluetoothUtils.getExclusiveManagers().firstOrNull() ?: FAKE_EXCLUSIVE_MANAGER_NAME
+ `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+ .thenReturn(exclusiveManagerName.toByteArray())
+ `when`(packageManager.getPackageInfo(exclusiveManagerName, 0)).thenReturn(PackageInfo())
+ `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(bluetoothDevice.isConnected).thenReturn(true)
+ audioManager.setMode(AudioManager.MODE_NORMAL)
+
+ assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testConnectedFactory_isFilterMatched_noExclusiveManager_returnsTrue() {
+ `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(bluetoothDevice.isConnected).thenReturn(true)
+ audioManager.setMode(AudioManager.MODE_NORMAL)
+
+ assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testConnectedFactory_isFilterMatched_notAllowedExclusiveManager_returnsTrue() {
+ `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+ .thenReturn(FAKE_EXCLUSIVE_MANAGER_NAME.toByteArray())
+ `when`(packageManager.getPackageInfo(FAKE_EXCLUSIVE_MANAGER_NAME, 0))
+ .thenReturn(PackageInfo())
+ `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(bluetoothDevice.isConnected).thenReturn(true)
+ audioManager.setMode(AudioManager.MODE_NORMAL)
+
+ assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testConnectedFactory_isFilterMatched_uninstalledExclusiveManager_returnsTrue() {
+ val exclusiveManagerName =
+ BluetoothUtils.getExclusiveManagers().firstOrNull() ?: FAKE_EXCLUSIVE_MANAGER_NAME
+ `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+ .thenReturn(exclusiveManagerName.toByteArray())
+ `when`(packageManager.getPackageInfo(exclusiveManagerName, 0))
+ .thenThrow(PackageManager.NameNotFoundException("Test!"))
+ `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(bluetoothDevice.isConnected).thenReturn(true)
+ audioManager.setMode(AudioManager.MODE_NORMAL)
+
+ assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalse() {
+ `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
+ `when`(bluetoothDevice.isConnected).thenReturn(true)
+ audioManager.setMode(AudioManager.MODE_NORMAL)
+
+ assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+ fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notConnected_returnsFalse() {
+ `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
+ `when`(bluetoothDevice.isConnected).thenReturn(false)
+ audioManager.setMode(AudioManager.MODE_NORMAL)
+
+ assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
+ .isFalse()
+ }
+
private fun assertDeviceItem(deviceItem: DeviceItem?, deviceItemType: DeviceItemType) {
assertThat(deviceItem).isNotNull()
assertThat(deviceItem!!.type).isEqualTo(deviceItemType)
@@ -83,5 +317,7 @@ class DeviceItemFactoryTest : SysuiTestCase() {
companion object {
const val DEVICE_NAME = "DeviceName"
const val CONNECTION_SUMMARY = "ConnectionSummary"
+ private const val FAKE_EXCLUSIVE_MANAGER_NAME = "com.fake.name"
+ private const val DEVICE_ADDRESS = "04:52:C7:0B:D8:3C"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
index e236f4a7730f..ddf0b9a78165 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
@@ -279,6 +279,7 @@ class DeviceItemInteractorTest : SysuiTestCase() {
): DeviceItemFactory {
return object : DeviceItemFactory() {
override fun isFilterMatched(
+ context: Context,
cachedDevice: CachedBluetoothDevice,
audioManager: AudioManager?
) = isFilterMatchFunc(cachedDevice)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
index 460892af1154..660e8da72bf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
@@ -49,6 +49,7 @@ import android.window.TransitionInfo;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.RemoteAnimationTargetCompat;
import com.android.wm.shell.shared.TransitionUtil;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
index 77f501f550d7..68ef55573dc8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
@@ -33,6 +33,10 @@ class FakeFacePropertyRepository : FacePropertyRepository {
override val sensorLocation: StateFlow<Point?>
get() = faceSensorLocation
+ private val currentCameraInfo = MutableStateFlow<CameraInfo?>(null)
+ override val cameraInfo: StateFlow<CameraInfo?>
+ get() = currentCameraInfo
+
fun setLockoutMode(userId: Int, mode: LockoutMode) {
lockoutModesForUser[userId] = mode
}
@@ -47,4 +51,8 @@ class FakeFacePropertyRepository : FacePropertyRepository {
fun setSensorLocation(value: Point?) {
faceSensorLocation.value = value
}
+
+ fun setCameraIno(value: CameraInfo?) {
+ currentCameraInfo.value = value
+ }
}
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index 6b6736476210..371c3acab144 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -55,3 +55,5 @@ class android.content.Context stub
method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; stub
class android.content.pm.PackageManager stub
method <init> ()V stub
+class android.text.ClipboardManager stub
+ method <init> ()V stub
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 3668b03e58d3..c17d0903f856 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -16,18 +16,28 @@
package android.platform.test.ravenwood;
+import android.content.ClipboardManager;
import android.content.Context;
import android.hardware.ISerialManager;
import android.hardware.SerialManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
import android.os.PermissionEnforcer;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.test.mock.MockContext;
import android.util.ArrayMap;
import android.util.Singleton;
+import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.function.Supplier;
public class RavenwoodContext extends MockContext {
+ private final String mPackageName;
+ private final HandlerThread mMainThread;
+
private final RavenwoodPermissionEnforcer mEnforcer = new RavenwoodPermissionEnforcer();
private final ArrayMap<Class<?>, String> mClassToName = new ArrayMap<>();
@@ -39,7 +49,13 @@ public class RavenwoodContext extends MockContext {
mNameToFactory.put(serviceName, serviceSupplier);
}
- public RavenwoodContext() {
+ public RavenwoodContext(String packageName, HandlerThread mainThread) {
+ mPackageName = packageName;
+ mMainThread = mainThread;
+
+ registerService(ClipboardManager.class,
+ Context.CLIPBOARD_SERVICE, asSingleton(() ->
+ new ClipboardManager(this, getMainThreadHandler())));
registerService(PermissionEnforcer.class,
Context.PERMISSION_ENFORCER_SERVICE, () -> mEnforcer);
registerService(SerialManager.class,
@@ -73,18 +89,79 @@ public class RavenwoodContext extends MockContext {
}
}
+ @Override
+ public Looper getMainLooper() {
+ Objects.requireNonNull(mMainThread,
+ "Test must request setProvideMainThread() via RavenwoodRule");
+ return mMainThread.getLooper();
+ }
+
+ @Override
+ public Handler getMainThreadHandler() {
+ Objects.requireNonNull(mMainThread,
+ "Test must request setProvideMainThread() via RavenwoodRule");
+ return mMainThread.getThreadHandler();
+ }
+
+ @Override
+ public Executor getMainExecutor() {
+ Objects.requireNonNull(mMainThread,
+ "Test must request setProvideMainThread() via RavenwoodRule");
+ return mMainThread.getThreadExecutor();
+ }
+
+ @Override
+ public String getPackageName() {
+ return Objects.requireNonNull(mPackageName,
+ "Test must request setPackageName() via RavenwoodRule");
+ }
+
+ @Override
+ public String getOpPackageName() {
+ return Objects.requireNonNull(mPackageName,
+ "Test must request setPackageName() via RavenwoodRule");
+ }
+
+ @Override
+ public String getAttributionTag() {
+ return null;
+ }
+
+ @Override
+ public UserHandle getUser() {
+ return android.os.UserHandle.of(android.os.UserHandle.myUserId());
+ }
+
+ @Override
+ public int getUserId() {
+ return android.os.UserHandle.myUserId();
+ }
+
+ @Override
+ public int getDeviceId() {
+ return Context.DEVICE_ID_DEFAULT;
+ }
+
/**
* Wrap the given {@link Supplier} to become a memoized singleton.
*/
- private static <T> Supplier<T> asSingleton(Supplier<T> supplier) {
+ private static <T> Supplier<T> asSingleton(ThrowingSupplier<T> supplier) {
final Singleton<T> singleton = new Singleton<>() {
@Override
protected T create() {
- return supplier.get();
+ try {
+ return supplier.get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
};
return () -> {
return singleton.get();
};
}
+
+ public interface ThrowingSupplier<T> {
+ T get() throws Exception;
+ }
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 231cce95f353..56a3c64a5750 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -110,13 +110,16 @@ public class RavenwoodRuleImpl {
ActivityManager.init$ravenwood(rule.mCurrentUser);
+ final HandlerThread main;
if (rule.mProvideMainThread) {
- final HandlerThread main = new HandlerThread(MAIN_THREAD_NAME);
+ main = new HandlerThread(MAIN_THREAD_NAME);
main.start();
Looper.setMainLooperForTest(main.getLooper());
+ } else {
+ main = null;
}
- rule.mContext = new RavenwoodContext();
+ rule.mContext = new RavenwoodContext(rule.mPackageName, main);
rule.mInstrumentation = new Instrumentation();
rule.mInstrumentation.basicInit(rule.mContext);
InstrumentationRegistry.registerInstance(rule.mInstrumentation, Bundle.EMPTY);
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index bb280f47ccd9..3de96c0990ea 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -16,6 +16,7 @@
package android.platform.test.ravenwood;
+import android.content.ClipboardManager;
import android.hardware.SerialManager;
import android.os.SystemClock;
import android.util.ArrayMap;
@@ -40,7 +41,10 @@ public class RavenwoodSystemServer {
// authors to exhaustively declare all transitive services
static {
- sKnownServices.put(SerialManager.class, "com.android.server.SerialService$Lifecycle");
+ sKnownServices.put(ClipboardManager.class,
+ "com.android.server.FakeClipboardService$Lifecycle");
+ sKnownServices.put(SerialManager.class,
+ "com.android.server.SerialService$Lifecycle");
}
private static TimingsTraceAndSlog sTimings;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index a8c24fcbd7e0..a520d4ccafa1 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -121,6 +121,8 @@ public class RavenwoodRule implements TestRule {
int mUid = NOBODY_UID;
int mPid = sNextPid.getAndIncrement();
+ String mPackageName;
+
boolean mProvideMainThread = false;
final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
@@ -158,6 +160,15 @@ public class RavenwoodRule implements TestRule {
}
/**
+ * Configure the identity of this process to be the given package name for the duration
+ * of the test. Has no effect on non-Ravenwood environments.
+ */
+ public Builder setPackageName(/* @NonNull */ String packageName) {
+ mRule.mPackageName = Objects.requireNonNull(packageName);
+ return this;
+ }
+
+ /**
* Configure a "main" thread to be available for the duration of the test, as defined
* by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
*/
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index eb3c55cb4ff6..9b4d378cc7b7 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -186,6 +186,7 @@ android.os.WorkSource
android.content.ClipData
android.content.ClipData$Item
android.content.ClipDescription
+android.content.ClipboardManager
android.content.ComponentName
android.content.ContentUris
android.content.ContentValues
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index 4a2f3da0eb06..e9f6031e5048 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -38,12 +38,12 @@ public class ScriptC extends Script {
private static final String TAG = "ScriptC";
/**
- * In targetSdkVersion 35 and above, Renderscript's ScriptC stops being supported
+ * In targetSdkVersion 36 and above, Renderscript's ScriptC stops being supported
* and an exception is thrown when the class is instantiated.
- * In targetSdkVersion 34 and below, Renderscript's ScriptC still works.
+ * In targetSdkVersion 35 and below, Renderscript's ScriptC still works.
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = 35)
+ @EnabledAfter(targetSdkVersion = 36)
private static final long RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID = 297019750L;
/**
@@ -113,9 +113,9 @@ public class ScriptC extends Script {
throw new UnsupportedOperationException(s);
}
- // Throw an exception if the target API is 35 or above
+ // Throw an exception if the target API is 36 or above
String message =
- "ScriptC scripts are not supported when targeting an API Level >= 35. Please refer "
+ "ScriptC scripts are not supported when targeting an API Level >= 36. Please refer "
+ "to https://developer.android.com/guide/topics/renderscript/migration-guide "
+ "for proposed alternatives.";
Slog.w(TAG, message);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 16119d11ee1e..54e545d6d73a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.view.InputDevice.SOURCE_CLASS_POINTER;
+import static android.view.MotionEvent.ACTION_SCROLL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
@@ -425,6 +427,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
boolean shouldProcessMultiDeviceEvent(InputEvent event, int policyFlags) {
if (event instanceof MotionEvent motion) {
+ if (!motion.isFromSource(SOURCE_CLASS_POINTER) || motion.getAction() == ACTION_SCROLL) {
+ // Non-pointer events are focus-dispatched and don't require special logic.
+ // Scroll events are stand-alone and therefore can be considered to not be part of
+ // a stream.
+ return true;
+ }
// Only allow 1 device to be sending motion events at a time
// If the event is from an active device, let it through.
// If the event is not from an active device, only let it through if it starts a new
@@ -481,7 +489,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
- if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
+ if (!state.shouldProcessScroll() && event.getActionMasked() == ACTION_SCROLL) {
super.onInputEvent(event, policyFlags);
return;
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c7b844b991c4..d779fbf2eabc 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -193,7 +193,6 @@ import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.PrintWriter;
-import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -2821,8 +2820,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mSessionFlags.mExpiredResponse = false;
final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
- final Serializable exception = data.getSerializable(
- CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
+ final GetCredentialException exception = data.getSerializable(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
+ GetCredentialException.class);
final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
if (sDebug) {
@@ -5126,6 +5126,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
toIpcFriendlyResultReceiver(resultReceiver);
Intent metadataIntent = dataset.getCredentialFillInIntent();
+ if (metadataIntent == null) {
+ metadataIntent = new Intent();
+ }
+
metadataIntent.putExtra(
android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
ipcFriendlyResultReceiver);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 966fe5bbeecc..d1bda7991f45 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -126,6 +126,7 @@ public class SettingsToPropertiesMapper {
"arc_next",
"avic",
"bluetooth",
+ "brownout_mitigation_audio",
"build",
"biometrics",
"biometrics_framework",
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
index 5f12ce1e4163..9f31f375dafe 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -73,8 +73,7 @@ public class AmbientContextManagerService extends
private static final Set<Integer> DEFAULT_EVENT_SET = Sets.newHashSet(
AmbientContextEvent.EVENT_COUGH,
AmbientContextEvent.EVENT_SNORE,
- AmbientContextEvent.EVENT_BACK_DOUBLE_TAP,
- AmbientContextEvent.EVENT_HEART_RATE);
+ AmbientContextEvent.EVENT_BACK_DOUBLE_TAP);
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_SERVICE_ENABLED = true;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 0cc64941cfe6..fb627854209d 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -661,8 +661,12 @@ public class AppOpsService extends IAppOpsService.Stub {
@NonNull OpEntry createEntryLocked(String persistentDeviceId) {
// TODO(b/308201969): Update this method when we introduce disk persistence of events
// for accesses on external devices.
- final ArrayMap<String, AttributedOp> attributedOps = mDeviceAttributedOps.get(
+ ArrayMap<String, AttributedOp> attributedOps = mDeviceAttributedOps.get(
persistentDeviceId);
+ if (attributedOps == null) {
+ attributedOps = new ArrayMap<>();
+ }
+
final ArrayMap<String, AppOpsManager.AttributedOpEntry> attributionEntries =
new ArrayMap<>(attributedOps.size());
for (int i = 0; i < attributedOps.size(); i++) {
@@ -680,8 +684,12 @@ public class AppOpsService extends IAppOpsService.Stub {
@NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
// TODO(b/308201969): Update this method when we introduce disk persistence of events
// for accesses on external devices.
- final ArrayMap<String, AttributedOp> attributedOps = mDeviceAttributedOps.get(
+ ArrayMap<String, AttributedOp> attributedOps = mDeviceAttributedOps.get(
PERSISTENT_DEVICE_ID_DEFAULT);
+ if (attributedOps == null) {
+ attributedOps = new ArrayMap<>();
+ }
+
final ArrayMap<String, AttributedOpEntry> attributionEntries = new ArrayMap<>(1);
if (attributedOps.get(attributionTag) != null) {
attributionEntries.put(attributionTag,
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 082776ad6085..fb4976d3cef2 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import static com.android.server.display.BrightnessMappingStrategy.INVALID_LUX;
import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString;
import android.annotation.IntDef;
@@ -202,7 +203,7 @@ public class AutomaticBrightnessController {
private float mScreenBrighteningThreshold;
private float mScreenDarkeningThreshold;
// The most recent light sample.
- private float mLastObservedLux;
+ private float mLastObservedLux = INVALID_LUX;
// The time of the most light recent sample.
private long mLastObservedLuxTime;
@@ -403,8 +404,8 @@ public class AutomaticBrightnessController {
brightnessEvent.setFlags(brightnessEvent.getFlags()
| (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0)
| (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE
- ? BrightnessEvent.FLAG_DOZE_SCALE : 0)
- | (isInIdleMode() ? BrightnessEvent.FLAG_IDLE_CURVE : 0));
+ ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
+ brightnessEvent.setAutoBrightnessMode(getMode());
}
if (!mAmbientLuxValid) {
@@ -420,6 +421,35 @@ public class AutomaticBrightnessController {
return mRawScreenAutoBrightness;
}
+ /**
+ * Get the automatic screen brightness based on the last observed lux reading. Used e.g. when
+ * entering doze - we disable the light sensor, invalidate the lux, but we still need to set
+ * the initial brightness in doze mode.
+ */
+ public float getAutomaticScreenBrightnessBasedOnLastObservedLux(
+ BrightnessEvent brightnessEvent) {
+ if (mLastObservedLux == INVALID_LUX) {
+ return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
+
+ float brightness = mCurrentBrightnessMapper.getBrightness(mLastObservedLux,
+ mForegroundAppPackageName, mForegroundAppCategory);
+ if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) {
+ brightness *= mDozeScaleFactor;
+ }
+
+ if (brightnessEvent != null) {
+ brightnessEvent.setLux(mLastObservedLux);
+ brightnessEvent.setRecommendedBrightness(brightness);
+ brightnessEvent.setFlags(brightnessEvent.getFlags()
+ | (mLastObservedLux == INVALID_LUX ? BrightnessEvent.FLAG_INVALID_LUX : 0)
+ | (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE
+ ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
+ brightnessEvent.setAutoBrightnessMode(getMode());
+ }
+ return brightness;
+ }
+
public boolean hasValidAmbientLux() {
return mAmbientLuxValid;
}
@@ -539,7 +569,7 @@ public class AutomaticBrightnessController {
}
private boolean setScreenBrightnessByUser(float lux, float brightness) {
- if (lux == BrightnessMappingStrategy.INVALID_LUX || Float.isNaN(brightness)) {
+ if (lux == INVALID_LUX || Float.isNaN(brightness)) {
return false;
}
mCurrentBrightnessMapper.addUserDataPoint(lux, brightness);
@@ -564,6 +594,15 @@ public class AutomaticBrightnessController {
return false;
}
+ /**
+ * @return The auto-brightness mode of the current mapping strategy. Different modes use
+ * different brightness curves.
+ */
+ @AutomaticBrightnessController.AutomaticBrightnessMode
+ public int getMode() {
+ return mCurrentBrightnessMapper.getMode();
+ }
+
public boolean isInIdleMode() {
return mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_IDLE;
}
@@ -1236,12 +1275,12 @@ public class AutomaticBrightnessController {
// light.
// The anchor determines what were the light levels when the user has set their preference,
// and we use a relative threshold to determine when to revert to the OEM curve.
- private float mAnchor = BrightnessMappingStrategy.INVALID_LUX;
+ private float mAnchor = INVALID_LUX;
private float mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private boolean mIsValid = false;
private void reset() {
- mAnchor = BrightnessMappingStrategy.INVALID_LUX;
+ mAnchor = INVALID_LUX;
mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mIsValid = false;
}
@@ -1265,7 +1304,7 @@ public class AutomaticBrightnessController {
private boolean maybeReset(float currentLux) {
// If the short term model was invalidated and the change is drastic enough, reset it.
// Otherwise, we revalidate it.
- if (!mIsValid && mAnchor != BrightnessMappingStrategy.INVALID_LUX) {
+ if (!mIsValid && mAnchor != INVALID_LUX) {
if (mCurrentBrightnessMapper.shouldResetShortTermModel(currentLux, mAnchor)) {
resetShortTermModel();
} else {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 3965d55b0c28..dbe85ea0fa04 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
@@ -37,7 +39,6 @@ import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
-import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
@@ -1378,7 +1379,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Switch to doze auto-brightness mode if needed
if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
&& !mAutomaticBrightnessController.isInIdleMode()) {
- setAutomaticScreenBrightnessMode(Display.isDozeState(state)
+ mAutomaticBrightnessController.switchMode(mPowerRequest.policy == POLICY_DOZE
? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
}
@@ -1466,6 +1467,22 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
}
+ // If there's an offload session and auto-brightness is on, we need to set the initial doze
+ // brightness using the doze auto-brightness curve before the offload session starts
+ // controlling the brightness.
+ if (Float.isNaN(brightnessState) && mFlags.areAutoBrightnessModesEnabled()
+ && mFlags.isDisplayOffloadEnabled()
+ && mPowerRequest.policy == POLICY_DOZE
+ && mDisplayOffloadSession != null
+ && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
+ rawBrightnessState = mAutomaticBrightnessController
+ .getAutomaticScreenBrightnessBasedOnLastObservedLux(mTempBrightnessEvent);
+ if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) {
+ brightnessState = clampScreenBrightness(rawBrightnessState);
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL);
+ }
+ }
+
// Use default brightness when dozing unless overridden.
if ((Float.isNaN(brightnessState))
&& Display.isDozeState(state)) {
@@ -1618,7 +1635,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// if doze or suspend state is requested, we want to finish brightnes animation fast
// to allow state animation to start
- if (mPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE
+ if (mPowerRequest.policy == POLICY_DOZE
&& (mPowerRequest.dozeScreenState == Display.STATE_UNKNOWN // dozing
|| mPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND
|| mPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND)) {
@@ -1706,6 +1723,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mTempBrightnessEvent.setTime(System.currentTimeMillis());
mTempBrightnessEvent.setBrightness(brightnessState);
mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId);
+ mTempBrightnessEvent.setDisplayState(state);
mTempBrightnessEvent.setReason(mBrightnessReason);
mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax());
mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode());
@@ -2879,7 +2897,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
(flags & BrightnessEvent.FLAG_INVALID_LUX) > 0,
(flags & BrightnessEvent.FLAG_DOZE_SCALE) > 0,
(flags & BrightnessEvent.FLAG_USER_SET) > 0,
- (flags & BrightnessEvent.FLAG_IDLE_CURVE) > 0,
+ event.getAutoBrightnessMode() == AUTO_BRIGHTNESS_MODE_IDLE,
(flags & BrightnessEvent.FLAG_LOW_POWER_MODE) > 0);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index d4d1bae78a33..5423b74711a2 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -16,11 +16,16 @@
package com.android.server.display.brightness;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString;
+
import android.hardware.display.BrightnessInfo;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.AutomaticBrightnessController;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -33,7 +38,6 @@ public final class BrightnessEvent {
public static final int FLAG_INVALID_LUX = 0x2;
public static final int FLAG_DOZE_SCALE = 0x4;
public static final int FLAG_USER_SET = 0x8;
- public static final int FLAG_IDLE_CURVE = 0x10;
public static final int FLAG_LOW_POWER_MODE = 0x20;
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
@@ -41,6 +45,7 @@ public final class BrightnessEvent {
private BrightnessReason mReason = new BrightnessReason();
private int mDisplayId;
private String mPhysicalDisplayId;
+ private int mDisplayState;
private long mTime;
private float mLux;
private float mPreThresholdLux;
@@ -58,6 +63,8 @@ public final class BrightnessEvent {
private int mAdjustmentFlags;
private boolean mAutomaticBrightnessEnabled;
private String mDisplayBrightnessStrategyName;
+ @AutomaticBrightnessController.AutomaticBrightnessMode
+ private int mAutoBrightnessMode;
public BrightnessEvent(BrightnessEvent that) {
copyFrom(that);
@@ -77,6 +84,7 @@ public final class BrightnessEvent {
mReason.set(that.getReason());
mDisplayId = that.getDisplayId();
mPhysicalDisplayId = that.getPhysicalDisplayId();
+ mDisplayState = that.mDisplayState;
mTime = that.getTime();
// Lux values
mLux = that.getLux();
@@ -98,6 +106,7 @@ public final class BrightnessEvent {
// Auto-brightness setting
mAutomaticBrightnessEnabled = that.isAutomaticBrightnessEnabled();
mDisplayBrightnessStrategyName = that.getDisplayBrightnessStrategyName();
+ mAutoBrightnessMode = that.mAutoBrightnessMode;
}
/**
@@ -107,6 +116,7 @@ public final class BrightnessEvent {
mReason = new BrightnessReason();
mTime = SystemClock.uptimeMillis();
mPhysicalDisplayId = "";
+ mDisplayState = Display.STATE_UNKNOWN;
// Lux values
mLux = 0;
mPreThresholdLux = 0;
@@ -127,6 +137,7 @@ public final class BrightnessEvent {
// Auto-brightness setting
mAutomaticBrightnessEnabled = true;
mDisplayBrightnessStrategyName = "";
+ mAutoBrightnessMode = AUTO_BRIGHTNESS_MODE_DEFAULT;
}
/**
@@ -143,6 +154,7 @@ public final class BrightnessEvent {
return mReason.equals(that.mReason)
&& mDisplayId == that.mDisplayId
&& mPhysicalDisplayId.equals(that.mPhysicalDisplayId)
+ && mDisplayState == that.mDisplayState
&& Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux)
&& Float.floatToRawIntBits(mPreThresholdLux)
== Float.floatToRawIntBits(that.mPreThresholdLux)
@@ -163,7 +175,8 @@ public final class BrightnessEvent {
&& mFlags == that.mFlags
&& mAdjustmentFlags == that.mAdjustmentFlags
&& mAutomaticBrightnessEnabled == that.mAutomaticBrightnessEnabled
- && mDisplayBrightnessStrategyName.equals(that.mDisplayBrightnessStrategyName);
+ && mDisplayBrightnessStrategyName.equals(that.mDisplayBrightnessStrategyName)
+ && mAutoBrightnessMode == that.mAutoBrightnessMode;
}
/**
@@ -177,6 +190,7 @@ public final class BrightnessEvent {
+ "BrightnessEvent: "
+ "disp=" + mDisplayId
+ ", physDisp=" + mPhysicalDisplayId
+ + ", displayState=" + Display.stateToString(mDisplayState)
+ ", brt=" + mBrightness + ((mFlags & FLAG_USER_SET) != 0 ? "(user_set)" : "")
+ ", initBrt=" + mInitialBrightness
+ ", rcmdBrt=" + mRecommendedBrightness
@@ -192,7 +206,8 @@ public final class BrightnessEvent {
+ ", flags=" + flagsToString()
+ ", reason=" + mReason.toString(mAdjustmentFlags)
+ ", autoBrightness=" + mAutomaticBrightnessEnabled
- + ", strategy=" + mDisplayBrightnessStrategyName;
+ + ", strategy=" + mDisplayBrightnessStrategyName
+ + ", autoBrightnessMode=" + autoBrightnessModeToString(mAutoBrightnessMode);
}
@Override
@@ -232,6 +247,10 @@ public final class BrightnessEvent {
this.mPhysicalDisplayId = mPhysicalDisplayId;
}
+ public void setDisplayState(int state) {
+ mDisplayState = state;
+ }
+
public float getLux() {
return mLux;
}
@@ -374,6 +393,16 @@ public final class BrightnessEvent {
this.mAutomaticBrightnessEnabled = mAutomaticBrightnessEnabled;
}
+ @AutomaticBrightnessController.AutomaticBrightnessMode
+ public int getAutoBrightnessMode() {
+ return mAutoBrightnessMode;
+ }
+
+ public void setAutoBrightnessMode(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ mAutoBrightnessMode = mode;
+ }
+
/**
* A utility to stringify flags from a BrightnessEvent
* @return Stringified flags from BrightnessEvent
@@ -384,7 +413,6 @@ public final class BrightnessEvent {
+ ((mFlags & FLAG_RBC) != 0 ? "rbc " : "")
+ ((mFlags & FLAG_INVALID_LUX) != 0 ? "invalid_lux " : "")
+ ((mFlags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "")
- + ((mFlags & FLAG_IDLE_CURVE) != 0 ? "idle_curve " : "")
+ ((mFlags & FLAG_LOW_POWER_MODE) != 0 ? "low_power_mode " : "");
}
}
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index bc443a8167ab..fc95d15ae6f4 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -40,7 +40,8 @@ public final class BrightnessReason {
public static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 9;
public static final int REASON_FOLLOWER = 10;
public static final int REASON_OFFLOAD = 11;
- public static final int REASON_MAX = REASON_OFFLOAD;
+ public static final int REASON_DOZE_INITIAL = 12;
+ public static final int REASON_MAX = REASON_DOZE_INITIAL;
public static final int MODIFIER_DIMMED = 0x1;
public static final int MODIFIER_LOW_POWER = 0x2;
@@ -207,6 +208,8 @@ public final class BrightnessReason {
return "follower";
case REASON_OFFLOAD:
return "offload";
+ case REASON_DOZE_INITIAL:
+ return "doze_initial";
default:
return Integer.toString(reason);
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 3e6e09da9753..8eaecef6e562 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -267,6 +267,23 @@ public class AutomaticBrightnessStrategy {
}
/**
+ * Get the automatic screen brightness based on the last observed lux reading. Used e.g. when
+ * entering doze - we disable the light sensor, invalidate the lux, but we still need to set
+ * the initial brightness in doze mode.
+ * @param brightnessEvent Event object to populate with details about why the specific
+ * brightness was chosen.
+ */
+ public float getAutomaticScreenBrightnessBasedOnLastObservedLux(
+ BrightnessEvent brightnessEvent) {
+ float brightness = (mAutomaticBrightnessController != null)
+ ? mAutomaticBrightnessController
+ .getAutomaticScreenBrightnessBasedOnLastObservedLux(brightnessEvent)
+ : PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ adjustAutomaticBrightnessStateIfValid(brightness);
+ return brightness;
+ }
+
+ /**
* Gets the auto-brightness adjustment flag change reason
*/
public int getAutoBrightnessAdjustmentReasonsFlags() {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index dbef427e23a7..a79e7715f064 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -38,7 +38,6 @@ import android.graphics.PointF;
import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManager.Sensors;
import android.hardware.SensorPrivacyManagerInternal;
-import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayViewport;
import android.hardware.input.HostUsiVersion;
@@ -86,9 +85,7 @@ import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import android.view.Display;
-import android.view.DisplayInfo;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
import android.view.IInputMonitorHost;
@@ -117,7 +114,6 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
-import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.input.InputManagerInternal.LidSwitchCallback;
import com.android.server.input.debug.FocusEventDebugView;
@@ -325,6 +321,9 @@ public class InputManagerService extends IInputManager.Stub
// Manages Keyboard modifier keys remapping
private final KeyRemapper mKeyRemapper;
+ // Manages loading PointerIcons
+ private final PointerIconCache mPointerIconCache;
+
// Maximum number of milliseconds to wait for input event injection.
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
@@ -411,74 +410,6 @@ public class InputManagerService extends IInputManager.Stub
private boolean mShowKeyPresses = false;
private boolean mShowRotaryInput = false;
- @GuardedBy("mLoadedPointerIconsByDisplayAndType")
- final SparseArray<SparseArray<PointerIcon>> mLoadedPointerIconsByDisplayAndType =
- new SparseArray<>();
- @GuardedBy("mLoadedPointerIconsByDisplayAndType")
- boolean mUseLargePointerIcons = false;
- @GuardedBy("mLoadedPointerIconsByDisplayAndType")
- final SparseArray<Context> mDisplayContexts = new SparseArray<>();
- @GuardedBy("mLoadedPointerIconsByDisplayAndType")
- final SparseIntArray mDisplayDensities = new SparseIntArray();
-
- final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
- @Override
- public void onDisplayAdded(int displayId) {
- synchronized (mLoadedPointerIconsByDisplayAndType) {
- updateDisplayDensity(displayId);
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- synchronized (mLoadedPointerIconsByDisplayAndType) {
- mLoadedPointerIconsByDisplayAndType.remove(displayId);
- mDisplayContexts.remove(displayId);
- mDisplayDensities.delete(displayId);
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- synchronized (mLoadedPointerIconsByDisplayAndType) {
- if (!updateDisplayDensity(displayId)) {
- return;
- }
- // The display density changed, so force all cached pointer icons to be
- // reloaded for the display.
- Slog.i(TAG,
- "Reloading pointer icons due to density change on display: " + displayId);
- var iconsByType = mLoadedPointerIconsByDisplayAndType.get(displayId);
- if (iconsByType == null) {
- return;
- }
- iconsByType.clear();
- mDisplayContexts.remove(displayId);
- }
- mNative.reloadPointerIcons();
- }
-
- // Updates the cached display density for the given displayId, and returns true if
- // the cached density changed.
- @GuardedBy("mLoadedPointerIconsByDisplayAndType")
- private boolean updateDisplayDensity(int displayId) {
- final DisplayManager displayManager = Objects.requireNonNull(
- mContext.getSystemService(DisplayManager.class));
- final Display display = displayManager.getDisplay(displayId);
- if (display == null) {
- return false;
- }
- DisplayInfo info = new DisplayInfo();
- display.getDisplayInfo(info);
- final int oldDensity = mDisplayDensities.get(displayId, 0 /* default */);
- if (oldDensity == info.logicalDensityDpi) {
- return false;
- }
- mDisplayDensities.put(displayId, info.logicalDensityDpi);
- return true;
- }
- };
-
/** Point of injection for test dependencies. */
@VisibleForTesting
static class Injector {
@@ -537,6 +468,7 @@ public class InputManagerService extends IInputManager.Stub
: new KeyboardBacklightControllerInterface() {};
mStickyModifierStateController = new StickyModifierStateController();
mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
+ mPointerIconCache = new PointerIconCache(mContext, mNative);
mUseDevInputEventForAudioJack =
mContext.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
@@ -646,18 +578,11 @@ public class InputManagerService extends IInputManager.Stub
mWiredAccessoryCallbacks.systemReady();
}
- final DisplayManager displayManager = Objects.requireNonNull(
- mContext.getSystemService(DisplayManager.class));
- displayManager.registerDisplayListener(mDisplayListener, UiThread.getHandler());
- final Display[] displays = displayManager.getDisplays();
- for (int i = 0; i < displays.length; i++) {
- mDisplayListener.onDisplayAdded(displays[i].getDisplayId());
- }
-
mKeyboardLayoutManager.systemRunning();
mBatteryController.systemRunning();
mKeyboardBacklightController.systemRunning();
mKeyRemapper.systemRunning();
+ mPointerIconCache.systemRunning();
}
private void reloadDeviceAliases() {
@@ -2411,8 +2336,8 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ }
synchronized (mAdditionalDisplayInputPropertiesLock) { /* Test if blocked by props lock */ }
- synchronized (mLoadedPointerIconsByDisplayAndType) { /* Test if blocked by pointer lock */}
mBatteryController.monitor();
+ mPointerIconCache.monitor();
mNative.monitor();
}
@@ -2806,21 +2731,7 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
@SuppressWarnings("unused")
private @NonNull PointerIcon getLoadedPointerIcon(int displayId, int type) {
- synchronized (mLoadedPointerIconsByDisplayAndType) {
- SparseArray<PointerIcon> iconsByType = mLoadedPointerIconsByDisplayAndType.get(
- displayId);
- if (iconsByType == null) {
- iconsByType = new SparseArray<>();
- mLoadedPointerIconsByDisplayAndType.put(displayId, iconsByType);
- }
- PointerIcon icon = iconsByType.get(type);
- if (icon == null) {
- icon = PointerIcon.getLoadedSystemIcon(getContextForDisplay(displayId), type,
- mUseLargePointerIcons);
- iconsByType.put(type, icon);
- }
- return Objects.requireNonNull(icon);
- }
+ return mPointerIconCache.getLoadedPointerIcon(displayId, type);
}
// Native callback.
@@ -2833,33 +2744,6 @@ public class InputManagerService extends IInputManager.Stub
return sc.mNativeObject;
}
- @GuardedBy("mLoadedPointerIconsByDisplayAndType")
- @NonNull
- private Context getContextForDisplay(int displayId) {
- if (displayId == Display.INVALID_DISPLAY) {
- // Fallback to using the default context.
- return mContext;
- }
- if (displayId == mContext.getDisplay().getDisplayId()) {
- return mContext;
- }
-
- Context displayContext = mDisplayContexts.get(displayId);
- if (displayContext == null) {
- final DisplayManager displayManager = Objects.requireNonNull(
- mContext.getSystemService(DisplayManager.class));
- final Display display = displayManager.getDisplay(displayId);
- if (display == null) {
- // Fallback to using the default context.
- return mContext;
- }
-
- displayContext = mContext.createDisplayContext(display);
- mDisplayContexts.put(displayId, displayContext);
- }
- return displayContext;
- }
-
// Native callback.
@SuppressWarnings("unused")
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier, String languageTag,
@@ -3665,15 +3549,7 @@ public class InputManagerService extends IInputManager.Stub
}
void setUseLargePointerIcons(boolean useLargeIcons) {
- synchronized (mLoadedPointerIconsByDisplayAndType) {
- if (mUseLargePointerIcons == useLargeIcons) {
- return;
- }
- mUseLargePointerIcons = useLargeIcons;
- // Clear all cached icons on all displays.
- mLoadedPointerIconsByDisplayAndType.clear();
- }
- UiThread.getHandler().post(mNative::reloadPointerIcons);
+ mPointerIconCache.setUseLargePointerIcons(useLargeIcons);
}
interface KeyboardBacklightControllerInterface {
diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java
new file mode 100644
index 000000000000..233b865c69a6
--- /dev/null
+++ b/services/core/java/com/android/server/input/PointerIconCache.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.PointerIcon;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.UiThread;
+
+import java.util.Objects;
+
+/**
+ * A thread-safe component of {@link InputManagerService} responsible for caching loaded
+ * {@link PointerIcon}s and triggering reloading of the icons.
+ */
+final class PointerIconCache {
+ private static final String TAG = PointerIconCache.class.getSimpleName();
+
+ private final Context mContext;
+
+ // Do not hold the lock when calling into native code.
+ private final NativeInputManagerService mNative;
+
+ // We use the UI thread for loading pointer icons.
+ private final Handler mUiThreadHandler = UiThread.getHandler();
+
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ private final SparseArray<SparseArray<PointerIcon>> mLoadedPointerIconsByDisplayAndType =
+ new SparseArray<>();
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ private boolean mUseLargePointerIcons = false;
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ private final SparseArray<Context> mDisplayContexts = new SparseArray<>();
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ private final SparseIntArray mDisplayDensities = new SparseIntArray();
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ synchronized (mLoadedPointerIconsByDisplayAndType) {
+ updateDisplayDensityLocked(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLoadedPointerIconsByDisplayAndType) {
+ mLoadedPointerIconsByDisplayAndType.remove(displayId);
+ mDisplayContexts.remove(displayId);
+ mDisplayDensities.delete(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ handleDisplayChanged(displayId);
+ }
+ };
+
+ /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService) {
+ mContext = context;
+ mNative = nativeService;
+ }
+
+ public void systemRunning() {
+ final DisplayManager displayManager = Objects.requireNonNull(
+ mContext.getSystemService(DisplayManager.class));
+ displayManager.registerDisplayListener(mDisplayListener, mUiThreadHandler);
+ final Display[] displays = displayManager.getDisplays();
+ for (int i = 0; i < displays.length; i++) {
+ mDisplayListener.onDisplayAdded(displays[i].getDisplayId());
+ }
+ }
+
+ public void monitor() {
+ synchronized (mLoadedPointerIconsByDisplayAndType) { /* Test if blocked by lock */}
+ }
+
+ /** Set whether the large pointer icons should be used for accessibility. */
+ public void setUseLargePointerIcons(boolean useLargeIcons) {
+ mUiThreadHandler.post(() -> handleSetUseLargePointerIcons(useLargeIcons));
+ }
+
+ /**
+ * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if
+ * it isn't already cached.
+ */
+ public @NonNull PointerIcon getLoadedPointerIcon(int displayId, int type) {
+ synchronized (mLoadedPointerIconsByDisplayAndType) {
+ SparseArray<PointerIcon> iconsByType = mLoadedPointerIconsByDisplayAndType.get(
+ displayId);
+ if (iconsByType == null) {
+ iconsByType = new SparseArray<>();
+ mLoadedPointerIconsByDisplayAndType.put(displayId, iconsByType);
+ }
+ PointerIcon icon = iconsByType.get(type);
+ if (icon == null) {
+ icon = PointerIcon.getLoadedSystemIcon(getContextForDisplayLocked(displayId), type,
+ mUseLargePointerIcons);
+ iconsByType.put(type, icon);
+ }
+ return Objects.requireNonNull(icon);
+ }
+ }
+
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ private @NonNull Context getContextForDisplayLocked(int displayId) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ // Fallback to using the default context.
+ return mContext;
+ }
+ if (displayId == mContext.getDisplay().getDisplayId()) {
+ return mContext;
+ }
+
+ Context displayContext = mDisplayContexts.get(displayId);
+ if (displayContext == null) {
+ final DisplayManager displayManager = Objects.requireNonNull(
+ mContext.getSystemService(DisplayManager.class));
+ final Display display = displayManager.getDisplay(displayId);
+ if (display == null) {
+ // Fallback to using the default context.
+ return mContext;
+ }
+
+ displayContext = mContext.createDisplayContext(display);
+ mDisplayContexts.put(displayId, displayContext);
+ }
+ return displayContext;
+ }
+
+ @android.annotation.UiThread
+ private void handleDisplayChanged(int displayId) {
+ synchronized (mLoadedPointerIconsByDisplayAndType) {
+ if (!updateDisplayDensityLocked(displayId)) {
+ return;
+ }
+ // The display density changed, so force all cached pointer icons to be
+ // reloaded for the display.
+ Slog.i(TAG, "Reloading pointer icons due to density change on display: " + displayId);
+ var iconsByType = mLoadedPointerIconsByDisplayAndType.get(displayId);
+ if (iconsByType == null) {
+ return;
+ }
+ iconsByType.clear();
+ mDisplayContexts.remove(displayId);
+ }
+ mNative.reloadPointerIcons();
+ }
+
+ @android.annotation.UiThread
+ private void handleSetUseLargePointerIcons(boolean useLargeIcons) {
+ synchronized (mLoadedPointerIconsByDisplayAndType) {
+ if (mUseLargePointerIcons == useLargeIcons) {
+ return;
+ }
+ mUseLargePointerIcons = useLargeIcons;
+ // Clear all cached icons on all displays.
+ mLoadedPointerIconsByDisplayAndType.clear();
+ }
+ mNative.reloadPointerIcons();
+ }
+
+ // Updates the cached display density for the given displayId, and returns true if
+ // the cached density changed.
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ private boolean updateDisplayDensityLocked(int displayId) {
+ final DisplayManager displayManager = Objects.requireNonNull(
+ mContext.getSystemService(DisplayManager.class));
+ final Display display = displayManager.getDisplay(displayId);
+ if (display == null) {
+ return false;
+ }
+ DisplayInfo info = new DisplayInfo();
+ display.getDisplayInfo(info);
+ final int oldDensity = mDisplayDensities.get(displayId, 0 /* default */);
+ if (oldDensity == info.logicalDensityDpi) {
+ return false;
+ }
+ mDisplayDensities.put(displayId, info.logicalDensityDpi);
+ return true;
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 796d8d73fc51..582058d21256 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -729,7 +729,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* Map of uid -> UidStateCallbackInfo objects holding the data received from
* {@link IUidObserver#onUidStateChanged(int, int, long, int)} callbacks. In order to avoid
* creating a new object for every callback received, we hold onto the object created for each
- * uid and reuse it.
+ * uid and reuse it until the uid stays alive.
*
* Note that the lock used for accessing this object should not be used for anything else and we
* should not be acquiring new locks or doing any heavy work while this lock is held since this
@@ -802,6 +802,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
Clock.systemUTC());
}
+ @VisibleForTesting
+ UidState getUidStateForTest(int uid) {
+ synchronized (mUidRulesFirstLock) {
+ return mUidState.get(uid);
+ }
+ }
+
static class Dependencies {
final Context mContext;
final NetworkStatsManager mNetworkStatsManager;
@@ -1257,6 +1264,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
@Override public void onUidGone(int uid, boolean disabled) {
+ synchronized (mUidStateCallbackInfos) {
+ mUidStateCallbackInfos.remove(uid);
+ }
+ // TODO: b/327058756 - Remove any pending UID_MSG_STATE_CHANGED on the handler.
mUidEventHandler.obtainMessage(UID_MSG_GONE, uid, 0).sendToTarget();
}
};
@@ -5918,7 +5929,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
void handleUidGone(int uid) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidGone");
try {
- boolean updated;
+ final boolean updated;
synchronized (mUidRulesFirstLock) {
updated = removeUidStateUL(uid);
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index eed0eecb0a22..6857869e3776 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1285,8 +1285,8 @@ public class ZenModeHelper {
*/
private static void updateZenDeviceEffects(ZenRule zenRule,
@Nullable ZenDeviceEffects newEffects, boolean isFromApp, boolean updateBitmask) {
+ // Same as with ZenPolicy, supplying null effects means keeping the previous ones.
if (newEffects == null) {
- zenRule.zenDeviceEffects = null;
return;
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8cc242c10c16..5bd8ca63c3ff 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -17,6 +17,8 @@
package com.android.server.pm;
import static android.content.pm.Flags.disallowSdkLibsToBeApps;
+import static android.content.pm.PackageManager.APP_METADATA_SOURCE_APK;
+import static android.content.pm.PackageManager.APP_METADATA_SOURCE_INSTALLER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
@@ -43,11 +45,11 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.APP_METADATA_FILE_NAME;
import static com.android.server.pm.DexOptHelper.useArtService;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
-import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
@@ -2225,10 +2227,16 @@ final class InstallPackageHelper {
if (appMetadataFile.exists()) {
ps.setAppMetadataFilePath(appMetadataFile.getAbsolutePath());
if (Flags.aslInApkAppMetadataSource()) {
- ps.setAppMetadataSource(PackageManager.APP_METADATA_SOURCE_INSTALLER);
+ ps.setAppMetadataSource(APP_METADATA_SOURCE_INSTALLER);
}
} else {
- ps.setAppMetadataFilePath(null);
+ if (Flags.aslInApkAppMetadataSource()
+ && parsedPackage.isAppMetadataFileInApk()) {
+ ps.setAppMetadataFilePath(appMetadataFile.getAbsolutePath());
+ ps.setAppMetadataSource(APP_METADATA_SOURCE_APK);
+ } else {
+ ps.setAppMetadataFilePath(null);
+ }
}
}
if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5792d864a8ab..ba903789e759 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -358,7 +358,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** Default byte size limit for app metadata */
private static final long DEFAULT_APP_METADATA_BYTE_SIZE_LIMIT = 32000;
- private static final int APP_METADATA_FILE_ACCESS_MODE = 0640;
+ static final int APP_METADATA_FILE_ACCESS_MODE = 0640;
/**
* Throws IllegalArgumentException if the {@link IntentSender} from an immutable
@@ -964,7 +964,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private boolean isEmergencyInstallerEnabled(String packageName, Computer snapshot) {
final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName);
- if (ps == null || ps.getPkg() == null || !ps.isSystem()) {
+ if (ps == null || ps.getPkg() == null) {
return false;
}
String emergencyInstaller = ps.getPkg().getEmergencyInstaller();
@@ -1782,8 +1782,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private File getStagedAppMetadataFile() {
- File file = new File(stageDir, APP_METADATA_FILE_NAME);
- return file.exists() ? file : null;
+ return new File(stageDir, APP_METADATA_FILE_NAME);
}
private static boolean isAppMetadata(String name) {
@@ -1799,7 +1798,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertCallerIsOwnerOrRoot();
synchronized (mLock) {
assertPreparedAndNotCommittedOrDestroyedLocked("getAppMetadataFd");
- if (getStagedAppMetadataFile() == null) {
+ if (!getStagedAppMetadataFile().exists()) {
return null;
}
try {
@@ -1813,12 +1812,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void removeAppMetadata() {
File file = getStagedAppMetadataFile();
- if (file != null) {
+ if (file.exists()) {
file.delete();
}
}
- private static long getAppMetadataSizeLimit() {
+ static long getAppMetadataSizeLimit() {
final long token = Binder.clearCallingIdentity();
try {
return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
@@ -2131,7 +2130,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
File appMetadataFile = getStagedAppMetadataFile();
- if (appMetadataFile != null) {
+ if (appMetadataFile.exists()) {
long sizeLimit = getAppMetadataSizeLimit();
if (appMetadataFile.length() > sizeLimit) {
appMetadataFile.delete();
@@ -3419,7 +3418,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final List<ApkLite> addedFiles = getAddedApkLitesLocked();
if (addedFiles.isEmpty()
- && (removeSplitList.size() == 0 || getStagedAppMetadataFile() != null)) {
+ && (removeSplitList.size() == 0 || getStagedAppMetadataFile().exists())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
TextUtils.formatSimple("Session: %d. No packages staged in %s", sessionId,
stageDir.getAbsolutePath()));
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3c256b1caa7d..69f790637d80 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18,6 +18,8 @@ package com.android.server.pm;
import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.content.pm.PackageManager.APP_METADATA_SOURCE_APK;
+import static android.content.pm.PackageManager.APP_METADATA_SOURCE_UNKNOWN;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -596,7 +598,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
static final String RANDOM_DIR_PREFIX = "~~";
static final char RANDOM_CODEPATH_PREFIX = '-';
- static final String APP_METADATA_FILE_NAME = "app.metadata";
+ public static final String APP_METADATA_FILE_NAME = "app.metadata";
+ public static final String APP_METADATA_FILE_IN_APK_PATH = "assets/" + APP_METADATA_FILE_NAME;
static final int DEFAULT_FILE_ACCESS_MODE = 0644;
@@ -5227,15 +5230,30 @@ public class PackageManagerService implements PackageSender, TestUtilityService
new PackageManager.NameNotFoundException(packageName));
}
String filePath = ps.getAppMetadataFilePath();
- if (filePath != null) {
- File file = new File(filePath);
- try {
- return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
- } catch (FileNotFoundException e) {
+ if (filePath == null) {
+ return null;
+ }
+ File file = new File(filePath);
+ if (Flags.aslInApkAppMetadataSource() && !file.exists()
+ && ps.getAppMetadataSource() == APP_METADATA_SOURCE_APK) {
+ String apkPath = ps.getPkg().getSplits().get(0).getPath();
+ if (!PackageManagerServiceUtils.extractAppMetadataFromApk(apkPath, file)) {
+ if (file.exists()) {
+ file.delete();
+ }
+ synchronized (mLock) {
+ PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
+ pkgSetting.setAppMetadataFilePath(null);
+ pkgSetting.setAppMetadataSource(APP_METADATA_SOURCE_UNKNOWN);
+ }
return null;
}
}
- return null;
+ try {
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
}
@android.annotation.EnforcePermission(android.Manifest.permission.GET_APP_METADATA)
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 0a3dfc08686a..189a138a191a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -27,6 +27,9 @@ import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH;
import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
+import static com.android.server.pm.PackageInstallerSession.APP_METADATA_FILE_ACCESS_MODE;
+import static com.android.server.pm.PackageInstallerSession.getAppMetadataSizeLimit;
+import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_IN_APK_PATH;
import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
@@ -142,6 +145,8 @@ import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
/**
* Class containing helper methods for the PackageManagerService.
@@ -1559,6 +1564,35 @@ public class PackageManagerServiceUtils {
return initiatingPackageName == null || SHELL_PACKAGE_NAME.equals(initiatingPackageName);
}
+ /**
+ * Extract the app.metadata file from apk.
+ */
+ public static boolean extractAppMetadataFromApk(String apkPath, File appMetadataFile) {
+ boolean found = false;
+ try (ZipInputStream zipInputStream =
+ new ZipInputStream(new FileInputStream(new File(apkPath)))) {
+ ZipEntry zipEntry = null;
+ while ((zipEntry = zipInputStream.getNextEntry()) != null) {
+ if (zipEntry.getName().equals(APP_METADATA_FILE_IN_APK_PATH)) {
+ found = true;
+ try (FileOutputStream out = new FileOutputStream(appMetadataFile)) {
+ FileUtils.copy(zipInputStream, out);
+ }
+ if (appMetadataFile.length() > getAppMetadataSizeLimit()) {
+ appMetadataFile.delete();
+ return false;
+ }
+ Os.chmod(appMetadataFile.getAbsolutePath(), APP_METADATA_FILE_ACCESS_MODE);
+ break;
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, e.getMessage());
+ return false;
+ }
+ return found;
+ }
+
public static void linkFilesToOldDirs(@NonNull Installer installer,
@NonNull String packageName,
@NonNull File newPath,
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 1c70af0a56ea..b18503d7d5cb 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -596,7 +596,7 @@ public class PackageInfoUtils {
ai.requiredDisplayCategory = a.getRequiredDisplayCategory();
ai.requireContentUriPermissionFromCaller = a.getRequireContentUriPermissionFromCaller();
ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
- assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId);
+ assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, state, userId);
return ai;
}
@@ -659,7 +659,7 @@ public class PackageInfoUtils {
// Backwards compatibility, coerce to null if empty
si.metaData = metaData.isEmpty() ? null : metaData;
}
- assignFieldsComponentInfoParsedMainComponent(si, s, pkgSetting, userId);
+ assignFieldsComponentInfoParsedMainComponent(si, s, pkgSetting, state, userId);
return si;
}
@@ -710,7 +710,7 @@ public class PackageInfoUtils {
pi.metaData = metaData.isEmpty() ? null : metaData;
}
pi.applicationInfo = applicationInfo;
- assignFieldsComponentInfoParsedMainComponent(pi, p, pkgSetting, userId);
+ assignFieldsComponentInfoParsedMainComponent(pi, p, pkgSetting, state, userId);
return pi;
}
@@ -903,8 +903,13 @@ public class PackageInfoUtils {
private static void assignFieldsComponentInfoParsedMainComponent(
@NonNull ComponentInfo info, @NonNull ParsedMainComponent component,
- @NonNull PackageStateInternal pkgSetting, @UserIdInt int userId) {
+ @NonNull PackageStateInternal pkgSetting, @NonNull PackageUserStateInternal state,
+ @UserIdInt int userId) {
assignFieldsComponentInfoParsedMainComponent(info, component);
+ // overwrite the enabled state with the current user state
+ info.enabled = PackageUserStateUtils.isEnabled(state, info.applicationInfo.enabled,
+ info.enabled, info.name, /* flags */ 0);
+
Pair<CharSequence, Integer> labelAndIcon =
ParsedComponentStateUtils.getNonLocalizedLabelAndIcon(component, pkgSetting,
userId);
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index f15646f634c2..984994170876 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -237,7 +237,7 @@ public final class PermissionPolicyService extends SystemService {
mAppOpsCallback = new IAppOpsCallback.Stub() {
public void opChanged(int op, int uid, @Nullable String packageName,
String persistentDeviceId) {
- if (Objects.equals(persistentDeviceId,
+ if (!Objects.equals(persistentDeviceId,
VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {
return;
}
diff --git a/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java b/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
index b0dcf95b3f5d..881583ad8c91 100644
--- a/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
+++ b/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
@@ -282,10 +282,7 @@ class AggregatedMobileDataStatsPuller {
Slog.d(TAG,
"pullDataBytesTransferLocked() done. results count " + pulledData.size());
}
- if (!pulledData.isEmpty()) {
- return StatsManager.PULL_SUCCESS;
- }
- return StatsManager.PULL_SKIP;
+ return StatsManager.PULL_SUCCESS;
}
private static boolean isEmpty(NetworkStats stats) {
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 17f5e9585c22..7206c0377bf7 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -164,7 +164,6 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
registerCarrierPrivilegesCallbacks();
}
- // TODO(b/221306368): Refactor with the new onCarrierServiceChange in the new CPCallback
private void registerCarrierPrivilegesCallbacks() {
final HandlerExecutor executor = new HandlerExecutor(mHandler);
final int modemCount = mTelephonyManager.getActiveModemCount();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d60fe4b7d7e1..5036fc646327 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -10059,7 +10059,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pendingResults, pendingNewIntents, configChangeFlags,
new MergedConfiguration(getProcessGlobalConfiguration(),
getMergedOverrideConfiguration()),
- preserveWindow);
+ preserveWindow, getActivityWindowInfo());
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(token, isTransitionForward(),
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 066d26222fbd..c137c54949e9 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1548,6 +1548,8 @@ class ActivityStarter {
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, options, inTask, inTaskFragment, balVerdict,
intentGrants, realCallingUid);
+ } catch (Exception ex) {
+ Slog.e(TAG, "Exception on startActivityInner", ex);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
startedActivityRootTask = handleStartResult(r, options, result, newTransition,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e0faddf171ca..aefa777a1db7 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1893,7 +1893,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Check that we aren't reparenting to the same root task that the task is already in
if (prevRootTask != null && prevRootTask.mTaskId == rootTaskId) {
Slog.w(TAG, "Can not reparent to same root task, task=" + task
- + " already in rootTaskId=" + rootTaskId);
+ + " already in rootTaskId=" + rootTaskId + " by " + Debug.getCallers(8));
return prevRootTask;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5cf9acdbc0d6..7f3df958664c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -47,6 +47,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLO
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -1533,9 +1534,19 @@ public class DisplayPolicy {
// Check the windows that overlap with system bars to determine system bars' appearance.
if ((appWindow && attached == null && attrs.isFullscreen())
|| attrs.type == TYPE_VOICE_INTERACTION) {
- // Record the top-fullscreen-app-window which will be used to determine system UI
+
+ // If this is the exiting starting window, don't let it control the system bars.
+ // The app window behind it should be the controlling window instead. Reason: when an
+ // activity starts another activity behind a starting window, the app window of the
+ // first activity will lose the window focus. And then mTopFullscreenOpaqueWindowState
+ // will control the system bars. The logic here is to let first app window keep
+ // controlling system bars until the second app window is ready.
+ final boolean exitingStartingWindow =
+ attrs.type == TYPE_APPLICATION_STARTING && win.mAnimatingExit;
+
+ // Record the top-fullscreen-app-window which will be used to determine the system UI
// controlling window.
- if (mTopFullscreenOpaqueWindowState == null) {
+ if (mTopFullscreenOpaqueWindowState == null && !exitingStartingWindow) {
mTopFullscreenOpaqueWindowState = win;
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index b68e67e291e7..34ea4ac35224 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.view.View.DRAG_FLAG_GLOBAL;
import static android.view.View.DRAG_FLAG_GLOBAL_SAME_APPLICATION;
+import static android.view.View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG;
import static com.android.input.flags.Flags.enablePointerChoreographer;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
@@ -373,8 +374,11 @@ class DragDropController {
boolean notifyUnhandledDrop(DragEvent dropEvent, String reason) {
final boolean isLocalDrag =
(mDragState.mFlags & (DRAG_FLAG_GLOBAL_SAME_APPLICATION | DRAG_FLAG_GLOBAL)) == 0;
+ final boolean shouldDelegateUnhandledDrag =
+ (mDragState.mFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0;
if (!com.android.window.flags.Flags.delegateUnhandledDrags()
|| mGlobalDragListener == null
+ || !shouldDelegateUnhandledDrag
|| isLocalDrag) {
// Skip if the flag is disabled, there is no unhandled-drag listener, or if this is a
// purely local drag
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 76038b945288..b266caa82056 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -216,8 +216,7 @@ class DragState {
mIsClosing = true;
// Unregister the input interceptor.
if (mInputInterceptor != null) {
- if (DEBUG_DRAG)
- Slog.d(TAG_WM, "unregistering drag input channel");
+ if (DEBUG_DRAG) Slog.d(TAG_WM, "Unregistering drag input channel");
// Input channel should be disposed on the thread where the input is being handled.
mDragDropController.sendHandlerMessage(
@@ -227,9 +226,7 @@ class DragState {
// Send drag end broadcast if drag start has been sent.
if (mDragInProgress) {
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "broadcasting DRAG_ENDED");
- }
+ if (DEBUG_DRAG) Slog.d(TAG_WM, "Broadcasting DRAG_ENDED");
for (WindowState ws : mNotifiedWindows) {
float x = 0;
float y = 0;
@@ -248,6 +245,7 @@ class DragState {
x, y, mThumbOffsetX, mThumbOffsetY, null, null, null, dragSurface, null,
mDragResult);
try {
+ if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DRAG_ENDED to " + ws);
ws.mClient.dispatchDragEvent(event);
} catch (RemoteException e) {
Slog.w(TAG_WM, "Unable to drag-end window " + ws);
@@ -364,7 +362,7 @@ class DragState {
return false;
}
- if (DEBUG_DRAG) Slog.d(TAG_WM, "sending DROP to " + touchedWin);
+ if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to " + touchedWin);
final IBinder clientToken = touchedWin.mClient.asBinder();
final DragEvent event = createDropEvent(x, y, touchedWin, false /* includePrivateInfo */);
@@ -460,7 +458,7 @@ class DragState {
*/
CompletableFuture<Void> register(Display display) {
display.getRealSize(mDisplaySize);
- if (DEBUG_DRAG) Slog.d(TAG_WM, "registering drag input channel");
+ if (DEBUG_DRAG) Slog.d(TAG_WM, "Registering drag input channel");
if (mInputInterceptor != null) {
Slog.e(TAG_WM, "Duplicate register of drag input channel");
return completedFuture(null);
@@ -489,7 +487,7 @@ class DragState {
mSourceUserId, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
+ Slog.d(TAG_WM, "Broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
final boolean containsAppExtras = containsApplicationExtras(mDataDescription);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 9d45cfb2fb7e..027337f04b6e 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -18,7 +18,6 @@ package com.android.server.credentials;
import static android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -161,26 +160,6 @@ public class CredentialManagerUi {
*/
public PendingIntent createPendingIntent(
RequestInfo requestInfo, ArrayList<ProviderData> providerDataList) {
- return createPendingIntent(requestInfo, providerDataList, /*forAutofill=*/ false);
- }
-
- /**
- * Creates a {@link PendingIntent} to be used to invoke the credential manager selector UI,
- * by the calling app process. This intent is invoked from the Autofill flow, when the user
- * requests to bring up the 'All Options' page of the credential bottom-sheet. When the user
- * clicks on the pinned entry, the intent will bring up the 'All Options' page of the
- * bottom-sheet. The provider data list is processed by the credential autofill service for
- * each autofill id and passed in as an auth extra.
- *
- * @param requestInfo the information about the request
- */
- public PendingIntent createPendingIntentForAutofill(RequestInfo requestInfo) {
- return createPendingIntent(requestInfo, /*providerDataList=*/ null, /*forAutofill=*/ true);
- }
-
- private PendingIntent createPendingIntent(
- RequestInfo requestInfo, @Nullable ArrayList<ProviderData> providerDataList,
- boolean forAutofill) {
List<CredentialProviderInfo> allProviders =
CredentialProviderInfoFactory.getCredentialProviderServices(
mContext,
@@ -196,15 +175,9 @@ public class CredentialManagerUi {
disabledProvider.getComponentName().flattenToString())).toList();
Intent intent;
- if (forAutofill) {
- intent = IntentFactory.createCredentialSelectorIntentForAutofill(
- mContext, requestInfo, new ArrayList<>(disabledProviderDataList),
- mResultReceiver);
- } else {
- intent = IntentFactory.createCredentialSelectorIntent(
- mContext, requestInfo, providerDataList,
- new ArrayList<>(disabledProviderDataList), mResultReceiver);
- }
+ intent = IntentFactory.createCredentialSelectorIntent(
+ mContext, requestInfo, providerDataList,
+ new ArrayList<>(disabledProviderDataList), mResultReceiver);
intent.setAction(UUID.randomUUID().toString());
//TODO: Create unique pending intent using request code and cancel any pre-existing pending
// intents
@@ -213,4 +186,21 @@ public class CredentialManagerUi {
PendingIntent.FLAG_MUTABLE, /*options=*/null,
UserHandle.of(mUserId));
}
+
+ /**
+ * Creates an {@link Intent} to be used to invoke the credential manager selector UI,
+ * by the calling app process. This intent is invoked from the Autofill flow, when the user
+ * requests to bring up the 'All Options' page of the credential bottom-sheet. When the user
+ * clicks on the pinned entry, the intent will bring up the 'All Options' page of the
+ * bottom-sheet. The provider data list is processed by the credential autofill service for
+ * each autofill id and passed in as extras in the pending intent set as authentication
+ * of the pinned entry.
+ *
+ * @param requestInfo the information about the request
+ */
+ public Intent createIntentForAutofill(RequestInfo requestInfo) {
+ return IntentFactory.createCredentialSelectorIntentForAutofill(
+ mContext, requestInfo, new ArrayList<>(),
+ mResultReceiver);
+ }
}
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index ca72638fd0ff..3cb98eb4cd7a 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.credentials.Constants;
import android.credentials.CredentialProviderInfo;
import android.credentials.GetCandidateCredentialsException;
@@ -114,8 +115,7 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ
return;
}
- cancelExistingPendingIntent();
- mPendingIntent = mCredentialManagerUi.createPendingIntentForAutofill(
+ Intent intent = mCredentialManagerUi.createIntentForAutofill(
RequestInfo.newGetRequestInfo(
mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
@@ -129,7 +129,7 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ
try {
invokeClientCallbackSuccess(new GetCandidateCredentialsResponse(
- candidateProviderDataList, mPendingIntent));
+ candidateProviderDataList, intent));
} catch (RemoteException e) {
Slog.e(TAG, "Issue while responding to client with error : " + e);
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index fa63bc899cb5..cad9a09247e6 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -93,7 +93,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
android.credentials.GetCredentialRequest filteredRequest =
filterOptions(providerInfo.getCapabilities(),
getRequestSession.mClientRequest,
- providerInfo);
+ providerInfo, getRequestSession.mHybridService);
if (filteredRequest != null) {
Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
new HashMap<>();
@@ -129,7 +129,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
android.credentials.GetCredentialRequest filteredRequest =
filterOptions(providerInfo.getCapabilities(),
getRequestSession.mClientRequest,
- providerInfo);
+ providerInfo, getRequestSession.mHybridService);
if (filteredRequest != null) {
Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
new HashMap<>();
@@ -179,9 +179,19 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
private static android.credentials.GetCredentialRequest filterOptions(
List<String> providerCapabilities,
android.credentials.GetCredentialRequest clientRequest,
- CredentialProviderInfo info
- ) {
+ CredentialProviderInfo info,
+ String hybridService) {
Slog.i(TAG, "Filtering request options for: " + info.getComponentName());
+ if (android.credentials.flags.Flags.hybridFilterFixEnabled()) {
+ ComponentName hybridComponentName = ComponentName.unflattenFromString(hybridService);
+ if (hybridComponentName != null && hybridComponentName
+ .equals(info.getComponentName())) {
+ Slog.i(TAG, "Skipping filtering of options for hybrid service");
+ return clientRequest;
+ }
+ Slog.w(TAG, "Could not parse hybrid service while filtering options");
+ }
+
List<CredentialOption> filteredOptions = new ArrayList<>();
for (CredentialOption option : clientRequest.getCredentialOptions()) {
if (providerCapabilities.contains(option.getType())
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
index df7f3084aeb3..1000bfa5f6c9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
@@ -53,7 +53,17 @@ class EnterpriseSpecificIdCalculator {
TelephonyManager telephonyService = context.getSystemService(TelephonyManager.class);
Preconditions.checkState(telephonyService != null, "Unable to access telephony service");
mImei = telephonyService.getImei(0);
- mMeid = telephonyService.getMeid(0);
+ String meid;
+ try {
+ meid = telephonyService.getMeid(0);
+ } catch (UnsupportedOperationException doesNotSupportCdma) {
+ // Instead of catching the exception, we could check for FEATURE_TELEPHONY_CDMA.
+ // However that runs the risk of changing a device's existing ESID if on these devices
+ // telephonyService.getMeid() actually returns non-null even when the device does not
+ // declare FEATURE_TELEPHONY_CDMA.
+ meid = null;
+ }
+ mMeid = meid;
mSerialNumber = Build.getSerial();
WifiManager wifiManager = context.getSystemService(WifiManager.class);
Preconditions.checkState(wifiManager != null, "Unable to access WiFi service");
diff --git a/services/fakes/Android.bp b/services/fakes/Android.bp
new file mode 100644
index 000000000000..148054b31e89
--- /dev/null
+++ b/services/fakes/Android.bp
@@ -0,0 +1,20 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// NOTE: These "fake" services are intended for use under the Ravenwood
+// deviceless test environment, and should *not* be included in the build
+// artifacts for physical devices, as they already supply "real" services
+filegroup {
+ name: "services.fakes-sources",
+ srcs: [
+ "java/**/*.java",
+ ],
+ path: "java",
+ visibility: ["//frameworks/base"],
+}
diff --git a/services/fakes/java/com/android/server/FakeClipboardService.java b/services/fakes/java/com/android/server/FakeClipboardService.java
new file mode 100644
index 000000000000..01016219e73d
--- /dev/null
+++ b/services/fakes/java/com/android/server/FakeClipboardService.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.content.IClipboard;
+import android.content.IOnPrimaryClipChangedListener;
+import android.os.PermissionEnforcer;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Fake implementation of {@code ClipboardManager} since the real implementation is tightly
+ * coupled with many other internal services.
+ */
+public class FakeClipboardService extends IClipboard.Stub {
+ private final RemoteCallbackList<IOnPrimaryClipChangedListener> mListeners =
+ new RemoteCallbackList<>();
+
+ private ClipData mPrimaryClip;
+ private String mPrimaryClipSource;
+
+ public FakeClipboardService(Context context) {
+ super(PermissionEnforcer.fromContext(context));
+ }
+
+ public static class Lifecycle extends SystemService {
+ private FakeClipboardService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new FakeClipboardService(getContext());
+ publishBinderService(Context.CLIPBOARD_SERVICE, mService);
+ }
+ }
+
+ private static void checkArguments(int userId, int deviceId) {
+ Preconditions.checkArgument(userId == UserHandle.USER_SYSTEM,
+ "Fake only supports USER_SYSTEM user");
+ Preconditions.checkArgument(deviceId == Context.DEVICE_ID_DEFAULT,
+ "Fake only supports DEVICE_ID_DEFAULT device");
+ }
+
+ private void dispatchPrimaryClipChanged() {
+ mListeners.broadcast((listener) -> {
+ try {
+ listener.dispatchPrimaryClipChanged();
+ } catch (RemoteException ignored) {
+ }
+ });
+ }
+
+ @Override
+ public void setPrimaryClip(ClipData clip, String callingPackage, String attributionTag,
+ int userId, int deviceId) {
+ checkArguments(userId, deviceId);
+ mPrimaryClip = clip;
+ mPrimaryClipSource = callingPackage;
+ dispatchPrimaryClipChanged();
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.SET_CLIP_SOURCE)
+ public void setPrimaryClipAsPackage(ClipData clip, String callingPackage, String attributionTag,
+ int userId, int deviceId, String sourcePackage) {
+ setPrimaryClipAsPackage_enforcePermission();
+ checkArguments(userId, deviceId);
+ mPrimaryClip = clip;
+ mPrimaryClipSource = sourcePackage;
+ dispatchPrimaryClipChanged();
+ }
+
+ @Override
+ public void clearPrimaryClip(String callingPackage, String attributionTag, int userId,
+ int deviceId) {
+ checkArguments(userId, deviceId);
+ mPrimaryClip = null;
+ mPrimaryClipSource = null;
+ dispatchPrimaryClipChanged();
+ }
+
+ @Override
+ public ClipData getPrimaryClip(String pkg, String attributionTag, int userId, int deviceId) {
+ checkArguments(userId, deviceId);
+ return mPrimaryClip;
+ }
+
+ @Override
+ public ClipDescription getPrimaryClipDescription(String callingPackage, String attributionTag,
+ int userId, int deviceId) {
+ checkArguments(userId, deviceId);
+ return (mPrimaryClip != null) ? mPrimaryClip.getDescription() : null;
+ }
+
+ @Override
+ public boolean hasPrimaryClip(String callingPackage, String attributionTag, int userId,
+ int deviceId) {
+ checkArguments(userId, deviceId);
+ return mPrimaryClip != null;
+ }
+
+ @Override
+ public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
+ String callingPackage, String attributionTag, int userId, int deviceId) {
+ checkArguments(userId, deviceId);
+ mListeners.register(listener);
+ }
+
+ @Override
+ public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
+ String callingPackage, String attributionTag, int userId, int deviceId) {
+ checkArguments(userId, deviceId);
+ mListeners.unregister(listener);
+ }
+
+ @Override
+ public boolean hasClipboardText(String callingPackage, String attributionTag, int userId,
+ int deviceId) {
+ checkArguments(userId, deviceId);
+ return (mPrimaryClip != null) && (mPrimaryClip.getItemCount() > 0)
+ && (mPrimaryClip.getItemAt(0).getText() != null);
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.SET_CLIP_SOURCE)
+ public String getPrimaryClipSource(String callingPackage, String attributionTag, int userId,
+ int deviceId) {
+ getPrimaryClipSource_enforcePermission();
+ checkArguments(userId, deviceId);
+ return mPrimaryClipSource;
+ }
+
+ @Override
+ public boolean areClipboardAccessNotificationsEnabledForUser(int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setClipboardAccessNotificationsEnabledForUser(boolean enable, int userId) {
+ throw new UnsupportedOperationException();
+ }
+}
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 d4b57f191ecd..96e9ca0c19b5 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
@@ -275,6 +275,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
AndroidPackage::isUpdatableSystem,
AndroidPackage::getEmergencyInstaller,
AndroidPackage::isAllowCrossUidActivitySwitchFromBelow,
+ PackageImpl::isAppMetadataFileInApk,
)
override fun extraParams() = listOf(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 418b78cd696b..9c9aeeaadb71 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
@@ -1016,4 +1017,34 @@ public class AutomaticBrightnessControllerTest {
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
assertEquals(500, mController.getAmbientLux(), EPSILON);
}
+
+ @Test
+ public void testBrightnessBasedOnLastObservedLux() throws Exception {
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Set up system to return 0.3f as a brightness value
+ float lux = 100.0f;
+ // Brightness as float (from 0.0f to 1.0f)
+ float normalizedBrightness = 0.3f;
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+ when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt()))
+ .thenReturn(normalizedBrightness);
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+ when(mBrightnessThrottler.isThrottled()).thenReturn(true);
+
+ // Send a new sensor value, disable the sensor and verify
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+ mController.configure(AUTO_BRIGHTNESS_DISABLED, /* configuration= */ null,
+ /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
+ /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT,
+ /* shouldResetShortTermModel= */ true);
+ assertEquals(normalizedBrightness,
+ mController.getAutomaticScreenBrightnessBasedOnLastObservedLux(
+ /* brightnessEvent= */ null), EPSILON);
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 807774f90655..acddc9d22953 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1665,6 +1665,35 @@ public final class DisplayPowerControllerTest {
/* luxTimestamps= */ any());
}
+ @Test
+ public void testInitialDozeBrightness() {
+ when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
+ when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
+ float brightness = 0.277f;
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController
+ .getAutomaticScreenBrightnessBasedOnLastObservedLux(any(BrightnessEvent.class)))
+ .thenReturn(brightness);
+ when(mHolder.hbmController.getCurrentBrightnessMax())
+ .thenReturn(PowerManager.BRIGHTNESS_MAX);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(brightness),
+ /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+ /* ignoreAnimationLimits= */ anyBoolean());
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
index c0c63c69add8..060f99b4317a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -16,9 +16,12 @@
package com.android.server.display.brightness;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
+
import static org.junit.Assert.assertEquals;
import android.hardware.display.BrightnessInfo;
+import android.view.Display;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -39,6 +42,7 @@ public final class BrightnessEventTest {
mBrightnessEvent.setReason(
getReason(BrightnessReason.REASON_DOZE, BrightnessReason.MODIFIER_LOW_POWER));
mBrightnessEvent.setPhysicalDisplayId("test");
+ mBrightnessEvent.setDisplayState(Display.STATE_ON);
mBrightnessEvent.setLux(100.0f);
mBrightnessEvent.setPreThresholdLux(150.0f);
mBrightnessEvent.setTime(System.currentTimeMillis());
@@ -55,6 +59,7 @@ public final class BrightnessEventTest {
mBrightnessEvent.setAdjustmentFlags(0);
mBrightnessEvent.setAutomaticBrightnessEnabled(true);
mBrightnessEvent.setDisplayBrightnessStrategyName(DISPLAY_BRIGHTNESS_STRATEGY_NAME);
+ mBrightnessEvent.setAutoBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
}
@Test
@@ -69,20 +74,20 @@ public final class BrightnessEventTest {
public void testToStringWorksAsExpected() {
String actualString = mBrightnessEvent.toString(false);
String expectedString =
- "BrightnessEvent: disp=1, physDisp=test, brt=0.6, initBrt=25.0, rcmdBrt=0.6,"
- + " preBrt=NaN, lux=100.0, preLux=150.0, hbmMax=0.62, hbmMode=off, rbcStrength=-1,"
- + " thrmMax=0.65, powerFactor=0.2, wasShortTermModelActive=true, flags=,"
- + " reason=doze [ low_pwr ], autoBrightness=true, strategy="
- + DISPLAY_BRIGHTNESS_STRATEGY_NAME;
+ "BrightnessEvent: disp=1, physDisp=test, displayState=ON, brt=0.6, initBrt=25.0,"
+ + " rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150.0, hbmMax=0.62, hbmMode=off,"
+ + " rbcStrength=-1, thrmMax=0.65, powerFactor=0.2, wasShortTermModelActive=true,"
+ + " flags=, reason=doze [ low_pwr ], autoBrightness=true, strategy="
+ + DISPLAY_BRIGHTNESS_STRATEGY_NAME + ", autoBrightnessMode=idle";
assertEquals(expectedString, actualString);
}
@Test
public void testFlagsToString() {
mBrightnessEvent.reset();
- mBrightnessEvent.setFlags(mBrightnessEvent.getFlags() | BrightnessEvent.FLAG_IDLE_CURVE);
+ mBrightnessEvent.setFlags(mBrightnessEvent.getFlags() | BrightnessEvent.FLAG_RBC);
String actualString = mBrightnessEvent.flagsToString();
- String expectedString = "idle_curve ";
+ String expectedString = "rbc ";
assertEquals(expectedString, actualString);
}
@@ -90,10 +95,10 @@ public final class BrightnessEventTest {
public void testFlagsToString_multipleFlags() {
mBrightnessEvent.reset();
mBrightnessEvent.setFlags(mBrightnessEvent.getFlags()
- | BrightnessEvent.FLAG_IDLE_CURVE
+ | BrightnessEvent.FLAG_RBC
| BrightnessEvent.FLAG_LOW_POWER_MODE);
String actualString = mBrightnessEvent.flagsToString();
- String expectedString = "idle_curve low_power_mode ";
+ String expectedString = "rbc low_power_mode ";
assertEquals(expectedString, actualString);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 5408e1143330..886780655de2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -359,11 +359,17 @@ public class AutomaticBrightnessStrategyTest {
AutomaticBrightnessController.class);
when(automaticBrightnessController.getAutomaticScreenBrightness(any(BrightnessEvent.class)))
.thenReturn(automaticScreenBrightness);
+ when(automaticBrightnessController.getAutomaticScreenBrightnessBasedOnLastObservedLux(
+ any(BrightnessEvent.class)))
+ .thenReturn(automaticScreenBrightness);
mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
automaticBrightnessController);
assertEquals(automaticScreenBrightness,
mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(
new BrightnessEvent(DISPLAY_ID)), 0.0f);
+ assertEquals(automaticScreenBrightness,
+ mAutomaticBrightnessStrategy.getAutomaticScreenBrightnessBasedOnLastObservedLux(
+ new BrightnessEvent(DISPLAY_ID)), 0.0f);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index d6e246fc7ee1..6e416857abbf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -25,6 +25,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -42,6 +44,7 @@ import android.content.rollback.RollbackManager;
import android.crashrecovery.flags.Flags;
import android.os.Handler;
import android.os.MessageQueue;
+import android.os.SystemProperties;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.runner.AndroidJUnit4;
@@ -65,6 +68,7 @@ import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import java.time.Duration;
+import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -90,7 +94,7 @@ public class RollbackPackageHealthObserverTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
+ private HashMap<String, String> mSystemSettingsMap;
private MockitoSession mSession;
private static final String APP_A = "com.package.a";
private static final String APP_B = "com.package.b";
@@ -99,6 +103,9 @@ public class RollbackPackageHealthObserverTest {
private static final long VERSION_CODE_2 = 2L;
private static final String LOG_TAG = "RollbackPackageHealthObserverTest";
+ private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG =
+ "persist.device_config.configuration.disable_high_impact_rollback";
+
private SystemConfig mSysConfig;
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
@@ -111,11 +118,34 @@ public class RollbackPackageHealthObserverTest {
.initMocks(this)
.strictness(Strictness.LENIENT)
.spyStatic(PackageWatchdog.class)
+ .spyStatic(SystemProperties.class)
.startMocking();
+ mSystemSettingsMap = new HashMap<>();
// Mock PackageWatchdog
doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog)
.when(() -> PackageWatchdog.getInstance(mMockContext));
+
+ // Mock SystemProperties setter and various getters
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ String value = invocationOnMock.getArgument(1);
+
+ mSystemSettingsMap.put(key, value);
+ return null;
+ }
+ ).when(() -> SystemProperties.set(anyString(), anyString()));
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ boolean defaultValue = invocationOnMock.getArgument(1);
+
+ String storedValue = mSystemSettingsMap.get(key);
+ return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue);
+ }
+ ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean()));
+
+ SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(false));
}
@After
@@ -609,6 +639,32 @@ public class RollbackPackageHealthObserverTest {
observer.onBootLoop(1));
}
+ @Test
+ public void onBootLoop_impactLevelHighDisableHighImpactRollback_onePackage()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true));
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null, false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
+ observer.onBootLoop(1));
+ }
+
/**
* When the rollback impact level is manual only return user impact level 0. (User impact level
* 0 is ignored by package watchdog)
@@ -924,6 +980,50 @@ public class RollbackPackageHealthObserverTest {
assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2));
}
+ /**
+ * Don't roll back if kill switch is enabled.
+ */
+ @Test
+ public void executeBootLoopMitigation_impactLevelHighKillSwitchTrue_rollbackHigh()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true));
+ int rollbackId1 = 1;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoB),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ int rollbackId2 = 2;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoA),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.executeBootLoopMitigation(1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, never()).commitRollback(
+ argument.capture(), any(), any());
+ }
+
private void waitForIdleHandler(Handler handler, Duration timeout) {
final MessageQueue queue = handler.getLooper().getQueue();
final CountDownLatch latch = new CountDownLatch(1);
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 3743483377b5..37967fa86b0f 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -85,6 +85,7 @@ android_test {
"ravenwood-junit",
"net_flags_lib",
"CtsVirtualDeviceCommonLib",
+ "com_android_server_accessibility_flags_lib",
],
libs: [
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
index 5c8c6bb69ef8..e6c94c51d1b1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
@@ -25,6 +25,7 @@ import android.view.Display.DEFAULT_DISPLAY
import android.view.DisplayAdjustments
import android.view.DisplayInfo
import android.view.IInputFilterHost
+import android.view.InputDevice.SOURCE_JOYSTICK
import android.view.InputDevice.SOURCE_STYLUS
import android.view.InputDevice.SOURCE_TOUCHSCREEN
import android.view.InputEvent
@@ -130,6 +131,8 @@ class AccessibilityInputFilterInputTest {
private val fromTouchScreen = allOf(withDeviceId(touchDeviceId), withSource(SOURCE_TOUCHSCREEN))
private val stylusDeviceId = 2
private val fromStylus = allOf(withDeviceId(stylusDeviceId), withSource(STYLUS_SOURCE))
+ private val joystickDeviceId = 3
+ private val fromJoystick = allOf(withDeviceId(joystickDeviceId), withSource(SOURCE_JOYSTICK))
@Before
fun setUp() {
@@ -457,6 +460,23 @@ class AccessibilityInputFilterInputTest {
verifier.assertNoEvents()
}
+ /**
+ * Send some joystick events and ensure they pass through normally.
+ */
+ @Test
+ fun testJoystickEvents() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ sendJoystickEvent()
+ verifier.assertReceivedMotion(fromJoystick)
+
+ sendJoystickEvent()
+ verifier.assertReceivedMotion(fromJoystick)
+
+ sendJoystickEvent()
+ verifier.assertReceivedMotion(fromJoystick)
+ }
+
private fun createStubDisplay(displayId: Int, displayInfo: DisplayInfo): Display {
val display = Display(DisplayManagerGlobal.getInstance(), displayId,
displayInfo, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS)
@@ -477,6 +497,11 @@ class AccessibilityInputFilterInputTest {
send(createMotionEvent(action, downTime, eventTime, STYLUS_SOURCE, stylusDeviceId))
}
+ private fun sendJoystickEvent() {
+ val time = SystemClock.uptimeMillis()
+ send(createMotionEvent(ACTION_MOVE, time, time, SOURCE_JOYSTICK, joystickDeviceId))
+ }
+
private fun send(event: InputEvent) {
// We need to make a copy of the event before sending it to the filter, because the filter
// will recycle it, but the caller of this function might want to still be able to use
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 89d146da1b75..8717a0500e57 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -47,6 +47,9 @@ import android.os.IBinder;
import android.os.LocaleList;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.SparseArray;
import android.view.Display;
import android.view.IWindow;
@@ -67,6 +70,7 @@ import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -77,9 +81,16 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+// This test verifies deprecated codepath. Probably changing this file means
+// AccessibilityWindowManagerWithAccessibilityWindowTest also needs to be updated.
+// LINT.IfChange
+
/**
- * Tests for the AccessibilityWindowManager
+ * Tests for the AccessibilityWindowManager with Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y enabled.
+ * TODO(b/322444245): Merge with AccessibilityWindowManagerWithAccessibilityWindowTest
+ * after completing the flag migration.
*/
+@RequiresFlagsDisabled(Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y)
public class AccessibilityWindowManagerTest {
private static final String PACKAGE_NAME = "com.android.server.accessibility";
private static final boolean FORCE_SEND = true;
@@ -132,6 +143,9 @@ public class AccessibilityWindowManagerTest {
@Mock private IBinder mMockEmbeddedToken;
@Mock private IBinder mMockInvalidToken;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
@@ -1227,3 +1241,4 @@ public class AccessibilityWindowManagerTest {
}
}
}
+// LINT.ThenChange(/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java)
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
new file mode 100644
index 000000000000..4db27d272f8e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
@@ -0,0 +1,1247 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT;
+import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId;
+import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges;
+import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.LocaleList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.IWindow;
+import android.view.WindowInfo;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowAttributes;
+import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+
+import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
+import com.android.server.accessibility.test.MessageCapturingHandler;
+import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for the AccessibilityWindowManager with Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y
+ * TODO(b/322444245): Merge with AccessibilityWindowManagerTest
+ * after completing the flag migration.
+ */
+@RequiresFlagsEnabled(Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y)
+public class AccessibilityWindowManagerWithAccessibilityWindowTest {
+ private static final String PACKAGE_NAME = "com.android.server.accessibility";
+ private static final boolean FORCE_SEND = true;
+ private static final boolean SEND_ON_WINDOW_CHANGES = false;
+ private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM;
+ private static final int USER_PROFILE = 11;
+ private static final int USER_PROFILE_PARENT = 1;
+ private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
+ private static final int NUM_GLOBAL_WINDOWS = 4;
+ private static final int NUM_APP_WINDOWS = 4;
+ private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS);
+ private static final int DEFAULT_FOCUSED_INDEX = 1;
+ private static final int SCREEN_WIDTH = 1080;
+ private static final int SCREEN_HEIGHT = 1920;
+ private static final int INVALID_ID = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ private static final int HOST_WINDOW_ID = 10;
+ private static final int EMBEDDED_WINDOW_ID = 11;
+ private static final int OTHER_WINDOW_ID = 12;
+
+ private AccessibilityWindowManager mA11yWindowManager;
+ // Window manager will support multiple focused window if config_perDisplayFocusEnabled is true,
+ // i.e., each display would have its current focused window, and one of all focused windows
+ // would be top focused window. Otherwise, window manager only supports one focused window
+ // at all displays, and that focused window would be top focused window.
+ private boolean mSupportPerDisplayFocus = false;
+ private int mTopFocusedDisplayId = Display.INVALID_DISPLAY;
+ private IBinder mTopFocusedWindowToken = null;
+
+ // List of window token, mapping from windowId -> window token.
+ private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>();
+ // List of window info lists, mapping from displayId -> window info lists.
+ private final SparseArray<ArrayList<WindowInfo>> mWindowInfos =
+ new SparseArray<>();
+ // List of callback, mapping from displayId -> callback.
+ private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows =
+ new SparseArray<>();
+ // List of display ID.
+ private final ArrayList<Integer> mExpectedDisplayList = new ArrayList<>(Arrays.asList(
+ Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID));
+
+ private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
+
+ @Mock
+ private WindowManagerInternal mMockWindowManagerInternal;
+ @Mock
+ private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender;
+ @Mock
+ private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
+ @Mock
+ private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
+ @Mock
+ private AccessibilityTraceManager mMockA11yTraceManager;
+
+ @Mock
+ private IBinder mMockHostToken;
+ @Mock
+ private IBinder mMockEmbeddedToken;
+ @Mock
+ private IBinder mMockInvalidToken;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ when(mMockA11yUserManager.getCurrentUserIdLocked()).thenReturn(USER_SYSTEM_ID);
+ when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
+ USER_PROFILE)).thenReturn(USER_PROFILE_PARENT);
+ when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
+ USER_SYSTEM_ID)).thenReturn(USER_SYSTEM_ID);
+ when(mMockA11ySecurityPolicy.resolveValidReportedPackageLocked(
+ anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME);
+
+ doAnswer((invocation) -> {
+ onWindowsForAccessibilityChanged(invocation.getArgument(0), false);
+ return null;
+ }).when(mMockWindowManagerInternal).computeWindowsForAccessibility(anyInt());
+
+ mA11yWindowManager = new AccessibilityWindowManager(new Object(), mHandler,
+ mMockWindowManagerInternal,
+ mMockA11yEventSender,
+ mMockA11ySecurityPolicy,
+ mMockA11yUserManager,
+ mMockA11yTraceManager);
+ // Starts tracking window of default display and sets the default display
+ // as top focused display before each testing starts.
+ startTrackingPerDisplay(Display.DEFAULT_DISPLAY);
+
+ // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged.
+ // Resets it for mockito verify of further test case.
+ Mockito.reset(mMockA11yEventSender);
+
+ registerLeashedTokenAndWindowId();
+ }
+
+ @After
+ public void tearDown() {
+ mHandler.removeAllMessages();
+ }
+
+ @Test
+ public void startTrackingWindows_shouldEnableWindowManagerCallback() {
+ // AccessibilityWindowManager#startTrackingWindows already invoked in setup.
+ assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
+ final WindowsForAccessibilityCallback callbacks =
+ mCallbackOfWindows.get(Display.DEFAULT_DISPLAY);
+ verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(
+ eq(Display.DEFAULT_DISPLAY), eq(callbacks));
+ }
+
+ @Test
+ public void stopTrackingWindows_shouldDisableWindowManagerCallback() {
+ assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
+ Mockito.reset(mMockWindowManagerInternal);
+
+ mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
+ assertFalse(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
+ verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(
+ eq(Display.DEFAULT_DISPLAY), isNull());
+
+ }
+
+ @Test
+ public void stopTrackingWindows_shouldClearWindows() {
+ assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
+ final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
+
+ mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
+ assertNull(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY));
+ assertEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
+ AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
+ assertEquals(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID),
+ activeWindowId);
+ }
+
+ @Test
+ public void stopTrackingWindows_onNonTopFocusedDisplay_shouldNotResetTopFocusWindow()
+ throws RemoteException {
+ // At setup, the default display sets be the top focused display and
+ // its current focused window sets be the top focused window.
+ // Starts tracking window of second display.
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+ assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID));
+ // Stops tracking windows of second display.
+ mA11yWindowManager.stopTrackingWindows(SECONDARY_DISPLAY_ID);
+ assertNotEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
+ AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
+ }
+
+ @Test
+ public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() {
+ final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
+ WindowInfo focusedWindowInfo =
+ mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX);
+ assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, focusedWindowInfo.token));
+
+ focusedWindowInfo.focused = false;
+ focusedWindowInfo =
+ mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1);
+ focusedWindowInfo.focused = true;
+
+ mA11yWindowManager.onTouchInteractionStart();
+ setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1);
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
+ }
+
+ @Test
+ public void
+ onWindowsChanged_focusChangeOnNonTopFocusedDisplay_perDisplayFocusOn_notChangeWindow()
+ throws RemoteException {
+ // At setup, the default display sets be the top focused display and
+ // its current focused window sets be the top focused window.
+ // Sets supporting multiple focused window, i.e., config_perDisplayFocusEnabled is true.
+ mSupportPerDisplayFocus = true;
+ // Starts tracking window of second display.
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+ // Gets the active window.
+ final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
+ // Gets the top focused window.
+ final int topFocusedWindowId =
+ mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT);
+ // Changes the current focused window at second display.
+ changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
+ DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
+
+ onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
+ // The active window should not be changed.
+ assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
+ // The top focused window should not be changed.
+ assertEquals(topFocusedWindowId,
+ mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT));
+ }
+
+ @Test
+ public void
+ onWindowChange_focusChangeToNonTopFocusedDisplay_perDisplayFocusOff_shouldChangeWindow()
+ throws RemoteException {
+ // At setup, the default display sets be the top focused display and
+ // its current focused window sets be the top focused window.
+ // Sets not supporting multiple focused window, i.e., config_perDisplayFocusEnabled is
+ // false.
+ mSupportPerDisplayFocus = false;
+ // Starts tracking window of second display.
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+ // Gets the active window.
+ final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
+ // Gets the top focused window.
+ final int topFocusedWindowId =
+ mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT);
+ // Changes the current focused window from default display to second display.
+ changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
+ DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
+ // The active window should be changed.
+ assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
+ // The top focused window should be changed.
+ assertNotEquals(topFocusedWindowId,
+ mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT));
+ }
+
+ @Test
+ public void onWindowsChanged_shouldReportCorrectLayer() {
+ // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
+ List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ for (int i = 0; i < a11yWindows.size(); i++) {
+ final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
+ final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i);
+ assertThat(mWindowInfos.get(Display.DEFAULT_DISPLAY).size() - windowInfo.layer - 1,
+ is(a11yWindow.getLayer()));
+ }
+ }
+
+ @Test
+ public void onWindowsChanged_shouldReportCorrectOrder() {
+ // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
+ List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ for (int i = 0; i < a11yWindows.size(); i++) {
+ final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
+ final IBinder windowToken = mA11yWindowManager
+ .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId());
+ final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i);
+ assertThat(windowToken, is(windowInfo.token));
+ }
+ }
+
+ @Test
+ public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
+ final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+ final int correctLayer =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer();
+ windowInfo.layer += 1;
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+ assertNotEquals(correctLayer,
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer());
+ }
+
+ @Test
+ public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() {
+ final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+ final int correctLayer =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer();
+ windowInfo.layer += 1;
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ assertEquals(correctLayer,
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer());
+ }
+
+ @Test
+ public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows()
+ throws RemoteException {
+ final AccessibilityWindowInfo oldWindow =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0);
+ final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ true, USER_SYSTEM_ID);
+ final WindowInfo windowInfo = WindowInfo.obtain();
+ windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
+ windowInfo.token = token.asBinder();
+ windowInfo.layer = 0;
+ windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ mWindowInfos.get(Display.DEFAULT_DISPLAY).set(0, windowInfo);
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ assertNotEquals(oldWindow,
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0));
+ }
+
+ @Test
+ public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() {
+ final WindowInfo focusedWindowInfo =
+ mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX);
+ final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+ focusedWindowInfo.focused = false;
+ windowInfo.focused = true;
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)
+ .isFocused());
+ }
+
+ @Test
+ public void removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved() {
+ for (int i = 0; i < NUM_OF_WINDOWS; i++) {
+ final int windowId = mA11yWindowTokens.keyAt(i);
+ final IWindow windowToken = mA11yWindowTokens.valueAt(i);
+ assertNotNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId));
+
+ mA11yWindowManager.removeAccessibilityInteractionConnection(windowToken);
+ assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId));
+ }
+ }
+
+ @Test
+ public void remoteAccessibilityConnection_binderDied_shouldRemoveConnection() {
+ for (int i = 0; i < NUM_OF_WINDOWS; i++) {
+ final int windowId = mA11yWindowTokens.keyAt(i);
+ final RemoteAccessibilityConnection remoteA11yConnection =
+ mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId);
+ assertNotNull(remoteA11yConnection);
+
+ remoteA11yConnection.binderDied();
+ assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId));
+ }
+ }
+
+ @Test
+ public void getWindowTokenForUserAndWindowId_shouldNotNull() {
+ final List<AccessibilityWindowInfo> windows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ for (int i = 0; i < windows.size(); i++) {
+ final int windowId = windows.get(i).getId();
+
+ assertNotNull(mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
+ USER_SYSTEM_ID, windowId));
+ }
+ }
+
+ @Test
+ public void findWindowId() {
+ final List<AccessibilityWindowInfo> windows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ for (int i = 0; i < windows.size(); i++) {
+ final int windowId = windows.get(i).getId();
+ final IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
+ USER_SYSTEM_ID, windowId);
+
+ assertEquals(mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, windowToken), windowId);
+ }
+ }
+
+ @Test
+ public void resolveParentWindowId_windowIsNotEmbedded_shouldReturnGivenId()
+ throws RemoteException {
+ final int windowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false,
+ Mockito.mock(IBinder.class), USER_SYSTEM_ID);
+ assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
+ }
+
+ @Test
+ public void resolveParentWindowId_windowIsNotRegistered_shouldReturnGivenId() {
+ final int windowId = -1;
+ assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
+ }
+
+ @Test
+ public void resolveParentWindowId_windowIsAssociated_shouldReturnParentWindowId()
+ throws RemoteException {
+ final IBinder mockHostToken = Mockito.mock(IBinder.class);
+ final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
+ final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, mockHostToken, USER_SYSTEM_ID);
+ final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, mockEmbeddedToken, USER_SYSTEM_ID);
+
+ mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
+
+ final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
+ embeddedWindowId);
+ assertEquals(hostWindowId, resolvedWindowId);
+ }
+
+ @Test
+ public void resolveParentWindowId_windowIsDisassociated_shouldReturnGivenId()
+ throws RemoteException {
+ final IBinder mockHostToken = Mockito.mock(IBinder.class);
+ final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
+ final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, mockHostToken, USER_SYSTEM_ID);
+ final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, mockEmbeddedToken, USER_SYSTEM_ID);
+
+ mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
+ mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken);
+
+ final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
+ embeddedWindowId);
+ assertNotEquals(hostWindowId, resolvedWindowId);
+ assertEquals(embeddedWindowId, resolvedWindowId);
+ }
+
+ @Test
+ public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() {
+ // Updates top 2 z-order WindowInfo are whole visible.
+ WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+ windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
+ windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1);
+ windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2,
+ SCREEN_WIDTH, SCREEN_HEIGHT);
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ final Region outBounds = new Region();
+ int windowId = a11yWindows.get(0).getId();
+
+ mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
+ assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH));
+ assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2));
+
+ windowId = a11yWindows.get(1).getId();
+
+ mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
+ assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH));
+ assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2));
+ }
+
+ @Test
+ public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() {
+ // Updates z-order #1 WindowInfo is half visible.
+ WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+ windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ final Region outBounds = new Region();
+ int windowId = a11yWindows.get(1).getId();
+
+ mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
+ assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH));
+ assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2));
+ }
+
+ @Test
+ public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() {
+ // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible.
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ final Region outBounds = new Region();
+ int windowId = a11yWindows.get(1).getId();
+
+ mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
+ assertTrue(outBounds.getBounds().isEmpty());
+ }
+
+ @Test
+ public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() {
+ // Updates z-order #0 WindowInfo to have two interact-able areas.
+ Region region = new Region(0, 0, SCREEN_WIDTH, 200);
+ region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION);
+ WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+ windowInfo.regionInScreen.set(region);
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ final Region outBounds = new Region();
+ int windowId = a11yWindows.get(1).getId();
+
+ mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
+ assertFalse(outBounds.getBounds().isEmpty());
+ assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH));
+ assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT - 400));
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() {
+ final IBinder eventWindowToken =
+ mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1).token;
+ final int eventWindowId = mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, eventWindowToken);
+ when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates())
+ .thenReturn(eventWindowToken);
+
+ final int noUse = 0;
+ mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ eventWindowId,
+ noUse,
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ noUse);
+ assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId));
+ assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
+ is(eventWindowId));
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_hoverEvent_touchInteract_shouldSetActiveWindow() {
+ final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+ DEFAULT_FOCUSED_INDEX + 1);
+ final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
+ assertThat(currentActiveWindowId, is(not(eventWindowId)));
+
+ final int noUse = 0;
+ mA11yWindowManager.onTouchInteractionStart();
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ eventWindowId,
+ noUse,
+ AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
+ noUse);
+ assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId));
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(2))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(currentActiveWindowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
+ assertThat(captor.getAllValues().get(1),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(eventWindowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_shouldUpdateA11yFocus() {
+ final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+ DEFAULT_FOCUSED_INDEX);
+ final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
+ assertThat(currentA11yFocusedWindowId, is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID));
+
+ final int noUse = 0;
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ eventWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ noUse);
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId));
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(eventWindowId),
+ a11yWindowChanges(
+ AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_defaultToSecondary()
+ throws RemoteException {
+ runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
+ Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID);
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_SecondaryToDefault()
+ throws RemoteException {
+ runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
+ SECONDARY_DISPLAY_ID, Display.DEFAULT_DISPLAY);
+ }
+
+ private void runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
+ int initialDisplayId, int eventDisplayId) throws RemoteException {
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+ final int initialWindowId = getWindowIdFromWindowInfosForDisplay(
+ initialDisplayId, DEFAULT_FOCUSED_INDEX);
+ final int noUse = 0;
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ initialWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ noUse);
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(initialWindowId));
+ Mockito.reset(mMockA11yEventSender);
+
+ final int eventWindowId = getWindowIdFromWindowInfosForDisplay(
+ eventDisplayId, DEFAULT_FOCUSED_INDEX);
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ eventWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ noUse);
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId));
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(2))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(initialDisplayId),
+ a11yWindowId(initialWindowId),
+ a11yWindowChanges(
+ AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
+ assertThat(captor.getAllValues().get(1),
+ allOf(displayId(eventDisplayId),
+ a11yWindowId(eventWindowId),
+ a11yWindowChanges(
+ AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_clearA11yFocusEvent_shouldClearA11yFocus() {
+ final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+ DEFAULT_FOCUSED_INDEX);
+ final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
+ assertThat(currentA11yFocusedWindowId, is(not(eventWindowId)));
+
+ final int noUse = 0;
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ eventWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ noUse);
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId));
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ eventWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
+ noUse);
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY),
+ is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID));
+ }
+
+ @Test
+ public void onTouchInteractionEnd_shouldRollbackActiveWindow() {
+ final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+ DEFAULT_FOCUSED_INDEX + 1);
+ final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
+ assertThat(currentActiveWindowId, is(not(eventWindowId)));
+
+ final int noUse = 0;
+ mA11yWindowManager.onTouchInteractionStart();
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ eventWindowId,
+ noUse,
+ AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
+ noUse);
+ assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId));
+ // AccessibilityEventSender is invoked after active window changed. Reset it.
+ Mockito.reset(mMockA11yEventSender);
+
+ mA11yWindowManager.onTouchInteractionEnd();
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(2))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(eventWindowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
+ assertThat(captor.getAllValues().get(1),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(currentActiveWindowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
+ }
+
+ @Test
+ public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus()
+ throws RemoteException {
+ final IBinder defaultFocusWinToken =
+ mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).token;
+ final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, defaultFocusWinToken);
+ when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates())
+ .thenReturn(defaultFocusWinToken);
+ final int newFocusWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+ DEFAULT_FOCUSED_INDEX + 1);
+ final IAccessibilityInteractionConnection mockNewFocusConnection =
+ mA11yWindowManager.getConnectionLocked(
+ USER_SYSTEM_ID, newFocusWindowId).getRemote();
+
+ mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
+ final int noUse = 0;
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ defaultFocusWindowId,
+ noUse,
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ noUse);
+ assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(defaultFocusWindowId));
+ assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
+ is(defaultFocusWindowId));
+
+ mA11yWindowManager.onTouchInteractionStart();
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ newFocusWindowId,
+ noUse,
+ AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
+ noUse);
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ newFocusWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ noUse);
+ assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(newFocusWindowId));
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(newFocusWindowId));
+
+ mA11yWindowManager.onTouchInteractionEnd();
+ mHandler.sendLastMessage();
+ verify(mockNewFocusConnection).clearAccessibilityFocus();
+ }
+
+ @Test
+ public void getPictureInPictureWindow_shouldNotNull() {
+ assertNull(mA11yWindowManager.getPictureInPictureWindowLocked());
+ mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1).inPictureInPicture = true;
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked());
+ }
+
+ @Test
+ public void notifyOutsideTouch() throws RemoteException {
+ final int targetWindowId =
+ getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 1);
+ final int outsideWindowId =
+ getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
+ final IAccessibilityInteractionConnection mockRemoteConnection =
+ mA11yWindowManager.getConnectionLocked(
+ USER_SYSTEM_ID, outsideWindowId).getRemote();
+ mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0).hasFlagWatchOutsideTouch = true;
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId);
+ verify(mockRemoteConnection).notifyOutsideTouch();
+ }
+
+ @Test
+ public void addAccessibilityInteractionConnection_profileUser_findInParentUser()
+ throws RemoteException {
+ final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, USER_PROFILE);
+ final int windowId = mA11yWindowManager.findWindowIdLocked(
+ USER_PROFILE_PARENT, token.asBinder());
+ assertTrue(windowId >= 0);
+ }
+
+ @Test
+ public void getDisplayList() throws RemoteException {
+ // Starts tracking window of second display.
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+
+ final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked(
+ DISPLAY_TYPE_DEFAULT);
+ assertTrue(displayList.equals(mExpectedDisplayList));
+ }
+
+ @Test
+ public void setAccessibilityWindowIdToSurfaceMetadata()
+ throws RemoteException {
+ final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ true, USER_SYSTEM_ID);
+ int windowId = -1;
+ for (int i = 0; i < mA11yWindowTokens.size(); i++) {
+ if (mA11yWindowTokens.valueAt(i).equals(token)) {
+ windowId = mA11yWindowTokens.keyAt(i);
+ }
+ }
+ assertNotEquals("Returned token is not found in mA11yWindowTokens", -1, windowId);
+ verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata(
+ token.asBinder(), windowId);
+
+ mA11yWindowManager.removeAccessibilityInteractionConnection(token);
+ verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata(
+ token.asBinder(), -1);
+ }
+
+ @Test
+ public void getHostTokenLocked_hierarchiesAreAssociated_shouldReturnHostToken() {
+ mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
+ final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
+ assertEquals(hostToken, mMockHostToken);
+ }
+
+ @Test
+ public void getHostTokenLocked_hierarchiesAreNotAssociated_shouldReturnNull() {
+ final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
+ assertNull(hostToken);
+ }
+
+ @Test
+ public void getHostTokenLocked_embeddedHierarchiesAreDisassociated_shouldReturnNull() {
+ mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
+ mA11yWindowManager.disassociateLocked(mMockEmbeddedToken);
+ final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
+ assertNull(hostToken);
+ }
+
+ @Test
+ public void getHostTokenLocked_hostHierarchiesAreDisassociated_shouldReturnNull() {
+ mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
+ mA11yWindowManager.disassociateLocked(mMockHostToken);
+ final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockHostToken);
+ assertNull(hostToken);
+ }
+
+ @Test
+ public void getWindowIdLocked_windowIsRegistered_shouldReturnWindowId() {
+ final int windowId = mA11yWindowManager.getWindowIdLocked(mMockHostToken);
+ assertEquals(windowId, HOST_WINDOW_ID);
+ }
+
+ @Test
+ public void getWindowIdLocked_windowIsNotRegistered_shouldReturnInvalidWindowId() {
+ final int windowId = mA11yWindowManager.getWindowIdLocked(mMockInvalidToken);
+ assertEquals(windowId, INVALID_ID);
+ }
+
+ @Test
+ public void getTokenLocked_windowIsRegistered_shouldReturnToken() {
+ final IBinder token = mA11yWindowManager.getLeashTokenLocked(HOST_WINDOW_ID);
+ assertEquals(token, mMockHostToken);
+ }
+
+ @Test
+ public void getTokenLocked_windowIsNotRegistered_shouldReturnNull() {
+ final IBinder token = mA11yWindowManager.getLeashTokenLocked(OTHER_WINDOW_ID);
+ assertNull(token);
+ }
+
+ @Test
+ public void setAccessibilityWindowAttributes_windowIsNotRegistered_titleIsChanged() {
+ final int windowId =
+ getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+ layoutParams.accessibilityTitle = "accessibility window title";
+ final AccessibilityWindowAttributes attributes = new AccessibilityWindowAttributes(
+ layoutParams, new LocaleList());
+
+ mA11yWindowManager.setAccessibilityWindowAttributes(Display.DEFAULT_DISPLAY, windowId,
+ USER_SYSTEM_ID, attributes);
+
+ final AccessibilityWindowInfo a11yWindow = mA11yWindowManager.findA11yWindowInfoByIdLocked(
+ windowId);
+ assertEquals(toString(layoutParams.accessibilityTitle), toString(a11yWindow.getTitle()));
+ }
+
+ @Test
+ public void sendAccessibilityEventOnWindowRemoval() {
+ final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+
+ // Removing index 0 because it's not focused, and avoids unnecessary layer change.
+ final int windowId =
+ getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
+ infos.remove(0);
+ for (WindowInfo info : infos) {
+ // Adjust layer number because it should start from 0.
+ info.layer--;
+ }
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(windowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED)));
+ }
+
+ @Test
+ public void sendAccessibilityEventOnWindowAddition() throws RemoteException {
+ final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+
+ for (WindowInfo info : infos) {
+ // Adjust layer number because new window will have 0 so that layer number in
+ // A11yWindowInfo in window won't be changed.
+ info.layer++;
+ }
+
+ final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, USER_SYSTEM_ID);
+ addWindowInfo(infos, token, 0);
+ final int windowId =
+ getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, infos.size() - 1);
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(windowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED)));
+ }
+
+ @Test
+ public void sendAccessibilityEventOnWindowChange() {
+ final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+ infos.get(0).title = "new title";
+ final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(windowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE)));
+ }
+
+ private void registerLeashedTokenAndWindowId() {
+ mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
+ mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);
+ }
+
+ private void startTrackingPerDisplay(int displayId) throws RemoteException {
+ ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
+ // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
+ // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos
+ // for the test.
+ int layer = 0;
+ for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) {
+ final IWindow token = addAccessibilityInteractionConnection(displayId,
+ true, USER_SYSTEM_ID);
+ addWindowInfo(windowInfosForDisplay, token, layer++);
+
+ }
+ for (int i = 0; i < NUM_APP_WINDOWS; i++) {
+ final IWindow token = addAccessibilityInteractionConnection(displayId,
+ false, USER_SYSTEM_ID);
+ addWindowInfo(windowInfosForDisplay, token, layer++);
+ }
+ // Sets up current focused window of display.
+ // Each display has its own current focused window if config_perDisplayFocusEnabled is true.
+ // Otherwise only default display needs to current focused window.
+ if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) {
+ windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true;
+ }
+ // Turns on windows tracking, and update window info.
+ mA11yWindowManager.startTrackingWindows(displayId, false);
+ // Puts window lists into array.
+ mWindowInfos.put(displayId, windowInfosForDisplay);
+ // Sets the default display is the top focused display and
+ // its current focused window is the top focused window.
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX);
+ }
+ // Invokes callback for sending window lists to A11y framework.
+ onWindowsForAccessibilityChanged(displayId, FORCE_SEND);
+
+ assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(),
+ windowInfosForDisplay.size());
+ }
+
+ private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) {
+ ArgumentCaptor<WindowsForAccessibilityCallback> windowsForAccessibilityCallbacksCaptor =
+ ArgumentCaptor.forClass(
+ WindowsForAccessibilityCallback.class);
+ verify(mMockWindowManagerInternal)
+ .setWindowsForAccessibilityCallback(eq(displayId),
+ windowsForAccessibilityCallbacksCaptor.capture());
+ return windowsForAccessibilityCallbacksCaptor.getValue();
+ }
+
+ private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal,
+ int userId) throws RemoteException {
+ final IWindow mockWindowToken = Mockito.mock(IWindow.class);
+ final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
+ IAccessibilityInteractionConnection.class);
+ final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
+ final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
+ final IBinder mockLeashToken = Mockito.mock(IBinder.class);
+ when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
+ when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
+ when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
+ .thenReturn(bGlobal);
+ when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
+ .thenReturn(displayId);
+
+ int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
+ mockWindowToken, mockLeashToken, mockA11yConnection, PACKAGE_NAME, userId);
+ mA11yWindowTokens.put(windowId, mockWindowToken);
+ return mockWindowToken;
+ }
+
+ private int addAccessibilityInteractionConnection(int displayId, boolean bGlobal,
+ IBinder leashToken, int userId) throws RemoteException {
+ final IWindow mockWindowToken = Mockito.mock(IWindow.class);
+ final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
+ IAccessibilityInteractionConnection.class);
+ final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
+ final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
+ when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
+ when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
+ when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
+ .thenReturn(bGlobal);
+ when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
+ .thenReturn(displayId);
+
+ int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
+ mockWindowToken, leashToken, mockA11yConnection, PACKAGE_NAME, userId);
+ mA11yWindowTokens.put(windowId, mockWindowToken);
+ return windowId;
+ }
+
+ private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) {
+ final WindowInfo windowInfo = WindowInfo.obtain();
+ windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
+ windowInfo.token = windowToken.asBinder();
+ windowInfo.layer = layer;
+ windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ windowInfos.add(windowInfo);
+ }
+
+ private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) {
+ final IBinder windowToken = mWindowInfos.get(displayId).get(index).token;
+ return mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, windowToken);
+ }
+
+ private void setTopFocusedWindowAndDisplay(int displayId, int index) {
+ // Sets the top focus window.
+ mTopFocusedWindowToken = mWindowInfos.get(displayId).get(index).token;
+ // Sets the top focused display.
+ mTopFocusedDisplayId = displayId;
+ }
+
+ private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) {
+ WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId);
+ if (callbacks == null) {
+ callbacks = getWindowsForAccessibilityCallbacks(displayId);
+ mCallbackOfWindows.put(displayId, callbacks);
+ }
+ callbacks.onWindowsForAccessibilityChanged(forceSend, mTopFocusedDisplayId,
+ mTopFocusedWindowToken, mWindowInfos.get(displayId));
+ }
+
+ private void changeFocusedWindowOnDisplayPerDisplayFocusConfig(
+ int changeFocusedDisplayId, int newFocusedWindowIndex, int oldTopFocusedDisplayId,
+ int oldFocusedWindowIndex) {
+ if (mSupportPerDisplayFocus) {
+ // Gets the old focused window of display which wants to change focused window.
+ WindowInfo focusedWindowInfo =
+ mWindowInfos.get(changeFocusedDisplayId).get(oldFocusedWindowIndex);
+ // Resets the focus of old focused window.
+ focusedWindowInfo.focused = false;
+ // Gets the new window of display which wants to change focused window.
+ focusedWindowInfo =
+ mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex);
+ // Sets the focus of new focused window.
+ focusedWindowInfo.focused = true;
+ } else {
+ // Gets the window of display which wants to change focused window.
+ WindowInfo focusedWindowInfo =
+ mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex);
+ // Sets the focus of new focused window.
+ focusedWindowInfo.focused = true;
+ // Gets the old focused window of old top focused display.
+ focusedWindowInfo =
+ mWindowInfos.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex);
+ // Resets the focus of old focused window.
+ focusedWindowInfo.focused = false;
+ // Changes the top focused display and window.
+ setTopFocusedWindowAndDisplay(changeFocusedDisplayId, newFocusedWindowIndex);
+ }
+ }
+
+ @Nullable
+ private static String toString(@Nullable CharSequence cs) {
+ return cs == null ? null : cs.toString();
+ }
+
+ static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+ private final int mDisplayId;
+
+ DisplayIdMatcher(int displayId) {
+ super();
+ mDisplayId = displayId;
+ }
+
+ static DisplayIdMatcher displayId(int displayId) {
+ return new DisplayIdMatcher(displayId);
+ }
+
+ @Override
+ protected boolean matchesSafely(AccessibilityEvent event) {
+ return event.getDisplayId() == mDisplayId;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matching to displayId " + mDisplayId);
+ }
+ }
+
+ static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+ private int mWindowId;
+
+ WindowIdMatcher(int windowId) {
+ super();
+ mWindowId = windowId;
+ }
+
+ static WindowIdMatcher a11yWindowId(int windowId) {
+ return new WindowIdMatcher(windowId);
+ }
+
+ @Override
+ protected boolean matchesSafely(AccessibilityEvent event) {
+ return event.getWindowId() == mWindowId;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matching to windowId " + mWindowId);
+ }
+ }
+
+ static class WindowChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+ private int mWindowChanges;
+
+ WindowChangesMatcher(int windowChanges) {
+ super();
+ mWindowChanges = windowChanges;
+ }
+
+ static WindowChangesMatcher a11yWindowChanges(int windowChanges) {
+ return new WindowChangesMatcher(windowChanges);
+ }
+
+ @Override
+ protected boolean matchesSafely(AccessibilityEvent event) {
+ return event.getWindowChanges() == mWindowChanges;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matching to window changes " + mWindowChanges);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index a529382bfb26..b1e62f9c9a82 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -149,6 +149,7 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
import android.net.NetworkStateSnapshot;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
@@ -1620,6 +1621,12 @@ public class NetworkPolicyManagerServiceTest {
verify(mActivityManagerInternal).notifyNetworkPolicyRulesUpdated(UID_A, procStateSeq);
}
+ private void callAndWaitOnUidGone(int uid) throws Exception {
+ // The disabled argument is used only for ephemeral apps and does not matter here.
+ mUidObserver.onUidGone(uid, false /* disabled */);
+ waitForUidEventHandlerIdle();
+ }
+
private void callAndWaitOnUidStateChanged(int uid, int procState, long procStateSeq)
throws Exception {
callAndWaitOnUidStateChanged(uid, procState, procStateSeq,
@@ -2250,6 +2257,15 @@ public class NetworkPolicyManagerServiceTest {
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
}
+ private boolean isUidState(int uid, int procState, int procStateSeq, int capability) {
+ final NetworkPolicyManager.UidState uidState = mService.getUidStateForTest(uid);
+ if (uidState == null) {
+ return false;
+ }
+ return uidState.uid == uid && uidState.procStateSeq == procStateSeq
+ && uidState.procState == procState && uidState.capability == capability;
+ }
+
@Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
@@ -2371,6 +2387,31 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
+ public void testUidStateChangeAfterUidGone() throws Exception {
+ final int testProcStateSeq = 51;
+ final int testProcState = PROCESS_STATE_IMPORTANT_FOREGROUND;
+
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // First callback for uid.
+ callOnUidStatechanged(UID_B, testProcState, testProcStateSeq, PROCESS_CAPABILITY_NONE);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ assertTrue(isUidState(UID_B, testProcState, testProcStateSeq, PROCESS_CAPABILITY_NONE));
+
+ callAndWaitOnUidGone(UID_B);
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // Even though the procState is the same, the uid had exited - so this should be
+ // processed as a fresh callback.
+ callOnUidStatechanged(UID_B, testProcState, testProcStateSeq + 1,
+ PROCESS_CAPABILITY_NONE);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ assertTrue(isUidState(UID_B, testProcState, testProcStateSeq + 1, PROCESS_CAPABILITY_NONE));
+ }
+
+ @Test
public void testLowPowerStandbyAllowlist() throws Exception {
// Chain background is also enabled but these procstates are important enough to be exempt.
callAndWaitOnUidStateChanged(UID_A, PROCESS_STATE_TOP, 0);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 8c50ef406ec6..8c2fd1013056 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -4420,13 +4420,13 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@EnableFlags(Flags.FLAG_MODES_API)
public void updateAutomaticZenRule_nullDeviceEffectsUpdate() {
// Adds a starting rule with empty zen policies and device effects
+ ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
- .setDeviceEffects(new ZenDeviceEffects.Builder().build())
+ .setDeviceEffects(zde)
.build();
// Adds the rule using the app, to avoid having any user modified bits set.
String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
- AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
// Sets Device Effects to null
@@ -4437,10 +4437,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// user modified, it can be updated.
mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_APP, "reason",
Process.SYSTEM_UID);
- rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+ AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
- // When AZR's ZenDeviceEffects is null, the updated rule's device effects will be null.
- assertThat(rule.getDeviceEffects()).isNull();
+ // When AZR's ZenDeviceEffects is null, the updated rule's device effects are kept.
+ assertThat(rule.getDeviceEffects()).isEqualTo(zde);
}
@Test
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 417fbd06be66..0d5bf95d959d 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -67,6 +67,7 @@ public class VibratorControlServiceTest {
@Mock
private PackageManagerInternal mPackageManagerInternalMock;
+ private TestLooper mTestLooper;
private FakeVibratorController mFakeVibratorController;
private VibratorControlService mVibratorControlService;
private VibrationSettings mVibrationSettings;
@@ -74,6 +75,7 @@ public class VibratorControlServiceTest {
@Before
public void setUp() throws Exception {
+ mTestLooper = new TestLooper();
when(mPackageManagerInternalMock.getSystemUiServiceComponent())
.thenReturn(new ComponentName("", ""));
LocalServices.removeServiceForTest(PackageManagerInternal.class);
@@ -83,7 +85,7 @@ public class VibratorControlServiceTest {
mVibrationSettings = new VibrationSettings(
ApplicationProvider.getApplicationContext(), new Handler(testLooper.getLooper()));
- mFakeVibratorController = new FakeVibratorController();
+ mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper());
mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(),
mMockVibrationScaler, mVibrationSettings, mLock);
}
@@ -108,13 +110,13 @@ public class VibratorControlServiceTest {
@Test
public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest()
throws RemoteException {
- FakeVibratorController fakeController1 = new FakeVibratorController();
- FakeVibratorController fakeController2 = new FakeVibratorController();
- mVibratorControlService.registerVibratorController(fakeController1);
- mVibratorControlService.unregisterVibratorController(fakeController2);
+ FakeVibratorController controller1 = new FakeVibratorController(mTestLooper.getLooper());
+ FakeVibratorController controller2 = new FakeVibratorController(mTestLooper.getLooper());
+ mVibratorControlService.registerVibratorController(controller1);
+ mVibratorControlService.unregisterVibratorController(controller2);
verifyZeroInteractions(mMockVibrationScaler);
- assertThat(fakeController1.isLinkedToDeath).isTrue();
+ assertThat(controller1.isLinkedToDeath).isTrue();
}
@Test
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
index 79abe21a301d..db823d649cb6 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
@@ -18,23 +18,26 @@ package com.android.server.vibrator;
import static com.google.common.truth.Truth.assertThat;
-import android.os.RemoteException;
+import android.os.test.TestLooper;
import org.junit.Before;
import org.junit.Test;
public class VibratorControllerHolderTest {
- private final FakeVibratorController mFakeVibratorController = new FakeVibratorController();
+ private TestLooper mTestLooper;
+ private FakeVibratorController mFakeVibratorController;
private VibratorControllerHolder mVibratorControllerHolder;
@Before
public void setUp() throws Exception {
+ mTestLooper = new TestLooper();
+ mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper());
mVibratorControllerHolder = new VibratorControllerHolder();
}
@Test
- public void testSetVibratorController_linksVibratorControllerToDeath() throws RemoteException {
+ public void testSetVibratorController_linksVibratorControllerToDeath() {
mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
assertThat(mVibratorControllerHolder.getVibratorController())
.isEqualTo(mFakeVibratorController);
@@ -42,8 +45,7 @@ public class VibratorControllerHolderTest {
}
@Test
- public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath()
- throws RemoteException {
+ public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath() {
mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
mVibratorControllerHolder.setVibratorController(null);
assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
@@ -51,8 +53,7 @@ public class VibratorControllerHolderTest {
}
@Test
- public void testBinderDied_withValidController_unlinksVibratorControllerToDeath()
- throws RemoteException {
+ public void testBinderDied_withValidController_unlinksVibratorControllerToDeath() {
mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
mVibratorControllerHolder.binderDied(mFakeVibratorController);
assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
@@ -60,10 +61,10 @@ public class VibratorControllerHolderTest {
}
@Test
- public void testBinderDied_withInvalidController_ignoresRequest()
- throws RemoteException {
+ public void testBinderDied_withInvalidController_ignoresRequest() {
mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
- FakeVibratorController imposterVibratorController = new FakeVibratorController();
+ FakeVibratorController imposterVibratorController =
+ new FakeVibratorController(mTestLooper.getLooper());
mVibratorControllerHolder.binderDied(imposterVibratorController);
assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
assertThat(mVibratorControllerHolder.getVibratorController())
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index ed89ccf07453..d2ad61f2ba9f 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -97,7 +97,6 @@ import android.view.InputDevice;
import android.view.flags.Flags;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.FrameworkStatsLog;
@@ -117,6 +116,7 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -128,9 +128,7 @@ import java.util.function.Predicate;
public class VibratorManagerServiceTest {
private static final int TEST_TIMEOUT_MILLIS = 1_000;
- // Time to allow for a cancellation to complete (notably including system ramp down), but not so
- // long that tests easily get really slow or flaky. If a vibration is close to this, it should
- // be cancelled in the body of the individual test.
+ // Time to allow for a cancellation to complete and the vibrators to become idle.
private static final int CLEANUP_TIMEOUT_MILLIS = 100;
private static final int UID = Process.ROOT_UID;
private static final int VIRTUAL_DEVICE_ID = 1;
@@ -186,8 +184,8 @@ public class VibratorManagerServiceTest {
private AudioManager mAudioManagerMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
-
- private SparseArray<VibrationEffect> mHapticFeedbackVibrationMap = new SparseArray<>();
+ private final SparseArray<VibrationEffect> mHapticFeedbackVibrationMap = new SparseArray<>();
+ private final List<HalVibration> mPendingVibrations = new ArrayList<>();
private VibratorManagerService mService;
private Context mContextSpy;
@@ -207,7 +205,7 @@ public class VibratorManagerServiceTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
mVibrationConfig = new VibrationConfig(mContextSpy.getResources());
- mFakeVibratorController = new FakeVibratorController();
+ mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper());
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
@@ -253,12 +251,15 @@ public class VibratorManagerServiceTest {
@After
public void tearDown() throws Exception {
if (mService != null) {
- // Wait until all vibrators have stopped vibrating, with a bit of flexibility for tests
- // that just do a click or have cancelled at the end (waiting for ramp-down).
- //
- // Note: if a test is flaky here, check whether a VibrationEffect duration is close to
- // CLEANUP_TIMEOUT_MILLIS - in which case it's probably best to just cancel that effect
- // explicitly at the end of the test case (rather than letting it run and race flakily).
+ if (!mPendingVibrations.stream().allMatch(HalVibration::hasEnded)) {
+ // Cancel any pending vibration from tests.
+ cancelVibrate(mService);
+ for (HalVibration vibration : mPendingVibrations) {
+ vibration.waitForEnd();
+ }
+ }
+ // Wait until all vibrators have stopped vibrating, waiting for ramp-down.
+ // Note: if a test is flaky here something is wrong with the vibration finalization.
assertTrue(waitUntil(s -> {
for (int vibratorId : mService.getVibratorIds()) {
if (s.isVibrating(vibratorId)) {
@@ -266,7 +267,7 @@ public class VibratorManagerServiceTest {
}
}
return true;
- }, mService, CLEANUP_TIMEOUT_MILLIS));
+ }, mService, mVibrationConfig.getRampDownDurationMs() + CLEANUP_TIMEOUT_MILLIS));
}
LocalServices.removeServiceForTest(PackageManagerInternal.class);
@@ -323,21 +324,26 @@ public class VibratorManagerServiceTest {
(VibratorManagerService.ExternalVibratorService) service;
} else if (service instanceof VibratorControlService) {
mVibratorControlService = (VibratorControlService) service;
+ mFakeVibratorController.setVibratorControlService(
+ mVibratorControlService);
}
}
+ @Override
HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
Resources resources, VibratorInfo vibratorInfo) {
return new HapticFeedbackVibrationProvider(
resources, vibratorInfo, mHapticFeedbackVibrationMap);
}
+ @Override
VibratorControllerHolder createVibratorControllerHolder() {
VibratorControllerHolder holder = new VibratorControllerHolder();
holder.setVibratorController(mFakeVibratorController);
return holder;
}
+ @Override
boolean isServiceDeclared(String name) {
return true;
}
@@ -486,8 +492,9 @@ public class VibratorManagerServiceTest {
InOrder inOrderVerifier = inOrder(listenerMock);
// First notification done when listener is registered.
inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
+ // Vibrator on notification done before vibration ended, no need to wait.
inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
- // The last notification is after the vibration has completed.
+ // Vibrator off notification done after vibration completed, wait for notification.
inOrderVerifier.verify(listenerMock, timeout(TEST_TIMEOUT_MILLIS)).onVibrating(eq(false));
inOrderVerifier.verifyNoMoreInteractions();
@@ -517,6 +524,7 @@ public class VibratorManagerServiceTest {
InOrder inOrderVerifier = inOrder(listenerMock);
// First notification done when listener is registered.
inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
+ // Vibrator on notification done before vibration ended, no need to wait.
inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
inOrderVerifier.verify(listenerMock, atLeastOnce()).asBinder(); // unregister
inOrderVerifier.verifyNoMoreInteractions();
@@ -541,7 +549,6 @@ public class VibratorManagerServiceTest {
verify(listeners[0]).onVibrating(eq(true));
verify(listeners[1]).onVibrating(eq(true));
verify(listeners[2], never()).onVibrating(eq(true));
- cancelVibrate(service); // Clean up long-ish effect.
}
@Test
@@ -889,21 +896,24 @@ public class VibratorManagerServiceTest {
eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
}
- @FlakyTest
@Test
public void vibrate_withOngoingRepeatingVibration_ignoresEffect() throws Exception {
mockVibrators(1);
- mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
- new long[]{10_000, 10_000}, new int[]{128, 255}, 1);
- vibrate(service, repeatingEffect, new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_UNKNOWN).build());
+ new long[]{10, 10_000}, new int[]{128, 255}, 1);
+ vibrate(service, repeatingEffect,
+ new VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_UNKNOWN)
+ .build());
- // VibrationThread will start this vibration async, wait until it has started.
- assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
- service, TEST_TIMEOUT_MILLIS));
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service,
+ TEST_TIMEOUT_MILLIS));
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
HAPTIC_FEEDBACK_ATTRS);
@@ -914,9 +924,8 @@ public class VibratorManagerServiceTest {
// The second vibration shouldn't have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
// No segment played is the prebaked CLICK from the second vibration.
- assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream()
+ assertFalse(fakeVibrator.getAllEffectSegments().stream()
.anyMatch(PrebakedSegment.class::isInstance));
- cancelVibrate(service); // Clean up repeating effect.
}
@Test
@@ -930,11 +939,13 @@ public class VibratorManagerServiceTest {
VibratorManagerService service = createSystemReadyService();
VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
- new long[]{10, 10_000}, new int[]{255, 0}, 1);
+ new long[]{10, 10_000}, new int[]{128, 255}, 1);
vibrate(service, repeatingEffect, ALARM_ATTRS);
- // VibrationThread will start this vibration async, wait until the off waveform step.
- assertTrue(waitUntil(s -> fakeVibrator.getOffCount() > 0, service, TEST_TIMEOUT_MILLIS));
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service,
+ TEST_TIMEOUT_MILLIS));
// Cancel vibration right before requesting a new one.
// This should trigger slow IVibrator.off before setting the vibration status to cancelled.
@@ -942,6 +953,8 @@ public class VibratorManagerServiceTest {
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
ALARM_ATTRS);
+ // The second vibration should have recorded that the vibrators were turned on.
+ verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
// Check that second vibration was played.
assertTrue(fakeVibrator.getAllEffectSegments().stream()
.anyMatch(PrebakedSegment.class::isInstance));
@@ -951,45 +964,47 @@ public class VibratorManagerServiceTest {
public void vibrate_withNewSameImportanceVibrationAndBothRepeating_cancelsOngoingEffect()
throws Exception {
mockVibrators(1);
- mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
- new long[]{10_000, 10_000}, new int[]{128, 255}, 1);
+ new long[]{10, 10_000}, new int[]{128, 255}, 1);
vibrate(service, repeatingEffect, ALARM_ATTRS);
- // VibrationThread will start this vibration async, wait until it has started.
- assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
- service, TEST_TIMEOUT_MILLIS));
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service,
+ TEST_TIMEOUT_MILLIS));
VibrationEffect repeatingEffect2 = VibrationEffect.createWaveform(
- new long[]{10_000, 10_000}, new int[]{128, 255}, 1);
+ new long[]{10, 10_000}, new int[]{255, 128}, 1);
vibrate(service, repeatingEffect2, ALARM_ATTRS);
- // VibrationThread will start this vibration async, so wait before checking it started.
- assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() == 2,
- service, TEST_TIMEOUT_MILLIS));
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 4, service,
+ TEST_TIMEOUT_MILLIS));
// The second vibration should have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
-
- cancelVibrate(service); // Clean up repeating effect.
}
- @FlakyTest
@Test
public void vibrate_withNewSameImportanceVibrationButOngoingIsRepeating_ignoreNewVibration()
throws Exception {
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
- new long[]{10, 10_000}, new int[]{255, 0}, 1);
+ new long[]{10, 10_000}, new int[]{128, 255}, 1);
vibrate(service, repeatingEffect, ALARM_ATTRS);
- // VibrationThread will start this vibration async, wait until it has started.
- assertTrue(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(), service,
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service,
TEST_TIMEOUT_MILLIS));
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
@@ -998,53 +1013,55 @@ public class VibratorManagerServiceTest {
// The second vibration shouldn't have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
// The second vibration shouldn't have played any prebaked segment.
- assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream()
+ assertFalse(fakeVibrator.getAllEffectSegments().stream()
.anyMatch(PrebakedSegment.class::isInstance));
- cancelVibrate(service); // Clean up long effect.
}
@Test
public void vibrate_withNewUnknownUsageVibrationAndRepeating_cancelsOngoingEffect()
throws Exception {
mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
- new long[]{10_000, 10_000}, new int[]{128, 255}, 1);
+ new long[]{10, 10_000}, new int[]{128, 255}, 1);
vibrate(service, repeatingEffect, ALARM_ATTRS);
- // VibrationThread will start this vibration async, wait until it has started.
- assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
- service, TEST_TIMEOUT_MILLIS));
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service,
+ TEST_TIMEOUT_MILLIS));
VibrationEffect repeatingEffect2 = VibrationEffect.createWaveform(
- new long[]{10_000, 10_000}, new int[]{128, 255}, 1);
+ new long[]{10, 10_000}, new int[]{255, 128}, 1);
vibrate(service, repeatingEffect2, UNKNOWN_ATTRS);
- // VibrationThread will start this vibration async, so wait before checking it started.
- assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() == 2,
- service, TEST_TIMEOUT_MILLIS));
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 4, service,
+ TEST_TIMEOUT_MILLIS));
// The second vibration should have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
-
- cancelVibrate(service); // Clean up repeating effect.
}
- @FlakyTest
@Test
public void vibrate_withNewUnknownUsageVibrationAndNotRepeating_ignoreNewVibration()
throws Exception {
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
VibrationEffect alarmEffect = VibrationEffect.createWaveform(
- new long[]{10, 10_000}, new int[]{255, 0}, -1);
+ new long[]{10, 10_000}, new int[]{128, 255}, -1);
vibrate(service, alarmEffect, ALARM_ATTRS);
- // VibrationThread will start this vibration async, wait until it has started.
- assertTrue(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(), service,
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service,
TEST_TIMEOUT_MILLIS));
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
@@ -1053,23 +1070,24 @@ public class VibratorManagerServiceTest {
// The second vibration shouldn't have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
// The second vibration shouldn't have played any prebaked segment.
- assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream()
+ assertFalse(fakeVibrator.getAllEffectSegments().stream()
.anyMatch(PrebakedSegment.class::isInstance));
- cancelVibrate(service); // Clean up long effect.
}
@Test
public void vibrate_withOngoingHigherImportanceVibration_ignoresEffect() throws Exception {
mockVibrators(1);
- mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
VibrationEffect effect = VibrationEffect.createWaveform(
- new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
+ new long[]{10, 10_000}, new int[]{128, 255}, -1);
vibrate(service, effect, ALARM_ATTRS);
- // VibrationThread will start this vibration async, so wait before checking it started.
- assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2,
service, TEST_TIMEOUT_MILLIS));
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
@@ -1078,26 +1096,27 @@ public class VibratorManagerServiceTest {
// The second vibration shouldn't have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
// The second vibration shouldn't have played any prebaked segment.
- assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream()
+ assertFalse(fakeVibrator.getAllEffectSegments().stream()
.anyMatch(PrebakedSegment.class::isInstance));
- cancelVibrate(service); // Clean up long effect.
}
@Test
public void vibrate_withOngoingLowerImportanceVibration_cancelsOngoingEffect()
throws Exception {
mockVibrators(1);
- mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createSystemReadyService();
VibrationEffect effect = VibrationEffect.createWaveform(
- new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
+ new long[]{10, 10_000}, new int[]{128, 255}, -1);
vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
- // VibrationThread will start this vibration async, so wait before checking it started.
- assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
- service, TEST_TIMEOUT_MILLIS));
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service,
+ TEST_TIMEOUT_MILLIS));
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
RINGTONE_ATTRS);
@@ -1105,10 +1124,8 @@ public class VibratorManagerServiceTest {
// The second vibration should have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
// One segment played is the prebaked CLICK from the second vibration.
- assertEquals(1,
- mVibratorProviders.get(1).getAllEffectSegments().stream()
- .filter(PrebakedSegment.class::isInstance)
- .count());
+ assertEquals(1, fakeVibrator.getAllEffectSegments().stream()
+ .filter(PrebakedSegment.class::isInstance).count());
}
@Test
@@ -1139,10 +1156,8 @@ public class VibratorManagerServiceTest {
// The new vibration should have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
// One segment played is the prebaked CLICK from the new vibration.
- assertEquals(1,
- mVibratorProviders.get(1).getAllEffectSegments().stream()
- .filter(PrebakedSegment.class::isInstance)
- .count());
+ assertEquals(1, mVibratorProviders.get(1).getAllEffectSegments().stream()
+ .filter(PrebakedSegment.class::isInstance).count());
}
@Test
@@ -1153,8 +1168,9 @@ public class VibratorManagerServiceTest {
.build();
mockVibrators(1);
- mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createSystemReadyService();
VibrationEffect effect = VibrationEffect.createWaveform(
@@ -1173,7 +1189,7 @@ public class VibratorManagerServiceTest {
verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
// One step segment (with several amplitudes) and one click should have played. Notably
// there is no primitive segment.
- List<VibrationEffectSegment> played = mVibratorProviders.get(1).getAllEffectSegments();
+ List<VibrationEffectSegment> played = fakeVibrator.getAllEffectSegments();
assertEquals(2, played.size());
assertEquals(1, played.stream().filter(StepSegment.class::isInstance).count());
assertEquals(1, played.stream().filter(PrebakedSegment.class::isInstance).count());
@@ -1182,8 +1198,9 @@ public class VibratorManagerServiceTest {
@Test
public void vibrate_withInputDevices_vibratesInputDevices() throws Exception {
mockVibrators(1);
- mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
@@ -1198,7 +1215,7 @@ public class VibratorManagerServiceTest {
vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any());
- assertTrue(mVibratorProviders.get(1).getAllEffectSegments().isEmpty());
+ assertTrue(fakeVibrator.getAllEffectSegments().isEmpty());
}
@Test
@@ -1497,8 +1514,6 @@ public class VibratorManagerServiceTest {
// Alarm vibration will be scaled with SCALE_NONE.
assertEquals(1f,
((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(2)).getScale(), 1e-5);
-
- cancelVibrate(service); // Clean up long-ish effect.
}
@Test
@@ -1533,8 +1548,6 @@ public class VibratorManagerServiceTest {
assertEquals(1, fakeVibrator.getAllEffectSegments().size());
assertEquals(defaultAmplitude / 255f, fakeVibrator.getAmplitudes().get(0), 1e-5);
-
- cancelVibrate(service); // Clean up long-ish effect.
}
@Test
@@ -1570,7 +1583,6 @@ public class VibratorManagerServiceTest {
// Vibration is not stopped nearly after updating service.
assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
- cancelVibrate(service); // Clean up long effect.
}
@Test
@@ -1596,8 +1608,6 @@ public class VibratorManagerServiceTest {
HAPTIC_FEEDBACK_ATTRS);
// Haptic feedback played normally when it's from the default device.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
-
- cancelVibrate(service); // Clean up long-ish effect.
}
@Test
@@ -1854,9 +1864,7 @@ public class VibratorManagerServiceTest {
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is not cancelled.
- assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS));
assertEquals(Arrays.asList(false), mVibratorProviders.get(1).getExternalControlStates());
- cancelVibrate(service); // Clean up long effect.
}
@Test
@@ -1910,9 +1918,7 @@ public class VibratorManagerServiceTest {
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is not cancelled.
- assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS));
assertEquals(Arrays.asList(false), mVibratorProviders.get(1).getExternalControlStates());
- cancelVibrate(service); // Clean up long effect.
}
@Test
@@ -2536,29 +2542,29 @@ public class VibratorManagerServiceTest {
private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service,
CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException {
- HalVibration vib =
- service.vibrateWithPermissionCheck(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME,
- effect, attrs, "some reason", service);
+ HalVibration vib = vibrate(service, effect, attrs);
if (vib != null) {
vib.waitForEnd();
}
-
return vib;
}
- private void vibrate(VibratorManagerService service, VibrationEffect effect,
+ private HalVibration vibrate(VibratorManagerService service, VibrationEffect effect,
VibrationAttributes attrs) {
- vibrate(service, CombinedVibration.createParallel(effect), attrs);
+ return vibrate(service, CombinedVibration.createParallel(effect), attrs);
}
- private void vibrate(VibratorManagerService service, CombinedVibration effect,
+ private HalVibration vibrate(VibratorManagerService service, CombinedVibration effect,
VibrationAttributes attrs) {
- vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs);
+ return vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs);
}
- private void vibrateWithDevice(VibratorManagerService service, int deviceId,
+ private HalVibration vibrateWithDevice(VibratorManagerService service, int deviceId,
CombinedVibration effect, VibrationAttributes attrs) {
- service.vibrate(UID, deviceId, PACKAGE_NAME, effect, attrs, "some reason", service);
+ HalVibration vib = service.vibrateWithPermissionCheck(UID, deviceId, PACKAGE_NAME, effect,
+ attrs, "some reason", service);
+ mPendingVibrations.add(vib);
+ return vib;
}
private boolean waitUntil(Predicate<VibratorManagerService> predicate,
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
index 391220669f2f..2a010f0a82a9 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
@@ -18,7 +18,10 @@ package com.android.server.vibrator;
import android.annotation.NonNull;
import android.frameworks.vibrator.IVibratorController;
+import android.frameworks.vibrator.VibrationParam;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.VibrationAttributes;
@@ -28,17 +31,42 @@ import android.os.VibrationAttributes;
*/
public final class FakeVibratorController extends IVibratorController.Stub {
+ private final Handler mHandler;
+ private VibratorControlService mVibratorControlService;
+ private VibrationParam[] mRequestResult = new VibrationParam[0];
+
public boolean isLinkedToDeath = false;
public boolean didRequestVibrationParams = false;
public int requestVibrationType = VibrationAttributes.USAGE_UNKNOWN;
public long requestTimeoutInMillis = 0;
+ public FakeVibratorController(Looper looper) {
+ mHandler = new Handler(looper);
+ }
+
+ public void setVibratorControlService(VibratorControlService service) {
+ mVibratorControlService = service;
+ }
+
+ public void setRequestResult(VibrationParam... params) {
+ mRequestResult = params;
+ }
+
@Override
- public void requestVibrationParams(int vibrationType, long timeoutInMillis, IBinder iBinder)
+ public void requestVibrationParams(int vibrationType, long timeoutInMillis, IBinder token)
throws RemoteException {
didRequestVibrationParams = true;
requestVibrationType = vibrationType;
requestTimeoutInMillis = timeoutInMillis;
+ mHandler.post(() -> {
+ if (mVibratorControlService != null) {
+ try {
+ mVibratorControlService.onRequestVibrationParamsComplete(token, mRequestResult);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 4c32a586ae78..a0461a6ea9a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -42,6 +42,7 @@ import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -541,7 +542,7 @@ public class DragDropControllerTests extends WindowTestsBase {
}
@Test
- public void testUnhandledDragListenerNotCalledForNormalDrags() throws RemoteException {
+ public void testUnhandledDragNotCalledForNormalDrags() throws RemoteException {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
final IGlobalDragListener listener = mock(IGlobalDragListener.class);
@@ -552,15 +553,16 @@ public class DragDropControllerTests extends WindowTestsBase {
}
@Test
- public void testUnhandledDragListenerReceivesUnhandledDropOverWindow() {
+ public void testUnhandledDragReceivesUnhandledDropOverWindow() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
- startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
- // Notify the unhandled drag listener
+ startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
+ ClipData.newPlainText("label", "Test"), () -> {
+ // Trigger an unhandled drop and verify the global drag listener was called
mTarget.reportDropWindow(mWindow.mInputChannelToken, invalidXY, invalidXY);
mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
mTarget.reportDropResult(mWindow.mClient, false);
@@ -575,15 +577,16 @@ public class DragDropControllerTests extends WindowTestsBase {
}
@Test
- public void testUnhandledDragListenerReceivesUnhandledDropOverNoValidWindow() {
+ public void testUnhandledDragReceivesUnhandledDropOverNoValidWindow() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
- startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
- // Notify the unhandled drag listener
+ startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
+ ClipData.newPlainText("label", "Test"), () -> {
+ // Trigger an unhandled drop and verify the global drag listener was called
mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
mTarget.onUnhandledDropCallback(true);
@@ -597,15 +600,38 @@ public class DragDropControllerTests extends WindowTestsBase {
}
@Test
- public void testUnhandledDragListenerCallbackTimeout() {
+ public void testUnhandledDragDoesNotReceiveUnhandledDropWithoutDragFlag() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
- startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
- // Notify the unhandled drag listener
+ startDrag(View.DRAG_FLAG_GLOBAL,
+ ClipData.newPlainText("label", "Test"), () -> {
+ // Trigger an unhandled drop and verify the global drag listener was not called
+ mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
+ mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+ mToken = null;
+ try {
+ verify(listener, never()).onUnhandledDrop(any(), any());
+ } catch (RemoteException e) {
+ fail("Failed to verify unhandled drop: " + e);
+ }
+ });
+ }
+
+ @Test
+ public void testUnhandledDragCallbackTimeout() {
+ assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
+
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
+ doReturn(mock(Binder.class)).when(listener).asBinder();
+ mTarget.setGlobalDragListener(listener);
+ final int invalidXY = 100_000;
+ startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
+ ClipData.newPlainText("label", "Test"), () -> {
+ // Trigger an unhandled drop and verify the global drag listener was called
mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING
index eca258c5a74d..b5d5b5fb6d92 100644
--- a/tools/hoststubgen/TEST_MAPPING
+++ b/tools/hoststubgen/TEST_MAPPING
@@ -1,6 +1,7 @@
{
"presubmit": [
- { "name": "tiny-framework-dump-test" },
+ // TODO(b/326897452): Reenable after JDK 21 switch.
+ // { "name": "tiny-framework-dump-test" },
{ "name": "hoststubgentest" },
{ "name": "hoststubgen-invoke-test" }
],