summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java25
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING4
-rw-r--r--api/api.go14
-rw-r--r--cmds/incidentd/src/WorkDirectory.cpp2
-rw-r--r--core/api/current.txt3
-rw-r--r--core/api/module-lib-current.txt11
-rw-r--r--core/api/system-current.txt6
-rw-r--r--core/api/system-lint-baseline.txt1
-rw-r--r--core/api/test-current.txt24
-rw-r--r--core/java/android/app/ActivityThread.java3
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java48
-rw-r--r--core/java/android/app/trust/ITrustManager.aidl2
-rw-r--r--core/java/android/app/trust/TrustManager.java73
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java22
-rw-r--r--core/java/android/content/Context.java4
-rw-r--r--core/java/android/hardware/biometrics/IBiometricContextListener.aidl27
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl4
-rw-r--r--core/java/android/hardware/input/InputManager.java17
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java36
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java21
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java76
-rw-r--r--core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java72
-rw-r--r--core/java/android/os/BatteryStats.java126
-rw-r--r--core/java/android/service/autofill/AutofillService.java8
-rw-r--r--core/java/android/service/autofill/FillRequest.java55
-rw-r--r--core/java/android/service/autofill/FillResponse.java16
-rw-r--r--core/java/android/service/trust/TrustAgentService.java4
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java14
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java20
-rw-r--r--core/java/android/widget/RemoteViews.java49
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl2
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java266
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java11
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl3
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl3
-rw-r--r--core/java/com/android/internal/util/UserIcons.java24
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl8
-rw-r--r--core/jni/android/opengl/OWNERS1
-rw-r--r--core/jni/android_hardware_SensorManager.cpp15
-rw-r--r--core/jni/android_media_AudioSystem.cpp2
-rw-r--r--core/res/AndroidManifest.xml3
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/res/layout/remote_view_relative_layout.xml24
-rw-r--r--core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml25
-rw-r--r--core/tests/coretests/res/layout/remote_views_light_background_text.xml21
-rw-r--r--core/tests/coretests/res/layout/remote_views_list.xml21
-rw-r--r--core/tests/coretests/res/values/styles.xml10
-rw-r--r--core/tests/coretests/src/android/widget/RemoteViewsTest.java191
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java212
-rw-r--r--data/fonts/fonts.xml14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java138
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h9
-rw-r--r--media/native/midi/amidi.cpp6
-rw-r--r--native/android/libandroid.map.txt12
-rw-r--r--native/android/performance_hint.cpp7
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp2
-rw-r--r--packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java12
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java6
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/AndroidManifest.xml6
-rw-r--r--packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml30
-rw-r--r--packages/SettingsLib/res/drawable/avatar_selector.xml25
-rw-r--r--packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml30
-rw-r--r--packages/SettingsLib/res/drawable/ic_account_circle_outline.xml25
-rw-r--r--packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml24
-rw-r--r--packages/SettingsLib/res/layout/avatar_item.xml24
-rw-r--r--packages/SettingsLib/res/layout/avatar_picker.xml38
-rw-r--r--packages/SettingsLib/res/values-w1280dp-land/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w1440dp-land/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w1600dp-land/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w480dp-port/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w600dp-port/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w720dp-port/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w840dp-port/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w960dp-land/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values/dimens.xml7
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java297
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java334
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java36
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java437
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java10
-rw-r--r--packages/SystemUI/res/layout/controls_fullscreen.xml3
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complications_layer.xml24
-rw-r--r--packages/SystemUI/res/values-land/config.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/config.xml1
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml3
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/config.xml3
-rw-r--r--packages/SystemUI/res/values-w500dp/config.xml20
-rw-r--r--packages/SystemUI/res/values-w500dp/dimens.xml21
-rw-r--r--packages/SystemUI/res/values-w850dp/config.xml20
-rw-r--r--packages/SystemUI/res/values-w850dp/dimens.xml21
-rw-r--r--packages/SystemUI/res/values/dimens.xml34
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java273
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java252
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt153
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java5
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java6
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java146
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java2
-rw-r--r--services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java2
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java102
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java4
-rw-r--r--services/core/java/com/android/server/am/DataConnectionStats.java2
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java4
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricContext.java47
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java105
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricLogger.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java31
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java29
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java22
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RemovalClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/StartUserClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/StopUserClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java35
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java29
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java15
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java59
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java55
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java26
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java23
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java52
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java55
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java21
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java10
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java23
-rw-r--r--services/core/java/com/android/server/display/HighBrightnessModeController.java4
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java29
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java21
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java32
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodMenuController.java2
-rw-r--r--services/core/java/com/android/server/location/LocationShellCommand.java30
-rw-r--r--services/core/java/com/android/server/notification/NotificationComparator.java20
-rw-r--r--services/core/java/com/android/server/notification/PermissionHelper.java5
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java22
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java16
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java11
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java12
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java32
-rw-r--r--services/core/java/com/android/server/wm/Transition.java2
-rw-r--r--services/core/jni/com_android_server_companion_virtual_InputController.cpp36
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp13
-rw-r--r--services/core/jni/gnss/AGnssRil.cpp2
-rw-r--r--services/core/jni/gnss/GnssMeasurementCallback.cpp13
-rw-r--r--services/core/jni/gnss/GnssMeasurementCallback.h4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java529
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java115
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java128
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java116
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java107
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java265
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java116
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java224
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java302
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java24
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java9
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java10
-rw-r--r--services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java9
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java2
271 files changed, 7696 insertions, 1445 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 9b1f2d07d58a..ddcc74696d94 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -216,4 +216,11 @@ public interface AppStandbyInternal {
void dumpState(String[] args, PrintWriter pw);
boolean isAppIdleEnabled();
+
+ /**
+ * Returns the duration (in millis) for the window where events occurring will be
+ * considered as broadcast response, starting from the point when an app receives
+ * a broadcast.
+ */
+ long getBroadcastResponseWindowDurationMs();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 8b2639781181..050e3df5b5e1 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -347,6 +347,14 @@ public class AppStandbyController
*/
boolean mLinkCrossProfileApps =
ConstantsObserver.DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS;
+
+ /**
+ * Duration (in millis) for the window where events occurring will be considered as
+ * broadcast response, starting from the point when an app receives a broadcast.
+ */
+ volatile long mBroadcastResponseWindowDurationMillis =
+ ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS;
+
/**
* Whether we should allow apps into the
* {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not.
@@ -1774,6 +1782,10 @@ public class AppStandbyController
}
}
+ @Override
+ public long getBroadcastResponseWindowDurationMs() {
+ return mBroadcastResponseWindowDurationMillis;
+ }
@Override
public void flushToDisk() {
@@ -2042,6 +2054,10 @@ public class AppStandbyController
TimeUtils.formatDuration(mSystemUpdateUsageTimeoutMillis, pw);
pw.println();
+ pw.print(" mBroadcastResponseWindowDurationMillis=");
+ TimeUtils.formatDuration(mBroadcastResponseWindowDurationMillis, pw);
+ pw.println();
+
pw.println();
pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
pw.print(" mAllowRestrictedBucket=");
@@ -2473,6 +2489,8 @@ public class AppStandbyController
KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "rare",
KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "restricted"
};
+ private static final String KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS =
+ "broadcast_response_window_timeout_ms";
public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS =
COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
public static final long DEFAULT_STRONG_USAGE_TIMEOUT =
@@ -2502,6 +2520,8 @@ public class AppStandbyController
public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS =
COMPRESS_TIME ? ONE_MINUTE : ONE_DAY;
public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true;
+ public static final long DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS =
+ 2 * ONE_MINUTE;
ConstantsObserver(Handler handler) {
super(handler);
@@ -2619,6 +2639,11 @@ public class AppStandbyController
KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT);
break;
+ case KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS:
+ mBroadcastResponseWindowDurationMillis = properties.getLong(
+ KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS,
+ DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS);
+ break;
default:
if (!timeThresholdsUpdated
&& (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD)
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index c5dc51cc9c24..e407e3126058 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -5,7 +5,9 @@
"options": [
{"include-filter": "android.app.usage.cts.UsageStatsTest"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.MediumTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
},
{
diff --git a/api/api.go b/api/api.go
index aa9e399e7272..17649e80e81a 100644
--- a/api/api.go
+++ b/api/api.go
@@ -27,7 +27,6 @@ import (
const art = "art.module.public.api"
const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
-var modules_with_only_public_scope = []string{i18n, conscrypt}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
@@ -149,8 +148,6 @@ func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
// This produces the same annotations.zip as framework-doc-stubs, but by using
// outputs from individual modules instead of all the source code.
func createMergedAnnotations(ctx android.LoadHookContext, modules []string) {
- // Conscrypt and i18n currently do not enable annotations
- modules = removeAll(modules, []string{conscrypt, i18n})
props := genruleProps{}
props.Name = proptools.StringPtr("sdk-annotations.zip")
props.Tools = []string{"merge_annotation_zips", "soong_zip"}
@@ -195,11 +192,8 @@ func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) {
func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) {
props := libraryProps{}
- modules_with_system_stubs := removeAll(modules, modules_with_only_public_scope)
props.Name = proptools.StringPtr("all-modules-system-stubs")
- props.Static_libs = append(
- transformArray(modules_with_only_public_scope, "", ".stubs"),
- transformArray(modules_with_system_stubs, "", ".stubs.system")...)
+ props.Static_libs = transformArray(modules, "", ".stubs.system")
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
@@ -226,8 +220,6 @@ func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []str
func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
var textFiles []MergedTxtDefinition
- // Two module libraries currently do not support @SystemApi so only have the public scope.
- bcpWithSystemApi := removeAll(bootclasspath, modules_with_only_public_scope)
tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
for i, f := range []string{"current.txt", "removed.txt"} {
@@ -241,14 +233,14 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: f,
BaseTxt: ":non-updatable-system-" + f,
- Modules: bcpWithSystemApi,
+ Modules: bootclasspath,
ModuleTag: "{.system" + tagSuffix[i],
Scope: "system",
})
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: f,
BaseTxt: ":non-updatable-module-lib-" + f,
- Modules: bcpWithSystemApi,
+ Modules: bootclasspath,
ModuleTag: "{.module-lib" + tagSuffix[i],
Scope: "module-lib",
})
diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp
index dd33fdfc28c3..c8d2e0ec4aca 100644
--- a/cmds/incidentd/src/WorkDirectory.cpp
+++ b/cmds/incidentd/src/WorkDirectory.cpp
@@ -280,7 +280,7 @@ void ReportFile::addReport(const IncidentReportArgs& args) {
// Lower privacy policy (less restrictive) wins.
report->set_privacy_policy(args.getPrivacyPolicy());
}
- report->set_all_sections(report->all_sections() | args.all());
+ report->set_all_sections(report->all_sections() || args.all());
for (int section: args.sections()) {
if (!has_section(*report, section)) {
report->add_section(section);
diff --git a/core/api/current.txt b/core/api/current.txt
index 922ad16c2a6e..fe938a9a99ec 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -37882,6 +37882,7 @@ package android.service.autofill {
method public abstract void onFillRequest(@NonNull android.service.autofill.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
method public abstract void onSaveRequest(@NonNull android.service.autofill.SaveRequest, @NonNull android.service.autofill.SaveCallback);
method public void onSavedDatasetsInfoRequest(@NonNull android.service.autofill.SavedDatasetsInfoCallback);
+ field public static final String EXTRA_FILL_RESPONSE = "android.service.autofill.extra.FILL_RESPONSE";
field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
field public static final String SERVICE_META_DATA = "android.autofill";
}
@@ -38037,6 +38038,7 @@ package android.service.autofill {
public final class FillRequest implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.os.Bundle getClientState();
+ method @Nullable public android.content.IntentSender getDelayedFillIntentSender();
method @NonNull public java.util.List<android.service.autofill.FillContext> getFillContexts();
method public int getFlags();
method public int getId();
@@ -38051,6 +38053,7 @@ package android.service.autofill {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.FillResponse> CREATOR;
+ field public static final int FLAG_DELAY_FILL = 4; // 0x4
field public static final int FLAG_DISABLE_ACTIVITY_ONLY = 2; // 0x2
field public static final int FLAG_TRACK_CONTEXT_COMMITED = 1; // 0x1
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 8ea1abca7a3f..df61a968ffc0 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -446,6 +446,17 @@ package android.net {
}
+package android.net.netstats {
+
+ public class NetworkStatsDataMigrationUtils {
+ method @NonNull public static android.net.NetworkStatsCollection readPlatformCollection(@NonNull String, long) throws java.io.IOException;
+ field public static final String PREFIX_UID = "uid";
+ field public static final String PREFIX_UID_TAG = "uid_tag";
+ field public static final String PREFIX_XT = "xt";
+ }
+
+}
+
package android.os {
public final class BatteryStatsManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ba5873766d2f..9da2abbf20cd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2610,6 +2610,8 @@ package android.companion.virtual {
public final class VirtualDeviceParams implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public java.util.Set<android.content.ComponentName> getAllowedActivities();
+ method @Nullable public java.util.Set<android.content.ComponentName> getBlockedActivities();
method public int getLockState();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -2621,6 +2623,8 @@ package android.companion.virtual {
public static final class VirtualDeviceParams.Builder {
ctor public VirtualDeviceParams.Builder();
method @NonNull public android.companion.virtual.VirtualDeviceParams build();
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@Nullable java.util.Set<android.content.ComponentName>);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@Nullable java.util.Set<android.content.ComponentName>);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
}
@@ -2688,7 +2692,7 @@ package android.content {
field public static final String BATTERY_STATS_SERVICE = "batterystats";
field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
- field public static final String CLOUDSEARCH_SERVICE = "cloudsearch_service";
+ field public static final String CLOUDSEARCH_SERVICE = "cloudsearch";
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
field public static final String ETHERNET_SERVICE = "ethernet";
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index dbb427467a68..e17a9bb1512c 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -294,7 +294,6 @@ SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit
ServiceName: android.content.Context#CLOUDSEARCH_SERVICE:
- Inconsistent service value; expected `cloudsearch`, was `cloudsearch_service` (Note: Do not change the name of already released services, which will break tools using `adb shell dumpsys`. Instead add `@SuppressLint("ServiceName"))`
UserHandleName: android.app.search.SearchAction.Builder#setUserHandle(android.os.UserHandle):
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 39e12f40a79d..c39394bb1e4f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2,6 +2,7 @@
package android {
public static final class Manifest.permission {
+ field public static final String ACCESS_KEYGUARD_SECURE_STORAGE = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE";
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
@@ -287,8 +288,8 @@ package android.app {
}
public class KeyguardManager {
- method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean checkLock(int, @Nullable byte[]);
- method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]);
+ method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE}) public boolean checkLock(int, @Nullable byte[]);
+ method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]);
}
public class LocaleManager {
@@ -462,6 +463,7 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
+ method public int getDeviceOwnerType(@NonNull android.content.ComponentName);
method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String);
method public long getLastBugReportRequestTime();
method public long getLastNetworkLogRetrievalTime();
@@ -477,6 +479,7 @@ package android.app.admin {
method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
+ method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
@@ -582,6 +585,15 @@ package android.app.prediction {
}
+package android.app.trust {
+
+ public class TrustManager {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void enableTrustAgentForUserForTest(@NonNull android.content.ComponentName, int);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUserRequestedUnlock(int);
+ }
+
+}
+
package android.app.usage {
public class NetworkStatsManager {
@@ -2362,6 +2374,14 @@ package android.service.quicksettings {
}
+package android.service.trust {
+
+ public class TrustAgentService extends android.app.Service {
+ method public void onUserRequestedUnlock();
+ }
+
+}
+
package android.service.voice {
public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3b2176ee10a2..876e4014ba45 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -200,6 +200,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SomeArgs;
@@ -7812,6 +7813,8 @@ public final class ActivityThread extends ClientTransactionHandler
MediaFrameworkPlatformInitializer.setMediaServiceManager(new MediaServiceManager());
MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager());
BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager());
+ BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
+ BinderCallsStats.startForBluetooth(context); });
}
private void purgePendingResources() {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3960f4e1518e..9ac4030c73a7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -454,6 +454,52 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
* </ul>
*
+ * <p>Once the device admin app is set as the device owner, the following APIs are available for
+ * managing polices on the device:
+ * <ul>
+ * <li>{@link #isDeviceManaged()}</li>
+ * <li>{@link #isUninstallBlocked(ComponentName, String)}</li>
+ * <li>{@link #setUninstallBlocked(ComponentName, String, boolean)}</li>
+ * <li>{@link #setUserControlDisabledPackages(ComponentName, List)}</li>
+ * <li>{@link #getUserControlDisabledPackages(ComponentName)}</li>
+ * <li>{@link #setOrganizationName(ComponentName, CharSequence)}</li>
+ * <li>{@link #setShortSupportMessage(ComponentName, CharSequence)}</li>
+ * <li>{@link #isBackupServiceEnabled(ComponentName)}</li>
+ * <li>{@link #setBackupServiceEnabled(ComponentName, boolean)}</li>
+ * <li>{@link #isLockTaskPermitted(String)}</li>
+ * <li>{@link #setLockTaskFeatures(ComponentName, int)}, where the following lock task features
+ * can be set (otherwise a {@link SecurityException} will be thrown):</li>
+ * <ul>
+ * <li>{@link #LOCK_TASK_FEATURE_SYSTEM_INFO}</li>
+ * <li>{@link #LOCK_TASK_FEATURE_KEYGUARD}</li>
+ * <li>{@link #LOCK_TASK_FEATURE_HOME}</li>
+ * <li>{@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}</li>
+ * <li>{@link #LOCK_TASK_FEATURE_NOTIFICATIONS}</li>
+ * </ul>
+ * <li>{@link #setLockTaskPackages(ComponentName, String[])}</li>
+ * <li>{@link #addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}</li>
+ * <li>{@link #clearPackagePersistentPreferredActivities(ComponentName, String)} </li>
+ * <li>{@link #wipeData(int)}</li>
+ * <li>{@link #isDeviceOwnerApp(String)}</li>
+ * <li>{@link #clearDeviceOwnerApp(String)}</li>
+ * <li>{@link #setPermissionGrantState(ComponentName, String, String, int)}, where
+ * {@link permission#READ_PHONE_STATE} is the <b>only</b> permission that can be
+ * {@link #PERMISSION_GRANT_STATE_GRANTED}, {@link #PERMISSION_GRANT_STATE_DENIED}, or
+ * {@link #PERMISSION_GRANT_STATE_DEFAULT} and can <b>only</b> be applied to the device admin
+ * app (otherwise a {@link SecurityException} will be thrown)</li>
+ * <li>{@link #addUserRestriction(ComponentName, String)}, where the following user restrictions
+ * are permitted (otherwise a {@link SecurityException} will be thrown):</li>
+ * <ul>
+ * <li>{@link UserManager#DISALLOW_ADD_USER}</li>
+ * <li>{@link UserManager#DISALLOW_DEBUGGING_FEATURES}</li>
+ * <li>{@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}</li>
+ * <li>{@link UserManager#DISALLOW_SAFE_BOOT}</li>
+ * <li>{@link UserManager#DISALLOW_CONFIG_DATE_TIME}</li>
+ * <li>{@link UserManager#DISALLOW_OUTGOING_CALLS}</li>
+ * </ul>
+ * <li>{@link #clearUserRestriction(ComponentName, String)}</li>
+ * </ul>
+ *
* @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -14577,6 +14623,7 @@ public class DevicePolicyManager {
*
* @hide
*/
+ @TestApi
public void setDeviceOwnerType(@NonNull ComponentName admin,
@DeviceOwnerType int deviceOwnerType) {
throwIfParentInstance("setDeviceOwnerType");
@@ -14600,6 +14647,7 @@ public class DevicePolicyManager {
*
* @hide
*/
+ @TestApi
@DeviceOwnerType
public int getDeviceOwnerType(@NonNull ComponentName admin) {
throwIfParentInstance("getDeviceOwnerType");
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index edabccf23c2c..7956a35c7b3d 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -17,6 +17,7 @@
package android.app.trust;
import android.app.trust.ITrustListener;
+import android.content.ComponentName;
import android.hardware.biometrics.BiometricSourceType;
/**
@@ -29,6 +30,7 @@ interface ITrustManager {
void reportUserRequestedUnlock(int userId);
void reportUnlockLockout(int timeoutMs, int userId);
void reportEnabledTrustAgentsChanged(int userId);
+ void enableTrustAgentForUserForTest(in ComponentName componentName, int userId);
void registerTrustListener(in ITrustListener trustListener);
void unregisterTrustListener(in ITrustListener trustListener);
void reportKeyguardShowingChanged();
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 70b7de0767e4..fba2d3e03769 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -16,10 +16,14 @@
package android.app.trust;
-import android.Manifest;
+import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.Context;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
@@ -33,9 +37,17 @@ import java.util.ArrayList;
import java.util.List;
/**
- * See {@link com.android.server.trust.TrustManagerService}
+ * Interface to the system service managing trust.
+ *
+ * <p>This class is for internal use only. This class is marked {@code @TestApi} to
+ * enable testing the trust system including {@link android.service.trust.TrustAgentService}.
+ * Methods which are currently not used in tests are marked @hide.
+ *
+ * @see com.android.server.trust.TrustManagerService
+ *
* @hide
*/
+@TestApi
@SystemService(Context.TRUST_SERVICE)
public class TrustManager {
@@ -51,7 +63,8 @@ public class TrustManager {
private final ITrustManager mService;
private final ArrayMap<TrustListener, ITrustListener> mTrustListeners;
- public TrustManager(IBinder b) {
+ /** @hide */
+ public TrustManager(@NonNull IBinder b) {
mService = ITrustManager.Stub.asInterface(b);
mTrustListeners = new ArrayMap<TrustListener, ITrustListener>();
}
@@ -62,8 +75,10 @@ public class TrustManager {
*
* @param userId The id for the user to be locked/unlocked.
* @param locked The value for that user's locked state.
+ *
+ * @hide
*/
- @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
+ @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void setDeviceLockedForUser(int userId, boolean locked) {
try {
mService.setDeviceLockedForUser(userId, locked);
@@ -78,8 +93,11 @@ public class TrustManager {
* @param successful if true, the unlock attempt was successful.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
+ * @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportUnlockAttempt(boolean successful, int userId) {
try {
mService.reportUnlockAttempt(successful, userId);
@@ -93,6 +111,7 @@ public class TrustManager {
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
*/
+ @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportUserRequestedUnlock(int userId) {
try {
mService.reportUserRequestedUnlock(userId);
@@ -112,7 +131,10 @@ public class TrustManager {
* attempt to unlock the device again.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
+ * @hide
*/
+ @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportUnlockLockout(int timeoutMs, int userId) {
try {
mService.reportUnlockLockout(timeoutMs, userId);
@@ -125,7 +147,10 @@ public class TrustManager {
* Reports that the list of enabled trust agents changed for user {@param userId}.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
+ * @hide
*/
+ @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportEnabledTrustAgentsChanged(int userId) {
try {
mService.reportEnabledTrustAgentsChanged(userId);
@@ -135,10 +160,33 @@ public class TrustManager {
}
/**
+ * Enables a trust agent.
+ *
+ * <p>The agent is specified by {@code componentName} and must be a subclass of
+ * {@link android.service.trust.TrustAgentService} and otherwise meet the requirements
+ * to be a trust agent.
+ *
+ * <p>This method can only be used in tests.
+ *
+ * @param componentName the trust agent to enable
+ */
+ @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
+ public void enableTrustAgentForUserForTest(@NonNull ComponentName componentName, int userId) {
+ try {
+ mService.enableTrustAgentForUserForTest(componentName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Reports that the visibility of the keyguard has changed.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
+ * @hide
*/
+ @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportKeyguardShowingChanged() {
try {
mService.reportKeyguardShowingChanged();
@@ -151,7 +199,10 @@ public class TrustManager {
* Registers a listener for trust events.
*
* Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission.
+ *
+ * @hide
*/
+ @RequiresPermission(android.Manifest.permission.TRUST_LISTENER)
public void registerTrustListener(final TrustListener trustListener) {
try {
ITrustListener.Stub iTrustListener = new ITrustListener.Stub() {
@@ -192,7 +243,10 @@ public class TrustManager {
* Unregisters a listener for trust events.
*
* Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission.
+ *
+ * @hide
*/
+ @RequiresPermission(android.Manifest.permission.TRUST_LISTENER)
public void unregisterTrustListener(final TrustListener trustListener) {
ITrustListener iTrustListener = mTrustListeners.remove(trustListener);
if (iTrustListener != null) {
@@ -207,6 +261,8 @@ public class TrustManager {
/**
* @return whether {@param userId} has enabled and configured trust agents. Ignores short-term
* unavailability of trust due to {@link LockPatternUtils.StrongAuthTracker}.
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.TRUST_LISTENER)
public boolean isTrustUsuallyManaged(int userId) {
@@ -223,8 +279,10 @@ public class TrustManager {
* can be skipped.
*
* @param userId
+ *
+ * @hide
*/
- @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
+ @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void unlockedByBiometricForUser(int userId, BiometricSourceType source) {
try {
mService.unlockedByBiometricForUser(userId, source);
@@ -235,8 +293,10 @@ public class TrustManager {
/**
* Clears authentication by the specified biometric type for all users.
+ *
+ * @hide
*/
- @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
+ @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void clearAllBiometricRecognized(BiometricSourceType source, int unlockedUser) {
try {
mService.clearAllBiometricRecognized(source, unlockedUser);
@@ -264,6 +324,7 @@ public class TrustManager {
}
};
+ /** @hide */
public interface TrustListener {
/**
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 2ddfeb4c8ab5..1d0f7c091807 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -22,6 +22,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
@@ -106,11 +107,13 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
- * Returns the set of activities allowed to be streamed, or {@code null} if this is not set.
+ * Returns the set of activities allowed to be streamed, or {@code null} if all activities are
+ * allowed, except the ones explicitly blocked.
*
* @see Builder#setAllowedActivities(Set)
- * @hide // TODO(b/194949534): Unhide this API
*/
+ // Null and empty have different semantics - Null allows all activities to be streamed
+ @SuppressLint("NullableCollection")
@Nullable
public Set<ComponentName> getAllowedActivities() {
if (mAllowedActivities == null) {
@@ -120,12 +123,13 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
- * Returns the set of activities that are blocked from streaming, or {@code null} if this is not
- * set.
+ * Returns the set of activities that are blocked from streaming, or {@code null} to indicate
+ * that all activities in {@link #getAllowedActivities} are allowed.
*
* @see Builder#setBlockedActivities(Set)
- * @hide // TODO(b/194949534): Unhide this API
*/
+ // Allowing null to enforce that at most one of allowed / blocked activities can be non-null
+ @SuppressLint("NullableCollection")
@Nullable
public Set<ComponentName> getBlockedActivities() {
if (mBlockedActivities == null) {
@@ -255,8 +259,10 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @param allowedActivities A set of activity {@link ComponentName} allowed to be launched
* in the virtual device.
- * @hide // TODO(b/194949534): Unhide this API
*/
+ // Null and empty have different semantics - Null allows all activities to be streamed
+ @SuppressLint("NullableCollection")
+ @NonNull
public Builder setAllowedActivities(@Nullable Set<ComponentName> allowedActivities) {
if (mBlockedActivities != null && allowedActivities != null) {
throw new IllegalArgumentException(
@@ -279,8 +285,10 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @param blockedActivities A set of {@link ComponentName} to be blocked launching from
* virtual device.
- * @hide // TODO(b/194949534): Unhide this API
*/
+ // Allowing null to enforce that at most one of allowed / blocked activities can be non-null
+ @SuppressLint("NullableCollection")
+ @NonNull
public Builder setBlockedActivities(@Nullable Set<ComponentName> blockedActivities) {
if (mAllowedActivities != null && blockedActivities != null) {
throw new IllegalArgumentException(
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4b4e00855ac1..0b8a8a23aee4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4987,10 +4987,8 @@ public abstract class Context {
* @hide
* @see #getSystemService(String)
*/
- // TODO(216507592): Change cloudsearch_service to cloudsearch.
@SystemApi
- @SuppressLint("ServiceName")
- public static final String CLOUDSEARCH_SERVICE = "cloudsearch_service";
+ public static final String CLOUDSEARCH_SERVICE = "cloudsearch";
/**
* Use with {@link #getSystemService(String)} to access the
diff --git a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
new file mode 100644
index 000000000000..55cab52fc4f7
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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 android.hardware.biometrics;
+
+/**
+ * A secondary communication channel from AuthController back to BiometricService for
+ * events that are not associated with an autentication session. See
+ * {@link IBiometricSysuiReceiver} for events associated with a session.
+ *
+ * @hide
+ */
+oneway interface IBiometricContextListener {
+ void onDozeChanged(boolean isDozing);
+}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 0304815ef8fe..27403ec4fe59 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -122,9 +122,9 @@ interface IInputManager {
void removePortAssociation(in String inputPort);
// Add a runtime association between the input device and display.
- void addUniqueIdAssociation(in String inputDeviceName, in String displayUniqueId);
+ void addUniqueIdAssociation(in String inputPort, in String displayUniqueId);
// Remove the runtime association between the input device and display.
- void removeUniqueIdAssociation(in String inputDeviceName);
+ void removeUniqueIdAssociation(in String inputPort);
InputSensorInfo[] getSensorList(int deviceId);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index cbc837393b6b..979e9dd6a1f6 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1359,19 +1359,18 @@ public final class InputManager {
}
/**
- * Add a runtime association between the input device name and display, by unique id. Input
- * device names are expected to be unique.
- * @param inputDeviceName The name of the input device.
+ * Add a runtime association between the input port and display, by unique id. Input ports are
+ * expected to be unique.
+ * @param inputPort The port of the input device.
* @param displayUniqueId The unique id of the associated display.
* <p>
* Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
- public void addUniqueIdAssociation(@NonNull String inputDeviceName,
- @NonNull String displayUniqueId) {
+ public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
try {
- mIm.addUniqueIdAssociation(inputDeviceName, displayUniqueId);
+ mIm.addUniqueIdAssociation(inputPort, displayUniqueId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1379,15 +1378,15 @@ public final class InputManager {
/**
* Removes a runtime association between the input device and display.
- * @param inputDeviceName The name of the input device.
+ * @param inputPort The port of the input device.
* <p>
* Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
- public void removeUniqueIdAssociation(@NonNull String inputDeviceName) {
+ public void removeUniqueIdAssociation(@NonNull String inputPort) {
try {
- mIm.removeUniqueIdAssociation(inputDeviceName);
+ mIm.removeUniqueIdAssociation(inputPort);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 41642e7a9fce..af57f793bf73 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -70,6 +70,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_SET_INPUT_CONTEXT = 20;
private static final int DO_UNSET_INPUT_CONTEXT = 30;
private static final int DO_START_INPUT = 32;
+ private static final int DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED = 35;
private static final int DO_CREATE_SESSION = 40;
private static final int DO_SET_SESSION_ENABLED = 45;
private static final int DO_SHOW_SOFT_INPUT = 60;
@@ -175,7 +176,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
try {
inputMethod.initializeInternal((IBinder) args.arg1,
(IInputMethodPrivilegedOperations) args.arg2, msg.arg1,
- (boolean) args.arg3);
+ (boolean) args.arg3, msg.arg2 != 0);
} finally {
args.recycle();
}
@@ -195,14 +196,22 @@ class IInputMethodWrapper extends IInputMethod.Stub
final EditorInfo info = (EditorInfo) args.arg3;
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
final boolean restarting = args.argi5 == 1;
+ final boolean shouldShowImeSwitcherWhenImeIsShown = args.argi6 != 0;
final InputConnection ic = inputContext != null
? new RemoteInputConnection(mTarget, inputContext, cancellationGroup)
: null;
info.makeCompatible(mTargetSdkVersion);
- inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken);
+ inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken,
+ shouldShowImeSwitcherWhenImeIsShown);
args.recycle();
return;
}
+ case DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED: {
+ final boolean shouldShowImeSwitcherWhenImeIsShown = msg.arg1 != 0;
+ inputMethod.onShouldShowImeSwitcherWhenImeIsShownChanged(
+ shouldShowImeSwitcherWhenImeIsShown);
+ return;
+ }
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
@@ -291,10 +300,11 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
- int configChanges, boolean stylusHwSupported) {
- mCaller.executeOrSendMessage(
- mCaller.obtainMessageIOOO(
- DO_INITIALIZE_INTERNAL, configChanges, token, privOps, stylusHwSupported));
+ int configChanges, boolean stylusHwSupported,
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_INITIALIZE_INTERNAL,
+ configChanges, shouldShowImeSwitcherWhenImeIsShown ? 1 : 0, token, privOps,
+ stylusHwSupported));
}
@BinderThread
@@ -334,13 +344,23 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
public void startInput(IBinder startInputToken, IInputContext inputContext,
- EditorInfo attribute, boolean restarting) {
+ EditorInfo attribute, boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) {
if (mCancellationGroup == null) {
Log.e(TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
- inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, 0 /* unused */));
+ inputContext, attribute, mCancellationGroup, restarting ? 1 : 0,
+ shouldShowImeSwitcherWhenImeIsShown ? 1 : 0));
+ }
+
+ @BinderThread
+ @Override
+ public void onShouldShowImeSwitcherWhenImeIsShownChanged(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageI(
+ DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED,
+ shouldShowImeSwitcherWhenImeIsShown ? 1 : 0));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 14f92fbb4194..f55c41594389 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -658,7 +658,7 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public final void initializeInternal(@NonNull IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
- boolean stylusHwSupported) {
+ boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) {
if (mDestroyed) {
Log.i(TAG, "The InputMethodService has already onDestroyed()."
+ "Ignore the initialization.");
@@ -671,6 +671,8 @@ public class InputMethodService extends AbstractInputMethodService {
if (stylusHwSupported) {
mInkWindow = new InkWindow(mWindow.getContext());
}
+ mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
+ shouldShowImeSwitcherWhenImeIsShown);
attachToken(token);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -780,9 +782,10 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) {
mPrivOps.reportStartInputAsync(startInputToken);
-
+ mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
+ shouldShowImeSwitcherWhenImeIsShown);
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
@@ -796,6 +799,18 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
+ public void onShouldShowImeSwitcherWhenImeIsShownChanged(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
+ shouldShowImeSwitcherWhenImeIsShown);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @MainThread
+ @Override
public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
IBinder hideInputToken) {
mSystemCallingHideSoftInput = true;
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 2484cf079c56..508172d13aa3 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -19,6 +19,7 @@ package android.inputmethodservice;
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+import android.animation.ValueAnimator;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -44,6 +45,8 @@ import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowManagerPolicyConstants;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import java.util.Objects;
@@ -71,6 +74,10 @@ final class NavigationBarController {
default void onDestroy() {
}
+ default void setShouldShowImeSwitcherWhenImeIsShown(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ }
+
default void onSystemBarAppearanceChanged(@Appearance int appearance) {
}
@@ -106,6 +113,10 @@ final class NavigationBarController {
mImpl.onDestroy();
}
+ void setShouldShowImeSwitcherWhenImeIsShown(boolean shouldShowImeSwitcherWhenImeIsShown) {
+ mImpl.setShouldShowImeSwitcherWhenImeIsShown(shouldShowImeSwitcherWhenImeIsShown);
+ }
+
void onSystemBarAppearanceChanged(@Appearance int appearance) {
mImpl.onSystemBarAppearanceChanged(appearance);
}
@@ -115,6 +126,12 @@ final class NavigationBarController {
}
private static final class Impl implements Callback {
+ private static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700;
+
+ // Copied from com.android.systemui.animation.Interpolators#LEGACY_DECELERATE
+ private static final Interpolator LEGACY_DECELERATE =
+ new PathInterpolator(0f, 0f, 0.2f, 1f);
+
@NonNull
private final InputMethodService mService;
@@ -130,9 +147,17 @@ final class NavigationBarController {
@Nullable
private BroadcastReceiver mSystemOverlayChangedReceiver;
+ private boolean mShouldShowImeSwitcherWhenImeIsShown;
+
@Appearance
private int mAppearance;
+ @FloatRange(from = 0.0f, to = 1.0f)
+ private float mDarkIntensity;
+
+ @Nullable
+ private ValueAnimator mTintAnimator;
+
Impl(@NonNull InputMethodService inputMethodService) {
mService = inputMethodService;
}
@@ -190,7 +215,9 @@ final class NavigationBarController {
// TODO(b/213337792): Support InputMethodService#setBackDisposition().
// TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
- | StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+ | (mShouldShowImeSwitcherWhenImeIsShown
+ ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN
+ : 0);
navigationBarView.setNavigationIconHints(hints);
}
} else {
@@ -368,6 +395,10 @@ final class NavigationBarController {
if (mDestroyed) {
return;
}
+ if (mTintAnimator != null) {
+ mTintAnimator.cancel();
+ mTintAnimator = null;
+ }
if (mSystemOverlayChangedReceiver != null) {
mService.unregisterReceiver(mSystemOverlayChangedReceiver);
mSystemOverlayChangedReceiver = null;
@@ -404,6 +435,31 @@ final class NavigationBarController {
}
@Override
+ public void setShouldShowImeSwitcherWhenImeIsShown(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ if (mDestroyed) {
+ return;
+ }
+ if (mShouldShowImeSwitcherWhenImeIsShown == shouldShowImeSwitcherWhenImeIsShown) {
+ return;
+ }
+ mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;
+
+ if (mNavigationBarFrame == null) {
+ return;
+ }
+ final NavigationBarView navigationBarView =
+ mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance);
+ if (navigationBarView == null) {
+ return;
+ }
+ final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
+ | (shouldShowImeSwitcherWhenImeIsShown
+ ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN : 0);
+ navigationBarView.setNavigationIconHints(hints);
+ }
+
+ @Override
public void onSystemBarAppearanceChanged(@Appearance int appearance) {
if (mDestroyed) {
return;
@@ -416,10 +472,24 @@ final class NavigationBarController {
}
final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance);
- setIconTintInternal(targetDarkIntensity);
+
+ if (mTintAnimator != null) {
+ mTintAnimator.cancel();
+ }
+ mTintAnimator = ValueAnimator.ofFloat(mDarkIntensity, targetDarkIntensity);
+ mTintAnimator.addUpdateListener(
+ animation -> setIconTintInternal((Float) animation.getAnimatedValue()));
+ mTintAnimator.setDuration(DEFAULT_COLOR_ADAPT_TRANSITION_TIME);
+ mTintAnimator.setStartDelay(0);
+ mTintAnimator.setInterpolator(LEGACY_DECELERATE);
+ mTintAnimator.start();
}
private void setIconTintInternal(float darkIntensity) {
+ mDarkIntensity = darkIntensity;
+ if (mNavigationBarFrame == null) {
+ return;
+ }
final NavigationBarView navigationBarView =
mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance);
if (navigationBarView == null) {
@@ -438,7 +508,9 @@ final class NavigationBarController {
public String toDebugString() {
return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons
+ " mNavigationBarFrame=" + mNavigationBarFrame
+ + " mShouldShowImeSwitcherWhenImeIsShown" + mShouldShowImeSwitcherWhenImeIsShown
+ " mAppearance=0x" + Integer.toHexString(mAppearance)
+ + " mDarkIntensity=" + mDarkIntensity
+ "}";
}
}
diff --git a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
index 24c22a99b78d..9772bde94ac9 100644
--- a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
+++ b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
@@ -16,9 +16,7 @@
package android.net.netstats;
-import static android.app.usage.NetworkStatsManager.PREFIX_UID;
-import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG;
-import static android.app.usage.NetworkStatsManager.PREFIX_XT;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -28,6 +26,7 @@ import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.net.NetworkIdentity;
import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
@@ -54,12 +53,27 @@ import java.util.HashSet;
import java.util.Set;
/**
- * Helper class to read old version of persistent network statistics, the implementation is
- * intended to be modified by OEM partners to accommodate their custom changes.
+ * Helper class to read old version of persistent network statistics.
+ *
+ * The implementation is intended to be modified by OEM partners to
+ * accommodate their custom changes.
+ *
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = MODULE_LIBRARIES)
public class NetworkStatsDataMigrationUtils {
+ /**
+ * Prefix of the files which are used to store per network interface statistics.
+ */
+ public static final String PREFIX_XT = "xt";
+ /**
+ * Prefix of the files which are used to store per uid statistics.
+ */
+ public static final String PREFIX_UID = "uid";
+ /**
+ * Prefix of the files which are used to store per uid tagged traffic statistics.
+ */
+ public static final String PREFIX_UID_TAG = "uid_tag";
private static final HashMap<String, String> sPrefixLegacyFileNameMap =
new HashMap<String, String>() {{
@@ -146,17 +160,51 @@ public class NetworkStatsDataMigrationUtils {
}
/**
- * Read legacy persisted network stats from disk. This function provides a default
- * implementation to read persisted network stats from two different locations.
- * And this is intended to be modified by OEM to read from custom file format or
- * locations if necessary.
+ * Read legacy persisted network stats from disk.
+ *
+ * This function provides the implementation to read legacy network stats
+ * from disk. It is used for migration of legacy network stats into the
+ * stats provided by the Connectivity module.
+ * This function needs to know about the previous format(s) of the network
+ * stats data that might be stored on this device so it can be read and
+ * conserved upon upgrade to Android 13 or above.
+ *
+ * This function will be called multiple times sequentially, all on the
+ * same thread, and will not be called multiple times concurrently. This
+ * function is expected to do a substantial amount of disk access, and
+ * doesn't need to return particularly fast, but the first boot after
+ * an upgrade to Android 13+ will be held until migration is done. As
+ * migration is only necessary once, after the first boot following the
+ * upgrade, this delay is not incurred.
+ *
+ * If this function fails in any way, it should throw an exception. If this
+ * happens, the system can't know about the data that was stored in the
+ * legacy files, but it will still count data usage happening on this
+ * session. On the next boot, the system will try migration again, and
+ * merge the returned data with the data used with the previous session.
+ * The system will only try the migration up to three (3) times. The remaining
+ * count is stored in the netstats_import_legacy_file_needed device config. The
+ * legacy data is never deleted by the mainline module to avoid any possible
+ * data loss.
+ *
+ * It is possible to set the netstats_import_legacy_file_needed device config
+ * to any positive integer to force the module to perform the migration. This
+ * can be achieved by calling the following command before rebooting :
+ * adb shell device_config put connectivity netstats_import_legacy_file_needed 1
+ *
+ * The AOSP implementation provides code to read persisted network stats as
+ * they were written by AOSP prior to Android 13.
+ * OEMs who have used the AOSP implementation of persisting network stats
+ * to disk don't need to change anything.
+ * OEM that had modifications to this format should modify this function
+ * to read from their custom file format or locations if necessary.
*
* @param prefix Type of data which is being read by the service.
* @param bucketDuration Duration of the buckets of the object, in milliseconds.
* @return {@link NetworkStatsCollection} instance.
*/
@NonNull
- public static NetworkStatsCollection readPlatformCollectionLocked(
+ public static NetworkStatsCollection readPlatformCollection(
@NonNull String prefix, long bucketDuration) throws IOException {
final NetworkStatsCollection.Builder builder =
new NetworkStatsCollection.Builder(bucketDuration);
@@ -397,7 +445,7 @@ public class NetworkStatsDataMigrationUtils {
if (version >= IdentitySetVersion.VERSION_ADD_OEM_MANAGED_NETWORK) {
oemNetCapabilities = in.readInt();
} else {
- oemNetCapabilities = NetworkIdentity.OEM_NONE;
+ oemNetCapabilities = NetworkTemplate.OEM_MANAGED_NO;
}
// Legacy files might contain TYPE_MOBILE_* types which were deprecated in later
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 2d338179186e..07a51324404c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -34,6 +34,7 @@ import android.server.ServerProtoEnums;
import android.service.batterystats.BatteryStatsServiceDumpHistoryProto;
import android.service.batterystats.BatteryStatsServiceDumpProto;
import android.telephony.CellSignalStrength;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.format.DateFormat;
import android.util.ArrayMap;
@@ -2654,6 +2655,46 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract Timer getPhoneDataConnectionTimer(int dataType);
+ /** @hide */
+ public static final int RADIO_ACCESS_TECHNOLOGY_OTHER = 0;
+ /** @hide */
+ public static final int RADIO_ACCESS_TECHNOLOGY_LTE = 1;
+ /** @hide */
+ public static final int RADIO_ACCESS_TECHNOLOGY_NR = 2;
+ /** @hide */
+ public static final int RADIO_ACCESS_TECHNOLOGY_COUNT = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "RADIO_ACCESS_TECHNOLOGY_",
+ value = {RADIO_ACCESS_TECHNOLOGY_OTHER, RADIO_ACCESS_TECHNOLOGY_LTE,
+ RADIO_ACCESS_TECHNOLOGY_NR})
+ public @interface RadioAccessTechnology {
+ }
+
+ /** @hide */
+ public static final String[] RADIO_ACCESS_TECHNOLOGY_NAMES = {"Other", "LTE", "NR"};
+
+ /**
+ * Returns the time in microseconds that the mobile radio has been active on a
+ * given Radio Access Technology (RAT), at a given frequency (NR RAT only), for a given
+ * transmission power level.
+ *
+ * @param rat Radio Access Technology {@see RadioAccessTechnology}
+ * @param frequencyRange frequency range {@see ServiceState.FrequencyRange}, only needed for
+ * RADIO_ACCESS_TECHNOLOGY_NR. Use
+ * {@link ServiceState.FREQUENCY_RANGE_UNKNOWN} for other Radio Access
+ * Technologies.
+ * @param signalStrength the cellular signal strength. {@see CellSignalStrength#getLevel()}
+ * @param elapsedRealtimeMs current elapsed realtime
+ * @return time (in milliseconds) the mobile radio spent active in the specified state,
+ * while on battery.
+ * @hide
+ */
+ public abstract long getActiveRadioDurationMs(@RadioAccessTechnology int rat,
+ @ServiceState.FrequencyRange int frequencyRange, int signalStrength,
+ long elapsedRealtimeMs);
+
static final String[] WIFI_SUPPL_STATE_NAMES = {
"invalid", "disconn", "disabled", "inactive", "scanning",
"authenticating", "associating", "associated", "4-way-handshake",
@@ -3997,6 +4038,89 @@ public abstract class BatteryStats implements Parcelable {
}
}
+ private void printCellularPerRatBreakdown(PrintWriter pw, StringBuilder sb, String prefix,
+ long rawRealtimeMs) {
+ final String allFrequenciesHeader =
+ " All frequencies:\n";
+ final String[] nrFrequencyRangeDescription = new String[]{
+ " Unknown frequency:\n",
+ " Low frequency (less than 1GHz):\n",
+ " Middle frequency (1GHz to 3GHz):\n",
+ " High frequency (3GHz to 6GHz):\n",
+ " Mmwave frequency (greater than 6GHz):\n"};
+ final String signalStrengthHeader =
+ " Signal Strength Time:\n";
+ final String[] signalStrengthDescription = new String[]{
+ " unknown: ",
+ " poor: ",
+ " moderate: ",
+ " good: ",
+ " great: "};
+
+ final long totalActiveTimesMs = getMobileRadioActiveTime(rawRealtimeMs * 1000,
+ STATS_SINCE_CHARGED) / 1000;
+
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append("Active Cellular Radio Access Technology Breakdown:");
+ pw.println(sb);
+
+ boolean hasData = false;
+ final int numSignalStrength = CellSignalStrength.getNumSignalStrengthLevels();
+ for (int rat = RADIO_ACCESS_TECHNOLOGY_COUNT - 1; rat >= 0; rat--) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" ");
+ sb.append(RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
+ sb.append(":\n");
+ sb.append(prefix);
+
+ final int numFreqLvl =
+ rat == RADIO_ACCESS_TECHNOLOGY_NR ? nrFrequencyRangeDescription.length : 1;
+ for (int freqLvl = numFreqLvl - 1; freqLvl >= 0; freqLvl--) {
+ final int freqDescriptionStart = sb.length();
+ boolean hasFreqData = false;
+ if (rat == RADIO_ACCESS_TECHNOLOGY_NR) {
+ sb.append(nrFrequencyRangeDescription[freqLvl]);
+ } else {
+ sb.append(allFrequenciesHeader);
+ }
+
+ sb.append(prefix);
+ sb.append(signalStrengthHeader);
+ for (int strength = 0; strength < numSignalStrength; strength++) {
+ final long timeMs = getActiveRadioDurationMs(rat, freqLvl, strength,
+ rawRealtimeMs);
+ if (timeMs <= 0) continue;
+ hasFreqData = true;
+ sb.append(prefix);
+ sb.append(signalStrengthDescription[strength]);
+ formatTimeMs(sb, timeMs);
+ sb.append("(");
+ sb.append(formatRatioLocked(timeMs, totalActiveTimesMs));
+ sb.append(")\n");
+ }
+
+ if (hasFreqData) {
+ hasData = true;
+ pw.print(sb);
+ sb.setLength(0);
+ sb.append(prefix);
+ } else {
+ // No useful data was printed, rewind sb to before the start of this frequency.
+ sb.setLength(freqDescriptionStart);
+ }
+ }
+ }
+
+ if (!hasData) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" (no activity)");
+ pw.println(sb);
+ }
+ }
+
/**
* Temporary for settings.
*/
@@ -5269,6 +5393,8 @@ public abstract class BatteryStats implements Parcelable {
printControllerActivity(pw, sb, prefix, CELLULAR_CONTROLLER_NAME,
getModemControllerActivity(), which);
+ printCellularPerRatBreakdown(pw, sb, prefix + " ", rawRealtimeMs);
+
pw.print(" Cellular data received: "); pw.println(formatBytesLocked(mobileRxTotalBytes));
pw.print(" Cellular data sent: "); pw.println(formatBytesLocked(mobileTxTotalBytes));
pw.print(" Cellular packets received: "); pw.println(mobileRxTotalPackets);
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 29c7796d8660..cb1b5d3d20b8 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -578,6 +578,14 @@ public abstract class AutofillService extends Service {
public static final String SERVICE_META_DATA = "android.autofill";
/**
+ * Name of the {@link FillResponse} extra used to return a delayed fill response.
+ *
+ * <p>Please see {@link FillRequest#getDelayedFillIntentSender()} on how to send a delayed
+ * fill response to framework.</p>
+ */
+ public static final String EXTRA_FILL_RESPONSE = "android.service.autofill.extra.FILL_RESPONSE";
+
+ /**
* Name of the {@link IResultReceiver} extra used to return the primary result of a request.
*
* @hide
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index f820f0389f0d..e4d3732361ed 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -19,6 +19,7 @@ package android.service.autofill;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -160,6 +161,19 @@ public final class FillRequest implements Parcelable {
*/
private final @Nullable InlineSuggestionsRequest mInlineSuggestionsRequest;
+ /**
+ * Gets the {@link IntentSender} to send a delayed fill response.
+ *
+ * <p>The autofill service must first indicate that it wants to return a delayed
+ * {@link FillResponse} by setting {@link FillResponse#FLAG_DELAY_FILL} in a successful
+ * fill response. Then it can use this IntentSender to send an Intent with extra
+ * {@link AutofillService#EXTRA_FILL_RESPONSE} with the delayed response.</p>
+ *
+ * <p>Note that this may be null if a delayed fill response is not supported for
+ * this fill request.</p>
+ */
+ private final @Nullable IntentSender mDelayedFillIntentSender;
+
private void onConstructed() {
Preconditions.checkCollectionElementsNotNull(mFillContexts, "contexts");
}
@@ -252,6 +266,16 @@ public final class FillRequest implements Parcelable {
*
* <p>The Autofill Service must set supportsInlineSuggestions in its XML to enable support
* for inline suggestions.</p>
+ * @param delayedFillIntentSender
+ * Gets the {@link IntentSender} to send a delayed fill response.
+ *
+ * <p>The autofill service must first indicate that it wants to return a delayed
+ * {@link FillResponse} by setting {@link FillResponse#FLAG_DELAY_FILL} in a successful
+ * fill response. Then it can use this IntentSender to send an Intent with extra
+ * {@link AutofillService#EXTRA_FILL_RESPONSE} with the delayed response.</p>
+ *
+ * <p>Note that this may be null if a delayed fill response is not supported for
+ * this fill request.</p>
* @hide
*/
@DataClass.Generated.Member
@@ -260,7 +284,8 @@ public final class FillRequest implements Parcelable {
@NonNull List<FillContext> fillContexts,
@Nullable Bundle clientState,
@RequestFlags int flags,
- @Nullable InlineSuggestionsRequest inlineSuggestionsRequest) {
+ @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+ @Nullable IntentSender delayedFillIntentSender) {
this.mId = id;
this.mFillContexts = fillContexts;
com.android.internal.util.AnnotationValidations.validate(
@@ -276,6 +301,7 @@ public final class FillRequest implements Parcelable {
| FLAG_VIEW_NOT_FOCUSED
| FLAG_ACTIVITY_START);
this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
+ this.mDelayedFillIntentSender = delayedFillIntentSender;
onConstructed();
}
@@ -348,6 +374,22 @@ public final class FillRequest implements Parcelable {
return mInlineSuggestionsRequest;
}
+ /**
+ * Gets the {@link IntentSender} to send a delayed fill response.
+ *
+ * <p>The autofill service must first indicate that it wants to return a delayed
+ * {@link FillResponse} by setting {@link FillResponse#FLAG_DELAY_FILL} in a successful
+ * fill response. Then it can use this IntentSender to send an Intent with extra
+ * {@link AutofillService#EXTRA_FILL_RESPONSE} with the delayed response.</p>
+ *
+ * <p>Note that this may be null if a delayed fill response is not supported for
+ * this fill request.</p>
+ */
+ @DataClass.Generated.Member
+ public @Nullable IntentSender getDelayedFillIntentSender() {
+ return mDelayedFillIntentSender;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -359,7 +401,8 @@ public final class FillRequest implements Parcelable {
"fillContexts = " + mFillContexts + ", " +
"clientState = " + mClientState + ", " +
"flags = " + requestFlagsToString(mFlags) + ", " +
- "inlineSuggestionsRequest = " + mInlineSuggestionsRequest +
+ "inlineSuggestionsRequest = " + mInlineSuggestionsRequest + ", " +
+ "delayedFillIntentSender = " + mDelayedFillIntentSender +
" }";
}
@@ -372,12 +415,14 @@ public final class FillRequest implements Parcelable {
byte flg = 0;
if (mClientState != null) flg |= 0x4;
if (mInlineSuggestionsRequest != null) flg |= 0x10;
+ if (mDelayedFillIntentSender != null) flg |= 0x20;
dest.writeByte(flg);
dest.writeInt(mId);
dest.writeParcelableList(mFillContexts, flags);
if (mClientState != null) dest.writeBundle(mClientState);
dest.writeInt(mFlags);
if (mInlineSuggestionsRequest != null) dest.writeTypedObject(mInlineSuggestionsRequest, flags);
+ if (mDelayedFillIntentSender != null) dest.writeTypedObject(mDelayedFillIntentSender, flags);
}
@Override
@@ -398,6 +443,7 @@ public final class FillRequest implements Parcelable {
Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle();
int flags = in.readInt();
InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR);
+ IntentSender delayedFillIntentSender = (flg & 0x20) == 0 ? null : (IntentSender) in.readTypedObject(IntentSender.CREATOR);
this.mId = id;
this.mFillContexts = fillContexts;
@@ -414,6 +460,7 @@ public final class FillRequest implements Parcelable {
| FLAG_VIEW_NOT_FOCUSED
| FLAG_ACTIVITY_START);
this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
+ this.mDelayedFillIntentSender = delayedFillIntentSender;
onConstructed();
}
@@ -433,10 +480,10 @@ public final class FillRequest implements Parcelable {
};
@DataClass.Generated(
- time = 1643052544776L,
+ time = 1643386870464L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
- inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_ACTIVITY_START\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+ inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_ACTIVITY_START\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 903e77fcb3d7..296877a448ab 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -65,10 +65,22 @@ public final class FillResponse implements Parcelable {
*/
public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2;
+ /**
+ * Flag used to request to wait for a delayed fill from the remote Autofill service if it's
+ * passed to {@link Builder#setFlags(int)}.
+ *
+ * <p>Some datasets (i.e. OTP) take time to produce. This flags allows remote service to send
+ * a {@link FillResponse} to the latest {@link FillRequest} via
+ * {@link FillRequest#getDelayedFillIntentSender()} even if the original {@link FillCallback}
+ * has timed out.
+ */
+ public static final int FLAG_DELAY_FILL = 0x4;
+
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_TRACK_CONTEXT_COMMITED,
- FLAG_DISABLE_ACTIVITY_ONLY
+ FLAG_DISABLE_ACTIVITY_ONLY,
+ FLAG_DELAY_FILL
})
@Retention(RetentionPolicy.SOURCE)
@interface FillResponseFlags {}
@@ -657,7 +669,7 @@ public final class FillResponse implements Parcelable {
public Builder setFlags(@FillResponseFlags int flags) {
throwIfDestroyed();
mFlags = Preconditions.checkFlagsArgument(flags,
- FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY);
+ FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY | FLAG_DELAY_FILL);
return this;
}
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index fba61cfd801e..5bd423599f5d 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Service;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -310,9 +311,10 @@ public class TrustAgentService extends Service {
*
* @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
*
- * TODO(b/213631672): Add CTS tests
+ * TODO(b/213631672): Remove @hide and @TestApi
* @hide
*/
+ @TestApi
public void onUserRequestedUnlock() {
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f2a03558663e..c91851a8896d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -905,11 +905,12 @@ public abstract class WallpaperService extends Service {
if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null) {
return;
}
+
+ SurfaceControl.Transaction surfaceControlTransaction = new SurfaceControl.Transaction();
// TODO: apply the dimming to preview as well once surface transparency works in
// preview mode.
if (!isPreview() && mShouldDim) {
Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount);
- SurfaceControl.Transaction surfaceControl = new SurfaceControl.Transaction();
// Animate dimming to gradually change the wallpaper alpha from the previous
// dim amount to the new amount only if the dim amount changed.
@@ -919,16 +920,15 @@ public abstract class WallpaperService extends Service {
? 0 : DIMMING_ANIMATION_DURATION_MS);
animator.addUpdateListener((ValueAnimator va) -> {
final float dimValue = (float) va.getAnimatedValue();
- surfaceControl
- .setAlpha(mBbqSurfaceControl, 1 - dimValue)
- .apply();
+ if (mBbqSurfaceControl != null) {
+ surfaceControlTransaction
+ .setAlpha(mBbqSurfaceControl, 1 - dimValue).apply();
+ }
});
animator.start();
} else {
Log.v(TAG, "Setting wallpaper dimming: " + 0);
- new SurfaceControl.Transaction()
- .setAlpha(mBbqSurfaceControl, 1.0f)
- .apply();
+ surfaceControlTransaction.setAlpha(mBbqSurfaceControl, 1.0f).apply();
}
}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index ff6903e814b7..08cc31c8534f 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -105,12 +105,14 @@ public interface InputMethod {
* current IME.
* @param configChanges {@link InputMethodInfo#getConfigChanges()} declared by IME.
* @param stylusHwSupported {@link InputMethodInfo#supportsStylusHandwriting()} declared by IME.
+ * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
+ * shown while the IME is shown.
* @hide
*/
@MainThread
default void initializeInternal(IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
- boolean stylusHwSupported) {
+ boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) {
attachToken(token);
}
@@ -229,6 +231,8 @@ public interface InputMethod {
* the next {@link #startInput(InputConnection, EditorInfo, IBinder)} as
* long as your implementation of {@link InputMethod} relies on such
* IPCs
+ * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
+ * shown while the IME is shown.
* @see #startInput(InputConnection, EditorInfo)
* @see #restartInput(InputConnection, EditorInfo)
* @see EditorInfo
@@ -237,7 +241,7 @@ public interface InputMethod {
@MainThread
default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) {
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
@@ -246,6 +250,18 @@ public interface InputMethod {
}
/**
+ * Notifies that whether the IME should show the IME switcher or not is being changed.
+ *
+ * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
+ * shown while the IME is shown.
+ * @hide
+ */
+ @MainThread
+ default void onShouldShowImeSwitcherWhenImeIsShownChanged(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ }
+
+ /**
* Create a new {@link InputMethodSession} that can be handed to client
* applications for interacting with the input method. You can later
* use {@link #revokeSession(InputMethodSession)} to destroy the session
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c6f64f4ad633..b00a3829f468 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -301,6 +301,13 @@ public class RemoteViews implements Parcelable, Filter {
public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
/**
+ * This mask determines which flags are propagated to nested RemoteViews (either added by
+ * addView, or set as portrait/landscape/sized RemoteViews).
+ */
+ static final int FLAG_MASK_TO_PROPAGATE =
+ FLAG_WIDGET_IS_COLLECTION_CHILD | FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
+
+ /**
* A ReadWriteHelper which has the same behavior as ReadWriteHelper.DEFAULT, but which is
* intentionally a different instance in order to trick Bundle reader so that it doesn't allow
* lazy initialization.
@@ -467,6 +474,18 @@ public class RemoteViews implements Parcelable, Filter {
*/
public void addFlags(@ApplyFlags int flags) {
mApplyFlags = mApplyFlags | flags;
+
+ int flagsToPropagate = flags & FLAG_MASK_TO_PROPAGATE;
+ if (flagsToPropagate != 0) {
+ if (hasSizedRemoteViews()) {
+ for (RemoteViews remoteView : mSizedRemoteViews) {
+ remoteView.addFlags(flagsToPropagate);
+ }
+ } else if (hasLandscapeAndPortraitLayouts()) {
+ mLandscape.addFlags(flagsToPropagate);
+ mPortrait.addFlags(flagsToPropagate);
+ }
+ }
}
/**
@@ -2407,6 +2426,10 @@ public class RemoteViews implements Parcelable, Filter {
// will return -1.
final int nextChild = getNextRecyclableChild(target);
RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
+
+ int flagsToPropagate = mApplyFlags & FLAG_MASK_TO_PROPAGATE;
+ if (flagsToPropagate != 0) rvToApply.addFlags(flagsToPropagate);
+
if (nextChild >= 0 && mStableId != NO_ID) {
// At that point, the views starting at index nextChild are the ones recyclable but
// not yet recycled. All views added on that round of application are placed before.
@@ -2419,8 +2442,8 @@ public class RemoteViews implements Parcelable, Filter {
target.removeViews(nextChild, recycledViewIndex - nextChild);
}
setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
- rvToApply.reapply(context, child, handler, null /* size */, colorResources,
- false /* topLevel */);
+ rvToApply.reapplyNestedViews(context, child, rootParent, handler,
+ null /* size */, colorResources);
return;
}
// If we cannot recycle the views, we still remove all views in between to
@@ -2431,8 +2454,8 @@ public class RemoteViews implements Parcelable, Filter {
// If we cannot recycle, insert the new view before the next recyclable child.
// Inflate nested views and add as children
- View nestedView = rvToApply.apply(context, target, handler, null /* size */,
- colorResources);
+ View nestedView = rvToApply.applyNestedViews(context, target, rootParent, handler,
+ null /* size */, colorResources);
if (mStableId != NO_ID) {
setStableId(nestedView, mStableId);
}
@@ -3780,7 +3803,7 @@ public class RemoteViews implements Parcelable, Filter {
* @param parcel
*/
public RemoteViews(Parcel parcel) {
- this(parcel, /* rootParent= */ null, /* info= */ null, /* depth= */ 0);
+ this(parcel, /* rootData= */ null, /* info= */ null, /* depth= */ 0);
}
private RemoteViews(@NonNull Parcel parcel, @Nullable HierarchyRootData rootData,
@@ -5580,6 +5603,16 @@ public class RemoteViews implements Parcelable, Filter {
return result;
}
+ private View applyNestedViews(Context context, ViewGroup directParent,
+ ViewGroup rootParent, InteractionHandler handler, SizeF size,
+ ColorResources colorResources) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+
+ View result = inflateView(context, rvToApply, directParent, 0, colorResources);
+ rvToApply.performApply(result, rootParent, handler, colorResources);
+ return result;
+ }
+
private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
return inflateView(context, rv, parent, 0, null);
}
@@ -5895,6 +5928,12 @@ public class RemoteViews implements Parcelable, Filter {
}
}
+ private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
+ InteractionHandler handler, SizeF size, ColorResources colorResources) {
+ RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+ rvToApply.performApply(v, rootParent, handler, colorResources);
+ }
+
/**
* Applies all the actions to the provided view, moving as much of the task on the background
* thread as possible.
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 07cb48eb1a0a..629a1b36b9e6 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -113,7 +113,7 @@ interface IBatteryStats {
void notePhoneOn();
void notePhoneOff();
void notePhoneSignalStrength(in SignalStrength signalStrength);
- void notePhoneDataConnectionState(int dataType, boolean hasData, int serviceType);
+ void notePhoneDataConnectionState(int dataType, boolean hasData, int serviceType, int nrFrequency);
void notePhoneState(int phoneState);
void noteWifiOn();
void noteWifiOff();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 25ee2d0fa019..4fc977f670f0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -69,10 +69,14 @@ import android.os.connectivity.GpsBatteryStats;
import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.connectivity.WifiBatteryStats;
import android.provider.Settings;
+import android.telephony.Annotation.NetworkType;
import android.telephony.CellSignalStrength;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthNr;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.ServiceState;
+import android.telephony.ServiceState.RegState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -935,6 +939,119 @@ public class BatteryStatsImpl extends BatteryStats {
final StopwatchTimer[] mPhoneDataConnectionsTimer =
new StopwatchTimer[NUM_DATA_CONNECTION_TYPES];
+ @RadioAccessTechnology
+ int mActiveRat = RADIO_ACCESS_TECHNOLOGY_OTHER;
+
+ private static class RadioAccessTechnologyBatteryStats {
+ /**
+ * This RAT is currently being used.
+ */
+ private boolean mActive = false;
+ /**
+ * Current active frequency range for this RAT.
+ */
+ @ServiceState.FrequencyRange
+ private int mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+ /**
+ * Current signal strength for this RAT.
+ */
+ private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ /**
+ * Timers for each combination of frequency range and signal strength.
+ */
+ public final StopwatchTimer[][] perStateTimers;
+
+ RadioAccessTechnologyBatteryStats(int freqCount, Clock clock, TimeBase timeBase) {
+ perStateTimers =
+ new StopwatchTimer[freqCount][CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS];
+ for (int i = 0; i < freqCount; i++) {
+ for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) {
+ perStateTimers[i][j] = new StopwatchTimer(clock, null, -1, null, timeBase);
+ }
+ }
+ }
+
+ /**
+ * Note this RAT is currently being used.
+ */
+ public void noteActive(boolean active, long elapsedRealtimeMs) {
+ if (mActive == active) return;
+ mActive = active;
+ if (mActive) {
+ perStateTimers[mFrequencyRange][mSignalStrength].startRunningLocked(
+ elapsedRealtimeMs);
+ } else {
+ perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(
+ elapsedRealtimeMs);
+ }
+ }
+
+ /**
+ * Note current frequency range has changed.
+ */
+ public void noteFrequencyRange(@ServiceState.FrequencyRange int frequencyRange,
+ long elapsedRealtimeMs) {
+ if (mFrequencyRange == frequencyRange) return;
+
+ if (!mActive) {
+ // RAT not in use, note the frequency change and move on.
+ mFrequencyRange = frequencyRange;
+ return;
+ }
+ perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(elapsedRealtimeMs);
+ perStateTimers[frequencyRange][mSignalStrength].startRunningLocked(elapsedRealtimeMs);
+ mFrequencyRange = frequencyRange;
+ }
+
+ /**
+ * Note current signal strength has changed.
+ */
+ public void noteSignalStrength(int signalStrength, long elapsedRealtimeMs) {
+ if (mSignalStrength == signalStrength) return;
+
+ if (!mActive) {
+ // RAT not in use, note the signal strength change and move on.
+ mSignalStrength = signalStrength;
+ return;
+ }
+ perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(elapsedRealtimeMs);
+ perStateTimers[mFrequencyRange][signalStrength].startRunningLocked(elapsedRealtimeMs);
+ mSignalStrength = signalStrength;
+ }
+
+ /**
+ * Reset display timers.
+ */
+ public void reset(long elapsedRealtimeUs) {
+ final int size = perStateTimers.length;
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) {
+ perStateTimers[i][j].reset(false, elapsedRealtimeUs);
+ }
+ }
+ }
+ }
+
+ /**
+ * Number of frequency ranges, keep in sync with {@link ServiceState.FrequencyRange}
+ */
+ private static final int NR_FREQUENCY_COUNT = 5;
+
+ RadioAccessTechnologyBatteryStats[] mPerRatBatteryStats =
+ new RadioAccessTechnologyBatteryStats[RADIO_ACCESS_TECHNOLOGY_COUNT];
+
+ @GuardedBy("this")
+ private RadioAccessTechnologyBatteryStats getRatBatteryStatsLocked(
+ @RadioAccessTechnology int rat) {
+ RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat];
+ if (stats == null) {
+ final int freqCount = rat == RADIO_ACCESS_TECHNOLOGY_NR ? NR_FREQUENCY_COUNT : 1;
+ stats = new RadioAccessTechnologyBatteryStats(freqCount, mClock, mOnBatteryTimeBase);
+ mPerRatBatteryStats[rat] = stats;
+ }
+ return stats;
+ }
+
final LongSamplingCounter[] mNetworkByteActivityCounters =
new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -5886,6 +6003,10 @@ public class BatteryStatsImpl extends BatteryStats {
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mMobileRadioPowerState = powerState;
+
+ // Inform current RatBatteryStats that the modem active state might have changed.
+ getRatBatteryStatsLocked(mActiveRat).noteActive(active, elapsedRealtimeMs);
+
if (active) {
mMobileRadioActiveTimer.startRunningLocked(elapsedRealtimeMs);
mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtimeMs);
@@ -6307,21 +6428,86 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength,
long elapsedRealtimeMs, long uptimeMs) {
- // Bin the strength.
- int bin = signalStrength.getLevel();
- updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, bin,
+ final int overallSignalStrength = signalStrength.getLevel();
+ final SparseIntArray perRatSignalStrength = new SparseIntArray(
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT);
+
+ // Extract signal strength level for each RAT.
+ final List<CellSignalStrength> cellSignalStrengths =
+ signalStrength.getCellSignalStrengths();
+ final int size = cellSignalStrengths.size();
+ for (int i = 0; i < size; i++) {
+ CellSignalStrength cellSignalStrength = cellSignalStrengths.get(i);
+ // Map each CellSignalStrength to a BatteryStats.RadioAccessTechnology
+ final int ratType;
+ final int level;
+ if (cellSignalStrength instanceof CellSignalStrengthNr) {
+ ratType = RADIO_ACCESS_TECHNOLOGY_NR;
+ level = cellSignalStrength.getLevel();
+ } else if (cellSignalStrength instanceof CellSignalStrengthLte) {
+ ratType = RADIO_ACCESS_TECHNOLOGY_LTE;
+ level = cellSignalStrength.getLevel();
+ } else {
+ ratType = RADIO_ACCESS_TECHNOLOGY_OTHER;
+ level = cellSignalStrength.getLevel();
+ }
+
+ // According to SignalStrength#getCellSignalStrengths(), multiple of the same
+ // cellSignalStrength can be present. Just take the highest level one for each RAT.
+ if (perRatSignalStrength.get(ratType, -1) < level) {
+ perRatSignalStrength.put(ratType, level);
+ }
+ }
+
+ notePhoneSignalStrengthLocked(overallSignalStrength, perRatSignalStrength,
+ elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Note phone signal strength change, including per RAT signal strength.
+ *
+ * @param signalStrength overall signal strength {@see SignalStrength#getLevel()}
+ * @param perRatSignalStrength signal strength of available RATs
+ */
+ @GuardedBy("this")
+ public void notePhoneSignalStrengthLocked(int signalStrength,
+ SparseIntArray perRatSignalStrength) {
+ notePhoneSignalStrengthLocked(signalStrength, perRatSignalStrength,
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
+ }
+
+ /**
+ * Note phone signal strength change, including per RAT signal strength.
+ *
+ * @param signalStrength overall signal strength {@see SignalStrength#getLevel()}
+ * @param perRatSignalStrength signal strength of available RATs
+ */
+ @GuardedBy("this")
+ public void notePhoneSignalStrengthLocked(int signalStrength,
+ SparseIntArray perRatSignalStrength,
+ long elapsedRealtimeMs, long uptimeMs) {
+ // Note each RAT's signal strength.
+ final int size = perRatSignalStrength.size();
+ for (int i = 0; i < size; i++) {
+ final int rat = perRatSignalStrength.keyAt(i);
+ final int ratSignalStrength = perRatSignalStrength.valueAt(i);
+ getRatBatteryStatsLocked(rat).noteSignalStrength(ratSignalStrength, elapsedRealtimeMs);
+ }
+ updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, signalStrength,
elapsedRealtimeMs, uptimeMs);
}
@UnsupportedAppUsage
@GuardedBy("this")
- public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType) {
- notePhoneDataConnectionStateLocked(dataType, hasData, serviceType,
+ public void notePhoneDataConnectionStateLocked(@NetworkType int dataType, boolean hasData,
+ @RegState int serviceType, @ServiceState.FrequencyRange int nrFrequency) {
+ notePhoneDataConnectionStateLocked(dataType, hasData, serviceType, nrFrequency,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
- public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType,
+ public void notePhoneDataConnectionStateLocked(@NetworkType int dataType, boolean hasData,
+ @RegState int serviceType, @ServiceState.FrequencyRange int nrFrequency,
long elapsedRealtimeMs, long uptimeMs) {
// BatteryStats uses 0 to represent no network type.
// Telephony does not have a concept of no network type, and uses 0 to represent unknown.
@@ -6344,6 +6530,13 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
}
+
+ final int newRat = mapNetworkTypeToRadioAccessTechnology(bin);
+ if (newRat == RADIO_ACCESS_TECHNOLOGY_NR) {
+ // Note possible frequency change for the NR RAT.
+ getRatBatteryStatsLocked(newRat).noteFrequencyRange(nrFrequency, elapsedRealtimeMs);
+ }
+
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
@@ -6357,6 +6550,45 @@ public class BatteryStatsImpl extends BatteryStats {
}
mPhoneDataConnectionType = bin;
mPhoneDataConnectionsTimer[bin].startRunningLocked(elapsedRealtimeMs);
+
+ if (mActiveRat != newRat) {
+ getRatBatteryStatsLocked(mActiveRat).noteActive(false, elapsedRealtimeMs);
+ mActiveRat = newRat;
+ }
+ final boolean modemActive = mMobileRadioActiveTimer.isRunningLocked();
+ getRatBatteryStatsLocked(newRat).noteActive(modemActive, elapsedRealtimeMs);
+ }
+ }
+
+ @RadioAccessTechnology
+ private static int mapNetworkTypeToRadioAccessTechnology(@NetworkType int dataType) {
+ switch (dataType) {
+ case TelephonyManager.NETWORK_TYPE_NR:
+ return RADIO_ACCESS_TECHNOLOGY_NR;
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ return RADIO_ACCESS_TECHNOLOGY_LTE;
+ case TelephonyManager.NETWORK_TYPE_UNKNOWN: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_GPRS: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EDGE: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_UMTS: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_CDMA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EVDO_0: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EVDO_A: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_1xRTT: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_HSDPA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_HSUPA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_HSPA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_IDEN: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EVDO_B: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EHRPD: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_HSPAP: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_GSM: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_TD_SCDMA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_IWLAN: //fallthrough
+ return RADIO_ACCESS_TECHNOLOGY_OTHER;
+ default:
+ Slog.w(TAG, "Unhandled NetworkType (" + dataType + "), mapping to OTHER");
+ return RADIO_ACCESS_TECHNOLOGY_OTHER;
}
}
@@ -7731,6 +7963,23 @@ public class BatteryStatsImpl extends BatteryStats {
return mPhoneDataConnectionsTimer[dataType];
}
+ @Override public long getActiveRadioDurationMs(@RadioAccessTechnology int rat,
+ @ServiceState.FrequencyRange int frequencyRange, int signalStrength,
+ long elapsedRealtimeMs) {
+ final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat];
+ if (stats == null) return 0L;
+
+ final int freqCount = stats.perStateTimers.length;
+ if (frequencyRange < 0 || frequencyRange >= freqCount) return 0L;
+
+ final StopwatchTimer[] strengthTimers = stats.perStateTimers[frequencyRange];
+ final int strengthCount = strengthTimers.length;
+ if (signalStrength < 0 || signalStrength >= strengthCount) return 0L;
+
+ return stats.perStateTimers[frequencyRange][signalStrength].getTotalTimeLocked(
+ elapsedRealtimeMs * 1000, STATS_SINCE_CHARGED) / 1000;
+ }
+
@UnsupportedAppUsage
@Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) {
return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
@@ -12553,6 +12802,11 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkByteActivityCounters[i].reset(false, elapsedRealtimeUs);
mNetworkPacketActivityCounters[i].reset(false, elapsedRealtimeUs);
}
+ for (int i = 0; i < RADIO_ACCESS_TECHNOLOGY_COUNT; i++) {
+ final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[i];
+ if (stats == null) continue;
+ stats.reset(elapsedRealtimeUs);
+ }
mMobileRadioActiveTimer.reset(false, elapsedRealtimeUs);
mMobileRadioActivePerAppTimer.reset(false, elapsedRealtimeUs);
mMobileRadioActiveAdjustedTime.reset(false, elapsedRealtimeUs);
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index be91aaca5d39..0a29fc5285a5 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -1159,6 +1159,17 @@ public class BinderCallsStats implements BinderInternal.Observer {
: Integer.compare(a.transactionCode, b.transactionCode);
}
+ /** @hide */
+ public static void startForBluetooth(Context context) {
+ new BinderCallsStats.SettingsObserver(
+ context,
+ new BinderCallsStats(
+ new BinderCallsStats.Injector(),
+ com.android.internal.os.BinderLatencyProto.Dims.BLUETOOTH));
+
+ }
+
+
/**
* Settings observer for other processes (not system_server).
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index a5cf7ce1d37c..23ebc9f94915 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -20,6 +20,7 @@ import android.app.ITransientNotificationCallback;
import android.content.ComponentName;
import android.graphics.drawable.Icon;
import android.graphics.Rect;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsHbmListener;
@@ -166,6 +167,8 @@ oneway interface IStatusBar
* Used to hide the authentication dialog, e.g. when the application cancels authentication.
*/
void hideAuthenticationDialog();
+ /* Used to notify the biometric service of events that occur outside of an operation. */
+ void setBiometicContextListener(in IBiometricContextListener listener);
/**
* Sets an instance of IUdfpsHbmListener for UdfpsController.
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index accb98645599..f28325e3cc51 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -20,6 +20,7 @@ import android.app.Notification;
import android.content.ComponentName;
import android.graphics.drawable.Icon;
import android.graphics.Rect;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsHbmListener;
@@ -125,6 +126,8 @@ interface IStatusBarService
void onBiometricError(int modality, int error, int vendorCode);
// Used to hide the authentication dialog, e.g. when the application cancels authentication
void hideAuthenticationDialog();
+ // Used to notify the biometric service of events that occur outside of an operation.
+ void setBiometicContextListener(in IBiometricContextListener listener);
/**
* Sets an instance of IUdfpsHbmListener for UdfpsController.
diff --git a/core/java/com/android/internal/util/UserIcons.java b/core/java/com/android/internal/util/UserIcons.java
index bfe43237da58..17b84ffc2f3f 100644
--- a/core/java/com/android/internal/util/UserIcons.java
+++ b/core/java/com/android/internal/util/UserIcons.java
@@ -16,6 +16,7 @@
package com.android.internal.util;
+import android.annotation.ColorInt;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -72,9 +73,30 @@ public class UserIcons {
// Return colored icon instead
colorResId = USER_ICON_COLORS[userId % USER_ICON_COLORS.length];
}
+ return getDefaultUserIconInColor(resources, resources.getColor(colorResId, null));
+ }
+
+ /**
+ * Returns a default user icon in a particular color.
+ *
+ * @param resources resources object to fetch the user icon
+ * @param color the color used for the icon
+ */
+ public static Drawable getDefaultUserIconInColor(Resources resources, @ColorInt int color) {
Drawable icon = resources.getDrawable(R.drawable.ic_account_circle, null).mutate();
- icon.setColorFilter(resources.getColor(colorResId, null), Mode.SRC_IN);
+ icon.setColorFilter(color, Mode.SRC_IN);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
return icon;
}
+
+ /**
+ * Returns an array containing colors to be used for default user icons.
+ */
+ public static int[] getUserIconColors(Resources resources) {
+ int[] result = new int[USER_ICON_COLORS.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = resources.getColor(USER_ICON_COLORS[i], null);
+ }
+ return result;
+ }
}
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index da24832f9d84..d2bc3442ed36 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -37,7 +37,8 @@ import com.android.internal.view.InlineSuggestionsRequestInfo;
*/
oneway interface IInputMethod {
void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
- int configChanges, boolean stylusHwSupported);
+ int configChanges, boolean stylusHwSupported,
+ boolean shouldShowImeSwitcherWhenImeIsShown);
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
in IInlineSuggestionsRequestCallback cb);
@@ -47,7 +48,10 @@ oneway interface IInputMethod {
void unbindInput();
void startInput(in IBinder startInputToken, in IInputContext inputContext,
- in EditorInfo attribute, boolean restarting);
+ in EditorInfo attribute, boolean restarting,
+ boolean shouldShowImeSwitcherWhenImeIsShown);
+
+ void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown);
void createSession(in InputChannel channel, IInputSessionCallback callback);
diff --git a/core/jni/android/opengl/OWNERS b/core/jni/android/opengl/OWNERS
new file mode 100644
index 000000000000..ce4b9075d59c
--- /dev/null
+++ b/core/jni/android/opengl/OWNERS
@@ -0,0 +1 @@
+file:/graphics/java/android/graphics/OWNERS
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index d039bcff3b26..78b403c39a17 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -133,6 +133,18 @@ nativeClassInit (JNIEnv *_env, jclass _this)
MakeGlobalRefOrDie(_env, _env->CallObjectMethod(empty.get(), stringOffsets.intern));
}
+uint64_t htonll(uint64_t ll) {
+ constexpr uint32_t kBytesToTest = 0x12345678;
+ constexpr uint8_t kFirstByte = (const uint8_t &)kBytesToTest;
+ constexpr bool kIsLittleEndian = kFirstByte == 0x78;
+
+ if constexpr (kIsLittleEndian) {
+ return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32);
+ } else {
+ return ll;
+ }
+}
+
static jstring getJavaInternedString(JNIEnv *env, const String8 &string) {
if (string == "") {
return gStringOffsets.emptyString;
@@ -193,7 +205,8 @@ translateNativeSensorToJavaSensor(JNIEnv *env, jobject sensor, const Sensor& nat
int32_t id = nativeSensor.getId();
env->CallVoidMethod(sensor, sensorOffsets.setId, id);
Sensor::uuid_t uuid = nativeSensor.getUuid();
- env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, uuid.i64[0], uuid.i64[1]);
+ env->CallVoidMethod(sensor, sensorOffsets.setUuid, htonll(uuid.i64[0]),
+ htonll(uuid.i64[1]));
}
return sensor;
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index e13b78868a2c..6b82ba80a26a 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1288,7 +1288,7 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile,
audioFormatFromNative(nAudioProfile->format), jSamplingRates.get(),
jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType);
- if (jAudioProfile == nullptr) {
+ if (*jAudioProfile == nullptr) {
return AUDIO_JAVA_ERROR;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c9bd22210a77..0c8c7f281fb3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -719,6 +719,7 @@
<protected-broadcast android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES" />
<protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" />
<protected-broadcast android:name="android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER" />
+ <protected-broadcast android:name="android.service.autofill.action.DELAYED_FILL" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
@@ -5416,7 +5417,7 @@
android:protectionLevel="signature|setup|role" />
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
- @hide -->
+ @hide @TestApi -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
android:protectionLevel="signature|setup" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 78841fc914c4..0edc0d6b9ed6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2707,6 +2707,9 @@
Values are bandwidth_estimator, carrier_config and modem. -->
<string name="config_bandwidthEstimateSource">bandwidth_estimator</string>
+ <!-- Whether force to enable telephony new data stack or not -->
+ <bool name="config_force_enable_telephony_new_data_stack">false</bool>
+
<!-- Whether WiFi display is supported by this device.
There are many prerequisites for this feature to work correctly.
Here are a few of them:
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 20c5c610abe4..602d5f9c691e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -472,6 +472,7 @@
<java-symbol type="integer" name="config_mobile_mtu" />
<java-symbol type="array" name="config_mobile_tcp_buffers" />
<java-symbol type="string" name="config_tcp_buffers" />
+ <java-symbol type="bool" name="config_force_enable_telephony_new_data_stack" />
<java-symbol type="integer" name="config_volte_replacement_rat"/>
<java-symbol type="integer" name="config_valid_wappush_index" />
<java-symbol type="integer" name="config_overrideHasPermanentMenuKey" />
diff --git a/core/tests/coretests/res/layout/remote_view_relative_layout.xml b/core/tests/coretests/res/layout/remote_view_relative_layout.xml
new file mode 100644
index 000000000000..713a4c89ea4d
--- /dev/null
+++ b/core/tests/coretests/res/layout/remote_view_relative_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml b/core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml
new file mode 100644
index 000000000000..74c939b7eaa3
--- /dev/null
+++ b/core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/themed_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:theme="@style/RelativeLayoutAlignTop25Alpha"/>
diff --git a/core/tests/coretests/res/layout/remote_views_light_background_text.xml b/core/tests/coretests/res/layout/remote_views_light_background_text.xml
new file mode 100644
index 000000000000..f300f0991a97
--- /dev/null
+++ b/core/tests/coretests/res/layout/remote_views_light_background_text.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/light_background_text"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/core/tests/coretests/res/layout/remote_views_list.xml b/core/tests/coretests/res/layout/remote_views_list.xml
new file mode 100644
index 000000000000..ca43bc8986e7
--- /dev/null
+++ b/core/tests/coretests/res/layout/remote_views_list.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml
index 352b4dceb3cc..32eebb35e0c3 100644
--- a/core/tests/coretests/res/values/styles.xml
+++ b/core/tests/coretests/res/values/styles.xml
@@ -34,6 +34,16 @@
<style name="LayoutInDisplayCutoutModeAlways">
<item name="android:windowLayoutInDisplayCutoutMode">always</item>
</style>
+ <style name="RelativeLayoutAlignBottom50Alpha">
+ <item name="android:layout_alignParentTop">false</item>
+ <item name="android:layout_alignParentBottom">true</item>
+ <item name="android:alpha">0.5</item>
+ </style>
+ <style name="RelativeLayoutAlignTop25Alpha">
+ <item name="android:layout_alignParentTop">true</item>
+ <item name="android:layout_alignParentBottom">false</item>
+ <item name="android:alpha">0.25</item>
+ </style>
<style name="WindowBackgroundColorLiteral">
<item name="android:windowBackground">#00FF00</item>
</style>
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 059c764213bc..00b3693c902b 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -16,8 +16,12 @@
package android.widget;
+import static com.android.internal.R.id.pending_intent_tag;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -31,7 +35,10 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Looper;
import android.os.Parcel;
+import android.util.SizeF;
+import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewGroup;
@@ -49,6 +56,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Map;
import java.util.concurrent.CountDownLatch;
/**
@@ -261,6 +269,148 @@ public class RemoteViewsTest {
verifyViewTree(syncView, asyncView, "row1-c1", "row1-c2", "row1-c3", "row2-c1", "row2-c2");
}
+ @Test
+ public void nestedViews_setRemoteAdapter_intent() {
+ Looper.prepare();
+
+ AppWidgetHostView widget = new AppWidgetHostView(mContext);
+ RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews inner1 = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews inner2 = new RemoteViews(mPackage, R.layout.remote_views_list);
+ inner2.setRemoteAdapter(R.id.list, new Intent());
+ inner1.addView(R.id.container, inner2);
+ top.addView(R.id.container, inner1);
+
+ View view = top.apply(mContext, widget);
+ widget.addView(view);
+
+ ListView listView = (ListView) view.findViewById(R.id.list);
+ listView.onRemoteAdapterConnected();
+ assertNotNull(listView.getAdapter());
+
+ top.reapply(mContext, view);
+ listView = (ListView) view.findViewById(R.id.list);
+ assertNotNull(listView.getAdapter());
+ }
+
+ @Test
+ public void nestedViews_setRemoteAdapter_remoteCollectionItems() {
+ AppWidgetHostView widget = new AppWidgetHostView(mContext);
+ RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews inner1 = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews inner2 = new RemoteViews(mPackage, R.layout.remote_views_list);
+ inner2.setRemoteAdapter(
+ R.id.list,
+ new RemoteViews.RemoteCollectionItems.Builder()
+ .addItem(0, new RemoteViews(mPackage, R.layout.remote_view_host))
+ .build());
+ inner1.addView(R.id.container, inner2);
+ top.addView(R.id.container, inner1);
+
+ View view = top.apply(mContext, widget);
+ widget.addView(view);
+
+ ListView listView = (ListView) view.findViewById(R.id.list);
+ assertNotNull(listView.getAdapter());
+
+ top.reapply(mContext, view);
+ listView = (ListView) view.findViewById(R.id.list);
+ assertNotNull(listView.getAdapter());
+ }
+
+ @Test
+ public void nestedViews_collectionChildFlag() throws Exception {
+ RemoteViews nested = new RemoteViews(mPackage, R.layout.remote_views_text);
+ nested.setOnClickPendingIntent(
+ R.id.text,
+ PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE)
+ );
+
+ RemoteViews listItem = new RemoteViews(mPackage, R.layout.remote_view_host);
+ listItem.addView(R.id.container, nested);
+ listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
+
+ View view = listItem.apply(mContext, mContainer);
+ TextView text = (TextView) view.findViewById(R.id.text);
+ assertNull(text.getTag(pending_intent_tag));
+ }
+
+ @Test
+ public void landscapePortraitViews_collectionChildFlag() throws Exception {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setOnClickPendingIntent(
+ R.id.text,
+ PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE)
+ );
+
+ RemoteViews listItem = new RemoteViews(inner, inner);
+ listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
+
+ View view = listItem.apply(mContext, mContainer);
+ TextView text = (TextView) view.findViewById(R.id.text);
+ assertNull(text.getTag(pending_intent_tag));
+ }
+
+ @Test
+ public void sizedViews_collectionChildFlag() throws Exception {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setOnClickPendingIntent(
+ R.id.text,
+ PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE)
+ );
+
+ RemoteViews listItem = new RemoteViews(
+ Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner));
+ listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
+
+ View view = listItem.apply(mContext, mContainer);
+ TextView text = (TextView) view.findViewById(R.id.text);
+ assertNull(text.getTag(pending_intent_tag));
+ }
+
+ @Test
+ public void nestedViews_lightBackgroundLayoutFlag() {
+ RemoteViews nested = new RemoteViews(mPackage, R.layout.remote_views_text);
+ nested.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(mPackage, R.layout.remote_view_host);
+ parent.addView(R.id.container, nested);
+ parent.setLightBackgroundLayoutId(R.layout.remote_view_host);
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = parent.apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
+
+ @Test
+ public void landscapePortraitViews_lightBackgroundLayoutFlag() {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(inner, inner);
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = parent.apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
+ @Test
+ public void sizedViews_lightBackgroundLayoutFlag() {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(
+ Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner));
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = parent.apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
private RemoteViews createViewChained(int depth, String... texts) {
RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host);
@@ -483,6 +633,47 @@ public class RemoteViewsTest {
index, inflated.getTag(com.android.internal.R.id.notification_action_index_tag));
}
+ @Test
+ public void nestedViews_themesPropagateCorrectly() {
+ Context themedContext =
+ new ContextThemeWrapper(mContext, R.style.RelativeLayoutAlignBottom50Alpha);
+ RelativeLayout rootParent = new RelativeLayout(themedContext);
+
+ RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_relative_layout);
+ RemoteViews inner1 =
+ new RemoteViews(mPackage, R.layout.remote_view_relative_layout_with_theme);
+ RemoteViews inner2 =
+ new RemoteViews(mPackage, R.layout.remote_view_relative_layout);
+
+ inner1.addView(R.id.themed_layout, inner2);
+ top.addView(R.id.container, inner1);
+
+ RelativeLayout root = (RelativeLayout) top.apply(themedContext, rootParent);
+ assertEquals(0.5, root.getAlpha(), 0.);
+ RelativeLayout.LayoutParams rootParams =
+ (RelativeLayout.LayoutParams) root.getLayoutParams();
+ assertEquals(RelativeLayout.TRUE,
+ rootParams.getRule(RelativeLayout.ALIGN_PARENT_BOTTOM));
+
+ // The theme is set on inner1View and its descendants. However, inner1View does
+ // not get its layout params from its theme (though its descendants do), but other
+ // attributes such as alpha are set.
+ RelativeLayout inner1View = (RelativeLayout) root.getChildAt(0);
+ assertEquals(R.id.themed_layout, inner1View.getId());
+ assertEquals(0.25, inner1View.getAlpha(), 0.);
+ RelativeLayout.LayoutParams inner1Params =
+ (RelativeLayout.LayoutParams) inner1View.getLayoutParams();
+ assertEquals(RelativeLayout.TRUE,
+ inner1Params.getRule(RelativeLayout.ALIGN_PARENT_BOTTOM));
+
+ RelativeLayout inner2View = (RelativeLayout) inner1View.getChildAt(0);
+ assertEquals(0.25, inner2View.getAlpha(), 0.);
+ RelativeLayout.LayoutParams inner2Params =
+ (RelativeLayout.LayoutParams) inner2View.getLayoutParams();
+ assertEquals(RelativeLayout.TRUE,
+ inner2Params.getRule(RelativeLayout.ALIGN_PARENT_TOP));
+ }
+
private class WidgetContainer extends AppWidgetHostView {
int[] mSharedViewIds;
String[] mSharedViewNames;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index b655369d7e60..f5cbffb64bb5 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -17,6 +17,7 @@
package com.android.internal.os;
import static android.os.BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
+import static android.os.BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR;
import static android.os.BatteryStats.STATS_SINCE_CHARGED;
import static android.os.BatteryStats.WAKE_TYPE_PARTIAL;
@@ -30,6 +31,12 @@ import android.os.BatteryStats.Uid.Sensor;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.telephony.Annotation;
+import android.telephony.CellSignalStrength;
+import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.view.Display;
@@ -1165,6 +1172,185 @@ public class BatteryStatsNoteTest extends TestCase {
"D", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
}
+ @SmallTest
+ public void testGetPerStateActiveRadioDurationMs() {
+ final MockClock clock = new MockClock(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock);
+ final int ratCount = BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT;
+ final int frequencyCount = ServiceState.FREQUENCY_RANGE_MMWAVE + 1;
+ final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels();
+
+ final long[][][] expectedDurationsMs = new long[ratCount][frequencyCount][txLevelCount];
+ for (int rat = 0; rat < ratCount; rat++) {
+ for (int freq = 0; freq < frequencyCount; freq++) {
+ for (int txLvl = 0; txLvl < txLevelCount; txLvl++) {
+ expectedDurationsMs[rat][freq][txLvl] = 0;
+ }
+ }
+ }
+
+ class ModemAndBatteryState {
+ public long currentTimeMs = 100;
+ public boolean onBattery = false;
+ public boolean modemActive = false;
+ @Annotation.NetworkType
+ public int currentNetworkDataType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ @BatteryStats.RadioAccessTechnology
+ public int currentRat = BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+ @ServiceState.FrequencyRange
+ public int currentFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+ public SparseIntArray currentSignalStrengths = new SparseIntArray();
+
+ void setOnBattery(boolean onBattery) {
+ this.onBattery = onBattery;
+ bi.updateTimeBasesLocked(onBattery, Display.STATE_OFF, currentTimeMs * 1000,
+ currentTimeMs * 1000);
+ }
+
+ void setModemActive(boolean active) {
+ modemActive = active;
+ final int state = active ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+ : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ bi.noteMobileRadioPowerStateLocked(state, currentTimeMs * 1000_000L, UID);
+ }
+
+ void setRatType(@Annotation.NetworkType int dataType,
+ @BatteryStats.RadioAccessTechnology int rat) {
+ currentNetworkDataType = dataType;
+ currentRat = rat;
+ bi.notePhoneDataConnectionStateLocked(dataType, true, ServiceState.STATE_IN_SERVICE,
+ currentFrequencyRange);
+ }
+
+ void setFrequencyRange(@ServiceState.FrequencyRange int frequency) {
+ currentFrequencyRange = frequency;
+ bi.notePhoneDataConnectionStateLocked(currentNetworkDataType, true,
+ ServiceState.STATE_IN_SERVICE, frequency);
+ }
+
+ void setSignalStrength(@BatteryStats.RadioAccessTechnology int rat, int strength) {
+ currentSignalStrengths.put(rat, strength);
+ final int size = currentSignalStrengths.size();
+ final int newestGenSignalStrength = currentSignalStrengths.valueAt(size - 1);
+ bi.notePhoneSignalStrengthLocked(newestGenSignalStrength, currentSignalStrengths);
+ }
+ }
+ final ModemAndBatteryState state = new ModemAndBatteryState();
+
+ IntConsumer incrementTime = inc -> {
+ state.currentTimeMs += inc;
+ clock.realtime = clock.uptime = state.currentTimeMs;
+
+ // If the device is not on battery, no timers should increment.
+ if (!state.onBattery) return;
+ // If the modem is not active, no timers should increment.
+ if (!state.modemActive) return;
+
+ final int currentRat = state.currentRat;
+ final int currentFrequencyRange =
+ currentRat == RADIO_ACCESS_TECHNOLOGY_NR ? state.currentFrequencyRange : 0;
+ int currentSignalStrength = state.currentSignalStrengths.get(currentRat);
+ expectedDurationsMs[currentRat][currentFrequencyRange][currentSignalStrength] += inc;
+ };
+
+ state.setOnBattery(false);
+ state.setModemActive(false);
+ state.setRatType(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER);
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_UNKNOWN);
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER,
+ CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // While not on battery, the timers should not increase.
+ state.setModemActive(true);
+ incrementTime.accept(100);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR);
+ incrementTime.accept(200);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
+ CellSignalStrength.SIGNAL_STRENGTH_GOOD);
+ incrementTime.accept(500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_MMWAVE);
+ incrementTime.accept(300);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setRatType(TelephonyManager.NETWORK_TYPE_LTE,
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE);
+ incrementTime.accept(400);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_MODERATE);
+ incrementTime.accept(500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // When set on battery, currently active state (RAT:LTE, Signal Strength:Moderate) should
+ // start counting up.
+ state.setOnBattery(true);
+ incrementTime.accept(600);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Changing LTE signal strength should be tracked.
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_POOR);
+ incrementTime.accept(700);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+ incrementTime.accept(800);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_GOOD);
+ incrementTime.accept(900);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_GREAT);
+ incrementTime.accept(1000);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Change in the signal strength of nonactive RAT should not affect anything.
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER,
+ CellSignalStrength.SIGNAL_STRENGTH_POOR);
+ incrementTime.accept(1100);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Changing to OTHER Rat should start tracking the poor signal strength.
+ state.setRatType(TelephonyManager.NETWORK_TYPE_CDMA,
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER);
+ incrementTime.accept(1200);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Noting frequency change should not affect non NR Rat.
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_HIGH);
+ incrementTime.accept(1300);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Now the NR Rat, HIGH frequency range, good signal strength should start counting.
+ state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR);
+ incrementTime.accept(1400);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Noting frequency change should not affect non NR Rat.
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_LOW);
+ incrementTime.accept(1500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Modem no longer active, should not be tracking any more.
+ state.setModemActive(false);
+ incrementTime.accept(1500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ }
+
private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) {
// Note that noteUidProcessStateLocked uses ActivityManager process states.
if (fgOn) {
@@ -1238,4 +1424,30 @@ public class BatteryStatsNoteTest extends TestCase {
bi.getScreenBrightnessTime(bin, currentTimeMs * 1000, STATS_SINCE_CHARGED));
}
}
+
+ private void checkPerStateActiveRadioDurations(long[][][] expectedDurationsMs,
+ BatteryStatsImpl bi, long currentTimeMs) {
+ for (int rat = 0; rat < expectedDurationsMs.length; rat++) {
+ final long[][] expectedRatDurationsMs = expectedDurationsMs[rat];
+ for (int freq = 0; freq < expectedRatDurationsMs.length; freq++) {
+ final long[] expectedFreqDurationsMs = expectedRatDurationsMs[freq];
+ for (int strength = 0; strength < expectedFreqDurationsMs.length; strength++) {
+ final long expectedSignalStrengthDurationMs = expectedFreqDurationsMs[strength];
+ final long actualDurationMs = bi.getActiveRadioDurationMs(rat, freq,
+ strength, currentTimeMs);
+
+ // Build a verbose fail message, just in case.
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Wrong time in state for RAT:");
+ sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
+ sb.append(", frequency:");
+ sb.append(ServiceState.frequencyRangeToString(freq));
+ sb.append(", strength:");
+ sb.append(strength);
+
+ assertEquals(sb.toString(), expectedSignalStrengthDurationMs, actualDurationMs);
+ }
+ }
+ }
+ }
}
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 4ae0fc4ae6ed..5a3a033c1cc8 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -272,13 +272,13 @@
<!-- fallback fonts -->
<family lang="und-Arab" variant="elegant">
- <font weight="400" style="normal" postScriptName="NotoNaskhArabic">
+ <font weight="400" style="normal">
NotoNaskhArabic-Regular.ttf
</font>
<font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
</family>
<family lang="und-Arab" variant="compact">
- <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI">
+ <font weight="400" style="normal">
NotoNaskhArabicUI-Regular.ttf
</font>
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
@@ -329,7 +329,7 @@
<font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf
</font>
<font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif" postScriptName="NotoSerifThai">
+ <font weight="400" style="normal" fallbackFor="serif">
NotoSerifThai-Regular.ttf
</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
@@ -923,16 +923,16 @@
<font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="elegant">
- <font weight="400" style="normal" postScriptName="NotoSansLao">NotoSansLao-Regular.ttf
+ <font weight="400" style="normal">NotoSansLao-Regular.ttf
</font>
<font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif" postScriptName="NotoSerifLao">
+ <font weight="400" style="normal" fallbackFor="serif">
NotoSerifLao-Regular.ttf
</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="compact">
- <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf
+ <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf
</font>
<font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
</family>
@@ -1013,7 +1013,7 @@
</font>
</family>
<family lang="und-Cans">
- <font weight="400" style="normal" postScriptName="NotoSansCanadianAboriginal">
+ <font weight="400" style="normal">
NotoSansCanadianAboriginal-Regular.ttf
</font>
</family>
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 eda09e3ce0b0..5ebdceba135b 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
@@ -150,43 +150,45 @@ public class DragAndDropPolicy {
if (inLandscape) {
final Rect leftHitRegion = new Rect();
- final Rect leftDrawRegion = topOrLeftBounds;
final Rect rightHitRegion = new Rect();
- final Rect rightDrawRegion = bottomOrRightBounds;
// If we have existing split regions use those bounds, otherwise split it 50/50
if (inSplitScreen) {
- // Add the divider bounds to each side since that counts for the hit region.
- leftHitRegion.set(topOrLeftBounds);
- leftHitRegion.right += dividerWidth / 2;
- rightHitRegion.set(bottomOrRightBounds);
- rightHitRegion.left -= dividerWidth / 2;
+ // The bounds of the existing split will have a divider bar, the hit region
+ // should include that space. Find the center of the divider bar:
+ float centerX = topOrLeftBounds.right + (dividerWidth / 2);
+ // Now set the hit regions using that center.
+ leftHitRegion.set(displayRegion);
+ leftHitRegion.right = (int) centerX;
+ rightHitRegion.set(displayRegion);
+ rightHitRegion.left = (int) centerX;
} else {
displayRegion.splitVertically(leftHitRegion, rightHitRegion);
}
- mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion));
- mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion));
+ mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds));
+ mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds));
} else {
final Rect topHitRegion = new Rect();
- final Rect topDrawRegion = topOrLeftBounds;
final Rect bottomHitRegion = new Rect();
- final Rect bottomDrawRegion = bottomOrRightBounds;
// If we have existing split regions use those bounds, otherwise split it 50/50
if (inSplitScreen) {
- // Add the divider bounds to each side since that counts for the hit region.
- topHitRegion.set(topOrLeftBounds);
- topHitRegion.bottom += dividerWidth / 2;
- bottomHitRegion.set(bottomOrRightBounds);
- bottomHitRegion.top -= dividerWidth / 2;
+ // The bounds of the existing split will have a divider bar, the hit region
+ // should include that space. Find the center of the divider bar:
+ float centerX = topOrLeftBounds.bottom + (dividerWidth / 2);
+ // Now set the hit regions using that center.
+ topHitRegion.set(displayRegion);
+ topHitRegion.bottom = (int) centerX;
+ bottomHitRegion.set(displayRegion);
+ bottomHitRegion.top = (int) centerX;
} else {
displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
}
- mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion));
- mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion));
+ mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds));
+ mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds));
}
} else {
// Split-screen not allowed, so only show the fullscreen target
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 fd3be2b11c15..d44db498451e 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
@@ -24,7 +24,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -156,10 +155,6 @@ public class DragLayout extends LinearLayout {
}
}
- public boolean hasDropTarget() {
- return mCurrentTarget != null;
- }
-
public boolean hasDropped() {
return mHasDropped;
}
@@ -271,6 +266,9 @@ public class DragLayout extends LinearLayout {
* Updates the visible drop target as the user drags.
*/
public void update(DragEvent event) {
+ if (mHasDropped) {
+ return;
+ }
// Find containing region, if the same as mCurrentRegion, then skip, otherwise, animate the
// visibility of the current region
DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation(
@@ -286,7 +284,8 @@ public class DragLayout extends LinearLayout {
animateHighlight(target);
} else {
// Switching between targets
- animateHighlight(target);
+ mDropZoneView1.animateSwitch();
+ mDropZoneView2.animateSwitch();
}
mCurrentTarget = target;
}
@@ -323,7 +322,7 @@ public class DragLayout extends LinearLayout {
: DISABLE_NONE);
mDropZoneView1.setShowingMargin(visible);
mDropZoneView2.setShowingMargin(visible);
- ObjectAnimator animator = mDropZoneView1.getAnimator();
+ Animator animator = mDropZoneView1.getAnimator();
if (animCompleteCallback != null) {
if (animator != null) {
animator.addListener(new AnimatorListenerAdapter() {
@@ -343,17 +342,11 @@ public class DragLayout extends LinearLayout {
if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_LEFT
|| target.type == DragAndDropPolicy.Target.TYPE_SPLIT_TOP) {
mDropZoneView1.setShowingHighlight(true);
- mDropZoneView1.setShowingSplash(false);
-
mDropZoneView2.setShowingHighlight(false);
- mDropZoneView2.setShowingSplash(true);
} else if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT
|| target.type == DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM) {
mDropZoneView1.setShowingHighlight(false);
- mDropZoneView1.setShowingSplash(true);
-
mDropZoneView2.setShowingHighlight(true);
- mDropZoneView2.setShowingSplash(false);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
index 2f47af57d496..a3ee8aed204d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.draganddrop;
import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
+import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
@@ -27,7 +28,6 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatProperty;
-import android.util.IntProperty;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -43,8 +43,8 @@ import com.android.wm.shell.R;
*/
public class DropZoneView extends FrameLayout {
- private static final int SPLASHSCREEN_ALPHA_INT = (int) (255 * 0.90f);
- private static final int HIGHLIGHT_ALPHA_INT = 255;
+ private static final float SPLASHSCREEN_ALPHA = 0.90f;
+ private static final float HIGHLIGHT_ALPHA = 1f;
private static final int MARGIN_ANIMATION_ENTER_DURATION = 400;
private static final int MARGIN_ANIMATION_EXIT_DURATION = 250;
@@ -61,54 +61,27 @@ public class DropZoneView extends FrameLayout {
}
};
- private static final IntProperty<ColorDrawable> SPLASHSCREEN_ALPHA =
- new IntProperty<ColorDrawable>("splashscreen") {
- @Override
- public void setValue(ColorDrawable d, int alpha) {
- d.setAlpha(alpha);
- }
-
- @Override
- public Integer get(ColorDrawable d) {
- return d.getAlpha();
- }
- };
-
- private static final IntProperty<ColorDrawable> HIGHLIGHT_ALPHA =
- new IntProperty<ColorDrawable>("highlight") {
- @Override
- public void setValue(ColorDrawable d, int alpha) {
- d.setAlpha(alpha);
- }
-
- @Override
- public Integer get(ColorDrawable d) {
- return d.getAlpha();
- }
- };
-
private final Path mPath = new Path();
private final float[] mContainerMargin = new float[4];
private float mCornerRadius;
private float mBottomInset;
private int mMarginColor; // i.e. color used for negative space like the container insets
- private int mHighlightColor;
private boolean mShowingHighlight;
private boolean mShowingSplash;
private boolean mShowingMargin;
- // TODO: might be more seamless to animate between splash/highlight color instead of 2 separate
- private ObjectAnimator mSplashAnimator;
- private ObjectAnimator mHighlightAnimator;
+ private int mSplashScreenColor;
+ private int mHighlightColor;
+
+ private ObjectAnimator mBackgroundAnimator;
private ObjectAnimator mMarginAnimator;
private float mMarginPercent;
// Renders a highlight or neutral transparent color
- private ColorDrawable mDropZoneDrawable;
+ private ColorDrawable mColorDrawable;
// Renders the translucent splashscreen with the app icon in the middle
private ImageView mSplashScreenView;
- private ColorDrawable mSplashBackgroundDrawable;
// Renders the margin / insets around the dropzone container
private MarginView mMarginView;
@@ -130,19 +103,14 @@ public class DropZoneView extends FrameLayout {
mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
mMarginColor = getResources().getColor(R.color.taskbar_background);
- mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
-
- mDropZoneDrawable = new ColorDrawable();
- mDropZoneDrawable.setColor(mHighlightColor);
- mDropZoneDrawable.setAlpha(0);
- setBackgroundDrawable(mDropZoneDrawable);
+ int c = getResources().getColor(android.R.color.system_accent1_500);
+ mHighlightColor = Color.argb(HIGHLIGHT_ALPHA, Color.red(c), Color.green(c), Color.blue(c));
+ mSplashScreenColor = Color.argb(SPLASHSCREEN_ALPHA, 0, 0, 0);
+ mColorDrawable = new ColorDrawable();
+ setBackgroundDrawable(mColorDrawable);
mSplashScreenView = new ImageView(context);
mSplashScreenView.setScaleType(ImageView.ScaleType.CENTER);
- mSplashBackgroundDrawable = new ColorDrawable();
- mSplashBackgroundDrawable.setColor(Color.WHITE);
- mSplashBackgroundDrawable.setAlpha(SPLASHSCREEN_ALPHA_INT);
- mSplashScreenView.setBackgroundDrawable(mSplashBackgroundDrawable);
addView(mSplashScreenView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mSplashScreenView.setAlpha(0f);
@@ -157,10 +125,6 @@ public class DropZoneView extends FrameLayout {
mMarginColor = getResources().getColor(R.color.taskbar_background);
mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
- final int alpha = mDropZoneDrawable.getAlpha();
- mDropZoneDrawable.setColor(mHighlightColor);
- mDropZoneDrawable.setAlpha(alpha);
-
if (mMarginPercent > 0) {
mMarginView.invalidate();
}
@@ -187,38 +151,39 @@ public class DropZoneView extends FrameLayout {
}
/** Sets the color and icon to use for the splashscreen when shown. */
- public void setAppInfo(int splashScreenColor, Drawable appIcon) {
- mSplashBackgroundDrawable.setColor(splashScreenColor);
+ public void setAppInfo(int color, Drawable appIcon) {
+ Color c = Color.valueOf(color);
+ mSplashScreenColor = Color.argb(SPLASHSCREEN_ALPHA, c.red(), c.green(), c.blue());
mSplashScreenView.setImageDrawable(appIcon);
}
/** @return an active animator for this view if one exists. */
@Nullable
- public ObjectAnimator getAnimator() {
+ public Animator getAnimator() {
if (mMarginAnimator != null && mMarginAnimator.isRunning()) {
return mMarginAnimator;
- } else if (mHighlightAnimator != null && mHighlightAnimator.isRunning()) {
- return mHighlightAnimator;
- } else if (mSplashAnimator != null && mSplashAnimator.isRunning()) {
- return mSplashAnimator;
+ } else if (mBackgroundAnimator != null && mBackgroundAnimator.isRunning()) {
+ return mBackgroundAnimator;
}
return null;
}
- /** Animates the splashscreen to show or hide. */
- public void setShowingSplash(boolean showingSplash) {
- if (mShowingSplash != showingSplash) {
- mShowingSplash = showingSplash;
- animateSplashToState();
- }
+ /** Animates between highlight and splashscreen depending on current state. */
+ public void animateSwitch() {
+ mShowingHighlight = !mShowingHighlight;
+ mShowingSplash = !mShowingHighlight;
+ final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor;
+ animateBackground(mColorDrawable.getColor(), newColor);
+ animateSplashScreenIcon();
}
/** Animates the highlight indicating the zone is hovered on or not. */
public void setShowingHighlight(boolean showingHighlight) {
- if (mShowingHighlight != showingHighlight) {
- mShowingHighlight = showingHighlight;
- animateHighlightToState();
- }
+ mShowingHighlight = showingHighlight;
+ mShowingSplash = !mShowingHighlight;
+ final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor;
+ animateBackground(Color.TRANSPARENT, newColor);
+ animateSplashScreenIcon();
}
/** Animates the margins around the drop zone to show or hide. */
@@ -228,38 +193,29 @@ public class DropZoneView extends FrameLayout {
animateMarginToState();
}
if (!mShowingMargin) {
- setShowingHighlight(false);
- setShowingSplash(false);
+ mShowingHighlight = false;
+ mShowingSplash = false;
+ animateBackground(mColorDrawable.getColor(), Color.TRANSPARENT);
+ animateSplashScreenIcon();
}
}
- private void animateSplashToState() {
- if (mSplashAnimator != null) {
- mSplashAnimator.cancel();
+ private void animateBackground(int startColor, int endColor) {
+ if (mBackgroundAnimator != null) {
+ mBackgroundAnimator.cancel();
}
- mSplashAnimator = ObjectAnimator.ofInt(mSplashBackgroundDrawable,
- SPLASHSCREEN_ALPHA,
- mSplashBackgroundDrawable.getAlpha(),
- mShowingSplash ? SPLASHSCREEN_ALPHA_INT : 0);
- if (!mShowingSplash) {
- mSplashAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ mBackgroundAnimator = ObjectAnimator.ofArgb(mColorDrawable,
+ "color",
+ startColor,
+ endColor);
+ if (!mShowingSplash && !mShowingHighlight) {
+ mBackgroundAnimator.setInterpolator(FAST_OUT_SLOW_IN);
}
- mSplashAnimator.start();
- mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start();
+ mBackgroundAnimator.start();
}
- private void animateHighlightToState() {
- if (mHighlightAnimator != null) {
- mHighlightAnimator.cancel();
- }
- mHighlightAnimator = ObjectAnimator.ofInt(mDropZoneDrawable,
- HIGHLIGHT_ALPHA,
- mDropZoneDrawable.getAlpha(),
- mShowingHighlight ? HIGHLIGHT_ALPHA_INT : 0);
- if (!mShowingHighlight) {
- mHighlightAnimator.setInterpolator(FAST_OUT_SLOW_IN);
- }
- mHighlightAnimator.start();
+ private void animateSplashScreenIcon() {
+ mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start();
}
private void animateMarginToState() {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 8ad8abcff2ed..25ed935b7a76 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,19 +16,18 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
-#include <optional>
-#include <vector>
-
-#include <performance_hint_private.h>
+#include <android/performance_hint.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
-#include "RenderTask.h"
+#include <optional>
+#include <vector>
#include "../FrameInfo.h"
#include "../Rect.h"
#include "../TreeInfo.h"
+#include "RenderTask.h"
namespace android {
namespace uirenderer {
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index aa076e85e30d..fd8a06da5c6d 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -401,10 +401,14 @@ ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uin
ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
const uint8_t *data, size_t numBytes, int64_t timestamp) {
- if (inputPort == nullptr || data == nullptr) {
+ if (inputPort == nullptr || data == nullptr || numBytes < 0 || timestamp < 0) {
return AMEDIA_ERROR_INVALID_PARAMETER;
}
+ if (numBytes == 0) {
+ return 0;
+ }
+
// AMIDI_logBuffer(data, numBytes);
uint8_t writeBuffer[AMIDI_BUFFER_SIZE + AMIDI_PACKET_OVERHEAD];
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 35c794e82695..f9a17746892e 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -315,18 +315,18 @@ LIBANDROID {
AThermal_registerThermalStatusListener; # introduced=30
AThermal_unregisterThermalStatusListener; # introduced=30
AThermal_getThermalHeadroom; # introduced=31
+ APerformanceHint_getManager; # introduced=Tiramisu
+ APerformanceHint_createSession; # introduced=Tiramisu
+ APerformanceHint_getPreferredUpdateRateNanos; # introduced=Tiramisu
+ APerformanceHint_updateTargetWorkDuration; # introduced=Tiramisu
+ APerformanceHint_reportActualWorkDuration; # introduced=Tiramisu
+ APerformanceHint_closeSession; # introduced=Tiramisu
local:
*;
};
LIBANDROID_PLATFORM {
global:
- APerformanceHint_getManager;
- APerformanceHint_createSession;
- APerformanceHint_getPreferredUpdateRateNanos;
- APerformanceHint_updateTargetWorkDuration;
- APerformanceHint_reportActualWorkDuration;
- APerformanceHint_closeSession;
APerformanceHint_setIHintManagerForTesting;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 51a0c99af66e..0c360519ceb2 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -16,17 +16,18 @@
#define LOG_TAG "perf_hint"
-#include <utility>
-#include <vector>
-
#include <android/os/IHintManager.h>
#include <android/os/IHintSession.h>
+#include <android/performance_hint.h>
#include <binder/Binder.h>
#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
#include <performance_hint_private.h>
#include <utils/SystemClock.h>
+#include <utility>
+#include <vector>
+
using namespace android;
using namespace android::os;
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 284e9ee909ee..b17850e5d1e4 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -18,10 +18,12 @@
#include <android/os/IHintManager.h>
#include <android/os/IHintSession.h>
+#include <android/performance_hint.h>
#include <binder/IBinder.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <performance_hint_private.h>
+
#include <memory>
#include <vector>
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 84adef53e488..5ce7e59b38ff 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -41,6 +41,7 @@ import android.net.NetworkStateSnapshot;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
import android.net.netstats.IUsageCallback;
+import android.net.netstats.NetworkStatsDataMigrationUtils;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
import android.os.Build;
@@ -126,17 +127,12 @@ public class NetworkStatsManager {
private final INetworkStatsService mService;
/**
- * Type constants for reading different types of Data Usage.
+ * @deprecated Use {@link NetworkStatsDataMigrationUtils#PREFIX_XT}
+ * instead.
* @hide
*/
- // @SystemApi(client = MODULE_LIBRARIES)
+ @Deprecated
public static final String PREFIX_DEV = "dev";
- /** @hide */
- public static final String PREFIX_XT = "xt";
- /** @hide */
- public static final String PREFIX_UID = "uid";
- /** @hide */
- public static final String PREFIX_UID_TAG = "uid_tag";
/** @hide */
public static final int FLAG_POLL_ON_OPEN = 1 << 0;
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 748b0ae02088..9f3371b724cf 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -20,9 +20,6 @@ import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.app.usage.NetworkStatsManager.PREFIX_DEV;
-import static android.app.usage.NetworkStatsManager.PREFIX_UID;
-import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG;
-import static android.app.usage.NetworkStatsManager.PREFIX_XT;
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_REMOVED;
@@ -50,6 +47,9 @@ import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_TETHERING;
import static android.net.TrafficStats.UNSUPPORTED;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index fcf2282160a7..684f4dee9ac2 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -50,6 +50,7 @@ android_library {
"SettingsLibSettingsTransition",
"SettingsLibActivityEmbedding",
"SettingsLibButtonPreference",
+ "setupdesign",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml
index a3473459948d..13f8a372c9b5 100644
--- a/packages/SettingsLib/AndroidManifest.xml
+++ b/packages/SettingsLib/AndroidManifest.xml
@@ -18,4 +18,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib">
+ <application>
+ <activity
+ android:name="com.android.settingslib.users.AvatarPickerActivity"
+ android:theme="@style/SudThemeGlifV2.DayNight"/>
+ </application>
+
</manifest>
diff --git a/packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml b/packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml
new file mode 100644
index 000000000000..97aec740ea43
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ 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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="oval">
+ <stroke
+ android:width="2dp"
+ android:color="?android:attr/colorPrimary"/>
+ </shape>
+ </item>
+ <item
+ android:left="@dimen/avatar_picker_icon_inset"
+ android:right="@dimen/avatar_picker_icon_inset"
+ android:top="@dimen/avatar_picker_icon_inset"
+ android:bottom="@dimen/avatar_picker_icon_inset"
+ android:drawable="@drawable/ic_avatar_choose_photo"/>
+</layer-list>
diff --git a/packages/SettingsLib/res/drawable/avatar_selector.xml b/packages/SettingsLib/res/drawable/avatar_selector.xml
new file mode 100644
index 000000000000..ccde59763a4a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/avatar_selector.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true">
+ <shape android:shape="oval">
+ <stroke
+ android:color="?android:attr/colorPrimary"
+ android:width="@dimen/avatar_picker_padding"/>
+ </shape>
+ </item>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml b/packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml
new file mode 100644
index 000000000000..7033aaeec911
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ 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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="oval">
+ <stroke
+ android:width="2dp"
+ android:color="?android:attr/colorPrimary"/>
+ </shape>
+ </item>
+ <item
+ android:left="@dimen/avatar_picker_icon_inset"
+ android:right="@dimen/avatar_picker_icon_inset"
+ android:top="@dimen/avatar_picker_icon_inset"
+ android:bottom="@dimen/avatar_picker_icon_inset"
+ android:drawable="@drawable/ic_avatar_take_photo"/>
+</layer-list>
diff --git a/packages/SettingsLib/res/drawable/ic_account_circle_outline.xml b/packages/SettingsLib/res/drawable/ic_account_circle_outline.xml
new file mode 100644
index 000000000000..0cc54b6b4972
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_account_circle_outline.xml
@@ -0,0 +1,25 @@
+<!--
+ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/colorPrimary"
+ android:pathData="M5.85,17.1q1.275,-0.975 2.85,-1.538Q10.275,15 12,15q1.725,0 3.3,0.563 1.575,0.562 2.85,1.537 0.875,-1.025 1.363,-2.325Q20,13.475 20,12q0,-3.325 -2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,1.475 0.487,2.775 0.488,1.3 1.363,2.325zM12,13q-1.475,0 -2.488,-1.012Q8.5,10.975 8.5,9.5t1.012,-2.487Q10.525,6 12,6t2.488,1.013Q15.5,8.024 15.5,9.5t-1.012,2.488Q13.475,13 12,13zM12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,20q1.325,0 2.5,-0.387 1.175,-0.388 2.15,-1.113 -0.975,-0.725 -2.15,-1.113Q13.325,17 12,17t-2.5,0.387q-1.175,0.388 -2.15,1.113 0.975,0.725 2.15,1.113Q10.675,20 12,20zM12,11q0.65,0 1.075,-0.425 0.425,-0.425 0.425,-1.075 0,-0.65 -0.425,-1.075Q12.65,8 12,8q-0.65,0 -1.075,0.425Q10.5,8.85 10.5,9.5q0,0.65 0.425,1.075Q11.35,11 12,11zM12,9.5zM12,18.5z"/>
+</vector>
+
diff --git a/packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml b/packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml
new file mode 100644
index 000000000000..b85fdc2de597
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/colorPrimary"
+ android:pathData="M9,14h10l-3.45,-4.5 -2.3,3 -1.55,-2zM8,18q-0.825,0 -1.412,-0.587Q6,16.825 6,16L6,4q0,-0.825 0.588,-1.413Q7.175,2 8,2h12q0.825,0 1.413,0.587Q22,3.175 22,4v12q0,0.825 -0.587,1.413Q20.825,18 20,18zM8,16h12L20,4L8,4v12zM4,22q-0.825,0 -1.413,-0.587Q2,20.825 2,20L2,6h2v14h14v2zM8,4v12L8,4z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml b/packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml
new file mode 100644
index 000000000000..5c56276ec4f3
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/colorPrimary"
+ android:pathData="M12,17.5q1.875,0 3.188,-1.313Q16.5,14.876 16.5,13q0,-1.875 -1.313,-3.188Q13.876,8.5 12,8.5q-1.875,0 -3.188,1.313Q7.5,11.124 7.5,13q0,1.875 1.313,3.188Q10.124,17.5 12,17.5zM4,21q-0.825,0 -1.413,-0.587Q2,19.825 2,19L2,7q0,-0.825 0.587,-1.412Q3.175,5 4,5h3.15L9,3h6l1.85,2L20,5q0.825,0 1.413,0.588Q22,6.175 22,7v12q0,0.825 -0.587,1.413Q20.825,21 20,21zM20,19L20,7L4,7v12zM4,19L4,7v12z"/>
+</vector>
diff --git a/packages/SettingsLib/res/layout/avatar_item.xml b/packages/SettingsLib/res/layout/avatar_item.xml
new file mode 100644
index 000000000000..c52f6648cb7e
--- /dev/null
+++ b/packages/SettingsLib/res/layout/avatar_item.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/avatar_image"
+ android:layout_height="@dimen/avatar_size_in_picker"
+ android:layout_width="@dimen/avatar_size_in_picker"
+ android:layout_margin="@dimen/avatar_picker_margin"
+ android:layout_gravity="center"
+ android:padding="@dimen/avatar_picker_padding"
+ android:background="@drawable/avatar_selector"/>
diff --git a/packages/SettingsLib/res/layout/avatar_picker.xml b/packages/SettingsLib/res/layout/avatar_picker.xml
new file mode 100644
index 000000000000..2d40bd0de7ea
--- /dev/null
+++ b/packages/SettingsLib/res/layout/avatar_picker.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<com.google.android.setupdesign.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/glif_layout"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:icon="@drawable/ic_account_circle_outline"
+ app:sucUsePartnerResource="true"
+ app:sucHeaderText="@string/avatar_picker_title">
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ style="@style/SudContentFrame">
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/avatar_grid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+</com.google.android.setupdesign.GlifLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w1280dp-land/dimens.xml b/packages/SettingsLib/res/values-w1280dp-land/dimens.xml
new file mode 100644
index 000000000000..4097d0564251
--- /dev/null
+++ b/packages/SettingsLib/res/values-w1280dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">104dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">3dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w1440dp-land/dimens.xml b/packages/SettingsLib/res/values-w1440dp-land/dimens.xml
new file mode 100644
index 000000000000..764870efe222
--- /dev/null
+++ b/packages/SettingsLib/res/values-w1440dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">128dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">4dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w1600dp-land/dimens.xml b/packages/SettingsLib/res/values-w1600dp-land/dimens.xml
new file mode 100644
index 000000000000..872b88a3776d
--- /dev/null
+++ b/packages/SettingsLib/res/values-w1600dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">148dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">6dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w480dp-port/dimens.xml b/packages/SettingsLib/res/values-w480dp-port/dimens.xml
new file mode 100644
index 000000000000..cab78d64ce8d
--- /dev/null
+++ b/packages/SettingsLib/res/values-w480dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">3</integer>
+ <dimen name="avatar_size_in_picker">112dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">3dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/res/values-w600dp-port/dimens.xml b/packages/SettingsLib/res/values-w600dp-port/dimens.xml
new file mode 100644
index 000000000000..4097d0564251
--- /dev/null
+++ b/packages/SettingsLib/res/values-w600dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">104dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">3dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w720dp-port/dimens.xml b/packages/SettingsLib/res/values-w720dp-port/dimens.xml
new file mode 100644
index 000000000000..764870efe222
--- /dev/null
+++ b/packages/SettingsLib/res/values-w720dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">128dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">4dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w840dp-port/dimens.xml b/packages/SettingsLib/res/values-w840dp-port/dimens.xml
new file mode 100644
index 000000000000..872b88a3776d
--- /dev/null
+++ b/packages/SettingsLib/res/values-w840dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">148dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">6dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w960dp-land/dimens.xml b/packages/SettingsLib/res/values-w960dp-land/dimens.xml
new file mode 100644
index 000000000000..8403dba54329
--- /dev/null
+++ b/packages/SettingsLib/res/values-w960dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">96dp</dimen>
+ <dimen name="avatar_picker_padding">6dp</dimen>
+ <dimen name="avatar_picker_margin">2dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 2b5e9cdc017d..93e3deef7aa5 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -647,4 +647,6 @@
<item>disabled</item>
</array>
+ <array name="avatar_images"/>
+
</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 9ee42b648d3d..120df76218b3 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -102,4 +102,11 @@
<dimen name="user_photo_size_in_profile_info_dialog">112dp</dimen>
<dimen name="add_a_photo_icon_size_in_profile_info_dialog">32dp</dimen>
+ <integer name="avatar_picker_columns">3</integer>
+ <dimen name="avatar_size_in_picker">96dp</dimen>
+ <dimen name="avatar_picker_padding">6dp</dimen>
+ <dimen name="avatar_picker_margin">2dp</dimen>
+
+ <dimen name="avatar_picker_icon_inset">25dp</dimen>
+
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 5ada02825b31..45f8f1debe34 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1548,4 +1548,7 @@
<!-- Content description of the no calling for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_no_calling">No calling.</string>
+
+ <!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
+ <string name="avatar_picker_title">Choose a profile picture</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
new file mode 100644
index 000000000000..61b8911acea4
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
@@ -0,0 +1,297 @@
+/*
+ * 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.settingslib.users;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.media.ExifInterface;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.StrictMode;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.util.EventLog;
+import android.util.Log;
+
+import androidx.core.content.FileProvider;
+
+import libcore.io.Streams;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+class AvatarPhotoController {
+ private static final String TAG = "AvatarPhotoController";
+
+ private static final int REQUEST_CODE_CHOOSE_PHOTO = 1001;
+ private static final int REQUEST_CODE_TAKE_PHOTO = 1002;
+ private static final int REQUEST_CODE_CROP_PHOTO = 1003;
+ // in rare cases we get a null Cursor when querying for DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI
+ // so we need a default photo size
+ private static final int DEFAULT_PHOTO_SIZE = 500;
+
+ private static final String IMAGES_DIR = "multi_user";
+ private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
+ private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
+
+ private final int mPhotoSize;
+
+ private final AvatarPickerActivity mActivity;
+ private final String mFileAuthority;
+
+ private final File mImagesDir;
+ private final Uri mCropPictureUri;
+ private final Uri mTakePictureUri;
+
+ AvatarPhotoController(AvatarPickerActivity activity, boolean waiting, String fileAuthority) {
+ mActivity = activity;
+ mFileAuthority = fileAuthority;
+
+ mImagesDir = new File(activity.getCacheDir(), IMAGES_DIR);
+ mImagesDir.mkdir();
+ mCropPictureUri = createTempImageUri(activity, CROP_PICTURE_FILE_NAME, !waiting);
+ mTakePictureUri = createTempImageUri(activity, TAKE_PICTURE_FILE_NAME, !waiting);
+ mPhotoSize = getPhotoSize(activity);
+ }
+
+ /**
+ * Handles activity result from containing activity/fragment after a take/choose/crop photo
+ * action result is received.
+ */
+ public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return false;
+ }
+ final Uri pictureUri = data != null && data.getData() != null
+ ? data.getData() : mTakePictureUri;
+
+ // Check if the result is a content uri
+ if (!ContentResolver.SCHEME_CONTENT.equals(pictureUri.getScheme())) {
+ Log.e(TAG, "Invalid pictureUri scheme: " + pictureUri.getScheme());
+ EventLog.writeEvent(0x534e4554, "172939189", -1, pictureUri.getPath());
+ return false;
+ }
+
+ switch (requestCode) {
+ case REQUEST_CODE_CROP_PHOTO:
+ mActivity.returnUriResult(pictureUri);
+ return true;
+ case REQUEST_CODE_TAKE_PHOTO:
+ case REQUEST_CODE_CHOOSE_PHOTO:
+ if (mTakePictureUri.equals(pictureUri)) {
+ if (PhotoCapabilityUtils.canCropPhoto(mActivity)) {
+ cropPhoto();
+ } else {
+ onPhotoNotCropped(pictureUri);
+ }
+ } else {
+ copyAndCropPhoto(pictureUri);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void takePhoto() {
+ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE);
+ appendOutputExtra(intent, mTakePictureUri);
+ mActivity.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
+ }
+
+ void choosePhoto() {
+ Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES, null);
+ intent.setType("image/*");
+ mActivity.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO);
+ }
+
+ private void copyAndCropPhoto(final Uri pictureUri) {
+ // TODO: Replace AsyncTask
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ final ContentResolver cr = mActivity.getContentResolver();
+ try (InputStream in = cr.openInputStream(pictureUri);
+ OutputStream out = cr.openOutputStream(mTakePictureUri)) {
+ Streams.copy(in, out);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to copy photo", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (!mActivity.isFinishing() && !mActivity.isDestroyed()) {
+ cropPhoto();
+ }
+ }
+ }.execute();
+ }
+
+ private void cropPhoto() {
+ // TODO: Use a public intent, when there is one.
+ Intent intent = new Intent("com.android.camera.action.CROP");
+ intent.setDataAndType(mTakePictureUri, "image/*");
+ appendOutputExtra(intent, mCropPictureUri);
+ appendCropExtras(intent);
+ if (intent.resolveActivity(mActivity.getPackageManager()) != null) {
+ try {
+ StrictMode.disableDeathOnFileUriExposure();
+ mActivity.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
+ } finally {
+ StrictMode.enableDeathOnFileUriExposure();
+ }
+ } else {
+ onPhotoNotCropped(mTakePictureUri);
+ }
+ }
+
+ private void appendOutputExtra(Intent intent, Uri pictureUri) {
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);
+ intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri));
+ }
+
+ private void appendCropExtras(Intent intent) {
+ intent.putExtra("crop", "true");
+ intent.putExtra("scale", true);
+ intent.putExtra("scaleUpIfNeeded", true);
+ intent.putExtra("aspectX", 1);
+ intent.putExtra("aspectY", 1);
+ intent.putExtra("outputX", mPhotoSize);
+ intent.putExtra("outputY", mPhotoSize);
+ }
+
+ private void onPhotoNotCropped(final Uri data) {
+ // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change
+ new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ // Scale and crop to a square aspect ratio
+ Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize,
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(croppedImage);
+ Bitmap fullImage;
+ try {
+ InputStream imageStream = mActivity.getContentResolver()
+ .openInputStream(data);
+ fullImage = BitmapFactory.decodeStream(imageStream);
+ } catch (FileNotFoundException fe) {
+ return null;
+ }
+ if (fullImage != null) {
+ int rotation = getRotation(mActivity, data);
+ final int squareSize = Math.min(fullImage.getWidth(),
+ fullImage.getHeight());
+ final int left = (fullImage.getWidth() - squareSize) / 2;
+ final int top = (fullImage.getHeight() - squareSize) / 2;
+
+ Matrix matrix = new Matrix();
+ RectF rectSource = new RectF(left, top,
+ left + squareSize, top + squareSize);
+ RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize);
+ matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER);
+ matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f);
+ canvas.drawBitmap(fullImage, matrix, new Paint());
+ return croppedImage;
+ } else {
+ // Bah! Got nothin.
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ saveBitmapToFile(bitmap, new File(mImagesDir, CROP_PICTURE_FILE_NAME));
+ mActivity.returnUriResult(mCropPictureUri);
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ }
+
+ /**
+ * Reads the image's exif data and determines the rotation degree needed to display the image
+ * in portrait mode.
+ */
+ private int getRotation(Context context, Uri selectedImage) {
+ int rotation = -1;
+ try {
+ InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
+ ExifInterface exif = new ExifInterface(imageStream);
+ rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
+ } catch (IOException exception) {
+ Log.e(TAG, "Error while getting rotation", exception);
+ }
+
+ switch (rotation) {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ return 90;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ return 180;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ return 270;
+ default:
+ return 0;
+ }
+ }
+
+ private void saveBitmapToFile(Bitmap bitmap, File file) {
+ try {
+ OutputStream os = new FileOutputStream(file);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
+ os.flush();
+ os.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot create temp file", e);
+ }
+ }
+
+ private static int getPhotoSize(Context context) {
+ try (Cursor cursor = context.getContentResolver().query(
+ ContactsContract.DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
+ new String[]{ContactsContract.DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null)) {
+ if (cursor != null) {
+ cursor.moveToFirst();
+ return cursor.getInt(0);
+ } else {
+ return DEFAULT_PHOTO_SIZE;
+ }
+ }
+ }
+
+ private Uri createTempImageUri(Context context, String fileName, boolean purge) {
+ final File fullPath = new File(mImagesDir, fileName);
+ if (purge) {
+ fullPath.delete();
+ }
+ return FileProvider.getUriForFile(context, mFileAuthority, fullPath);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
new file mode 100644
index 000000000000..50015e653399
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
@@ -0,0 +1,334 @@
+/*
+ * 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.settingslib.users;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.R;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.util.ThemeHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Activity to allow the user to choose a user profile picture.
+ *
+ * <p>Options are provided to take a photo or choose a photo using the photo picker. In addition,
+ * preselected avatar images may be provided in the resource array {@code avatar_images}. If
+ * provided, every element of that array must be a bitmap drawable.
+ *
+ * <p>If preselected images are not provided, the default avatar will be shown instead, in a range
+ * of colors.
+ *
+ * <p>This activity should be started with startActivityForResult. If a photo or a preselected image
+ * is selected, a Uri will be returned in the data field of the result intent. If a colored default
+ * avatar is selected, the chosen color will be returned as {@code EXTRA_DEFAULT_ICON_TINT_COLOR}
+ * and the data field will be empty.
+ */
+public class AvatarPickerActivity extends Activity {
+
+ static final String EXTRA_FILE_AUTHORITY = "file_authority";
+ static final String EXTRA_DEFAULT_ICON_TINT_COLOR = "default_icon_tint_color";
+
+ private static final String KEY_AWAITING_RESULT = "awaiting_result";
+ private static final String KEY_SELECTED_POSITION = "selected_position";
+
+ private boolean mWaitingForActivityResult;
+
+ private FooterButton mDoneButton;
+ private AvatarAdapter mAdapter;
+
+ private AvatarPhotoController mAvatarPhotoController;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ThemeHelper.trySetDynamicColor(this);
+ setContentView(R.layout.avatar_picker);
+ setUpButtons();
+
+ RecyclerView recyclerView = findViewById(R.id.avatar_grid);
+ mAdapter = new AvatarAdapter();
+ recyclerView.setAdapter(mAdapter);
+ recyclerView.setLayoutManager(new GridLayoutManager(this,
+ getResources().getInteger(R.integer.avatar_picker_columns)));
+
+ restoreState(savedInstanceState);
+
+ mAvatarPhotoController = new AvatarPhotoController(
+ this, mWaitingForActivityResult, getFileAuthority());
+ }
+
+ private void setUpButtons() {
+ GlifLayout glifLayout = findViewById(R.id.glif_layout);
+ FooterBarMixin mixin = glifLayout.getMixin(FooterBarMixin.class);
+
+ FooterButton secondaryButton =
+ new FooterButton.Builder(this)
+ .setText("Cancel")
+ .setListener(view -> cancel())
+ .build();
+
+ mDoneButton =
+ new FooterButton.Builder(this)
+ .setText("Done")
+ .setListener(view -> mAdapter.returnSelectionResult())
+ .build();
+ mDoneButton.setEnabled(false);
+
+ mixin.setSecondaryButton(secondaryButton);
+ mixin.setPrimaryButton(mDoneButton);
+ }
+
+ private String getFileAuthority() {
+ String authority = getIntent().getStringExtra(EXTRA_FILE_AUTHORITY);
+ if (authority == null) {
+ throw new IllegalStateException("File authority must be provided");
+ }
+ return authority;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mWaitingForActivityResult = false;
+ mAvatarPhotoController.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult);
+ outState.putInt(KEY_SELECTED_POSITION, mAdapter.mSelectedPosition);
+ super.onSaveInstanceState(outState);
+ }
+
+ private void restoreState(Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false);
+ mAdapter.mSelectedPosition =
+ savedInstanceState.getInt(KEY_SELECTED_POSITION, AvatarAdapter.NONE);
+ }
+ }
+
+ @Override
+ public void startActivityForResult(Intent intent, int requestCode) {
+ mWaitingForActivityResult = true;
+ super.startActivityForResult(intent, requestCode);
+ }
+
+ void returnUriResult(Uri uri) {
+ Intent resultData = new Intent();
+ resultData.setData(uri);
+ setResult(RESULT_OK, resultData);
+ finish();
+ }
+
+ void returnColorResult(int color) {
+ Intent resultData = new Intent();
+ resultData.putExtra(EXTRA_DEFAULT_ICON_TINT_COLOR, color);
+ setResult(RESULT_OK, resultData);
+ finish();
+ }
+
+ private void cancel() {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+
+ private class AvatarAdapter extends RecyclerView.Adapter<AvatarViewHolder> {
+
+ private static final int NONE = -1;
+
+ private final int mTakePhotoPosition;
+ private final int mChoosePhotoPosition;
+ private final int mPreselectedImageStartPosition;
+
+ private final List<Drawable> mImageDrawables;
+ private final TypedArray mPreselectedImages;
+ private final int[] mUserIconColors;
+ private int mSelectedPosition = NONE;
+
+ AvatarAdapter() {
+ final boolean canTakePhoto =
+ PhotoCapabilityUtils.canTakePhoto(AvatarPickerActivity.this);
+ final boolean canChoosePhoto =
+ PhotoCapabilityUtils.canChoosePhoto(AvatarPickerActivity.this);
+ mTakePhotoPosition = (canTakePhoto ? 0 : NONE);
+ mChoosePhotoPosition = (canChoosePhoto ? (canTakePhoto ? 1 : 0) : NONE);
+ mPreselectedImageStartPosition = (canTakePhoto ? 1 : 0) + (canChoosePhoto ? 1 : 0);
+
+ mPreselectedImages = getResources().obtainTypedArray(R.array.avatar_images);
+ mUserIconColors = UserIcons.getUserIconColors(getResources());
+ mImageDrawables = buildDrawableList();
+ }
+
+ @NonNull
+ @Override
+ public AvatarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
+ LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+ View itemView = layoutInflater.inflate(R.layout.avatar_item, parent, false);
+ return new AvatarViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull AvatarViewHolder viewHolder, int position) {
+ if (position == mTakePhotoPosition) {
+ viewHolder.setDrawable(getDrawable(R.drawable.avatar_take_photo_circled));
+ viewHolder.setClickListener(view -> mAvatarPhotoController.takePhoto());
+
+ } else if (position == mChoosePhotoPosition) {
+ viewHolder.setDrawable(getDrawable(R.drawable.avatar_choose_photo_circled));
+ viewHolder.setClickListener(view -> mAvatarPhotoController.choosePhoto());
+
+ } else if (position >= mPreselectedImageStartPosition) {
+ viewHolder.setSelected(position == mSelectedPosition);
+ viewHolder.setDrawable(mImageDrawables.get(indexFromPosition(position)));
+ viewHolder.setClickListener(view -> {
+ if (mSelectedPosition == position) {
+ deselect(position);
+ } else {
+ select(position);
+ }
+ });
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mPreselectedImageStartPosition + mImageDrawables.size();
+ }
+
+ private List<Drawable> buildDrawableList() {
+ List<Drawable> result = new ArrayList<>();
+
+ for (int i = 0; i < mPreselectedImages.length(); i++) {
+ Drawable drawable = mPreselectedImages.getDrawable(i);
+ if (drawable instanceof BitmapDrawable) {
+ result.add(circularDrawableFrom((BitmapDrawable) drawable));
+ } else {
+ throw new IllegalStateException("Avatar drawables must be bitmaps");
+ }
+ }
+ if (!result.isEmpty()) {
+ return result;
+ }
+
+ // No preselected images. Use tinted default icon.
+ for (int i = 0; i < mUserIconColors.length; i++) {
+ result.add(UserIcons.getDefaultUserIconInColor(getResources(), mUserIconColors[i]));
+ }
+ return result;
+ }
+
+ private Drawable circularDrawableFrom(BitmapDrawable drawable) {
+ Bitmap bitmap = drawable.getBitmap();
+
+ RoundedBitmapDrawable roundedBitmapDrawable =
+ RoundedBitmapDrawableFactory.create(getResources(), bitmap);
+ roundedBitmapDrawable.setCircular(true);
+
+ return roundedBitmapDrawable;
+ }
+
+ private int indexFromPosition(int position) {
+ return position - mPreselectedImageStartPosition;
+ }
+
+ private void select(int position) {
+ final int oldSelection = mSelectedPosition;
+ mSelectedPosition = position;
+ notifyItemChanged(position);
+ if (oldSelection != NONE) {
+ notifyItemChanged(oldSelection);
+ } else {
+ mDoneButton.setEnabled(true);
+ }
+ }
+
+ private void deselect(int position) {
+ mSelectedPosition = NONE;
+ notifyItemChanged(position);
+ mDoneButton.setEnabled(false);
+ }
+
+ private void returnSelectionResult() {
+ int index = indexFromPosition(mSelectedPosition);
+ if (mPreselectedImages.length() > 0) {
+ int resourceId = mPreselectedImages.getResourceId(index, -1);
+ if (resourceId == -1) {
+ throw new IllegalStateException("Preselected avatar images must be resources.");
+ }
+ returnUriResult(uriForResourceId(resourceId));
+ } else {
+ returnColorResult(
+ mUserIconColors[index]);
+ }
+ }
+
+ private Uri uriForResourceId(int resourceId) {
+ return new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .authority(getResources().getResourcePackageName(resourceId))
+ .appendPath(getResources().getResourceTypeName(resourceId))
+ .appendPath(getResources().getResourceEntryName(resourceId))
+ .build();
+ }
+ }
+
+ private static class AvatarViewHolder extends RecyclerView.ViewHolder {
+ private final ImageView mImageView;
+
+ AvatarViewHolder(View view) {
+ super(view);
+ mImageView = view.findViewById(R.id.avatar_image);
+ }
+
+ public void setDrawable(Drawable drawable) {
+ mImageView.setImageDrawable(drawable);
+ }
+
+ public void setClickListener(View.OnClickListener listener) {
+ mImageView.setOnClickListener(listener);
+ }
+
+ public void setSelected(boolean selected) {
+ mImageView.setSelected(selected);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
index 62043363d85f..80ee86f5e489 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
@@ -25,6 +25,7 @@ import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.UserManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
@@ -36,6 +37,8 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.util.UserIcons;
import com.android.settingslib.R;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.drawable.CircleFramedDrawable;
import java.io.File;
@@ -139,12 +142,20 @@ public class EditUserInfoController {
Drawable userIcon = getUserIcon(activity, defaultUserIcon);
userPhotoView.setImageDrawable(userIcon);
- if (canChangePhoto(activity)) {
- mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter,
- userPhotoView);
- } else {
- // some users can't change their photos, so we need to remove the suggestive icon
+ if (isChangePhotoRestrictedByBase(activity)) {
+ // some users can't change their photos so we need to remove the suggestive icon
content.findViewById(R.id.add_a_photo_icon).setVisibility(View.GONE);
+ } else {
+ RestrictedLockUtils.EnforcedAdmin adminRestriction =
+ getChangePhotoAdminRestriction(activity);
+ if (adminRestriction != null) {
+ userPhotoView.setOnClickListener(view ->
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
+ activity, adminRestriction));
+ } else {
+ mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter,
+ userPhotoView);
+ }
}
mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon,
@@ -203,16 +214,21 @@ public class EditUserInfoController {
}
@VisibleForTesting
- boolean canChangePhoto(Context context) {
- return (PhotoCapabilityUtils.canCropPhoto(context)
- && PhotoCapabilityUtils.canChoosePhoto(context))
- || PhotoCapabilityUtils.canTakePhoto(context);
+ boolean isChangePhotoRestrictedByBase(Context context) {
+ return RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ context, UserManager.DISALLOW_SET_USER_ICON, UserHandle.myUserId());
+ }
+
+ @VisibleForTesting
+ RestrictedLockUtils.EnforcedAdmin getChangePhotoAdminRestriction(Context context) {
+ return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context, UserManager.DISALLOW_SET_USER_ICON, UserHandle.myUserId());
}
@VisibleForTesting
EditUserPhotoController createEditUserPhotoController(Activity activity,
ActivityStarter activityStarter, ImageView userPhotoView) {
return new EditUserPhotoController(activity, activityStarter, userPhotoView,
- mSavedPhoto, mWaitingForActivityResult, mFileAuthority);
+ mSavedPhoto, mFileAuthority);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index f9584a3e15e9..f8bb38b5978e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -16,46 +16,21 @@
package com.android.settingslib.users;
+import android.annotation.NonNull;
import android.app.Activity;
-import android.content.ClipData;
-import android.content.ContentResolver;
-import android.content.Context;
import android.content.Intent;
-import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.media.ExifInterface;
import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.StrictMode;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.ContactsContract.DisplayPhoto;
-import android.provider.MediaStore;
-import android.util.EventLog;
import android.util.Log;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import android.widget.ImageView;
-import android.widget.ListPopupWindow;
-import android.widget.TextView;
-
-import androidx.core.content.FileProvider;
+import com.android.internal.util.UserIcons;
import com.android.settingslib.R;
-import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.drawable.CircleFramedDrawable;
-
-import libcore.io.Streams;
+import com.android.settingslib.utils.ThreadUtils;
import java.io.File;
import java.io.FileNotFoundException;
@@ -63,8 +38,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.concurrent.ExecutionException;
/**
* This class contains logic for starting activities to take/choose/crop photo, reads and transforms
@@ -75,45 +49,30 @@ public class EditUserPhotoController {
// It seems that this class generates custom request codes and they may
// collide with ours, these values are very unlikely to have a conflict.
- private static final int REQUEST_CODE_CHOOSE_PHOTO = 1001;
- private static final int REQUEST_CODE_TAKE_PHOTO = 1002;
- private static final int REQUEST_CODE_CROP_PHOTO = 1003;
- // in rare cases we get a null Cursor when querying for DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI
- // so we need a default photo size
- private static final int DEFAULT_PHOTO_SIZE = 500;
+ private static final int REQUEST_CODE_PICK_AVATAR = 1004;
private static final String IMAGES_DIR = "multi_user";
- private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
- private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
private static final String NEW_USER_PHOTO_FILE_NAME = "NewUserPhoto.png";
- private final int mPhotoSize;
-
private final Activity mActivity;
private final ActivityStarter mActivityStarter;
private final ImageView mImageView;
private final String mFileAuthority;
private final File mImagesDir;
- private final Uri mCropPictureUri;
- private final Uri mTakePictureUri;
-
private Bitmap mNewUserPhotoBitmap;
private Drawable mNewUserPhotoDrawable;
public EditUserPhotoController(Activity activity, ActivityStarter activityStarter,
- ImageView view, Bitmap bitmap, boolean waiting, String fileAuthority) {
+ ImageView view, Bitmap bitmap, String fileAuthority) {
mActivity = activity;
mActivityStarter = activityStarter;
- mImageView = view;
mFileAuthority = fileAuthority;
mImagesDir = new File(activity.getCacheDir(), IMAGES_DIR);
mImagesDir.mkdir();
- mCropPictureUri = createTempImageUri(activity, CROP_PICTURE_FILE_NAME, !waiting);
- mTakePictureUri = createTempImageUri(activity, TAKE_PICTURE_FILE_NAME, !waiting);
- mPhotoSize = getPhotoSize(activity);
- mImageView.setOnClickListener(v -> showUpdatePhotoPopup());
+ mImageView = view;
+ mImageView.setOnClickListener(v -> showAvatarPicker());
mNewUserPhotoBitmap = bitmap;
}
@@ -125,32 +84,19 @@ public class EditUserPhotoController {
if (resultCode != Activity.RESULT_OK) {
return false;
}
- final Uri pictureUri = data != null && data.getData() != null
- ? data.getData() : mTakePictureUri;
- // Check if the result is a content uri
- if (!ContentResolver.SCHEME_CONTENT.equals(pictureUri.getScheme())) {
- Log.e(TAG, "Invalid pictureUri scheme: " + pictureUri.getScheme());
- EventLog.writeEvent(0x534e4554, "172939189", -1, pictureUri.getPath());
- return false;
- }
-
- switch (requestCode) {
- case REQUEST_CODE_CROP_PHOTO:
- onPhotoCropped(pictureUri);
+ if (requestCode == REQUEST_CODE_PICK_AVATAR) {
+ if (data.hasExtra(AvatarPickerActivity.EXTRA_DEFAULT_ICON_TINT_COLOR)) {
+ int tintColor =
+ data.getIntExtra(AvatarPickerActivity.EXTRA_DEFAULT_ICON_TINT_COLOR, -1);
+ onDefaultIconSelected(tintColor);
return true;
- case REQUEST_CODE_TAKE_PHOTO:
- case REQUEST_CODE_CHOOSE_PHOTO:
- if (mTakePictureUri.equals(pictureUri)) {
- if (PhotoCapabilityUtils.canCropPhoto(mActivity)) {
- cropPhoto();
- } else {
- onPhotoNotCropped(pictureUri);
- }
- } else {
- copyAndCropPhoto(pictureUri);
- }
+ }
+ if (data.getData() != null) {
+ onPhotoCropped(data.getData());
return true;
+ }
+
}
return false;
}
@@ -159,224 +105,60 @@ public class EditUserPhotoController {
return mNewUserPhotoDrawable;
}
- private void showUpdatePhotoPopup() {
- final Context context = mImageView.getContext();
- final boolean canTakePhoto = PhotoCapabilityUtils.canTakePhoto(context);
- final boolean canChoosePhoto = PhotoCapabilityUtils.canChoosePhoto(context);
-
- if (!canTakePhoto && !canChoosePhoto) {
- return;
- }
-
- final List<EditUserPhotoController.RestrictedMenuItem> items = new ArrayList<>();
-
- if (canTakePhoto) {
- final String title = context.getString(R.string.user_image_take_photo);
- items.add(new RestrictedMenuItem(context, title, UserManager.DISALLOW_SET_USER_ICON,
- this::takePhoto));
- }
-
- if (canChoosePhoto) {
- final String title = context.getString(R.string.user_image_choose_photo);
- items.add(new RestrictedMenuItem(context, title, UserManager.DISALLOW_SET_USER_ICON,
- this::choosePhoto));
- }
-
- final ListPopupWindow listPopupWindow = new ListPopupWindow(context);
-
- listPopupWindow.setAnchorView(mImageView);
- listPopupWindow.setModal(true);
- listPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
- listPopupWindow.setAdapter(new RestrictedPopupMenuAdapter(context, items));
-
- final int width = Math.max(mImageView.getWidth(), context.getResources()
- .getDimensionPixelSize(R.dimen.update_user_photo_popup_min_width));
- listPopupWindow.setWidth(width);
- listPopupWindow.setDropDownGravity(Gravity.START);
-
- listPopupWindow.setOnItemClickListener((parent, view, position, id) -> {
- listPopupWindow.dismiss();
- final RestrictedMenuItem item =
- (RestrictedMenuItem) parent.getAdapter().getItem(position);
- item.doAction();
- });
-
- listPopupWindow.show();
- }
-
- private void takePhoto() {
- Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE);
- appendOutputExtra(intent, mTakePictureUri);
- mActivityStarter.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
- }
-
- private void choosePhoto() {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
- intent.setType("image/*");
- appendOutputExtra(intent, mTakePictureUri);
- mActivityStarter.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO);
+ private void showAvatarPicker() {
+ Intent intent = new Intent(mImageView.getContext(), AvatarPickerActivity.class);
+ intent.putExtra(AvatarPickerActivity.EXTRA_FILE_AUTHORITY, mFileAuthority);
+ mActivityStarter.startActivityForResult(intent, REQUEST_CODE_PICK_AVATAR);
}
- private void copyAndCropPhoto(final Uri pictureUri) {
- // TODO: Replace AsyncTask
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- final ContentResolver cr = mActivity.getContentResolver();
- try (InputStream in = cr.openInputStream(pictureUri);
- OutputStream out = cr.openOutputStream(mTakePictureUri)) {
- Streams.copy(in, out);
- } catch (IOException e) {
- Log.w(TAG, "Failed to copy photo", e);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- if (!mActivity.isFinishing() && !mActivity.isDestroyed()) {
- cropPhoto();
- }
- }
- }.execute();
- }
+ private void onDefaultIconSelected(int tintColor) {
+ try {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ Drawable drawable =
+ UserIcons.getDefaultUserIconInColor(mActivity.getResources(), tintColor);
+ Bitmap bitmap = convertToBitmap(drawable,
+ (int) mActivity.getResources().getDimension(R.dimen.circle_avatar_size));
- private void cropPhoto() {
- // TODO: Use a public intent, when there is one.
- Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(mTakePictureUri, "image/*");
- appendOutputExtra(intent, mCropPictureUri);
- appendCropExtras(intent);
- if (intent.resolveActivity(mActivity.getPackageManager()) != null) {
- try {
- StrictMode.disableDeathOnFileUriExposure();
- mActivityStarter.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
- }
- } else {
- onPhotoNotCropped(mTakePictureUri);
+ ThreadUtils.postOnMainThread(() -> onPhotoProcessed(bitmap));
+ }).get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Error processing default icon", e);
}
}
- private void appendOutputExtra(Intent intent, Uri pictureUri) {
- intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri));
- }
-
- private void appendCropExtras(Intent intent) {
- intent.putExtra("crop", "true");
- intent.putExtra("scale", true);
- intent.putExtra("scaleUpIfNeeded", true);
- intent.putExtra("aspectX", 1);
- intent.putExtra("aspectY", 1);
- intent.putExtra("outputX", mPhotoSize);
- intent.putExtra("outputY", mPhotoSize);
+ private static Bitmap convertToBitmap(@NonNull Drawable icon, int size) {
+ Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ icon.setBounds(0, 0, size, size);
+ icon.draw(canvas);
+ return bitmap;
}
private void onPhotoCropped(final Uri data) {
- // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change
- new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void... params) {
- InputStream imageStream = null;
- try {
- imageStream = mActivity.getContentResolver()
- .openInputStream(data);
- return BitmapFactory.decodeStream(imageStream);
- } catch (FileNotFoundException fe) {
- Log.w(TAG, "Cannot find image file", fe);
- return null;
- } finally {
- if (imageStream != null) {
- try {
- imageStream.close();
- } catch (IOException ioe) {
- Log.w(TAG, "Cannot close image stream", ioe);
- }
+ ThreadUtils.postOnBackgroundThread(() -> {
+ InputStream imageStream = null;
+ Bitmap bitmap = null;
+ try {
+ imageStream = mActivity.getContentResolver()
+ .openInputStream(data);
+ bitmap = BitmapFactory.decodeStream(imageStream);
+ } catch (FileNotFoundException fe) {
+ Log.w(TAG, "Cannot find image file", fe);
+ } finally {
+ if (imageStream != null) {
+ try {
+ imageStream.close();
+ } catch (IOException ioe) {
+ Log.w(TAG, "Cannot close image stream", ioe);
}
}
}
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- onPhotoProcessed(bitmap);
-
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
-
- private void onPhotoNotCropped(final Uri data) {
- // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change
- new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void... params) {
- // Scale and crop to a square aspect ratio
- Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize,
- Config.ARGB_8888);
- Canvas canvas = new Canvas(croppedImage);
- Bitmap fullImage;
- try {
- InputStream imageStream = mActivity.getContentResolver()
- .openInputStream(data);
- fullImage = BitmapFactory.decodeStream(imageStream);
- } catch (FileNotFoundException fe) {
- return null;
- }
- if (fullImage != null) {
- int rotation = getRotation(mActivity, data);
- final int squareSize = Math.min(fullImage.getWidth(),
- fullImage.getHeight());
- final int left = (fullImage.getWidth() - squareSize) / 2;
- final int top = (fullImage.getHeight() - squareSize) / 2;
-
- Matrix matrix = new Matrix();
- RectF rectSource = new RectF(left, top,
- left + squareSize, top + squareSize);
- RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize);
- matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER);
- matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f);
- canvas.drawBitmap(fullImage, matrix, new Paint());
- return croppedImage;
- } else {
- // Bah! Got nothin.
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- onPhotoProcessed(bitmap);
+ if (bitmap != null) {
+ Bitmap finalBitmap = bitmap;
+ ThreadUtils.postOnMainThread(() -> onPhotoProcessed(finalBitmap));
}
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
-
- /**
- * Reads the image's exif data and determines the rotation degree needed to display the image
- * in portrait mode.
- */
- private int getRotation(Context context, Uri selectedImage) {
- int rotation = -1;
- try {
- InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
- ExifInterface exif = new ExifInterface(imageStream);
- rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
- } catch (IOException exception) {
- Log.e(TAG, "Error while getting rotation", exception);
- }
-
- switch (rotation) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- return 90;
- case ExifInterface.ORIENTATION_ROTATE_180:
- return 180;
- case ExifInterface.ORIENTATION_ROTATE_270:
- return 270;
- default:
- return 0;
- }
+ });
}
private void onPhotoProcessed(Bitmap bitmap) {
@@ -386,29 +168,6 @@ public class EditUserPhotoController {
.getInstance(mImageView.getContext(), mNewUserPhotoBitmap);
mImageView.setImageDrawable(mNewUserPhotoDrawable);
}
- new File(mImagesDir, TAKE_PICTURE_FILE_NAME).delete();
- new File(mImagesDir, CROP_PICTURE_FILE_NAME).delete();
- }
-
- private static int getPhotoSize(Context context) {
- try (Cursor cursor = context.getContentResolver().query(
- DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
- new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null)) {
- if (cursor != null) {
- cursor.moveToFirst();
- return cursor.getInt(0);
- } else {
- return DEFAULT_PHOTO_SIZE;
- }
- }
- }
-
- private Uri createTempImageUri(Context context, String fileName, boolean purge) {
- final File fullPath = new File(mImagesDir, fileName);
- if (purge) {
- fullPath.delete();
- }
- return FileProvider.getUriForFile(context, mFileAuthority, fullPath);
}
File saveNewUserPhotoBitmap() {
@@ -435,84 +194,4 @@ public class EditUserPhotoController {
void removeNewUserPhotoBitmapFile() {
new File(mImagesDir, NEW_USER_PHOTO_FILE_NAME).delete();
}
-
- private static final class RestrictedMenuItem {
- private final Context mContext;
- private final String mTitle;
- private final Runnable mAction;
- private final RestrictedLockUtils.EnforcedAdmin mAdmin;
- // Restriction may be set by system or something else via UserManager.setUserRestriction().
- private final boolean mIsRestrictedByBase;
-
- /**
- * The menu item, used for popup menu. Any element of such a menu can be disabled by admin.
- *
- * @param context A context.
- * @param title The title of the menu item.
- * @param restriction The restriction, that if is set, blocks the menu item.
- * @param action The action on menu item click.
- */
- RestrictedMenuItem(Context context, String title, String restriction,
- Runnable action) {
- mContext = context;
- mTitle = title;
- mAction = action;
-
- final int myUserId = UserHandle.myUserId();
- mAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context,
- restriction, myUserId);
- mIsRestrictedByBase = RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
- restriction, myUserId);
- }
-
- @Override
- public String toString() {
- return mTitle;
- }
-
- void doAction() {
- if (isRestrictedByBase()) {
- return;
- }
-
- if (isRestrictedByAdmin()) {
- RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mAdmin);
- return;
- }
-
- mAction.run();
- }
-
- boolean isRestrictedByAdmin() {
- return mAdmin != null;
- }
-
- boolean isRestrictedByBase() {
- return mIsRestrictedByBase;
- }
- }
-
- /**
- * Provide this adapter to ListPopupWindow.setAdapter() to have a popup window menu, where
- * any element can be restricted by admin (profile owner or device owner).
- */
- private static final class RestrictedPopupMenuAdapter extends ArrayAdapter<RestrictedMenuItem> {
- RestrictedPopupMenuAdapter(Context context, List<RestrictedMenuItem> items) {
- super(context, R.layout.restricted_popup_menu_item, R.id.text, items);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final View view = super.getView(position, convertView, parent);
- final RestrictedMenuItem item = getItem(position);
- final TextView text = (TextView) view.findViewById(R.id.text);
- final ImageView image = (ImageView) view.findViewById(R.id.restricted_icon);
-
- text.setEnabled(!item.isRestrictedByAdmin() && !item.isRestrictedByBase());
- image.setVisibility(item.isRestrictedByAdmin() && !item.isRestrictedByBase()
- ? ImageView.VISIBLE : ImageView.GONE);
-
- return view;
- }
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java
index 165c2808f16d..b8615a7e5aa9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java
@@ -40,12 +40,12 @@ public class PhotoCapabilityUtils {
/**
* Check if the current user can perform any activity for
- * android.intent.action.GET_CONTENT action for images.
+ * ACTION_PICK_IMAGES action for images.
* Returns false if the device is currently locked and
* requires a PIN, pattern or password to unlock.
*/
public static boolean canChoosePhoto(Context context) {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
intent.setType("image/*");
boolean canPerformActivityForGetImage =
context.getPackageManager().queryIntentActivities(intent, 0).size() > 0;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
index d6c8816ecc58..a5ee4c35f724 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
@@ -62,7 +62,7 @@ public class EditUserInfoControllerTest {
@Mock
private ActivityStarter mActivityStarter;
- private boolean mCanChangePhoto;
+ private boolean mPhotoRestrictedByBase;
private Activity mActivity;
private TestEditUserInfoController mController;
@@ -85,8 +85,8 @@ public class EditUserInfoControllerTest {
}
@Override
- boolean canChangePhoto(Context context) {
- return mCanChangePhoto;
+ boolean isChangePhotoRestrictedByBase(Context context) {
+ return mPhotoRestrictedByBase;
}
}
@@ -96,7 +96,7 @@ public class EditUserInfoControllerTest {
mActivity = spy(ActivityController.of(new FragmentActivity()).get());
mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
mController = new TestEditUserInfoController();
- mCanChangePhoto = true;
+ mPhotoRestrictedByBase = true;
}
@Test
@@ -260,7 +260,7 @@ public class EditUserInfoControllerTest {
@Test
public void createDialog_canNotChangePhoto_nullPhotoController() {
- mCanChangePhoto = false;
+ mPhotoRestrictedByBase = false;
mController.createDialog(mActivity, mActivityStarter, mCurrentIcon,
"test", "title", null, null);
diff --git a/packages/SystemUI/res/layout/controls_fullscreen.xml b/packages/SystemUI/res/layout/controls_fullscreen.xml
index 7fd029cb2c1e..11a566588738 100644
--- a/packages/SystemUI/res/layout/controls_fullscreen.xml
+++ b/packages/SystemUI/res/layout/controls_fullscreen.xml
@@ -35,7 +35,8 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:orientation="vertical"
- android:clipToPadding="false" />
+ android:clipToPadding="false"
+ android:paddingHorizontal="@dimen/controls_padding_horizontal" />
</com.android.systemui.globalactions.MinHeightScrollView>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
index 51359471ff98..aaff3f930d04 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
@@ -20,6 +20,30 @@
android:id="@+id/dream_overlay_complications_layer"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/complication_top_guide"
+ app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_top_percent"
+ android:orientation="horizontal"/>
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/complication_end_guide"
+ app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_end_percent"
+ android:orientation="vertical"/>
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/complication_bottom_guide"
+ app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_bottom_percent"
+ android:orientation="horizontal"/>
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/complication_start_guide"
+ app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_start_percent"
+ android:orientation="vertical"/>
<TextClock
android:id="@+id/time_view"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index ac4dfd212bb8..8c5006de577e 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -31,9 +31,6 @@
<!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
<integer name="navigation_bar_deadzone_orientation">1</integer>
- <!-- Max number of columns for quick controls area -->
- <integer name="controls_max_columns">4</integer>
-
<!-- Max number of columns for power menu -->
<integer name="power_menu_max_columns">4</integer>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index dabc3108458f..fe546f65bb13 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -15,9 +15,6 @@
~ limitations under the License
-->
<resources>
- <!-- Max number of columns for quick controls area -->
- <integer name="controls_max_columns">2</integer>
-
<!-- The maximum number of rows in the QSPanel -->
<integer name="quick_settings_max_rows">3</integer>
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
index 02fd25bd077c..3c6a81e7c617 100644
--- a/packages/SystemUI/res/values-sw600dp-port/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -15,7 +15,6 @@
~ limitations under the License
-->
<resources>
-
<!-- The maximum number of tiles in the QuickQSPanel -->
<integer name="quick_qs_panel_max_tiles">6</integer>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index f5dc7e3e16da..1b8453ae824d 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -29,9 +29,6 @@
<!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
<integer name="navigation_bar_deadzone_orientation">0</integer>
- <!-- Max number of columns for quick controls area -->
- <integer name="controls_max_columns">4</integer>
-
<!-- How many lines to show in the security footer -->
<integer name="qs_security_footer_maxLines">1</integer>
diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml
index ae89ef4ccc86..be34a48d1cd6 100644
--- a/packages/SystemUI/res/values-sw720dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/config.xml
@@ -15,9 +15,6 @@
~ limitations under the License
-->
<resources>
- <!-- Max number of columns for quick controls area -->
- <integer name="controls_max_columns">2</integer>
-
<!-- The maximum number of rows in the QSPanel -->
<integer name="quick_settings_max_rows">3</integer>
diff --git a/packages/SystemUI/res/values-w500dp/config.xml b/packages/SystemUI/res/values-w500dp/config.xml
new file mode 100644
index 000000000000..ef499ff5cdb7
--- /dev/null
+++ b/packages/SystemUI/res/values-w500dp/config.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <!-- Max number of columns for quick controls area -->
+ <integer name="controls_max_columns">3</integer>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-w500dp/dimens.xml b/packages/SystemUI/res/values-w500dp/dimens.xml
new file mode 100644
index 000000000000..5ce5ceee6dc9
--- /dev/null
+++ b/packages/SystemUI/res/values-w500dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <dimen name="controls_padding_horizontal">75dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-w850dp/config.xml b/packages/SystemUI/res/values-w850dp/config.xml
new file mode 100644
index 000000000000..337ebe19c748
--- /dev/null
+++ b/packages/SystemUI/res/values-w850dp/config.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <!-- Max number of columns for quick controls area -->
+ <integer name="controls_max_columns">4</integer>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-w850dp/dimens.xml b/packages/SystemUI/res/values-w850dp/dimens.xml
new file mode 100644
index 000000000000..bb6ba8fb07b6
--- /dev/null
+++ b/packages/SystemUI/res/values-w850dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <dimen name="controls_padding_horizontal">205dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b35571c17db7..800dd0a6d7a3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1035,6 +1035,7 @@
<dimen name="controls_header_bottom_margin">24dp</dimen>
<dimen name="controls_header_app_icon_size">24dp</dimen>
<dimen name="controls_top_margin">48dp</dimen>
+ <dimen name="controls_padding_horizontal">0dp</dimen>
<dimen name="control_header_text_size">20sp</dimen>
<dimen name="control_item_text_size">16sp</dimen>
<dimen name="control_menu_item_text_size">16sp</dimen>
@@ -1343,4 +1344,37 @@
<!-- Height of the area at the top of the dream overlay to allow dragging down the notifications
shade. -->
<dimen name="dream_overlay_notifications_drag_area_height">100dp</dimen>
+
+ <!-- The position of the end guide, which dream overlay complications can align their start with
+ if their end is aligned with the parent end. Represented as the percentage over from the
+ start of the parent container. -->
+ <item name="dream_overlay_complication_guide_end_percent" format="float" type="dimen">
+ 0.75
+ </item>
+
+ <!-- The position of the start guide, which dream overlay complications can align their end to
+ if their start is aligned with the parent start. Represented as the percentage over from
+ the start of the parent container. -->
+ <item name="dream_overlay_complication_guide_start_percent" format="float" type="dimen">
+ 0.25
+ </item>
+
+ <!-- The position of the bottom guide, which dream overlay complications can align their top to
+ if their bottom is aligned with the parent bottom. Represented as the percentage over from
+ the top of the parent container. -->
+ <item name="dream_overlay_complication_guide_bottom_percent" format="float" type="dimen">
+ 0.90
+ </item>
+
+ <!-- The position of the top guide, which dream overlay complications can align their bottom to
+ if their top is aligned with the parent top. Represented as the percentage over from
+ the top of the parent container. -->
+ <item name="dream_overlay_complication_guide_top_percent" format="float" type="dimen">
+ 0.10
+ </item>
+
+ <!-- The percentage of the screen from which a swipe can start to reveal the bouncer. -->
+ <item name="dream_overlay_bouncer_start_region_screen_percentage" format="float" type="dimen">
+ .2
+ </item>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index b0f7e55112af..fe5e36ef23e6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -37,6 +37,7 @@ import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManager;
@@ -64,6 +65,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.Execution;
@@ -96,6 +98,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
private final Handler mHandler;
private final Execution mExecution;
private final CommandQueue mCommandQueue;
+ private final StatusBarStateController mStatusBarStateController;
private final ActivityTaskManager mActivityTaskManager;
@Nullable
private final FingerprintManager mFingerprintManager;
@@ -118,6 +121,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Nullable private UdfpsController mUdfpsController;
@Nullable private IUdfpsHbmListener mUdfpsHbmListener;
@Nullable private SidefpsController mSidefpsController;
+ @Nullable private IBiometricContextListener mBiometricContextListener;
@VisibleForTesting
TaskStackListener mTaskStackListener;
@VisibleForTesting
@@ -130,7 +134,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
@NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
- private SensorPrivacyManager mSensorPrivacyManager;
+ @NonNull private final SensorPrivacyManager mSensorPrivacyManager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private class BiometricTaskStackListener extends TaskStackListener {
@@ -491,6 +495,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
Provider<SidefpsController> sidefpsControllerFactory,
@NonNull DisplayManager displayManager,
WakefulnessLifecycle wakefulnessLifecycle,
+ @NonNull StatusBarStateController statusBarStateController,
@Main Handler handler) {
super(context);
mExecution = execution;
@@ -504,6 +509,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mSidefpsControllerFactory = sidefpsControllerFactory;
mWindowManager = windowManager;
mUdfpsEnrolledForUser = new SparseBooleanArray();
+
mOrientationListener = new BiometricDisplayListener(
context,
displayManager,
@@ -514,6 +520,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
return Unit.INSTANCE;
});
+ mStatusBarStateController = statusBarStateController;
+ mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ notifyDozeChanged(isDozing);
+ }
+ });
+
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
int[] faceAuthLocation = context.getResources().getIntArray(
@@ -564,6 +578,22 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
}
+ @Override
+ public void setBiometicContextListener(IBiometricContextListener listener) {
+ mBiometricContextListener = listener;
+ notifyDozeChanged(mStatusBarStateController.isDozing());
+ }
+
+ private void notifyDozeChanged(boolean isDozing) {
+ if (mBiometricContextListener != null) {
+ try {
+ mBiometricContextListener.onDozeChanged(isDozing);
+ } catch (RemoteException e) {
+ Log.w(TAG, "failed to notify initial doze state");
+ }
+ }
+ }
+
/**
* Stores the listener received from {@link com.android.server.display.DisplayModeDirector}.
*
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 3ee0cad32097..2160744c6803 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -88,16 +88,20 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
}
};
+ private DreamOverlayStateController mStateController;
+
@Inject
public DreamOverlayService(
Context context,
@Main Executor executor,
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
+ DreamOverlayStateController stateController,
KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mExecutor = executor;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
+ mStateController = stateController;
final DreamOverlayComponent component =
dreamOverlayComponentFactory.create(mViewModelStore, mHost);
@@ -118,6 +122,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
setCurrentState(Lifecycle.State.DESTROYED);
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
windowManager.removeView(mWindow.getDecorView());
+ mStateController.setOverlayActive(false);
super.onDestroy();
}
@@ -127,6 +132,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mExecutor.execute(() -> {
addOverlayWindowLocked(layoutParams);
setCurrentState(Lifecycle.State.RESUMED);
+ mStateController.setOverlayActive(true);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index e83884819f70..3e4ae57000f1 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -16,6 +16,8 @@
package com.android.systemui.dreams;
+import android.util.Log;
+
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
@@ -30,6 +32,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import javax.inject.Inject;
@@ -41,6 +44,16 @@ import javax.inject.Inject;
@SysUISingleton
public class DreamOverlayStateController implements
CallbackController<DreamOverlayStateController.Callback> {
+ private static final String TAG = "DreamOverlayStateCtlr";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0;
+
+ private static final int OP_CLEAR_STATE = 1;
+ private static final int OP_SET_STATE = 2;
+
+ private int mState;
+
/**
* Callback for dream overlay events.
*/
@@ -50,6 +63,12 @@ public class DreamOverlayStateController implements
*/
default void onComplicationsChanged() {
}
+
+ /**
+ * Called when the dream overlay state changes.
+ */
+ default void onStateChanged() {
+ }
}
private final Executor mExecutor;
@@ -92,6 +111,14 @@ public class DreamOverlayStateController implements
return Collections.unmodifiableCollection(mComplications);
}
+ private void notifyCallbacks(Consumer<Callback> callbackConsumer) {
+ mExecutor.execute(() -> {
+ for (Callback callback : mCallbacks) {
+ callbackConsumer.accept(callback);
+ }
+ });
+ }
+
@Override
public void addCallback(@NonNull Callback callback) {
mExecutor.execute(() -> {
@@ -117,4 +144,40 @@ public class DreamOverlayStateController implements
mCallbacks.remove(callback);
});
}
+
+ /**
+ * Returns whether the overlay is active.
+ * @return {@code true} if overlay is active, {@code false} otherwise.
+ */
+ public boolean isOverlayActive() {
+ return containsState(STATE_DREAM_OVERLAY_ACTIVE);
+ }
+
+ private boolean containsState(int state) {
+ return (mState & state) != 0;
+ }
+
+ private void modifyState(int op, int state) {
+ final int existingState = mState;
+ switch (op) {
+ case OP_CLEAR_STATE:
+ mState &= ~state;
+ break;
+ case OP_SET_STATE:
+ mState |= state;
+ break;
+ }
+
+ if (existingState != mState) {
+ notifyCallbacks(callback -> callback.onStateChanged());
+ }
+ }
+
+ /**
+ * Sets whether the overlay is active.
+ * @param active {@code true} if overlay is active, {@code false} otherwise.
+ */
+ public void setOverlayActive(boolean active) {
+ modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_ACTIVE);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
index 96cf50d58d10..4332f5880e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
@@ -154,6 +154,27 @@ public interface Complication {
int CATEGORY_SYSTEM = 1 << 1;
/**
+ * The type of dream complications which can be provided by a {@link Complication}.
+ */
+ @IntDef(prefix = {"COMPLICATION_TYPE_"}, flag = true, value = {
+ COMPLICATION_TYPE_NONE,
+ COMPLICATION_TYPE_TIME,
+ COMPLICATION_TYPE_DATE,
+ COMPLICATION_TYPE_WEATHER,
+ COMPLICATION_TYPE_AIR_QUALITY,
+ COMPLICATION_TYPE_CAST_INFO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ComplicationType {}
+
+ int COMPLICATION_TYPE_NONE = 0;
+ int COMPLICATION_TYPE_TIME = 1;
+ int COMPLICATION_TYPE_DATE = 1 << 1;
+ int COMPLICATION_TYPE_WEATHER = 1 << 2;
+ int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
+ int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
+
+ /**
* The {@link Host} interface specifies a way a {@link Complication} to communicate with its
* parent entity for information and actions.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index cb24ae609eeb..5223f379508f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -25,10 +25,13 @@ import android.view.ViewGroup;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Constraints;
+import com.android.systemui.R;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
@@ -102,6 +105,8 @@ public class ComplicationLayoutEngine {
final int direction = getLayoutParams().getDirection();
+ final boolean snapsToGuide = getLayoutParams().snapsToGuide();
+
// If no parent, view is the anchor. In this case, it is given the highest priority for
// alignment. All alignment preferences are done in relation to the parent container.
final boolean isRoot = head == mView;
@@ -125,6 +130,11 @@ public class ComplicationLayoutEngine {
} else {
params.startToEnd = head.getId();
}
+ if (snapsToGuide
+ && (direction == ComplicationLayoutParams.DIRECTION_DOWN
+ || direction == ComplicationLayoutParams.DIRECTION_UP)) {
+ params.endToStart = R.id.complication_start_guide;
+ }
break;
case ComplicationLayoutParams.POSITION_TOP:
if (isRoot || direction != ComplicationLayoutParams.DIRECTION_DOWN) {
@@ -132,6 +142,11 @@ public class ComplicationLayoutEngine {
} else {
params.topToBottom = head.getId();
}
+ if (snapsToGuide
+ && (direction == ComplicationLayoutParams.DIRECTION_END
+ || direction == ComplicationLayoutParams.DIRECTION_START)) {
+ params.endToStart = R.id.complication_top_guide;
+ }
break;
case ComplicationLayoutParams.POSITION_BOTTOM:
if (isRoot || direction != ComplicationLayoutParams.DIRECTION_UP) {
@@ -139,6 +154,11 @@ public class ComplicationLayoutEngine {
} else {
params.bottomToTop = head.getId();
}
+ if (snapsToGuide
+ && (direction == ComplicationLayoutParams.DIRECTION_END
+ || direction == ComplicationLayoutParams.DIRECTION_START)) {
+ params.topToBottom = R.id.complication_bottom_guide;
+ }
break;
case ComplicationLayoutParams.POSITION_END:
if (isRoot || direction != ComplicationLayoutParams.DIRECTION_START) {
@@ -146,6 +166,11 @@ public class ComplicationLayoutEngine {
} else {
params.endToStart = head.getId();
}
+ if (snapsToGuide
+ && (direction == ComplicationLayoutParams.DIRECTION_UP
+ || direction == ComplicationLayoutParams.DIRECTION_DOWN)) {
+ params.startToEnd = R.id.complication_end_guide;
+ }
break;
}
});
@@ -153,6 +178,16 @@ public class ComplicationLayoutEngine {
mView.setLayoutParams(params);
}
+ private void setGuide(ConstraintLayout.LayoutParams lp, int validDirections,
+ Consumer<ConstraintLayout.LayoutParams> consumer) {
+ final ComplicationLayoutParams layoutParams = getLayoutParams();
+ if (!layoutParams.snapsToGuide()) {
+ return;
+ }
+
+ consumer.accept(lp);
+ }
+
/**
* Informs the {@link ViewEntry}'s parent entity to remove the {@link ViewEntry} from
* being shown further.
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
index f9a69fadfedc..8e8cb72d6ad0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
@@ -40,13 +40,13 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
@interface Position {}
/** Align view with the top of parent or bottom of preceding {@link Complication}. */
- static final int POSITION_TOP = 1 << 0;
+ public static final int POSITION_TOP = 1 << 0;
/** Align view with the bottom of parent or top of preceding {@link Complication}. */
- static final int POSITION_BOTTOM = 1 << 1;
+ public static final int POSITION_BOTTOM = 1 << 1;
/** Align view with the start of parent or end of preceding {@link Complication}. */
- static final int POSITION_START = 1 << 2;
+ public static final int POSITION_START = 1 << 2;
/** Align view with the end of parent or start of preceding {@link Complication}. */
- static final int POSITION_END = 1 << 3;
+ public static final int POSITION_END = 1 << 3;
private static final int FIRST_POSITION = POSITION_TOP;
private static final int LAST_POSITION = POSITION_END;
@@ -61,13 +61,13 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
@interface Direction {}
/** Position view upward from position. */
- static final int DIRECTION_UP = 1 << 0;
+ public static final int DIRECTION_UP = 1 << 0;
/** Position view downward from position. */
- static final int DIRECTION_DOWN = 1 << 1;
+ public static final int DIRECTION_DOWN = 1 << 1;
/** Position view towards the start of the parent. */
- static final int DIRECTION_START = 1 << 2;
+ public static final int DIRECTION_START = 1 << 2;
/** Position view towards the end of parent. */
- static final int DIRECTION_END = 1 << 3;
+ public static final int DIRECTION_END = 1 << 3;
@Position
private final int mPosition;
@@ -77,6 +77,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
private final int mWeight;
+ private final boolean mSnapToGuide;
+
// Do not allow specifying opposite positions
private static final int[] INVALID_POSITIONS =
{ POSITION_BOTTOM | POSITION_TOP, POSITION_END | POSITION_START };
@@ -104,6 +106,27 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
@Direction int direction, int weight) {
+ this(width, height, position, direction, weight, false);
+ }
+
+ /**
+ * Constructs a {@link ComplicationLayoutParams}.
+ * @param width The width {@link android.view.View.MeasureSpec} for the view.
+ * @param height The height {@link android.view.View.MeasureSpec} for the view.
+ * @param position The place within the parent container where the view should be positioned.
+ * @param direction The direction the view should be laid out from either the parent container
+ * or preceding view.
+ * @param weight The weight that should be considered for this view when compared to other
+ * views. This has an impact on the placement of the view but not the rendering of
+ * the view.
+ * @param snapToGuide When set to {@code true}, the dimension perpendicular to the direction
+ * will be automatically set to align with a predetermined guide for that
+ * side. For example, if the complication is aligned to the top end and
+ * direction is down, then the width of the complication will be set to span
+ * from the end of the parent to the guide.
+ */
+ public ComplicationLayoutParams(int width, int height, @Position int position,
+ @Direction int direction, int weight, boolean snapToGuide) {
super(width, height);
if (!validatePosition(position)) {
@@ -118,6 +141,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
mDirection = direction;
mWeight = weight;
+
+ mSnapToGuide = snapToGuide;
}
/**
@@ -128,6 +153,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
mPosition = source.mPosition;
mDirection = source.mDirection;
mWeight = source.mWeight;
+ mSnapToGuide = source.mSnapToGuide;
}
private static boolean validateDirection(@Position int position, @Direction int direction) {
@@ -180,7 +206,19 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
return mPosition;
}
+ /**
+ * Returns the set weight for the complication. The weight determines ordering a complication
+ * given the same position/direction.
+ */
public int getWeight() {
return mWeight;
}
+
+ /**
+ * Returns whether the complication's dimension perpendicular to direction should be
+ * automatically set.
+ */
+ public boolean snapsToGuide() {
+ return mSnapToGuide;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
new file mode 100644
index 000000000000..3a2a6ef60f03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
@@ -0,0 +1,53 @@
+/*
+ * 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.dreams.complication;
+
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_AIR_QUALITY;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_NONE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
+
+import com.android.settingslib.dream.DreamBackend;
+
+/**
+ * A collection of utility methods for working with {@link Complication}.
+ */
+public class ComplicationUtils {
+ /**
+ * Converts a {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to
+ * {@link ComplicationType}.
+ */
+ @Complication.ComplicationType
+ public static int convertComplicationType(@DreamBackend.ComplicationType int type) {
+ switch (type) {
+ case DreamBackend.COMPLICATION_TYPE_TIME:
+ return COMPLICATION_TYPE_TIME;
+ case DreamBackend.COMPLICATION_TYPE_DATE:
+ return COMPLICATION_TYPE_DATE;
+ case DreamBackend.COMPLICATION_TYPE_WEATHER:
+ return COMPLICATION_TYPE_WEATHER;
+ case DreamBackend.COMPLICATION_TYPE_AIR_QUALITY:
+ return COMPLICATION_TYPE_AIR_QUALITY;
+ case DreamBackend.COMPLICATION_TYPE_CAST_INFO:
+ return COMPLICATION_TYPE_CAST_INFO;
+ default:
+ return COMPLICATION_TYPE_NONE;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 503817a23f7f..4eb5cb97607a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -34,7 +34,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayContainerView;
import com.android.systemui.dreams.DreamOverlayStatusBarView;
-import com.android.systemui.dreams.touch.DreamTouchHandler;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
@@ -44,7 +43,6 @@ import javax.inject.Named;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.IntoSet;
/** Dagger module for {@link DreamOverlayComponent}. */
@Module
@@ -149,12 +147,4 @@ public abstract class DreamOverlayModule {
static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) {
return lifecycleOwner.getLifecycle();
}
-
- // TODO: This stub should be removed once there is a {@link DreamTouchHandler}
- // implementation present.
- @Provides
- @IntoSet
- static DreamTouchHandler provideDreamTouchHandler() {
- return session -> { };
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
new file mode 100644
index 000000000000..d16c8c8c59d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -0,0 +1,273 @@
+/*
+ * 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.dreams.touch;
+
+import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING;
+import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING;
+import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION;
+
+import android.animation.ValueAnimator;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Monitor for tracking touches on the DreamOverlay to bring up the bouncer.
+ */
+public class BouncerSwipeTouchHandler implements DreamTouchHandler {
+ /**
+ * An interface for creating ValueAnimators.
+ */
+ public interface ValueAnimatorCreator {
+ /**
+ * Creates {@link ValueAnimator}.
+ */
+ ValueAnimator create(float start, float finish);
+ }
+
+ /**
+ * An interface for obtaining VelocityTrackers.
+ */
+ public interface VelocityTrackerFactory {
+ /**
+ * Obtains {@link VelocityTracker}.
+ */
+ VelocityTracker obtain();
+ }
+
+ public static final float FLING_PERCENTAGE_THRESHOLD = 0.5f;
+
+ private static final String TAG = "BouncerSwipeTouchHandler";
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final float mBouncerZoneScreenPercentage;
+
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private float mCurrentExpansion;
+ private final StatusBar mStatusBar;
+
+ private VelocityTracker mVelocityTracker;
+
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final FlingAnimationUtils mFlingAnimationUtilsClosing;
+
+ private Boolean mCapture;
+
+ private TouchSession mTouchSession;
+
+ private ValueAnimatorCreator mValueAnimatorCreator;
+
+ private VelocityTrackerFactory mVelocityTrackerFactory;
+
+ private final GestureDetector.OnGestureListener mOnGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ boolean mTrack;
+ boolean mBouncerPresent;
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // We only consider gestures that originate from the lower portion of the
+ // screen.
+ final float displayHeight = mStatusBar.getDisplayHeight();
+
+ mBouncerPresent = mStatusBar.isBouncerShowing();
+
+ // The target zone is either at the top or bottom of the screen, dependent on
+ // whether the bouncer is present.
+ final float zonePercentage =
+ Math.abs(e.getY() - (mBouncerPresent ? 0 : displayHeight))
+ / displayHeight;
+
+ mTrack = zonePercentage < mBouncerZoneScreenPercentage;
+
+ // Never capture onDown. While this might lead to some false positive touches
+ // being sent to other windows/layers, this is necessary to make sure the
+ // proper touch event sequence is received by others in the event we do not
+ // consume the sequence here.
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ // Do not handle scroll gestures if not tracking touch events.
+ if (!mTrack) {
+ return false;
+ }
+
+ if (mCapture == null) {
+ // If the user scrolling favors a vertical direction, begin capturing
+ // scrolls.
+ mCapture = Math.abs(distanceY) > Math.abs(distanceX);
+
+ if (mCapture) {
+ // Since the user is dragging the bouncer up, set scrimmed to false.
+ mStatusBarKeyguardViewManager.showBouncer(false);
+ }
+ }
+
+ if (!mCapture) {
+ return false;
+ }
+
+ // For consistency, we adopt the expansion definition found in the
+ // PanelViewController. In this case, expansion refers to the view above the
+ // bouncer. As that view's expansion shrinks, the bouncer appears. The bouncer
+ // is fully hidden at full expansion (1) and fully visible when fully collapsed
+ // (0).
+ final float screenTravelPercentage =
+ Math.abs((e1.getY() - e2.getY()) / mStatusBar.getDisplayHeight());
+ setPanelExpansion(
+ mBouncerPresent ? screenTravelPercentage : 1 - screenTravelPercentage);
+
+ return true;
+ }
+ };
+
+ private void setPanelExpansion(float expansion) {
+ mCurrentExpansion = expansion;
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(mCurrentExpansion, false, true);
+ }
+
+ @Inject
+ public BouncerSwipeTouchHandler(
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ StatusBar statusBar,
+ NotificationShadeWindowController notificationShadeWindowController,
+ ValueAnimatorCreator valueAnimatorCreator,
+ VelocityTrackerFactory velocityTrackerFactory,
+ @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
+ FlingAnimationUtils flingAnimationUtils,
+ @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
+ FlingAnimationUtils flingAnimationUtilsClosing,
+ @Named(SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage) {
+ mStatusBar = statusBar;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mNotificationShadeWindowController = notificationShadeWindowController;
+ mBouncerZoneScreenPercentage = swipeRegionPercentage;
+ mFlingAnimationUtils = flingAnimationUtils;
+ mFlingAnimationUtilsClosing = flingAnimationUtilsClosing;
+ mValueAnimatorCreator = valueAnimatorCreator;
+ mVelocityTrackerFactory = velocityTrackerFactory;
+ }
+
+ @Override
+ public void onSessionStart(TouchSession session) {
+ mVelocityTracker = mVelocityTrackerFactory.obtain();
+ mTouchSession = session;
+ mVelocityTracker.clear();
+ mNotificationShadeWindowController.setForcePluginOpen(true, this);
+ session.registerGestureListener(mOnGestureListener);
+ session.registerInputListener(ev -> onMotionEvent(ev));
+
+ }
+
+ @Override
+ public void onSessionEnd(TouchSession session) {
+ mVelocityTracker.recycle();
+ mCapture = null;
+ mNotificationShadeWindowController.setForcePluginOpen(false, this);
+ }
+
+ private void onMotionEvent(InputEvent event) {
+ if (!(event instanceof MotionEvent)) {
+ Log.e(TAG, "non MotionEvent received:" + event);
+ return;
+ }
+
+ final MotionEvent motionEvent = (MotionEvent) event;
+
+ switch(motionEvent.getAction()) {
+ case MotionEvent.ACTION_UP:
+ // If we are not capturing any input, there is no need to consider animating to
+ // finish transition.
+ if (mCapture == null || !mCapture) {
+ break;
+ }
+
+ // We must capture the resulting velocities as resetMonitor() will clear these
+ // values.
+ mVelocityTracker.computeCurrentVelocity(1000);
+ final float verticalVelocity = mVelocityTracker.getYVelocity();
+ final float horizontalVelocity = mVelocityTracker.getXVelocity();
+
+ final float velocityVector =
+ (float) Math.hypot(horizontalVelocity, verticalVelocity);
+
+
+ final float expansion = flingRevealsOverlay(verticalVelocity, velocityVector)
+ ? KeyguardBouncer.EXPANSION_HIDDEN : KeyguardBouncer.EXPANSION_VISIBLE;
+ flingToExpansion(verticalVelocity, expansion);
+
+ if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
+ mStatusBarKeyguardViewManager.reset(false);
+ }
+ mTouchSession.pop();
+ break;
+ default:
+ mVelocityTracker.addMovement(motionEvent);
+ break;
+ }
+ }
+
+ private ValueAnimator createExpansionAnimator(float targetExpansion) {
+ final ValueAnimator animator =
+ mValueAnimatorCreator.create(mCurrentExpansion, targetExpansion);
+ animator.addUpdateListener(
+ animation -> {
+ setPanelExpansion((float) animation.getAnimatedValue());
+ });
+ return animator;
+ }
+
+ protected boolean flingRevealsOverlay(float velocity, float velocityVector) {
+ // Fully expand if the user has expanded the bouncer less than halfway or final velocity was
+ // positive, indicating an downward direction.
+ if (Math.abs(velocityVector) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ return mCurrentExpansion > FLING_PERCENTAGE_THRESHOLD;
+ } else {
+ return velocity > 0;
+ }
+ }
+
+ protected void flingToExpansion(float velocity, float expansion) {
+ final float viewHeight = mStatusBar.getDisplayHeight();
+ final float currentHeight = viewHeight * mCurrentExpansion;
+ final float targetHeight = viewHeight * expansion;
+
+ final ValueAnimator animator = createExpansionAnimator(expansion);
+ if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
+ // The animation utils deal in pixel units, rather than expansion height.
+ mFlingAnimationUtils.apply(animator, currentHeight, targetHeight, velocity, viewHeight);
+ } else {
+ mFlingAnimationUtilsClosing.apply(
+ animator, mCurrentExpansion, currentHeight, targetHeight, viewHeight);
+ }
+
+ animator.start();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
new file mode 100644
index 000000000000..b9436f96c74f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
@@ -0,0 +1,128 @@
+/*
+ * 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.dreams.touch.dagger;
+
+import android.animation.ValueAnimator;
+import android.content.res.Resources;
+import android.util.TypedValue;
+import android.view.VelocityTracker;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.touch.BouncerSwipeTouchHandler;
+import com.android.systemui.dreams.touch.DreamTouchHandler;
+import com.android.systemui.statusbar.phone.PanelViewController;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+
+import javax.inject.Named;
+import javax.inject.Provider;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+/**
+ * This module captures the components associated with {@link BouncerSwipeTouchHandler}.
+ */
+@Module
+public class BouncerSwipeModule {
+ /**
+ * The region, defined as the percentage of the screen, from which a touch gesture to start
+ * swiping up to the bouncer can occur.
+ */
+ public static final String SWIPE_TO_BOUNCER_START_REGION = "swipe_to_bouncer_start_region";
+
+ /**
+ * The {@link android.view.animation.AnimationUtils} for animating the bouncer closing.
+ */
+ public static final String SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING =
+ "swipe_to_bouncer_fling_animation_utils_closing";
+
+ /**
+ * The {@link android.view.animation.AnimationUtils} for animating the bouncer opening.
+ */
+ public static final String SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING =
+ "swipe_to_bouncer_fling_animation_utils_opening";
+
+ /**
+ * Provides {@link BouncerSwipeTouchHandler} for inclusion in touch handling over the dream.
+ */
+ @Provides
+ @IntoSet
+ public static DreamTouchHandler providesBouncerSwipeTouchHandler(
+ BouncerSwipeTouchHandler touchHandler) {
+ return touchHandler;
+ }
+
+ /**
+ * Provides {@link android.view.animation.AnimationUtils} for closing.
+ */
+ @Provides
+ @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
+ public static FlingAnimationUtils providesSwipeToBouncerFlingAnimationUtilsClosing(
+ Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilderProvider) {
+ return flingAnimationUtilsBuilderProvider.get()
+ .reset()
+ .setMaxLengthSeconds(PanelViewController.FLING_CLOSING_MAX_LENGTH_SECONDS)
+ .setSpeedUpFactor(PanelViewController.FLING_SPEED_UP_FACTOR)
+ .build();
+ }
+
+ /**
+ * Provides {@link android.view.animation.AnimationUtils} for opening.
+ */
+ @Provides
+ @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
+ public static FlingAnimationUtils providesSwipeToBouncerFlingAnimationUtilsOpening(
+ Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilderProvider) {
+ return flingAnimationUtilsBuilderProvider.get()
+ .reset()
+ .setMaxLengthSeconds(PanelViewController.FLING_MAX_LENGTH_SECONDS)
+ .setSpeedUpFactor(PanelViewController.FLING_SPEED_UP_FACTOR)
+ .build();
+ }
+
+ /**
+ * Provides the region to start swipe gestures from.
+ */
+ @Provides
+ @Named(SWIPE_TO_BOUNCER_START_REGION)
+ public static float providesSwipeToBouncerStartRegion(@Main Resources resources) {
+ TypedValue typedValue = new TypedValue();
+ resources.getValue(R.dimen.dream_overlay_bouncer_start_region_screen_percentage,
+ typedValue, true);
+ return typedValue.getFloat();
+ }
+
+ /**
+ * Provides the default {@link BouncerSwipeTouchHandler.ValueAnimatorCreator}, which is simply
+ * a wrapper around {@link ValueAnimator}.
+ */
+ @Provides
+ public static BouncerSwipeTouchHandler.ValueAnimatorCreator providesValueAnimatorCreator() {
+ return (start, finish) -> ValueAnimator.ofFloat(start, finish);
+ }
+
+ /**
+ * Provides the default {@link BouncerSwipeTouchHandler.VelocityTrackerFactory}. which is a
+ * passthrough to {@link android.view.VelocityTracker}.
+ */
+ @Provides
+ public static BouncerSwipeTouchHandler.VelocityTrackerFactory providesVelocityTrackerFactory() {
+ return () -> VelocityTracker.obtain();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
index 7b77b593b330..dad0004613f6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
@@ -21,8 +21,10 @@ import dagger.Module;
/**
* {@link DreamTouchModule} encapsulates dream touch-related components.
*/
-@Module(subcomponents = {
- InputSessionComponent.class,
+@Module(includes = {
+ BouncerSwipeModule.class,
+ }, subcomponents = {
+ InputSessionComponent.class,
})
public interface DreamTouchModule {
String INPUT_SESSION_NAME = "INPUT_SESSION_NAME";
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 701d1391d271..fd2c6dd3ca36 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -106,6 +106,7 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -138,7 +139,7 @@ import dagger.Lazy;
* state of the keyguard, power management events that effect whether the keyguard
* should be shown or reset, callbacks to the phone window manager to notify
* it of when the keyguard is showing, and events from the keyguard view itself
- * stating that the keyguard was succesfully unlocked.
+ * stating that the keyguard was successfully unlocked.
*
* Note that the keyguard view is shown when the screen is off (as appropriate)
* so that once the screen comes on, it will be ready immediately.
@@ -152,15 +153,15 @@ import dagger.Lazy;
* - the keyguard is showing
*
* Example external events that translate to keyguard view changes:
- * - screen turned off -> reset the keyguard, and show it so it will be ready
+ * - screen turned off -> reset the keyguard, and show it, so it will be ready
* next time the screen turns on
* - keyboard is slid open -> if the keyguard is not secure, hide it
*
* Events from the keyguard view:
- * - user succesfully unlocked keyguard -> hide keyguard view, and no longer
+ * - user successfully unlocked keyguard -> hide keyguard view, and no longer
* restrict input events.
*
- * Note: in addition to normal power managment events that effect the state of
+ * Note: in addition to normal power management events that effect the state of
* whether the keyguard should be showing, external apps and services may request
* that the keyguard be disabled via {@link #setKeyguardEnabled(boolean)}. When
* false, this will override all other conditions for turning on the keyguard.
@@ -224,7 +225,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
/**
* How long we'll wait for the {@link ViewMediatorCallback#keyguardDoneDrawing()}
* callback before unblocking a call to {@link #setKeyguardEnabled(boolean)}
- * that is reenabling the keyguard.
+ * that is re-enabling the keyguard.
*/
private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000;
@@ -233,6 +234,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
* keyguard to show even if it is disabled for the current user.
*/
public static final String OPTION_FORCE_SHOW = "force_show";
+ private final DreamOverlayStateController mDreamOverlayStateController;
/** The stream type that the lock sounds are tied to. */
private int mUiSoundsStreamType;
@@ -273,14 +275,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// these are protected by synchronized (this)
/**
- * External apps (like the phone app) can tell us to disable the keygaurd.
+ * External apps (like the phone app) can tell us to disable the keyguard.
*/
private boolean mExternallyEnabled = true;
/**
* Remember if an external call to {@link #setKeyguardEnabled} with value
* false caused us to hide the keyguard, so that we need to reshow it once
- * the keygaurd is reenabled with another call with value true.
+ * the keyguard is re-enabled with another call with value true.
*/
private boolean mNeedToReshowWhenReenabled = false;
@@ -291,6 +293,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// AOD is enabled and status bar is in AOD state.
private boolean mAodShowing;
+ // Dream overlay is visible.
+ private boolean mDreamOverlayShowing;
+
/** Cached value of #isInputRestricted */
private boolean mInputRestricted;
@@ -304,7 +309,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private int mDelayedShowingSequence;
/**
- * Simiar to {@link #mDelayedProfileShowingSequence}, but it is for profile case.
+ * Similar to {@link #mDelayedProfileShowingSequence}, but it is for profile case.
*/
private int mDelayedProfileShowingSequence;
@@ -341,7 +346,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
/**
- * Whether a hide is pending an we are just waiting for #startKeyguardExitAnimation to be
+ * Whether a hide is pending and we are just waiting for #startKeyguardExitAnimation to be
* called.
* */
private boolean mHiding;
@@ -355,7 +360,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
/**
- * {@link #setKeyguardEnabled} waits on this condition when it reenables
+ * {@link #setKeyguardEnabled} waits on this condition when it re-enables
* the keyguard.
*/
private boolean mWaitingUntilKeyguardVisible = false;
@@ -470,6 +475,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
};
+ private final DreamOverlayStateController.Callback mDreamOverlayStateCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ mDreamOverlayShowing = mDreamOverlayStateController.isOverlayActive();
+ }
+ };
+
KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
@Override
@@ -494,7 +507,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
synchronized (KeyguardViewMediator.this) {
resetKeyguardDonePendingLocked();
if (mLockPatternUtils.isLockScreenDisabled(userId)) {
- // If we switching to a user that has keyguard disabled, dismiss keyguard.
+ // If we are switching to a user that has keyguard disabled, dismiss keyguard.
dismiss(null /* callback */, null /* message */);
} else {
resetStateLocked();
@@ -508,7 +521,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
if (userId != UserHandle.USER_SYSTEM) {
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
- // Don't try to dismiss if the user has Pin/Patter/Password set
+ // Don't try to dismiss if the user has Pin/Pattern/Password set
if (info == null || mLockPatternUtils.isSecure(userId)) {
return;
} else if (info.isGuest() || info.isDemo()) {
@@ -836,6 +849,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
+ DreamOverlayStateController dreamOverlayStateController,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy) {
super(context);
mFalsingCollector = falsingCollector;
@@ -875,6 +889,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mKeyguardUnlockAnimationControllerLazy = keyguardUnlockAnimationControllerLazy;
mScreenOffAnimationController = screenOffAnimationController;
mInteractionJankMonitor = interactionJankMonitor;
+ mDreamOverlayStateController = dreamOverlayStateController;
}
public void userActivity() {
@@ -980,6 +995,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mSystemReady = true;
doKeyguardLocked(null);
mUpdateMonitor.registerCallback(mUpdateCallback);
+ mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
}
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
@@ -1041,7 +1057,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mUpdateMonitor.dispatchStartedGoingToSleep(offReason);
- // Reset keyguard going away state so we can start listening for fingerprint. We
+ // Reset keyguard going away state, so we can start listening for fingerprint. We
// explicitly DO NOT want to call
// mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false)
// here, since that will mess with the device lock state.
@@ -1121,9 +1137,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
private long getLockTimeout(int userId) {
- // if the screen turned off because of timeout or the user hit the power button
+ // if the screen turned off because of timeout or the user hit the power button,
// and we don't need to lock immediately, set an alarm
- // to enable it a little bit later (i.e, give the user a chance
+ // to enable it a bit later (i.e, give the user a chance
// to turn the screen back on within a certain window without
// having to unlock the screen)
final ContentResolver cr = mContext.getContentResolver();
@@ -1217,7 +1233,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Let's us know when the device is waking up.
+ * It will let us know when the device is waking up.
*/
public void onStartedWakingUp(boolean cameraGestureTriggered) {
Trace.beginSection("KeyguardViewMediator#onStartedWakingUp");
@@ -1299,7 +1315,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (mExitSecureCallback != null) {
if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring");
// we're in the process of handling a request to verify the user
- // can get past the keyguard. ignore extraneous requests to disable / reenable
+ // can get past the keyguard. ignore extraneous requests to disable / re-enable
return;
}
@@ -1310,7 +1326,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
updateInputRestrictedLocked();
hideLocked();
} else if (enabled && mNeedToReshowWhenReenabled) {
- // reenabled after previously hidden, reshow
+ // re-enabled after previously hidden, reshow
if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling "
+ "status bar expansion");
mNeedToReshowWhenReenabled = false;
@@ -1328,8 +1344,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
} else {
showLocked(null);
- // block until we know the keygaurd is done drawing (and post a message
- // to unblock us after a timeout so we don't risk blocking too long
+ // block until we know the keyguard is done drawing (and post a message
+ // to unblock us after a timeout, so we don't risk blocking too long
// and causing an ANR).
mWaitingUntilKeyguardVisible = true;
mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS);
@@ -1917,7 +1933,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mExitSecureCallback = null;
- // after succesfully exiting securely, no need to reshow
+ // after successfully exiting securely, no need to reshow
// the keyguard when they've released the lock
mExternallyEnabled = true;
mNeedToReshowWhenReenabled = false;
@@ -1992,7 +2008,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return;
int id = mLockSounds.play(soundId,
- mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
+ mLockSoundVolume, mLockSoundVolume, 1/*priority*/, 0/*loop*/, 1.0f/*rate*/);
synchronized (this) {
mLockSoundStreamId = id;
}
@@ -2078,7 +2094,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
|| mScreenOnCoordinator.getWakeAndUnlocking()
&& mWallpaperSupportsAmbientMode) {
// When the wallpaper supports ambient mode, the scrim isn't fully opaque during
- // wake and unlock and we should fade in the app on top of the wallpaper
+ // wake and unlock, and we should fade in the app on top of the wallpaper
flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
}
if (mKeyguardViewControllerLazy.get().isUnlockWithWallpaper()) {
@@ -2123,7 +2139,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
Trace.beginSection("KeyguardViewMediator#handleHide");
// It's possible that the device was unlocked in a dream state. It's time to wake up.
- if (mAodShowing) {
+ if (mAodShowing || mDreamOverlayShowing) {
PowerManager pm = mContext.getSystemService(PowerManager.class);
pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:BOUNCER_DOZING");
@@ -2167,15 +2183,15 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
synchronized (KeyguardViewMediator.this) {
// Tell ActivityManager that we canceled the keyguard animation if
- // handleStartKeyguardExitAnimation was called but we're not hiding the keyguard, unless
- // we're animating the surface behind the keyguard and will be hiding the keyguard
- // shortly.
+ // handleStartKeyguardExitAnimation was called, but we're not hiding the keyguard,
+ // unless we're animating the surface behind the keyguard and will be hiding the
+ // keyguard shortly.
if (!mHiding
&& !mSurfaceBehindRemoteAnimationRequested
&& !mKeyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture()) {
if (finishedCallback != null) {
// There will not execute animation, send a finish callback to ensure the remote
- // animation won't hanging there.
+ // animation won't hang there.
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
@@ -2192,7 +2208,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (mScreenOnCoordinator.getWakeAndUnlocking()) {
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
- // the next draw from here so we don't have to wait for window manager to signal
+ // the next draw from here, so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw();
mScreenOnCoordinator.setWakeAndUnlocking(false);
@@ -2344,7 +2360,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Called if the keyguard exit animation has been cancelled and we should dismiss to the
+ * Called if the keyguard exit animation has been cancelled, and we should dismiss to the
* keyguard.
*
* This can happen due to the system cancelling the RemoteAnimation (due to a timeout, a new
@@ -2595,7 +2611,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * Notifies to System UI that the activity behind has now been drawn, and it's safe to remove
* the wallpaper and keyguard flag, and WindowManager has started running keyguard exit
* animation.
*
@@ -2609,7 +2625,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * Notifies to System UI that the activity behind has now been drawn, and it's safe to remove
* the wallpaper and keyguard flag, and System UI should start running keyguard exit animation.
*
* @param apps The list of apps to animate.
@@ -2625,7 +2641,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * Notifies to System UI that the activity behind has now been drawn, and it's safe to remove
* the wallpaper and keyguard flag, and start running keyguard exit animation.
*
* @param startTime the start time of the animation in uptime milliseconds. Deprecated.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index f14d13093620..b49b49cbbb6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -37,6 +37,7 @@ import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -100,6 +101,7 @@ public class KeyguardModule {
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
+ DreamOverlayStateController dreamOverlayStateController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController) {
return new KeyguardViewMediator(
context,
@@ -125,6 +127,7 @@ public class KeyguardModule {
notificationShadeDepthController,
screenOnCoordinator,
interactionJankMonitor,
+ dreamOverlayStateController,
notificationShadeWindowController
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 597e4242af66..9d43d303b834 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -37,6 +37,7 @@ import android.content.Context;
import android.graphics.drawable.Icon;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManager;
@@ -154,6 +155,7 @@ public class CommandQueue extends IStatusBar.Stub implements
private static final int MSG_SET_UDFPS_HBM_LISTENER = 60 << MSG_SHIFT;
private static final int MSG_TILE_SERVICE_REQUEST_ADD = 61 << MSG_SHIFT;
private static final int MSG_TILE_SERVICE_REQUEST_CANCEL = 62 << MSG_SHIFT;
+ private static final int MSG_SET_BIOMETRICS_LISTENER = 63 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -317,6 +319,12 @@ public class CommandQueue extends IStatusBar.Stub implements
}
/**
+ * @see IStatusBar#setBiometicContextListener(IBiometricContextListener)
+ */
+ default void setBiometicContextListener(IBiometricContextListener listener) {
+ }
+
+ /**
* @see IStatusBar#setUdfpsHbmListener(IUdfpsHbmListener)
*/
default void setUdfpsHbmListener(IUdfpsHbmListener listener) {
@@ -958,6 +966,13 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
+ public void setBiometicContextListener(IBiometricContextListener listener) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SET_BIOMETRICS_LISTENER, listener).sendToTarget();
+ }
+ }
+
+ @Override
public void setUdfpsHbmListener(IUdfpsHbmListener listener) {
synchronized (mLock) {
mHandler.obtainMessage(MSG_SET_UDFPS_HBM_LISTENER, listener).sendToTarget();
@@ -1411,6 +1426,12 @@ public class CommandQueue extends IStatusBar.Stub implements
mCallbacks.get(i).hideAuthenticationDialog();
}
break;
+ case MSG_SET_BIOMETRICS_LISTENER:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).setBiometicContextListener(
+ (IBiometricContextListener) msg.obj);
+ }
+ break;
case MSG_SET_UDFPS_HBM_LISTENER:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setUdfpsHbmListener((IUdfpsHbmListener) msg.obj);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index c136d9cc7272..b312ce20b313 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -337,11 +337,11 @@ class LockscreenShadeTransitionController @Inject constructor(
if (field != value || forceApplyAmount) {
field = value
if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
- nsslController.setTransitionToFullShadeAmount(field)
- notificationPanelController.setTransitionToFullShadeAmount(field,
- false /* animate */, 0 /* delay */)
qSDragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
+ nsslController.setTransitionToFullShadeAmount(field, qSDragProgress)
qS.setTransitionToFullShadeAmount(field, qSDragProgress)
+ notificationPanelController.setTransitionToFullShadeAmount(field,
+ false /* animate */, 0 /* delay */)
// TODO: appear media also in split shade
val mediaAmount = if (useSplitShade) 0f else field
mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 51a66aad39fb..3411eab23d63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.statusbar.phone.NotificationIconContainer.MAX_ICONS_ON_LOCKSCREEN;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -32,6 +34,7 @@ import android.view.animation.PathInterpolator;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -45,6 +48,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
+import com.android.systemui.util.Utils;
/**
* A notification shelf view that is placed inside the notification scroller. It manages the
@@ -81,6 +85,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
private int mIndexOfFirstViewInShelf = -1;
private float mCornerAnimationDistance;
private NotificationShelfController mController;
+ private int mActualWidth = -1;
+ private boolean mUseSplitShade;
+
+ /** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
+ private float mFractionToShade;
public NotificationShelf(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -122,13 +131,16 @@ public class NotificationShelf extends ActivatableNotificationView implements
layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
setLayoutParams(layoutParams);
- int padding = res.getDimensionPixelOffset(R.dimen.shelf_icon_container_padding);
+ final int padding = res.getDimensionPixelOffset(R.dimen.shelf_icon_container_padding);
mShelfIcons.setPadding(padding, 0, padding, 0);
mScrollFastThreshold = res.getDimensionPixelOffset(R.dimen.scroll_fast_threshold);
mShowNotificationShelf = res.getBoolean(R.bool.config_showNotificationShelf);
mCornerAnimationDistance = res.getDimensionPixelSize(
R.dimen.notification_corner_animation_distance);
+ // TODO(b/213480466) enable short shelf on split shade
+ mUseSplitShade = Utils.shouldUseSplitNotificationShade(mContext.getResources());
+
mShelfIcons.setInNotificationIconShelf(true);
if (!mShowNotificationShelf) {
setVisibility(GONE);
@@ -203,6 +215,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
viewState.yTranslation = stackEnd - viewState.height;
+
+ final int shortestWidth = mShelfIcons.calculateWidthFor(MAX_ICONS_ON_LOCKSCREEN);
+ final float fraction = Interpolators.STANDARD.getInterpolation(mFractionToShade);
+ updateStateWidth(viewState, fraction, shortestWidth);
} else {
viewState.hidden = true;
viewState.location = ExpandableViewState.LOCATION_GONE;
@@ -211,6 +227,77 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
/**
+ * @param shelfState View state for NotificationShelf
+ * @param fraction Fraction of lockscreen to shade transition
+ * @param shortestWidth Shortest width to use for lockscreen shelf
+ */
+ @VisibleForTesting
+ public void updateStateWidth(ShelfState shelfState, float fraction, int shortestWidth) {
+ shelfState.actualWidth = !mUseSplitShade && mAmbientState.isOnKeyguard()
+ ? (int) MathUtils.lerp(shortestWidth, getWidth(), fraction)
+ : getWidth();
+ }
+
+ /**
+ * @param fractionToShade Fraction of lockscreen to shade transition
+ */
+ public void setFractionToShade(float fractionToShade) {
+ mFractionToShade = fractionToShade;
+ }
+
+ /**
+ * @return Actual width of shelf, accounting for possible ongoing width animation
+ */
+ public int getActualWidth() {
+ return mActualWidth > -1 ? mActualWidth : getWidth();
+ }
+
+ /**
+ * @param localX Click x from left of screen
+ * @param slop Margin of error within which we count x for valid click
+ * @param left Left of shelf, from left of screen
+ * @param right Right of shelf, from left of screen
+ * @return Whether click x was in view
+ */
+ @VisibleForTesting
+ public boolean isXInView(float localX, float slop, float left, float right) {
+ return (left - slop) <= localX && localX < (right + slop);
+ }
+
+ /**
+ * @param localY Click y from top of shelf
+ * @param slop Margin of error within which we count y for valid click
+ * @param top Top of shelf
+ * @param bottom Height of shelf
+ * @return Whether click y was in view
+ */
+ @VisibleForTesting
+ public boolean isYInView(float localY, float slop, float top, float bottom) {
+ return (top - slop) <= localY && localY < (bottom + slop);
+ }
+
+ /**
+ * @param localX Click x
+ * @param localY Click y
+ * @param slop Margin of error for valid click
+ * @return Whether this click was on the visible (non-clipped) part of the shelf
+ */
+ @Override
+ public boolean pointInView(float localX, float localY, float slop) {
+ final float containerWidth = getWidth();
+ final float shelfWidth = getActualWidth();
+
+ final float left = isLayoutRtl() ? containerWidth - shelfWidth : 0;
+ final float right = isLayoutRtl() ? containerWidth : shelfWidth;
+
+ final float top = mClipTopAmount;
+ final float bottom = getActualHeight();
+
+ return isXInView(localX, slop, left, right)
+ && isYInView(localY, slop, top, bottom);
+ }
+
+ /**
* Update the shelf appearance based on the other notifications around it. This transforms
* the icons from the notification area into the shelf.
*/
@@ -732,11 +819,15 @@ public class NotificationShelf extends ActivatableNotificationView implements
// we always want to clip to our sides, such that nothing can draw outside of these bounds
int height = getResources().getDisplayMetrics().heightPixels;
mClipRect.set(0, -height, getWidth(), height);
- mShelfIcons.setClipBounds(mClipRect);
+ if (mShelfIcons != null) {
+ mShelfIcons.setClipBounds(mClipRect);
+ }
}
private void updateRelativeOffset() {
- mCollapsedIcons.getLocationOnScreen(mTmp);
+ if (mCollapsedIcons != null) {
+ mCollapsedIcons.getLocationOnScreen(mTmp);
+ }
getLocationOnScreen(mTmp);
}
@@ -831,9 +922,20 @@ public class NotificationShelf extends ActivatableNotificationView implements
mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf);
}
- private class ShelfState extends ExpandableViewState {
+ public class ShelfState extends ExpandableViewState {
private boolean hasItemsInStableShelf;
private ExpandableView firstViewInShelf;
+ public int actualWidth = -1;
+
+ private void updateShelfWidth(View view) {
+ if (actualWidth < 0) {
+ return;
+ }
+ mActualWidth = actualWidth;
+ ActivatableNotificationView anv = (ActivatableNotificationView) view;
+ anv.getBackgroundNormal().setActualWidth(actualWidth);
+ mShelfIcons.setActualLayoutWidth(actualWidth);
+ }
@Override
public void applyToView(View view) {
@@ -846,19 +948,21 @@ public class NotificationShelf extends ActivatableNotificationView implements
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
+ updateShelfWidth(view);
}
@Override
- public void animateTo(View child, AnimationProperties properties) {
+ public void animateTo(View view, AnimationProperties properties) {
if (!mShowNotificationShelf) {
return;
}
- super.animateTo(child, properties);
+ super.animateTo(view, properties);
setIndexOfFirstViewInShelf(firstViewInShelf);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
+ updateShelfWidth(view);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 5d6d0f701f12..fca2aa167e37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -162,6 +162,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
updateBackgroundTint();
}
+ /**
+ * @return The background of this view.
+ */
+ public NotificationBackgroundView getBackgroundNormal() {
+ return mBackgroundNormal;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 0f615aa9356f..c640ab6c3a90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -29,6 +29,7 @@ import android.view.View;
import com.android.internal.util.ArrayUtils;
import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
/**
@@ -39,15 +40,17 @@ public class NotificationBackgroundView extends View {
private final boolean mDontModifyCorners;
private Drawable mBackground;
private int mClipTopAmount;
- private int mActualHeight;
private int mClipBottomAmount;
private int mTintColor;
private final float[] mCornerRadii = new float[8];
private boolean mBottomIsRounded;
private int mBackgroundTop;
private boolean mBottomAmountClips = true;
+ private int mActualHeight = -1;
+ private int mActualWidth = -1;
private boolean mExpandAnimationRunning;
- private float mActualWidth;
+ private int mExpandAnimationWidth = -1;
+ private int mExpandAnimationHeight = -1;
private int mDrawableAlpha = 255;
private boolean mIsPressedAllowed;
@@ -59,11 +62,12 @@ public class NotificationBackgroundView extends View {
@Override
protected void onDraw(Canvas canvas) {
- if (mClipTopAmount + mClipBottomAmount < mActualHeight - mBackgroundTop
+ if (mClipTopAmount + mClipBottomAmount < getActualHeight() - mBackgroundTop
|| mExpandAnimationRunning) {
canvas.save();
if (!mExpandAnimationRunning) {
- canvas.clipRect(0, mClipTopAmount, getWidth(), mActualHeight - mClipBottomAmount);
+ canvas.clipRect(0, mClipTopAmount, getWidth(),
+ getActualHeight() - mClipBottomAmount);
}
draw(canvas, mBackground);
canvas.restore();
@@ -73,17 +77,23 @@ public class NotificationBackgroundView extends View {
private void draw(Canvas canvas, Drawable drawable) {
if (drawable != null) {
int top = mBackgroundTop;
- int bottom = mActualHeight;
+ int bottom = getActualHeight();
if (mBottomIsRounded
&& mBottomAmountClips
&& !mExpandAnimationRunning) {
bottom -= mClipBottomAmount;
}
- int left = 0;
- int right = getWidth();
+ final boolean isRtl = isLayoutRtl();
+ final int width = getWidth();
+ final int actualWidth = getActualWidth();
+
+ int left = isRtl ? width - actualWidth : 0;
+ int right = isRtl ? width : actualWidth;
+
if (mExpandAnimationRunning) {
- left = (int) ((getWidth() - mActualWidth) / 2.0f);
- right = (int) (left + mActualWidth);
+ // Horizontally center this background view inside of the container
+ left = (int) ((width - actualWidth) / 2.0f);
+ right = (int) (left + actualWidth);
}
drawable.setBounds(left, top, right, bottom);
drawable.draw(canvas);
@@ -152,8 +162,26 @@ public class NotificationBackgroundView extends View {
invalidate();
}
- public int getActualHeight() {
- return mActualHeight;
+ private int getActualHeight() {
+ if (mExpandAnimationRunning && mExpandAnimationHeight > -1) {
+ return mExpandAnimationHeight;
+ } else if (mActualHeight > -1) {
+ return mActualHeight;
+ }
+ return getHeight();
+ }
+
+ public void setActualWidth(int actualWidth) {
+ mActualWidth = actualWidth;
+ }
+
+ private int getActualWidth() {
+ if (mExpandAnimationRunning && mExpandAnimationWidth > -1) {
+ return mExpandAnimationWidth;
+ } else if (mActualWidth > -1) {
+ return mActualWidth;
+ }
+ return getWidth();
}
public void setClipTopAmount(int clipTopAmount) {
@@ -241,9 +269,9 @@ public class NotificationBackgroundView extends View {
}
/** Set the current expand animation size. */
- public void setExpandAnimationSize(int actualWidth, int actualHeight) {
- mActualHeight = actualHeight;
- mActualWidth = actualWidth;
+ public void setExpandAnimationSize(int width, int height) {
+ mExpandAnimationHeight = width;
+ mExpandAnimationWidth = height;
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9655a9ce5e45..9f6a81d3a9ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -5497,6 +5497,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
/**
+ * @param fraction Fraction of the lockscreen to shade transition. 0f for all other states.
+ * Once the lockscreen to shade transition completes and the shade is 100% open
+ * LockscreenShadeTransitionController resets fraction to 0
+ * where it remains until the next lockscreen-to-shade transition.
+ */
+ public void setFractionToShade(float fraction) {
+ mShelf.setFractionToShade(fraction);
+ requestChildrenUpdate();
+ }
+
+ /**
* Set a listener to when scrolling changes.
*/
public void setOnScrollListener(Consumer<Integer> listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 0d0e5e850523..334128a2b4ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1515,10 +1515,18 @@ public class NotificationStackScrollLayoutController {
}
/**
- * Set the amount of pixels we have currently dragged down if we're transitioning to the full
- * shade. 0.0f means we're not transitioning yet.
+ * @param amount The amount of pixels we have currently dragged down
+ * for the lockscreen to shade transition. 0f for all other states.
+ * @param fraction The fraction of lockscreen to shade transition.
+ * 0f for all other states.
+ *
+ * Once the lockscreen to shade transition completes and the shade is 100% open,
+ * LockscreenShadeTransitionController resets amount and fraction to 0, where they remain
+ * until the next lockscreen-to-shade transition.
*/
- public void setTransitionToFullShadeAmount(float amount) {
+ public void setTransitionToFullShadeAmount(float amount, float fraction) {
+ mView.setFractionToShade(fraction);
+
float extraTopInset = 0.0f;
if (mStatusBarStateController.getState() == KEYGUARD) {
float overallProgress = MathUtils.saturate(amount / mView.getHeight());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 0d2bddcc8b77..89b5aef00656 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -44,6 +44,7 @@ public class StackStateAnimator {
public static final int ANIMATION_DURATION_STANDARD = 360;
public static final int ANIMATION_DURATION_CORNER_RADIUS = 200;
public static final int ANIMATION_DURATION_WAKEUP = 500;
+ public static final int ANIMATION_DURATION_WAKEUP_SCRIM = 667;
public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
public static final int ANIMATION_DURATION_SWIPE = 200;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index c09c48540901..febf2b5b5f24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -135,7 +135,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
}.setDuration(CONTENT_FADE_DURATION);
- private static final int MAX_VISIBLE_ICONS_ON_LOCK = 5;
+ private static final int MAX_ICONS_ON_AOD = 5;
+ public static final int MAX_ICONS_ON_LOCKSCREEN = 3;
public static final int MAX_STATIC_ICONS = 4;
private static final int MAX_DOTS = 1;
@@ -386,6 +387,19 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
/**
+ * @return Width of shelf for the given number of icons and overflow dot
+ */
+ public int calculateWidthFor(int numMaxIcons) {
+ if (getChildCount() == 0) {
+ return 0;
+ }
+ return (int) (getActualPaddingStart()
+ + numMaxIcons * mIconSize
+ + mOverflowWidth
+ + getActualPaddingEnd());
+ }
+
+ /**
* Calculate the horizontal translations for each notification based on how much the icons
* are inserted into the notification container.
* If this is not a whole number, the fraction means by how much the icon is appearing.
@@ -394,7 +408,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
float translationX = getActualPaddingStart();
int firstOverflowIndex = -1;
int childCount = getChildCount();
- int maxVisibleIcons = mOnLockScreen ? MAX_VISIBLE_ICONS_ON_LOCK :
+ int maxVisibleIcons = mOnLockScreen ? MAX_ICONS_ON_AOD :
mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
float layoutEnd = getLayoutEnd();
float overflowStart = getMaxOverflowStart();
@@ -414,7 +428,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
&& iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
- boolean noOverflowAfter = i == childCount - 1;
+ boolean isLastChild = i == childCount - 1;
float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
? ((StatusBarIconView) view).getIconScaleIncreased()
: 1f;
@@ -423,10 +437,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
: StatusBarIconView.STATE_ICON;
boolean isOverflowing =
- (translationX > (noOverflowAfter ? layoutEnd - mIconSize
+ (translationX > (isLastChild ? layoutEnd - mIconSize
: overflowStart - mIconSize));
if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
- firstOverflowIndex = noOverflowAfter && !forceOverflow ? i - 1 : i;
+ firstOverflowIndex = isLastChild && !forceOverflow ? i - 1 : i;
mVisualOverflowStart = layoutEnd - mOverflowWidth;
if (forceOverflow || mIsStaticLayout) {
mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 9d3f19ad039a..bb0ed95cc6ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -4188,9 +4188,10 @@ public class NotificationPanelViewController extends PanelViewController
return false;
}
- // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able
- // to pull down QS or expand the shade.
- if (mStatusBar.isBouncerShowingScrimmed()) {
+ // Do not allow panel expansion if bouncer is scrimmed or showing over a dream,
+ // otherwise user would be able to pull down QS or expand the shade.
+ if (mStatusBar.isBouncerShowingScrimmed()
+ || mStatusBar.isBouncerShowingOverDream()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index dc6efba97ff5..c466a8ce6d3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -73,6 +73,10 @@ import java.io.PrintWriter;
public abstract class PanelViewController {
public static final boolean DEBUG = PanelView.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
+ public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
+ public static final float FLING_SPEED_UP_FACTOR = 0.6f;
+ public static final float FLING_CLOSING_MAX_LENGTH_SECONDS = 0.6f;
+ public static final float FLING_CLOSING_SPEED_UP_FACTOR = 0.6f;
private static final int NO_FIXED_DURATION = -1;
private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
private static final long SHADE_OPEN_SPRING_BACK_DURATION = 400L;
@@ -269,13 +273,13 @@ public abstract class PanelViewController {
mNotificationShadeWindowController = notificationShadeWindowController;
mFlingAnimationUtils = flingAnimationUtilsBuilder
.reset()
- .setMaxLengthSeconds(0.6f)
- .setSpeedUpFactor(0.6f)
+ .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS)
+ .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
.build();
mFlingAnimationUtilsClosing = flingAnimationUtilsBuilder
.reset()
- .setMaxLengthSeconds(0.5f)
- .setSpeedUpFactor(0.6f)
+ .setMaxLengthSeconds(FLING_CLOSING_MAX_LENGTH_SECONDS)
+ .setSpeedUpFactor(FLING_CLOSING_SPEED_UP_FACTOR)
.build();
mFlingAnimationUtilsDismissing = flingAnimationUtilsBuilder
.reset()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index d2e1650056ac..ef5f21658d83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -62,7 +62,7 @@ public enum ScrimState {
public void prepare(ScrimState previousState) {
mBlankScreen = false;
if (previousState == ScrimState.AOD) {
- mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+ mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP_SCRIM;
if (mDisplayRequiresBlanking) {
// DisplayPowerManager will blank the screen, we'll just
// set our scrim to black in this frame to avoid flickering and
@@ -70,7 +70,7 @@ public enum ScrimState {
mBlankScreen = true;
}
} else if (previousState == ScrimState.KEYGUARD) {
- mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+ mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP_SCRIM;
} else {
mAnimationDuration = ScrimController.ANIMATION_DURATION;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c09c3ca9dede..a1445337e7d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -150,6 +150,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.emergency.EmergencyGesture;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -338,6 +339,7 @@ public class StatusBar extends CoreStartable implements
}
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final DreamOverlayStateController mDreamOverlayStateController;
private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
@@ -781,7 +783,8 @@ public class StatusBar extends CoreStartable implements
ActivityLaunchAnimator activityLaunchAnimator,
NotifPipelineFlags notifPipelineFlags,
InteractionJankMonitor jankMonitor,
- DeviceStateManager deviceStateManager) {
+ DeviceStateManager deviceStateManager,
+ DreamOverlayStateController dreamOverlayStateController) {
super(context);
mNotificationsController = notificationsController;
mFragmentService = fragmentService;
@@ -869,6 +872,7 @@ public class StatusBar extends CoreStartable implements
mMessageRouter = messageRouter;
mWallpaperManager = wallpaperManager;
mJankMonitor = jankMonitor;
+ mDreamOverlayStateController = dreamOverlayStateController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -4144,6 +4148,10 @@ public class StatusBar extends CoreStartable implements
return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming();
}
+ public boolean isBouncerShowingOverDream() {
+ return isBouncerShowing() && mDreamOverlayStateController.isOverlayActive();
+ }
+
/**
* When {@link KeyguardBouncer} starts to be dismissed, playing its animation.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 316e68227e0c..b20349667379 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -51,6 +51,7 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.DejankUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -111,6 +112,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
+ private final DreamOverlayStateController mDreamOverlayStateController;
private KeyguardMessageAreaController mKeyguardMessageAreaController;
private final Lazy<ShadeController> mShadeController;
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@@ -235,6 +237,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
SysuiStatusBarStateController sysuiStatusBarStateController,
ConfigurationController configurationController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DreamOverlayStateController dreamOverlayStateController,
NavigationModeController navigationModeController,
DockManager dockManager,
NotificationShadeWindowController notificationShadeWindowController,
@@ -249,6 +252,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mConfigurationController = configurationController;
mNavigationModeController = navigationModeController;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mDreamOverlayStateController = dreamOverlayStateController;
mKeyguardStateController = keyguardStateController;
mMediaManager = notificationMediaManager;
mKeyguardUpdateManager = keyguardUpdateMonitor;
@@ -1174,7 +1178,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
public boolean bouncerNeedsScrimming() {
- return mOccluded || mBouncer.willDismissWithAction()
+ // When a dream overlay is active, scrimming will cause any expansion to immediately expand.
+ return (mOccluded && !mDreamOverlayStateController.isOverlayActive())
+ || mBouncer.willDismissWithAction()
|| mStatusBar.isFullScreenUserSwitcherState()
|| (mBouncer.isShowing() && mBouncer.isScrimmed())
|| mBouncer.isFullscreenBouncer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index f5364b9363b9..d3ff4a78c893 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -40,6 +40,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -231,7 +232,8 @@ public interface StatusBarPhoneModule {
ActivityLaunchAnimator activityLaunchAnimator,
NotifPipelineFlags notifPipelineFlags,
InteractionJankMonitor jankMonitor,
- DeviceStateManager deviceStateManager) {
+ DeviceStateManager deviceStateManager,
+ DreamOverlayStateController dreamOverlayStateController) {
return new StatusBar(
context,
notificationsController,
@@ -327,7 +329,8 @@ public interface StatusBarPhoneModule {
activityLaunchAnimator,
notifPipelineFlags,
jankMonitor,
- deviceStateManager
+ deviceStateManager,
+ dreamOverlayStateController
);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 5d39eef999d7..c37e966f3540 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -27,10 +27,12 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -46,6 +48,7 @@ import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
@@ -70,6 +73,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
@@ -80,6 +84,7 @@ import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -99,6 +104,8 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private IBiometricSysuiReceiver mReceiver;
@Mock
+ private IBiometricContextListener mContextListener;
+ @Mock
private AuthDialog mDialog1;
@Mock
private AuthDialog mDialog2;
@@ -120,10 +127,14 @@ public class AuthControllerTest extends SysuiTestCase {
private DisplayManager mDisplayManager;
@Mock
private WakefulnessLifecycle mWakefulnessLifecycle;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
@Captor
ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
@Captor
ArgumentCaptor<FingerprintStateListener> mFingerprintStateCaptor;
+ @Captor
+ ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
private TestableContext mContextSpy;
private Execution mExecution;
@@ -175,12 +186,15 @@ public class AuthControllerTest extends SysuiTestCase {
mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
- () -> mUdfpsController, () -> mSidefpsController);
+ () -> mUdfpsController, () -> mSidefpsController, mStatusBarStateController);
mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
mAuthenticatorsRegisteredCaptor.capture());
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
+
mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
// Ensures that the operations posted on the handler get executed.
@@ -198,7 +212,8 @@ public class AuthControllerTest extends SysuiTestCase {
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
- mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
+ mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
+ mStatusBarStateController);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -221,7 +236,8 @@ public class AuthControllerTest extends SysuiTestCase {
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
- mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
+ mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
+ mStatusBarStateController);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -656,6 +672,19 @@ public class AuthControllerTest extends SysuiTestCase {
verify(callback).onBiometricPromptDismissed();
}
+ @Test
+ public void testForwardsDozeEvent() throws RemoteException {
+ mAuthController.setBiometicContextListener(mContextListener);
+
+ mStatusBarStateListenerCaptor.getValue().onDozingChanged(false);
+ mStatusBarStateListenerCaptor.getValue().onDozingChanged(true);
+
+ InOrder order = inOrder(mContextListener);
+ // invoked twice since the initial state is false
+ order.verify(mContextListener, times(2)).onDozeChanged(eq(false));
+ order.verify(mContextListener).onDozeChanged(eq(true));
+ }
+
// Helpers
private void showDialog(int[] sensorIds, boolean credentialAllowed) {
@@ -705,10 +734,12 @@ public class AuthControllerTest extends SysuiTestCase {
FingerprintManager fingerprintManager,
FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory,
- Provider<SidefpsController> sidefpsControllerFactory) {
+ Provider<SidefpsController> sidefpsControllerFactory,
+ StatusBarStateController statusBarStateController) {
super(context, execution, commandQueue, activityTaskManager, windowManager,
fingerprintManager, faceManager, udfpsControllerFactory,
- sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle, mHandler);
+ sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle,
+ statusBarStateController, mHandler);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index b3b5fa509105..d5bd67adcf09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -91,6 +91,9 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Mock
DreamOverlayTouchMonitor mDreamOverlayTouchMonitor;
+ @Mock
+ DreamOverlayStateController mStateController;
+
DreamOverlayService mService;
@@ -115,6 +118,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
mService = new DreamOverlayService(mContext, mMainExecutor,
mDreamOverlayComponentFactory,
+ mStateController,
mKeyguardUpdateMonitor);
final IBinder proxy = mService.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 7d0833db7ae4..1859569bafe5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -16,9 +16,12 @@
package com.android.systemui.dreams;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -35,6 +38,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Collection;
@@ -56,7 +60,29 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
}
@Test
- public void testCallback() throws Exception {
+ public void testStateChange() {
+ final DreamOverlayStateController stateController = new DreamOverlayStateController(
+ mExecutor);
+ stateController.addCallback(mCallback);
+ stateController.setOverlayActive(true);
+ mExecutor.runAllReady();
+
+ verify(mCallback).onStateChanged();
+ assertThat(stateController.isOverlayActive()).isTrue();
+
+ Mockito.clearInvocations(mCallback);
+ stateController.setOverlayActive(true);
+ mExecutor.runAllReady();
+ verify(mCallback, never()).onStateChanged();
+
+ stateController.setOverlayActive(false);
+ mExecutor.runAllReady();
+ verify(mCallback).onStateChanged();
+ assertThat(stateController.isOverlayActive()).isFalse();
+ }
+
+ @Test
+ public void testCallback() {
final DreamOverlayStateController stateController = new DreamOverlayStateController(
mExecutor);
stateController.addCallback(mCallback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index f227a9b78c39..d5ab708f893b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -27,6 +27,7 @@ import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -122,6 +123,34 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
}
/**
+ * Makes sure the engine properly places a view within the {@link ConstraintLayout}.
+ */
+ @Test
+ public void testSnapToGuide() {
+ final ViewInfo firstViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0,
+ true),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+
+ final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+ addComplication(engine, firstViewInfo);
+
+ // Ensure the view is added to the top end corner
+ verifyChange(firstViewInfo, true, lp -> {
+ assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+ assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+ assertThat(lp.startToEnd == R.id.complication_end_guide).isTrue();
+ });
+ }
+
+ /**
* Ensures layout in a particular direction updates.
*/
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
new file mode 100644
index 000000000000..f1978b214594
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.dreams.complication;
+
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_AIR_QUALITY;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
+import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.dream.DreamBackend;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ComplicationUtilsTest extends SysuiTestCase {
+ @Test
+ public void testConvertComplicationType() {
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_TIME))
+ .isEqualTo(COMPLICATION_TYPE_TIME);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_DATE))
+ .isEqualTo(COMPLICATION_TYPE_DATE);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_WEATHER))
+ .isEqualTo(COMPLICATION_TYPE_WEATHER);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_AIR_QUALITY))
+ .isEqualTo(COMPLICATION_TYPE_AIR_QUALITY);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_CAST_INFO))
+ .isEqualTo(COMPLICATION_TYPE_CAST_INFO);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
new file mode 100644
index 000000000000..1a8326fd5bd1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -0,0 +1,252 @@
+/*
+ * 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.dreams.touch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.animation.ValueAnimator;
+import android.testing.AndroidTestingRunner;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Random;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
+ @Mock
+ StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
+ @Mock
+ StatusBar mStatusBar;
+
+ @Mock
+ NotificationShadeWindowController mNotificationShadeWindowController;
+
+ @Mock
+ FlingAnimationUtils mFlingAnimationUtils;
+
+
+ @Mock
+ FlingAnimationUtils mFlingAnimationUtilsClosing;
+
+ @Mock
+ DreamTouchHandler.TouchSession mTouchSession;
+
+ BouncerSwipeTouchHandler mTouchHandler;
+
+ @Mock
+ BouncerSwipeTouchHandler.ValueAnimatorCreator mValueAnimatorCreator;
+
+ @Mock
+ ValueAnimator mValueAnimator;
+
+ @Mock
+ BouncerSwipeTouchHandler.VelocityTrackerFactory mVelocityTrackerFactory;
+
+ @Mock
+ VelocityTracker mVelocityTracker;
+
+ private static final float TOUCH_REGION = .3f;
+ private static final float SCREEN_HEIGHT_PX = 100;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mTouchHandler = new BouncerSwipeTouchHandler(
+ mStatusBarKeyguardViewManager,
+ mStatusBar,
+ mNotificationShadeWindowController,
+ mValueAnimatorCreator,
+ mVelocityTrackerFactory,
+ mFlingAnimationUtils,
+ mFlingAnimationUtilsClosing,
+ TOUCH_REGION);
+ when(mStatusBar.getDisplayHeight()).thenReturn(SCREEN_HEIGHT_PX);
+ when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
+ when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
+ }
+
+ private static void beginValidSwipe(GestureDetector.OnGestureListener listener) {
+ listener.onDown(MotionEvent.obtain(0, 0,
+ MotionEvent.ACTION_DOWN, 0,
+ SCREEN_HEIGHT_PX - (.5f * TOUCH_REGION * SCREEN_HEIGHT_PX), 0));
+ }
+
+ /**
+ * Ensures expansion only happens when touch down happens in valid part of the screen.
+ */
+ @Test
+ public void testSessionStart() {
+ mTouchHandler.onSessionStart(mTouchSession);
+ verify(mNotificationShadeWindowController).setForcePluginOpen(eq(true), any());
+ ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor =
+ ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+ verify(mTouchSession).registerInputListener(eventListenerCaptor.capture());
+
+ final Random random = new Random(System.currentTimeMillis());
+
+ // If an initial touch down meeting criteria has been met, scroll behavior should be
+ // ignored.
+ assertThat(gestureListenerCaptor.getValue()
+ .onScroll(Mockito.mock(MotionEvent.class),
+ Mockito.mock(MotionEvent.class),
+ random.nextFloat(),
+ random.nextFloat())).isFalse();
+
+ // A touch at the top of the screen should also not trigger listening.
+ final MotionEvent touchDownEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN,
+ 0, 0, 0);
+
+ gestureListenerCaptor.getValue().onDown(touchDownEvent);
+ assertThat(gestureListenerCaptor.getValue()
+ .onScroll(Mockito.mock(MotionEvent.class),
+ Mockito.mock(MotionEvent.class),
+ random.nextFloat(),
+ random.nextFloat())).isFalse();
+
+ // A touch within range at the bottom of the screen should trigger listening
+ beginValidSwipe(gestureListenerCaptor.getValue());
+ assertThat(gestureListenerCaptor.getValue()
+ .onScroll(Mockito.mock(MotionEvent.class),
+ Mockito.mock(MotionEvent.class),
+ random.nextFloat(),
+ random.nextFloat())).isTrue();
+ }
+
+ /**
+ * Makes sure expansion amount is proportional to scroll.
+ */
+ @Test
+ public void testExpansionAmount() {
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+ beginValidSwipe(gestureListenerCaptor.getValue());
+
+ final float scrollAmount = .3f;
+ final float distanceY = SCREEN_HEIGHT_PX * scrollAmount;
+
+ final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX, 0);
+ final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX - distanceY, 0);
+
+ assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0 , distanceY))
+ .isTrue();
+
+ // Ensure only called once
+ verify(mStatusBarKeyguardViewManager)
+ .onPanelExpansionChanged(anyFloat(), anyBoolean(), anyBoolean());
+
+ // Ensure correct expansion passed in.
+ verify(mStatusBarKeyguardViewManager)
+ .onPanelExpansionChanged(eq(1 - scrollAmount), eq(false), eq(true));
+ }
+
+ private void swipeToPosition(float position, float velocityY) {
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ ArgumentCaptor<InputChannelCompat.InputEventListener> inputEventListenerCaptor =
+ ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+ verify(mTouchSession).registerInputListener(inputEventListenerCaptor.capture());
+
+ when(mVelocityTracker.getYVelocity()).thenReturn(velocityY);
+
+ beginValidSwipe(gestureListenerCaptor.getValue());
+
+ final float distanceY = SCREEN_HEIGHT_PX * position;
+
+ final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX, 0);
+ final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX - distanceY, 0);
+
+ assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0 , distanceY))
+ .isTrue();
+
+ final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP,
+ 0, 0, 0);
+
+ inputEventListenerCaptor.getValue().onInputEvent(upEvent);
+ }
+
+ /**
+ * Tests that ending a swipe before the set expansion threshold leads to bouncer collapsing
+ * down.
+ */
+ @Test
+ public void testCollapseOnThreshold() {
+ final float swipeUpPercentage = .3f;
+ swipeToPosition(swipeUpPercentage, -1);
+
+ verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage),
+ eq(KeyguardBouncer.EXPANSION_VISIBLE));
+ verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator), anyFloat(), anyFloat(),
+ anyFloat(), anyFloat());
+ verify(mValueAnimator).start();
+ }
+
+ /**
+ * Tests that ending a swipe above the set expansion threshold will continue the expansion.
+ */
+ @Test
+ public void testExpandOnThreshold() {
+ final float swipeUpPercentage = .7f;
+ swipeToPosition(swipeUpPercentage, 1);
+
+ verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage),
+ eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mFlingAnimationUtils).apply(eq(mValueAnimator), anyFloat(), anyFloat(),
+ anyFloat(), anyFloat());
+ verify(mValueAnimator).start();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index da8ab27d7e3d..d94e2eee9ffa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -48,6 +48,7 @@ import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -98,6 +99,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock InteractionJankMonitor mInteractionJankMonitor;
private @Mock ScreenOnCoordinator mScreenOnCoordinator;
private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy;
+ private @Mock DreamOverlayStateController mDreamOverlayStateController;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -202,6 +204,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
() -> mNotificationShadeDepthController,
mScreenOnCoordinator,
mInteractionJankMonitor,
+ mDreamOverlayStateController,
mNotificationShadeWindowControllerLazy);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 42647f7b026a..d51d370eecb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -227,7 +227,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
fun testDragDownAmountDoesntCallOutInLockedDownShade() {
whenever(nsslController.isInLockedDownShade).thenReturn(true)
transitionController.dragDownAmount = 10f
- verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
+ verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat())
verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
@@ -238,7 +238,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Test
fun testDragDownAmountCallsOut() {
transitionController.dragDownAmount = 10f
- verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
+ verify(nsslController).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController).setTransitionToFullShadeProgress(anyFloat())
verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
new file mode 100644
index 000000000000..d280f54e32f2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -0,0 +1,153 @@
+package com.android.systemui.statusbar.notification.stack
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationShelf
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.*
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * Tests for {@link NotificationShelf}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfTest : SysuiTestCase() {
+
+ private val shelf = NotificationShelf(context, /* attrs */ null)
+ private val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ private val ambientState = mock(AmbientState::class.java)
+
+ @Before
+ fun setUp() {
+ shelf.bind(ambientState, /* hostLayoutController */ null)
+ shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
+ }
+
+ @Test
+ fun testShadeWidth_BasedOnFractionToShade() {
+ setFractionToShade(0f)
+ setOnLockscreen(true)
+
+ shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
+ assertTrue(shelfState.actualWidth == 10)
+
+ shelf.updateStateWidth(shelfState, /* fraction */ 0.5f, /* shortestWidth */ 10)
+ assertTrue(shelfState.actualWidth == 20)
+
+ shelf.updateStateWidth(shelfState, /* fraction */ 1f, /* shortestWidth */ 10)
+ assertTrue(shelfState.actualWidth == 30)
+ }
+
+ @Test
+ fun testShelfIsLong_WhenNotOnLockscreen() {
+ setFractionToShade(0f)
+ setOnLockscreen(false)
+
+ shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
+ assertTrue(shelfState.actualWidth == 30)
+ }
+
+ @Test
+ fun testX_inViewForClick() {
+ val isXInView = shelf.isXInView(
+ /* localX */ 5f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertTrue(isXInView)
+ }
+
+ @Test
+ fun testXSlop_inViewForClick() {
+ val isLeftXSlopInView = shelf.isXInView(
+ /* localX */ -3f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertTrue(isLeftXSlopInView)
+
+ val isRightXSlopInView = shelf.isXInView(
+ /* localX */ 13f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertTrue(isRightXSlopInView)
+ }
+
+ @Test
+ fun testX_notInViewForClick() {
+ val isXLeftOfShelfInView = shelf.isXInView(
+ /* localX */ -10f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertFalse(isXLeftOfShelfInView)
+
+ val isXRightOfShelfInView = shelf.isXInView(
+ /* localX */ 20f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertFalse(isXRightOfShelfInView)
+ }
+
+ @Test
+ fun testY_inViewForClick() {
+ val isYInView = shelf.isYInView(
+ /* localY */ 5f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 10f)
+ assertTrue(isYInView)
+ }
+
+ @Test
+ fun testYSlop_inViewForClick() {
+ val isTopYSlopInView = shelf.isYInView(
+ /* localY */ -3f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 10f)
+ assertTrue(isTopYSlopInView)
+
+ val isBottomYSlopInView = shelf.isYInView(
+ /* localY */ 13f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 10f)
+ assertTrue(isBottomYSlopInView)
+ }
+
+ @Test
+ fun testY_notInViewForClick() {
+ val isYAboveShelfInView = shelf.isYInView(
+ /* localY */ -10f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 5f)
+ assertFalse(isYAboveShelfInView)
+
+ val isYBelowShelfInView = shelf.isYInView(
+ /* localY */ 15f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 5f)
+ assertFalse(isYBelowShelfInView)
+ }
+
+ private fun setFractionToShade(fraction: Float) {
+ shelf.setFractionToShade(fraction)
+ }
+
+ private fun setOnLockscreen(isOnLockscreen: Boolean) {
+ whenever(ambientState.isOnKeyguard).thenReturn(isOnLockscreen)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 5d80bca03e03..bb79941b0e53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -97,6 +98,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
private KeyguardMessageArea mKeyguardMessageArea;
@Mock
private ShadeController mShadeController;
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -116,6 +119,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarStateController,
mock(ConfigurationController.class),
mKeyguardUpdateMonitor,
+ mDreamOverlayStateController,
mock(NavigationModeController.class),
mock(DockManager.class),
mock(NotificationShadeWindowController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index b7c00fe5e3a1..1564dfe8cd06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -87,6 +87,7 @@ import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
@@ -285,6 +286,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotifLiveDataStore mNotifLiveDataStore;
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private DeviceStateManager mDeviceStateManager;
+ @Mock private DreamOverlayStateController mDreamOverlayStateController;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -474,7 +476,8 @@ public class StatusBarTest extends SysuiTestCase {
mActivityLaunchAnimator,
mNotifPipelineFlags,
mJankMonitor,
- mDeviceStateManager);
+ mDeviceStateManager,
+ mDreamOverlayStateController);
when(mKeyguardViewMediator.registerStatusBar(
any(StatusBar.class),
any(NotificationPanelViewController.class),
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 2168fb18888f..a65d5b3b94f7 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -3346,8 +3346,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// Isolate the changes relating to RROs. The app info must be copied to prevent
// affecting other parts of system server that may have cached this app info.
oldAppInfo = new ApplicationInfo(oldAppInfo);
- oldAppInfo.overlayPaths = newAppInfo.overlayPaths.clone();
- oldAppInfo.resourceDirs = newAppInfo.resourceDirs.clone();
+ oldAppInfo.overlayPaths = newAppInfo.overlayPaths == null
+ ? null : newAppInfo.overlayPaths.clone();
+ oldAppInfo.resourceDirs = newAppInfo.resourceDirs == null
+ ? null : newAppInfo.resourceDirs.clone();
provider.info.providerInfo.applicationInfo = oldAppInfo;
for (int j = 0, M = provider.widgets.size(); j < M; j++) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 095c1fc80d3e..76ee728fdb07 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -17,6 +17,7 @@
package com.android.server.autofill;
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
+import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE;
import static android.service.autofill.FillRequest.FLAG_ACTIVITY_START;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
@@ -47,13 +48,16 @@ import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityTaskManager;
import android.app.IAssistDataReceiver;
+import android.app.PendingIntent;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.AutofillOverlay;
import android.app.assist.AssistStructure.ViewNode;
+import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
@@ -148,6 +152,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
AutoFillUI.AutoFillUiCallback, ValueFinder {
private static final String TAG = "AutofillSession";
+ private static final String ACTION_DELAYED_FILL =
+ "android.service.autofill.action.DELAYED_FILL";
private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID";
final Object mLock;
@@ -155,6 +161,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private final AutofillManagerServiceImpl mService;
private final Handler mHandler;
private final AutoFillUI mUi;
+ @NonNull private final Context mContext;
private final MetricsLogger mMetricsLogger = new MetricsLogger();
@@ -269,6 +276,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
private boolean mHasCallback;
+ @GuardedBy("mLock")
+ private boolean mDelayedFillBroadcastReceiverRegistered;
+
+ @GuardedBy("mLock")
+ private PendingIntent mDelayedFillPendingIntent;
+
/**
* Extras sent by service on {@code onFillRequest()} calls; the most recent non-null extra is
* saved and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls.
@@ -356,6 +369,32 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private final AccessibilityManager mAccessibilityManager;
+ // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
+ // new one per Session.
+ private final BroadcastReceiver mDelayedFillBroadcastReceiver =
+ new BroadcastReceiver() {
+ // ErrorProne says mAssistReceiver#processDelayedFillLocked needs to be guarded by
+ // 'Session.this.mLock', which is the same as mLock.
+ @SuppressWarnings("GuardedBy")
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ if (!intent.getAction().equals(ACTION_DELAYED_FILL)) {
+ Slog.wtf(TAG, "Unexpected action is received.");
+ return;
+ }
+ if (!intent.hasExtra(EXTRA_REQUEST_ID)) {
+ Slog.e(TAG, "Delay fill action is missing request id extra.");
+ return;
+ }
+ Slog.v(TAG, "mDelayedFillBroadcastReceiver delayed fill action received");
+ synchronized (mLock) {
+ int requestId = intent.getIntExtra(EXTRA_REQUEST_ID, 0);
+ FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE);
+ mAssistReceiver.processDelayedFillLocked(requestId, response);
+ }
+ }
+ };
+
void onSwitchInputMethodLocked() {
// One caveat is that for the case where the focus is on a field for which regular autofill
// returns null, and augmented autofill is triggered, and then the user switches the input
@@ -408,31 +447,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
private final class SessionFlags {
/** Whether autofill is disabled by the service */
- @GuardedBy("mLock")
private boolean mAutofillDisabled;
/** Whether the autofill service supports inline suggestions */
- @GuardedBy("mLock")
private boolean mInlineSupportedByService;
/** True if session is for augmented only */
- @GuardedBy("mLock")
private boolean mAugmentedAutofillOnly;
/** Whether the session is currently showing the SaveUi. */
- @GuardedBy("mLock")
private boolean mShowingSaveUi;
/** Whether the current {@link FillResponse} is expired. */
- @GuardedBy("mLock")
private boolean mExpiredResponse;
/** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
- @GuardedBy("mLock")
private boolean mClientSuggestionsEnabled;
/** Whether the fill dialog UI is disabled. */
- @GuardedBy("mLock")
private boolean mFillDialogDisabled;
}
@@ -447,6 +479,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private InlineSuggestionsRequest mPendingInlineSuggestionsRequest;
@GuardedBy("mLock")
private FillRequest mPendingFillRequest;
+ @GuardedBy("mLock")
+ private FillRequest mLastFillRequest;
@Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState,
boolean isInlineRequest) {
@@ -473,6 +507,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPendingInlineSuggestionsRequest = inlineRequest;
}
+ @GuardedBy("mLock")
void maybeRequestFillFromServiceLocked() {
if (mPendingFillRequest == null) {
return;
@@ -490,9 +525,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
mPendingFillRequest.getFillContexts(),
mPendingFillRequest.getClientState(),
- mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+ mPendingFillRequest.getFlags(),
+ mPendingInlineSuggestionsRequest,
+ mPendingFillRequest.getDelayedFillIntentSender());
}
}
+ mLastFillRequest = mPendingFillRequest;
mRemoteFillService.onFillRequest(mPendingFillRequest);
mPendingInlineSuggestionsRequest = null;
@@ -594,8 +632,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ArrayList<FillContext> contexts =
mergePreviousSessionLocked(/* forSave= */ false);
+ mDelayedFillPendingIntent = createPendingIntent(requestId);
request = new FillRequest(requestId, contexts, mClientState, flags,
- /*inlineSuggestionsRequest=*/null);
+ /*inlineSuggestionsRequest=*/ null,
+ /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null
+ ? null
+ : mDelayedFillPendingIntent.getIntentSender());
mPendingFillRequest = request;
maybeRequestFillFromServiceLocked();
@@ -610,7 +652,70 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void onHandleAssistScreenshot(Bitmap screenshot) {
// Do nothing
}
- };
+
+ @GuardedBy("mLock")
+ void processDelayedFillLocked(int requestId, FillResponse response) {
+ if (mLastFillRequest != null && requestId == mLastFillRequest.getId()) {
+ Slog.v(TAG, "processDelayedFillLocked: "
+ + "calling onFillRequestSuccess with new response");
+ onFillRequestSuccess(requestId, response,
+ mService.getServicePackageName(), mLastFillRequest.getFlags());
+ }
+ }
+ }
+
+ /** Creates {@link PendingIntent} for autofill service to send a delayed fill. */
+ private PendingIntent createPendingIntent(int requestId) {
+ Slog.d(TAG, "createPendingIntent for request " + requestId);
+ PendingIntent pendingIntent;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Intent intent = new Intent(ACTION_DELAYED_FILL).setPackage("android")
+ .putExtra(EXTRA_REQUEST_ID, requestId);
+ pendingIntent = PendingIntent.getBroadcast(
+ mContext, this.id, intent,
+ PendingIntent.FLAG_MUTABLE
+ | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_CANCEL_CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return pendingIntent;
+ }
+
+ @GuardedBy("mLock")
+ private void clearPendingIntentLocked() {
+ Slog.d(TAG, "clearPendingIntentLocked");
+ if (mDelayedFillPendingIntent == null) {
+ return;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mDelayedFillPendingIntent.cancel();
+ mDelayedFillPendingIntent = null;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void registerDelayedFillBroadcastLocked() {
+ if (!mDelayedFillBroadcastReceiverRegistered) {
+ Slog.v(TAG, "registerDelayedFillBroadcastLocked()");
+ IntentFilter intentFilter = new IntentFilter(ACTION_DELAYED_FILL);
+ mContext.registerReceiver(mDelayedFillBroadcastReceiver, intentFilter);
+ mDelayedFillBroadcastReceiverRegistered = true;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void unregisterDelayedFillBroadcastLocked() {
+ if (mDelayedFillBroadcastReceiverRegistered) {
+ Slog.v(TAG, "unregisterDelayedFillBroadcastLocked()");
+ mContext.unregisterReceiver(mDelayedFillBroadcastReceiver);
+ mDelayedFillBroadcastReceiverRegistered = false;
+ }
+ }
/**
* Returns the ids of all entries in {@link #mViewStates} in the same order.
@@ -964,6 +1069,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mHasCallback = hasCallback;
mUiLatencyHistory = uiLatencyHistory;
mWtfHistory = wtfHistory;
+ mContext = context;
mComponentName = componentName;
mCompatMode = compatMode;
mSessionState = STATE_ACTIVE;
@@ -1096,6 +1202,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
processNullResponseLocked(requestId, requestFlags);
return;
}
+
+ final int flags = response.getFlags();
+ if ((flags & FillResponse.FLAG_DELAY_FILL) != 0) {
+ Slog.v(TAG, "Service requested to wait for delayed fill response.");
+ registerDelayedFillBroadcastLocked();
+ }
}
mService.setLastResponse(id, response);
@@ -1206,6 +1318,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Nullable CharSequence message) {
boolean showMessage = !TextUtils.isEmpty(message);
synchronized (mLock) {
+ unregisterDelayedFillBroadcastLocked();
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId
+ ") rejected - session: " + id + " destroyed");
@@ -1522,7 +1635,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.e(TAG, "Error sending input show up notification", e);
}
}
- synchronized (Session.this.mLock) {
+ synchronized (mLock) {
// stop to show fill dialog
mSessionFlags.mFillDialogDisabled = true;
}
@@ -3259,7 +3372,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private boolean isFillDialogUiEnabled() {
// TODO read from Settings or somewhere
final boolean isSettingsEnabledFillDialog = true;
- synchronized (Session.this.mLock) {
+ synchronized (mLock) {
return isSettingsEnabledFillDialog && !mSessionFlags.mFillDialogDisabled;
}
}
@@ -3530,6 +3643,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private void processNullResponseLocked(int requestId, int flags) {
+ unregisterDelayedFillBroadcastLocked();
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
}
@@ -3744,6 +3858,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// only if handling the current response requires it.
mUi.hideAll(this);
+ if ((newResponse.getFlags() & FillResponse.FLAG_DELAY_FILL) == 0) {
+ Slog.d(TAG, "Service did not request to wait for delayed fill response.");
+ unregisterDelayedFillBroadcastLocked();
+ }
+
final int requestId = newResponse.getRequestId();
if (sVerbose) {
Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId
@@ -4296,6 +4415,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return null;
}
+ clearPendingIntentLocked();
+ unregisterDelayedFillBroadcastLocked();
+
unlinkClientVultureLocked();
mUi.destroyAll(mPendingSaveUi, this, true);
mUi.clearCallback(this);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index cfd37988d234..c3ab2a79e288 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1255,7 +1255,7 @@ public class CompanionDeviceManagerService extends SystemService
}
@Override
- public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {
+ public void onDeviceDisconnected(BluetoothDevice device, int reason) {
Slog.d(LOG_TAG, device.getAddress() + " disconnected w/ reason: (" + reason + ") "
+ BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonText(reason));
CompanionDeviceManagerService.this.onDeviceDisconnected(device.getAddress());
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index dbe866b374f1..93cbe973b00e 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -91,7 +91,7 @@ class BluetoothCompanionDeviceConnectionListener
*/
@Override
public void onDeviceDisconnected(@NonNull BluetoothDevice device,
- @DisconnectReason int reason) {
+ int reason) {
if (DEBUG) {
Log.i(TAG, "onDevice_Disconnected() " + btDeviceToString(device));
Log.d(TAG, " reason=" + disconnectReasonText(reason));
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 6c56e2f777f3..e6bfd1ff7f1a 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -18,8 +18,11 @@ package com.android.server.companion.virtual;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.StringDef;
import android.graphics.Point;
import android.graphics.PointF;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
@@ -48,6 +51,20 @@ class InputController {
private static final String TAG = "VirtualInputController";
+ private static final AtomicLong sNextPhysId = new AtomicLong(1);
+
+ static final String PHYS_TYPE_KEYBOARD = "Keyboard";
+ static final String PHYS_TYPE_MOUSE = "Mouse";
+ static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen";
+ @StringDef(prefix = { "PHYS_TYPE_" }, value = {
+ PHYS_TYPE_KEYBOARD,
+ PHYS_TYPE_MOUSE,
+ PHYS_TYPE_TOUCHSCREEN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PhysType {
+ }
+
private final Object mLock;
/* Token -> file descriptor associations. */
@@ -56,6 +73,8 @@ class InputController {
final Map<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors = new ArrayMap<>();
private final NativeWrapper mNativeWrapper;
+ private final DisplayManagerInternal mDisplayManagerInternal;
+ private final InputManagerInternal mInputManagerInternal;
/**
* Because the pointer is a singleton, it can only be targeted at one display at a time. Because
@@ -73,6 +92,8 @@ class InputController {
mLock = lock;
mNativeWrapper = nativeWrapper;
mActivePointerDisplayId = Display.INVALID_DISPLAY;
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+ mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
}
void close() {
@@ -90,7 +111,9 @@ class InputController {
int productId,
@NonNull IBinder deviceToken,
int displayId) {
- final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId);
+ final String phys = createPhys(PHYS_TYPE_KEYBOARD);
+ setUniqueIdAssociation(displayId, phys);
+ final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys);
if (fd < 0) {
throw new RuntimeException(
"A native error occurred when creating keyboard: " + -fd);
@@ -99,7 +122,7 @@ class InputController {
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken,
new InputDeviceDescriptor(fd, binderDeathRecipient,
- InputDeviceDescriptor.TYPE_KEYBOARD, displayId));
+ InputDeviceDescriptor.TYPE_KEYBOARD, displayId, phys));
}
try {
deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
@@ -114,7 +137,9 @@ class InputController {
int productId,
@NonNull IBinder deviceToken,
int displayId) {
- final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId);
+ final String phys = createPhys(PHYS_TYPE_MOUSE);
+ setUniqueIdAssociation(displayId, phys);
+ final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys);
if (fd < 0) {
throw new RuntimeException(
"A native error occurred when creating mouse: " + -fd);
@@ -123,11 +148,9 @@ class InputController {
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken,
new InputDeviceDescriptor(fd, binderDeathRecipient,
- InputDeviceDescriptor.TYPE_MOUSE, displayId));
- final InputManagerInternal inputManagerInternal =
- LocalServices.getService(InputManagerInternal.class);
- inputManagerInternal.setVirtualMousePointerDisplayId(displayId);
- inputManagerInternal.setPointerAcceleration(1);
+ InputDeviceDescriptor.TYPE_MOUSE, displayId, phys));
+ mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
+ mInputManagerInternal.setPointerAcceleration(1);
mActivePointerDisplayId = displayId;
}
try {
@@ -144,7 +167,9 @@ class InputController {
@NonNull IBinder deviceToken,
int displayId,
@NonNull Point screenSize) {
- final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
+ final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN);
+ setUniqueIdAssociation(displayId, phys);
+ final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, phys,
screenSize.y, screenSize.x);
if (fd < 0) {
throw new RuntimeException(
@@ -154,7 +179,7 @@ class InputController {
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken,
new InputDeviceDescriptor(fd, binderDeathRecipient,
- InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId));
+ InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId, phys));
}
try {
deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
@@ -174,6 +199,7 @@ class InputController {
}
token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
+ InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys());
// Reset values to the default if all virtual mice are unregistered, or set display
// id if there's another mouse (choose the most recent).
@@ -197,9 +223,7 @@ class InputController {
}
}
if (mostRecentlyCreatedMouse != null) {
- final InputManagerInternal inputManagerInternal =
- LocalServices.getService(InputManagerInternal.class);
- inputManagerInternal.setVirtualMousePointerDisplayId(
+ mInputManagerInternal.setVirtualMousePointerDisplayId(
mostRecentlyCreatedMouse.getDisplayId());
mActivePointerDisplayId = mostRecentlyCreatedMouse.getDisplayId();
} else {
@@ -209,14 +233,21 @@ class InputController {
}
private void resetMouseValuesLocked() {
- final InputManagerInternal inputManagerInternal =
- LocalServices.getService(InputManagerInternal.class);
- inputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
- inputManagerInternal.setPointerAcceleration(
+ mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
+ mInputManagerInternal.setPointerAcceleration(
IInputConstants.DEFAULT_POINTER_ACCELERATION);
mActivePointerDisplayId = Display.INVALID_DISPLAY;
}
+ private static String createPhys(@PhysType String type) {
+ return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement());
+ }
+
+ private void setUniqueIdAssociation(int displayId, String phys) {
+ final String displayUniqueId = mDisplayManagerInternal.getDisplayInfo(displayId).uniqueId;
+ InputManager.getInstance().addUniqueIdAssociation(phys, displayUniqueId);
+ }
+
boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
synchronized (mLock) {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
@@ -321,17 +352,18 @@ class InputController {
fout.println(" creationOrder: "
+ inputDeviceDescriptor.getCreationOrderNumber());
fout.println(" type: " + inputDeviceDescriptor.getType());
+ fout.println(" phys: " + inputDeviceDescriptor.getPhys());
}
fout.println(" Active mouse display id: " + mActivePointerDisplayId);
}
}
private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId,
- int productId);
- private static native int nativeOpenUinputMouse(String deviceName, int vendorId,
- int productId);
+ int productId, String phys);
+ private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
+ String phys);
private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId,
- int productId, int height, int width);
+ int productId, String phys, int height, int width);
private static native boolean nativeCloseUinput(int fd);
private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action);
private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action);
@@ -345,20 +377,18 @@ class InputController {
/** Wrapper around the static native methods for tests. */
@VisibleForTesting
protected static class NativeWrapper {
- public int openUinputKeyboard(String deviceName, int vendorId, int productId) {
- return nativeOpenUinputKeyboard(deviceName, vendorId,
- productId);
+ public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) {
+ return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys);
}
- public int openUinputMouse(String deviceName, int vendorId, int productId) {
- return nativeOpenUinputMouse(deviceName, vendorId,
- productId);
+ public int openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
+ return nativeOpenUinputMouse(deviceName, vendorId, productId, phys);
}
- public int openUinputTouchscreen(String deviceName, int vendorId, int productId, int height,
- int width) {
- return nativeOpenUinputTouchscreen(deviceName, vendorId,
- productId, height, width);
+ public int openUinputTouchscreen(String deviceName, int vendorId,
+ int productId, String phys, int height, int width) {
+ return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height,
+ width);
}
public boolean closeUinput(int fd) {
@@ -410,15 +440,17 @@ class InputController {
private final IBinder.DeathRecipient mDeathRecipient;
private final @Type int mType;
private final int mDisplayId;
+ private final String mPhys;
// Monotonically increasing number; devices with lower numbers were created earlier.
private final long mCreationOrderNumber;
- InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient,
- @Type int type, int displayId) {
+ InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type,
+ int displayId, String phys) {
mFd = fd;
mDeathRecipient = deathRecipient;
mType = type;
mDisplayId = displayId;
+ mPhys = phys;
mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
}
@@ -445,6 +477,10 @@ class InputController {
public long getCreationOrderNumber() {
return mCreationOrderNumber;
}
+
+ public String getPhys() {
+ return mPhys;
+ }
}
private final class BinderDeathRecipient implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8dfb7dd21b4e..5da461d8e392 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1365,7 +1365,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
public void notePhoneDataConnectionState(final int dataType, final boolean hasData,
- final int serviceType) {
+ final int serviceType, final int nrFrequency) {
enforceCallingPermission();
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1373,7 +1373,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mHandler.post(() -> {
synchronized (mStats) {
mStats.notePhoneDataConnectionStateLocked(dataType, hasData, serviceType,
- elapsedRealtime, uptime);
+ nrFrequency, elapsedRealtime, uptime);
}
});
}
diff --git a/services/core/java/com/android/server/am/DataConnectionStats.java b/services/core/java/com/android/server/am/DataConnectionStats.java
index 6e39a4c802d9..f0910dcb0da2 100644
--- a/services/core/java/com/android/server/am/DataConnectionStats.java
+++ b/services/core/java/com/android/server/am/DataConnectionStats.java
@@ -109,7 +109,7 @@ public class DataConnectionStats extends BroadcastReceiver {
}
try {
mBatteryStats.notePhoneDataConnectionState(networkType, visible,
- mServiceState.getState());
+ mServiceState.getState(), mServiceState.getNrFrequencyRange());
} catch (RemoteException e) {
Log.w(TAG, "Error noting data connection state", e);
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 42fca9b840df..47f31d505867 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -486,9 +486,7 @@ public class BtHelper {
return;
}
final BluetoothDevice btDevice = deviceList.get(0);
- final @BluetoothProfile.BtProfileState int state =
- proxy.getConnectionState(btDevice);
- if (state == BluetoothProfile.STATE_CONNECTED) {
+ if (proxy.getConnectionState(btDevice) == BluetoothProfile.STATE_CONNECTED) {
mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(btDevice, null,
new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener"));
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
new file mode 100644
index 000000000000..8d28298542dc
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
@@ -0,0 +1,47 @@
+/*
+ * 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.server.biometrics.log;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.common.OperationContext;
+
+import java.util.function.Consumer;
+
+/**
+ * Cache for system state not directly related to biometric operations that is used for
+ * logging or optimizations.
+ */
+public interface BiometricContext {
+ /** Gets the context source. */
+ static BiometricContext getInstance() {
+ return BiometricContextProvider.sInstance.get();
+ }
+
+ /** If the display is in AOD. */
+ boolean isAoD();
+
+ /**
+ * Subscribe to context changes.
+ *
+ * @param context context that will be modified when changed
+ * @param consumer callback when the context is modified
+ */
+ void subscribe(@NonNull OperationContext context, @NonNull Consumer<OperationContext> consumer);
+
+ /** Unsubscribe from context changes. */
+ void unsubscribe(@NonNull OperationContext context);
+}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
new file mode 100644
index 000000000000..65e9e3dec60f
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -0,0 +1,105 @@
+/*
+ * 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.server.biometrics.log;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.IBiometricContextListener;
+import android.hardware.biometrics.common.OperationContext;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Singleton;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+/**
+ * A default provider for {@link BiometricContext}.
+ */
+class BiometricContextProvider implements BiometricContext {
+
+ private static final String TAG = "BiometricContextProvider";
+
+ static final Singleton<BiometricContextProvider> sInstance =
+ new Singleton<BiometricContextProvider>() {
+ @Override
+ protected BiometricContextProvider create() {
+ return new BiometricContextProvider(IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(
+ Context.STATUS_BAR_SERVICE)), null /* handler */);
+ }
+ };
+
+ @NonNull
+ private final Map<OperationContext, Consumer<OperationContext>> mSubscribers =
+ new ConcurrentHashMap<>();
+
+ @VisibleForTesting
+ BiometricContextProvider(@NonNull IStatusBarService service, @Nullable Handler handler) {
+ try {
+ service.setBiometicContextListener(new IBiometricContextListener.Stub() {
+ @Override
+ public void onDozeChanged(boolean isDozing) {
+ mIsDozing = isDozing;
+ notifyChanged();
+ }
+
+ private void notifyChanged() {
+ if (handler != null) {
+ handler.post(() -> notifySubscribers());
+ } else {
+ notifySubscribers();
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register biometric context listener", e);
+ }
+ }
+
+ private boolean mIsDozing = false;
+
+ @Override
+ public boolean isAoD() {
+ return mIsDozing;
+ }
+
+ @Override
+ public void subscribe(@NonNull OperationContext context,
+ @NonNull Consumer<OperationContext> consumer) {
+ mSubscribers.put(context, consumer);
+ }
+
+ @Override
+ public void unsubscribe(@NonNull OperationContext context) {
+ mSubscribers.remove(context);
+ }
+
+ private void notifySubscribers() {
+ mSubscribers.forEach((context, consumer) -> {
+ context.isAoD = mIsDozing;
+ consumer.accept(context);
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index d029af38c683..018839079e22 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -79,6 +79,12 @@ public class BiometricLogger {
}
};
+ /** Get a new logger with all unknown fields (for operations that do not require logs). */
+ public static BiometricLogger ofUnknown(@NonNull Context context) {
+ return new BiometricLogger(context, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ }
+
/**
* @param context system_server context
* @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants.
@@ -103,6 +109,11 @@ public class BiometricLogger {
mSensorManager = sensorManager;
}
+ /** Creates a new logger with the action replaced with the new action. */
+ public BiometricLogger swapAction(@NonNull Context context, int statsAction) {
+ return new BiometricLogger(context, mStatsModality, statsAction, mStatsClient);
+ }
+
/** Disable logging metrics and only log critical events, such as system health issues. */
public void disableMetrics() {
mShouldLogMetrics = false;
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 8b8103e6e2c9..e07a68c6d94d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -29,6 +29,9 @@ import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import java.util.function.Supplier;
/**
@@ -57,9 +60,9 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
public AcquisitionClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int cookie, int sensorId, boolean shouldVibrate,
- int statsModality, int statsAction, int statsClient) {
- super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality,
- statsAction, statsClient);
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId,
+ logger, biometricContext);
mPowerManager = context.getSystemService(PowerManager.class);
mShouldVibrate = shouldVibrate;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index b715faf3ca8f..949edd0e79ed 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -29,7 +29,6 @@ import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricOverlayConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -39,6 +38,8 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.ArrayList;
import java.util.List;
@@ -93,13 +94,13 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
public AuthenticationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int targetUserId, long operationId, boolean restricted, @NonNull String owner,
- int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric,
- int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener,
+ int cookie, boolean requireConfirmation, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener,
@NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication,
boolean shouldVibrate, boolean isKeyguardBypassEnabled) {
super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
- shouldVibrate, statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE,
- statsClient);
+ shouldVibrate, biometricLogger, biometricContext);
mIsStrongBiometric = isStrongBiometric;
mOperationId = operationId;
mRequireConfirmation = requireConfirmation;
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index e1f7e2ab5461..1b2e606117e7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -21,12 +21,12 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import java.util.NoSuchElementException;
@@ -50,6 +50,7 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
@NonNull private final String mOwner;
private final int mSensorId; // sensorId as configured by the framework
@NonNull private final BiometricLogger mLogger;
+ @NonNull private final BiometricContext mBiometricContext;
@Nullable private IBinder mToken;
private long mRequestId;
@@ -82,22 +83,13 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
* @param owner name of the client that owns this
* @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon)
* @param sensorId ID of the sensor that the operation should be requested of
- * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants
- * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants
- * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants
+ * @param logger framework stats logger
+ * @param biometricContext system context metadata
*/
public BaseClientMonitor(@NonNull Context context,
@Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
- int statsClient) {
- this(context, token, listener, userId, owner, cookie, sensorId,
- new BiometricLogger(context, statsModality, statsAction, statsClient));
- }
-
- @VisibleForTesting
- BaseClientMonitor(@NonNull Context context,
- @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int cookie, int sensorId, @NonNull BiometricLogger logger) {
+ @NonNull String owner, int cookie, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
mSequentialId = sCount++;
mContext = context;
mToken = token;
@@ -108,6 +100,7 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
mCookie = cookie;
mSensorId = sensorId;
mLogger = logger;
+ mBiometricContext = biometricContext;
try {
if (token != null) {
@@ -207,20 +200,29 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
return false;
}
+ /** System context that may change during operations. */
+ @NonNull
+ protected BiometricContext getBiometricContext() {
+ return mBiometricContext;
+ }
+
/** Logger for this client */
@NonNull
public BiometricLogger getLogger() {
return mLogger;
}
+ @NonNull
public final Context getContext() {
return mContext;
}
+ @NonNull
public final String getOwnerString() {
return mOwner;
}
+ @Nullable
public final ClientMonitorCallbackConverter getListener() {
return mListener;
}
@@ -229,6 +231,7 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
return mTargetUserId;
}
+ @Nullable
public final IBinder getToken() {
return mToken;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 74f4931cf2aa..483ce75eae98 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -20,13 +20,14 @@ import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricOverlayConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.fingerprint.FingerprintManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.Arrays;
import java.util.function.Supplier;
@@ -53,10 +54,10 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
public EnrollClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
- int timeoutSec, int statsModality, int sensorId, boolean shouldVibrate) {
+ int timeoutSec, int sensorId, boolean shouldVibrate,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- shouldVibrate, statsModality, BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ shouldVibrate, logger, biometricContext);
mBiometricUtils = utils;
mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
mTimeoutSec = timeoutSec;
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 9689418b1f1a..2adf0cb3bab4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -18,12 +18,13 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.function.Supplier;
@@ -33,10 +34,10 @@ public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> {
public GenerateChallengeClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- int userId, @NonNull String owner, int sensorId) {
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ biometricLogger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index 66a1c6e876ab..eabafceab590 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -19,9 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.OperationContext;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import java.util.function.Supplier;
/**
@@ -33,6 +36,9 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
@NonNull
protected final Supplier<T> mLazyDaemon;
+ @NonNull
+ protected final OperationContext mOperationContext = new OperationContext();
+
/**
* @param context system_server context
* @param lazyDaemon pointer for lazy retrieval of the HAL
@@ -42,16 +48,15 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
* @param owner name of the client that owns this
* @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon)
* @param sensorId ID of the sensor that the operation should be requested of
- * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants
- * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants
- * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants
+ * @param biometricLogger framework stats logger
+ * @param biometricContext system context metadata
*/
public HalClientMonitor(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
- int statsClient) {
- super(context, token, listener, userId, owner, cookie, sensorId, statsModality,
- statsAction, statsClient);
+ @NonNull String owner, int cookie, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
+ super(context, token, listener, userId, owner, cookie, sensorId,
+ biometricLogger, biometricContext);
mLazyDaemon = lazyDaemon;
}
@@ -71,4 +76,12 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
* {@link #start(ClientMonitorCallback)}.
*/
public abstract void unableToStart();
+
+ @Override
+ public void destroy() {
+ super.destroy();
+
+ // subclasses should do this earlier in most cases, but ensure it happens now
+ getBiometricContext().unsubscribe(mOperationContext);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 0e6d11ec5182..57ea812dbb3a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.ArrayList;
import java.util.List;
@@ -101,19 +102,22 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
Supplier<T> lazyDaemon, IBinder token, int userId, String owner,
- List<S> enrolledList, BiometricUtils<S> utils, int sensorId);
+ List<S> enrolledList, BiometricUtils<S> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext);
protected abstract RemovalClient<S, T> getRemovalClient(Context context,
Supplier<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner,
- BiometricUtils<S> utils, int sensorId, Map<Integer, Long> authenticatorIds);
+ BiometricUtils<S> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ Map<Integer, Long> authenticatorIds);
protected InternalCleanupClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
- int userId, @NonNull String owner, int sensorId, int statsModality,
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull List<S> enrolledList, @NonNull BiometricUtils<S> utils,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
- userId, owner, 0 /* cookie */, sensorId, statsModality,
- BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ userId, owner, 0 /* cookie */, sensorId, logger, biometricContext);
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
mEnrolledList = enrolledList;
@@ -127,7 +131,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
mUnknownHALTemplates.remove(template);
mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
template.mIdentifier.getBiometricId(), template.mUserId,
- getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds);
+ getContext().getPackageName(), mBiometricUtils, getSensorId(),
+ getLogger(), getBiometricContext(), mAuthenticatorIds);
getLogger().logUnknownEnrollmentInHal();
@@ -145,7 +150,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
// Start enumeration. Removal will start if necessary, when enumeration is completed.
mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
- getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
+ getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId(), getLogger(),
+ getBiometricContext());
Slog.d(TAG, "Starting enumerate: " + mCurrentTask);
mCurrentTask.start(mEnumerateCallback);
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 5f97f3711a60..7f8f38f3e9d2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.ArrayList;
import java.util.List;
@@ -47,12 +48,14 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
protected InternalEnumerateClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, int userId, @NonNull String owner,
@NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
- @NonNull BiometricUtils utils, int sensorId, int statsModality) {
+ @NonNull BiometricUtils utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
// Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
- 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
+ //, BiometricsProtoEnums.ACTION_ENUMERATE,
+ // BiometricsProtoEnums.CLIENT_UNKNOWN);
mEnrolledList = enrolledList;
mUtils = utils;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
index 697d77cfbee6..d5aa5e2c9db6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -19,12 +19,13 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IInvalidationCallback;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.Map;
import java.util.function.Supplier;
@@ -42,12 +43,13 @@ public abstract class InvalidationClient<S extends BiometricAuthenticator.Identi
@NonNull private final IInvalidationCallback mInvalidationCallback;
public InvalidationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
- int userId, int sensorId, @NonNull Map<Integer, Long> authenticatorIds,
+ int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull Map<Integer, Long> authenticatorIds,
@NonNull IInvalidationCallback callback) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId,
context.getOpPackageName(), 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mAuthenticatorIds = authenticatorIds;
mInvalidationCallback = callback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
index b2661a28012d..1097bb7da684 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
@@ -20,10 +20,11 @@ import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IInvalidationCallback;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
/**
* ClientMonitor subclass responsible for coordination of authenticatorId invalidation of other
@@ -74,11 +75,10 @@ public class InvalidationRequesterClient<S extends BiometricAuthenticator.Identi
};
public InvalidationRequesterClient(@NonNull Context context, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull BiometricUtils<S> utils) {
super(context, null /* token */, null /* listener */, userId,
- context.getOpPackageName(), 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ context.getOpPackageName(), 0 /* cookie */, sensorId, logger, biometricContext);
mBiometricManager = context.getSystemService(BiometricManager.class);
mUtils = utils;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index a0cef94fcf48..07ce841a7cac 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -19,12 +19,13 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.Map;
import java.util.function.Supplier;
@@ -44,10 +45,12 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier,
public RemovalClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils, int sensorId,
- @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- statsModality, BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
+ //, BiometricsProtoEnums.ACTION_REMOVE,
+ // BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index 7d8386337ece..88f4da261d62 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -18,20 +18,21 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.function.Supplier;
public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> {
public RevokeChallengeClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
- @NonNull IBinder token, int userId, @NonNull String owner, int sensorId) {
+ @NonNull IBinder token, int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, biometricLogger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java
index 1bc3248cd0e7..21c9f64eea5b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java
@@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.function.Supplier;
@@ -47,10 +48,10 @@ public abstract class StartUserClient<T, U> extends HalClientMonitor<T> {
public StartUserClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull UserStartedCallback<U> callback) {
super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(),
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mUserStartedCallback = callback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
index 3eafbb8ea9d7..e8654dc059a4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
@@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.function.Supplier;
@@ -47,10 +48,10 @@ public abstract class StopUserClient<T> extends HalClientMonitor<T> {
public StopUserClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull UserStoppedCallback callback) {
super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(),
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mUserStoppedCallback = callback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
index 006667ac659f..29eee6b5bb06 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
@@ -16,11 +16,11 @@
package com.android.server.biometrics.sensors.face.aidl;
+import static com.android.server.biometrics.sensors.face.aidl.Sensor.HalSessionCallback;
+
import android.annotation.NonNull;
import android.hardware.biometrics.face.ISession;
-import static com.android.server.biometrics.sensors.face.aidl.Sensor.HalSessionCallback;
-
/**
* A holder for an AIDL {@link ISession} with additional metadata about the current user
* and the backend.
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index c4e050215134..75a1e0cf21a8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -25,7 +25,6 @@ import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
@@ -37,7 +36,10 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -76,19 +78,36 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
- boolean isStrongBiometric, int statsClient, @NonNull UsageStats usageStats,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, @NonNull UsageStats usageStats,
@NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
boolean isKeyguardBypassEnabled) {
+ this(context, lazyDaemon, token, requestId, listener, targetUserId, operationId,
+ restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ isStrongBiometric, usageStats, lockoutCache, allowBackgroundAuthentication,
+ isKeyguardBypassEnabled, context.getSystemService(SensorPrivacyManager.class));
+ }
+
+ @VisibleForTesting
+ FaceAuthenticationClient(@NonNull Context context,
+ @NonNull Supplier<AidlSession> lazyDaemon,
+ @NonNull IBinder token, long requestId,
+ @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
+ boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, @NonNull UsageStats usageStats,
+ @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
+ boolean isKeyguardBypassEnabled, SensorPrivacyManager sensorPrivacyManager) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
- owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
- lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */,
+ owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ isStrongBiometric, null /* taskStackListener */, lockoutCache,
+ allowBackgroundAuthentication, true /* shouldVibrate */,
isKeyguardBypassEnabled);
setRequestId(requestId);
mUsageStats = usageStats;
mLockoutCache = lockoutCache;
mNotificationManager = context.getSystemService(NotificationManager.class);
- mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
+ mSensorPrivacyManager = sensorPrivacyManager;
final Resources resources = getContext().getResources();
mBiometricPromptIgnoreList = resources.getIntArray(
@@ -139,10 +158,10 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
if (session.hasContextMethods()) {
final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
+ // TODO: add reason, id
context.id = 0;
context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
+ context.isAoD = getBiometricContext().isAoD();
context.isCrypto = isCryptoOperation();
return session.getSession().authenticateWithContext(mOperationId, context);
} else {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 3f3db4342a7a..c79e60124c45 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
@@ -29,7 +28,10 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -52,13 +54,26 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements
FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId, boolean isStrongBiometric, int statsClient) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric) {
+ this(context, lazyDaemon, token, requestId, listener, userId, owner, sensorId,
+ logger, biometricContext, isStrongBiometric,
+ context.getSystemService(SensorPrivacyManager.class));
+ }
+
+ @VisibleForTesting
+ FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ @NonNull IBinder token, long requestId,
+ @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, SensorPrivacyManager sensorPrivacyManager) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FACE,
- BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ true /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
- mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
+ mSensorPrivacyManager = sensorPrivacyManager;
}
@Override
@@ -102,10 +117,10 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements
if (session.hasContextMethods()) {
final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
+ // TODO: add reason, id
context.id = 0;
context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
+ context.isAoD = getBiometricContext().isAoD();
context.isCrypto = isCryptoOperation();
return session.getSession().detectInteractionWithContext(context);
} else {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 8dc53b6346a4..6f6dadc8a041 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
@@ -40,6 +39,8 @@ import android.view.Surface;
import com.android.internal.R;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
@@ -89,11 +90,11 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
- @Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser,
- boolean debugConsent) {
+ @Nullable Surface previewSurface, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int maxTemplatesPerUser, boolean debugConsent) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
- timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
- false /* shouldVibrate */);
+ timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
@@ -200,10 +201,10 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
if (session.hasContextMethods()) {
final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
+ // TODO: add reason, id
context.id = 0;
context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
+ context.isAoD = getBiometricContext().isAoD();
context.isCrypto = isCryptoOperation();
return session.getSession().enrollWithContext(
hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, context);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index bdad268b9422..165c3a241043 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -37,8 +39,10 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<AidlSes
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger,
+ biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index 2f3187bb4fa0..1f4f6127dafd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -38,10 +39,10 @@ class FaceGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> {
FaceGetAuthenticatorIdClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
int userId, @NonNull String opPackageName, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, opPackageName,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FACE,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mAuthenticatorIds = authenticatorIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
index 79479bebc759..ef3b345402bf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.os.IBinder;
import android.os.RemoteException;
@@ -28,6 +27,8 @@ import android.provider.Settings;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
@@ -48,10 +49,10 @@ public class FaceGetFeatureClient extends HalClientMonitor<AidlSession> implemen
FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId) {
+ @NonNull String owner, int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mUserId = userId;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index a2b0339b282f..54f2033b363a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.face.Face;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalCleanupClient;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -39,29 +40,32 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession>
FaceInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils,
- @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList,
+ @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<AidlSession> getEnumerateClient(Context context,
Supplier<AidlSession> lazyDaemon, IBinder token, int userId, String owner,
- List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) {
+ List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId);
+ enrolledList, utils, sensorId, logger, biometricContext);
}
@Override
protected RemovalClient<Face, AidlSession> getRemovalClient(Context context,
Supplier<AidlSession> lazyDaemon, IBinder token,
int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
return new FaceRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
- utils, sensorId, authenticatorIds);
+ utils, sensorId, logger, biometricContext, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
index 88c9d3bd1035..d85455e0e76b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -40,9 +41,10 @@ class FaceInternalEnumerateClient extends InternalEnumerateClient<AidlSession> {
FaceInternalEnumerateClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, int userId,
@NonNull String owner, @NonNull List<Face> enrolledList,
- @NonNull BiometricUtils<Face> utils, int sensorId) {
+ @NonNull BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FACE);
+ logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
index 04ea2cfc6eff..39d8de07f652 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
@@ -23,6 +23,8 @@ import android.hardware.face.Face;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.InvalidationClient;
import java.util.Map;
@@ -33,8 +35,10 @@ public class FaceInvalidationClient extends InvalidationClient<Face, AidlSession
public FaceInvalidationClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds, @NonNull IInvalidationCallback callback) {
- super(context, lazyDaemon, userId, sensorId, authenticatorIds, callback);
+ super(context, lazyDaemon, userId, sensorId, logger, biometricContext,
+ authenticatorIds, callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 9d7a5529f473..64b0892e92bb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -23,6 +23,7 @@ import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
@@ -47,6 +48,8 @@ import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -237,6 +240,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceGetAuthenticatorIdClient client = new FaceGetAuthenticatorIdClient(
mContext, mSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
@@ -247,6 +253,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final InvalidationRequesterClient<Face> client =
new InvalidationRequesterClient<>(mContext, userId, sensorId,
+ BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
FaceUtils.getInstance(sensorId));
scheduleForSensor(sensorId, client);
});
@@ -285,6 +292,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final FaceInvalidationClient client = new FaceInvalidationClient(mContext,
mSensors.get(sensorId).getLazySession(), userId, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
mSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);
});
@@ -311,7 +321,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId);
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance());
scheduleForSensor(sensorId, client);
});
}
@@ -322,7 +335,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token, userId, opPackageName, sensorId,
- challenge);
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), challenge);
scheduleForSensor(sensorId, client);
});
}
@@ -340,8 +355,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
- ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
- debugConsent);
+ ENROLL_TIMEOUT_SEC, previewSurface, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), maxTemplatesPerUser, debugConsent);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -372,8 +389,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FaceDetectClient client = new FaceDetectClient(mContext,
mSensors.get(sensorId).getLazySession(),
- token, id, callback, userId, opPackageName,
- sensorId, isStrongBiometric, statsClient);
+ token, id, callback, userId, opPackageName, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ BiometricContext.getInstance(), isStrongBiometric);
scheduleForSensor(sensorId, client);
});
@@ -396,7 +414,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceAuthenticationClient client = new FaceAuthenticationClient(
mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
userId, operationId, restricted, opPackageName, cookie,
- false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
+ false /* requireConfirmation */, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ BiometricContext.getInstance(), isStrongBiometric,
mUsageStats, mSensors.get(sensorId).getLockoutCache(),
allowBackgroundAuthentication, isKeyguardBypassEnabled);
scheduleForSensor(sensorId, client);
@@ -450,6 +470,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), faceIds, userId,
opPackageName, FaceUtils.getInstance(sensorId), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
});
@@ -460,7 +483,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final FaceResetLockoutClient client = new FaceResetLockoutClient(
mContext, mSensors.get(sensorId).getLazySession(), userId,
- mContext.getOpPackageName(), sensorId, hardwareAuthToken,
+ mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), hardwareAuthToken,
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
scheduleForSensor(sensorId, client);
@@ -481,7 +507,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId,
- mContext.getOpPackageName(), sensorId, feature, enabled, hardwareAuthToken);
+ mContext.getOpPackageName(), sensorId,
+ BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ feature, enabled, hardwareAuthToken);
scheduleForSensor(sensorId, client);
});
}
@@ -498,7 +526,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext,
mSensors.get(sensorId).getLazySession(), token, callback, userId,
- mContext.getOpPackageName(), sensorId);
+ mContext.getOpPackageName(), sensorId, BiometricLogger.ofUnknown(mContext),
+ BiometricContext.getInstance());
scheduleForSensor(sensorId, client);
});
}
@@ -518,13 +547,21 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceInternalCleanupClient client =
new FaceInternalCleanupClient(mContext,
mSensors.get(sensorId).getLazySession(), userId,
- mContext.getOpPackageName(), sensorId, enrolledList,
+ mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), enrolledList,
FaceUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, callback);
});
}
+ private BiometricLogger createLogger(int statsAction, int statsClient) {
+ return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE,
+ statsAction, statsClient);
+ }
+
@Override
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
boolean clearSchedulerBuffer) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
index 130a05a861d9..0512017394af 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -44,9 +45,10 @@ class FaceRemovalClient extends RemovalClient<Face, AidlSession> {
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int[] biometricIds, int userId, @NonNull String owner,
@NonNull BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
+ logger, biometricContext, authenticatorIds);
mBiometricIds = biometricIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 67bf3f5b2e4f..de0a36a32b66 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
@@ -26,6 +25,8 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -50,11 +51,11 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
FaceResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
index acd2e0589ccc..8838345de4d6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.RevokeChallengeClient;
import java.util.function.Supplier;
@@ -38,8 +40,10 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<AidlSession
FaceRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId, long challenge) {
- super(context, lazyDaemon, token, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ long challenge) {
+ super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
mChallenge = challenge;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
index 9d535a26e12d..6c143872ff8c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.IBinder;
@@ -27,6 +26,8 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
@@ -47,11 +48,11 @@ public class FaceSetFeatureClient extends HalClientMonitor<AidlSession> implemen
FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId, int feature, boolean enabled,
- byte[] hardwareAuthToken) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int feature, boolean enabled, byte[] hardwareAuthToken) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mFeature = feature;
mEnabled = enabled;
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
index f5a98ff5881b..61e7ab781e66 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
@@ -27,6 +27,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StartUserClient;
@@ -40,9 +42,10 @@ public class FaceStartUserClient extends StartUserClient<IFace, ISession> {
public FaceStartUserClient(@NonNull Context context,
@NonNull Supplier<IFace> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull ISessionCallback sessionCallback,
@NonNull UserStartedCallback<ISession> callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
mSessionCallback = sessionCallback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
index 48b4856fa4b6..0110ae991ae4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StopUserClient;
@@ -33,8 +35,9 @@ public class FaceStopUserClient extends StopUserClient<AidlSession> {
public FaceStopUserClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull UserStoppedCallback callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 33e6fa4ebf93..fa07d120eb71 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -42,12 +42,15 @@ import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -88,7 +91,8 @@ public class Sensor {
@NonNull private final Supplier<AidlSession> mLazySession;
@Nullable private AidlSession mCurrentSession;
- static class HalSessionCallback extends ISessionCallback.Stub {
+ @VisibleForTesting
+ public static class HalSessionCallback extends ISessionCallback.Stub {
/**
* Interface to sends results to the HalSessionCallback's owner.
*/
@@ -487,7 +491,9 @@ public class Sensor {
@Override
public StopUserClient<?> getStopUserClient(int userId) {
return new FaceStopUserClient(mContext, mLazySession, mToken, userId,
- mSensorProperties.sensorId, () -> mCurrentSession = null);
+ mSensorProperties.sensorId,
+ BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ () -> mCurrentSession = null);
}
@NonNull
@@ -523,6 +529,7 @@ public class Sensor {
return new FaceStartUserClient(mContext, provider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
+ BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
resultController, userStartedCallback);
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 586abe2d6298..be1ed7d92aeb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -55,6 +55,8 @@ import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
@@ -533,7 +535,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId, sSystemClock.millis());
+ opPackageName, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), sSystemClock.millis());
mGeneratedChallengeCache = client;
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -562,7 +567,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mGeneratedChallengeCache = null;
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
- mLazyDaemon, token, userId, opPackageName, mSensorId);
+ mLazyDaemon, token, userId, opPackageName, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance());
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -590,7 +598,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
- ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
+ ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance());
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -637,7 +648,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
mLazyDaemon, token, requestId, receiver, userId, operationId, restricted,
opPackageName, cookie, false /* requireConfirmation */, mSensorId,
- isStrongBiometric, statsClient, mLockoutTracker, mUsageStats,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ BiometricContext.getInstance(), isStrongBiometric, mLockoutTracker, mUsageStats,
allowBackgroundAuthentication, isKeyguardBypassEnabled);
mScheduler.scheduleClientMonitor(client);
});
@@ -670,7 +682,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
- FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds);
+ FaceUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -685,7 +700,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), 0 /* faceId */, userId,
opPackageName,
- FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds);
+ FaceUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -702,7 +720,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
- hardwareAuthToken);
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), hardwareAuthToken);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -723,7 +743,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final int faceId = faces.get(0).getBiometricId();
final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId, feature, enabled, hardwareAuthToken, faceId);
+ opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext),
+ BiometricContext.getInstance(), feature, enabled, hardwareAuthToken, faceId);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -742,7 +763,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final int faceId = faces.get(0).getBiometricId();
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
- token, listener, userId, opPackageName, mSensorId, feature, faceId);
+ token, listener, userId, opPackageName, mSensorId,
+ BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ feature, faceId);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(
@@ -767,7 +790,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final List<Face> enrolledList = getEnrolledFaces(mSensorId, userId);
final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
- mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, enrolledList,
+ mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), enrolledList,
FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
});
@@ -890,7 +916,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final boolean hasEnrolled = !getEnrolledFaces(mSensorId, targetUserId).isEmpty();
final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
- hasEnrolled, mAuthenticatorIds);
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), hasEnrolled, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -904,6 +932,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
});
}
+ private BiometricLogger createLogger(int statsAction, int statsClient) {
+ return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE,
+ statsAction, statsClient);
+ }
+
/**
* Sends a debug message to the HAL with the provided FileDescriptor and arguments.
*/
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 9038435c1021..8d76e9f031f7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -23,7 +23,6 @@ import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.FaceManager;
import android.os.IBinder;
@@ -32,6 +31,8 @@ import android.util.Slog;
import com.android.internal.R;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -66,12 +67,13 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
- boolean isStrongBiometric, int statsClient, @NonNull LockoutTracker lockoutTracker,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, @NonNull LockoutTracker lockoutTracker,
@NonNull UsageStats usageStats, boolean allowBackgroundAuthentication,
boolean isKeyguardBypassEnabled) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
- owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
+ owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ isStrongBiometric, null /* taskStackListener */,
lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
isKeyguardBypassEnabled);
setRequestId(requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 92f7253779ed..226e458ad07b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.Face;
@@ -32,6 +31,8 @@ import android.view.Surface;
import com.android.internal.R;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -58,10 +59,10 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
- @Nullable Surface previewSurface, int sensorId) {
+ @Nullable Surface previewSurface, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
- false /* shouldVibrate */);
+ timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mEnrollIgnoreList = getContext().getResources()
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index b66ad608b4ca..97838a70cbd1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -25,6 +25,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.util.Preconditions;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -51,8 +53,10 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId, long now) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, long now) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger,
+ biometricContext);
mCreatedAt = now;
mWaiting = new ArrayList<>();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
index 1b387bf7879a..981253699322 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
@@ -19,7 +19,6 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.OptionalBool;
import android.hardware.biometrics.face.V1_0.Status;
@@ -28,6 +27,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -48,13 +49,13 @@ public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> {
FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId, int feature, int faceId) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int feature, int faceId) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mFeature = feature;
mFaceId = faceId;
-
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
index 93a2913fbfa1..d21a7501e516 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.Face;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalCleanupClient;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -40,29 +41,32 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsF
FaceInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils,
- @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList,
+ @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
Supplier<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
- List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) {
+ List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId);
+ enrolledList, utils, sensorId, logger, biometricContext);
}
@Override
protected RemovalClient<Face, IBiometricsFace> getRemovalClient(Context context,
Supplier<IBiometricsFace> lazyDaemon, IBinder token,
int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
return new FaceRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
- sensorId, authenticatorIds);
+ sensorId, logger, biometricContext, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
index f1788de38565..250dd7e0cdef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -41,9 +42,10 @@ class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFac
FaceInternalEnumerateClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
@NonNull String owner, @NonNull List<Face> enrolledList,
- @NonNull BiometricUtils<Face> utils, int sensorId) {
+ @NonNull BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FACE);
+ logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
index cbc23e49f4d8..0ee7a354d5a4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -44,9 +45,11 @@ class FaceRemovalClient extends RemovalClient<Face, IBiometricsFace> {
FaceRemovalClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils,
- int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, logger,
+ biometricContext, authenticatorIds);
mBiometricId = biometricId;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
index 88e2318b0570..6e74d3622c1a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
@@ -18,12 +18,13 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -42,10 +43,10 @@ public class FaceResetLockoutClient extends HalClientMonitor<IBiometricsFace> {
FaceResetLockoutClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull byte[] hardwareAuthToken) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mHardwareAuthToken = new ArrayList<>();
for (byte b : hardwareAuthToken) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
index ab8d16145fab..b7b0dc046633 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.RevokeChallengeClient;
import java.util.function.Supplier;
@@ -37,8 +39,9 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<IBiometrics
FaceRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
index b2b52e713ec9..3c82f9c73700 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.Status;
import android.os.IBinder;
@@ -26,6 +25,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -48,11 +49,11 @@ public class FaceSetFeatureClient extends HalClientMonitor<IBiometricsFace> {
FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId, int feature, boolean enabled,
- byte[] hardwareAuthToken, int faceId) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int feature, boolean enabled, byte[] hardwareAuthToken, int faceId) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mFeature = feature;
mEnabled = enabled;
mFaceId = faceId;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 04b93274e42b..8385c3fa7103 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.os.Environment;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -41,11 +42,11 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace
FaceUpdateActiveUserClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, boolean hasEnrolledBiometrics,
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, boolean hasEnrolledBiometrics,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mHasEnrolledBiometrics = hasEnrolledBiometrics;
mAuthenticatorIds = authenticatorIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
index 727101a69b06..55861bb4cc6f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
@@ -16,11 +16,11 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static com.android.server.biometrics.sensors.fingerprint.aidl.Sensor.HalSessionCallback;
+
import android.annotation.NonNull;
import android.hardware.biometrics.fingerprint.ISession;
-import static com.android.server.biometrics.sensors.fingerprint.aidl.Sensor.HalSessionCallback;
-
/**
* A holder for an AIDL {@link ISession} with additional metadata about the current user
* and the backend.
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 2c1c80ccabb3..184e1ea7d003 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -23,9 +23,7 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -35,6 +33,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthenticationClient;
@@ -72,15 +72,18 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
- int sensorId, boolean isStrongBiometric, int statsClient,
+ int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric,
@Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
- cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
+ cookie, requireConfirmation, sensorId,
+ biometricLogger, biometricContext,
+ isStrongBiometric, taskStackListener,
lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */,
false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
@@ -175,13 +178,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
- context.isCrypto = isCryptoOperation();
- return session.getSession().authenticateWithContext(mOperationId, context);
+ // TODO: add reason, id
+ mOperationContext.id = 0;
+ mOperationContext.reason = OperationReason.UNKNOWN;
+ mOperationContext.isAoD = getBiometricContext().isAoD();
+ mOperationContext.isCrypto = isCryptoOperation();
+ return session.getSession().authenticateWithContext(mOperationId, mOperationContext);
} else {
return session.getSession().authenticate(mOperationId);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 6645332c1fac..9d348e118b46 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricOverlayConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
@@ -30,6 +29,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -54,11 +55,10 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId,
- @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric,
- int statsClient) {
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ true /* shouldVibrate */, biometricLogger, biometricContext);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/);
@@ -100,10 +100,10 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements
if (session.hasContextMethods()) {
final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
+ // TODO: add reason, id
context.id = 0;
context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
+ context.isAoD = getBiometricContext().isAoD();
context.isCrypto = isCryptoOperation();
return session.getSession().detectInteractionWithContext(context);
} else {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index d0c5bb8851e6..ed16a6dd3b5c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
@@ -38,6 +37,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -68,14 +69,15 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
// UDFPS haptics occur when an image is acquired (instead of when the result is known)
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
- !sensorProps.isAnyUdfpsType() /* shouldVibrate */);
+ 0 /* timeoutSec */, sensorId,
+ !sensorProps.isAnyUdfpsType() /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mSensorProps = sensorProps;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
@@ -177,10 +179,10 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
if (session.hasContextMethods()) {
final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
+ // TODO: add reason, id
context.id = 0;
context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
+ context.isAoD = getBiometricContext().isAoD();
context.isCrypto = isCryptoOperation();
return session.getSession().enrollWithContext(hat, context);
} else {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 04a7ca086ced..ddae8bedf7c1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -38,8 +40,10 @@ class FingerprintGenerateChallengeClient extends GenerateChallengeClient<AidlSes
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener,
- int userId, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId,
+ biometricLogger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index 3a487fc98ca4..ea1a622c36ab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -36,11 +37,12 @@ class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSession>
private final Map<Integer, Long> mAuthenticatorIds;
FingerprintGetAuthenticatorIdClient(@NonNull Context context,
- @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, Map<Integer, Long> authenticatorIds) {
+ @NonNull Supplier<AidlSession> lazyDaemon,
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, biometricLogger, biometricContext);
mAuthenticatorIds = authenticatorIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index 0ecad725cbeb..09bdd6de49f0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -22,6 +22,8 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalCleanupClient;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -39,28 +41,35 @@ import java.util.function.Supplier;
class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint, AidlSession> {
FingerprintInternalCleanupClient(@NonNull Context context,
- @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, @NonNull List<Fingerprint> enrolledList,
+ @NonNull Supplier<AidlSession> lazyDaemon,
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull List<Fingerprint> enrolledList,
@NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId,
- BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
+ super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
+ enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<AidlSession> getEnumerateClient(Context context,
Supplier<AidlSession> lazyDaemon, IBinder token, int userId, String owner,
- List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId) {
+ List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId);
+ enrolledList, utils, sensorId,
+ logger.swapAction(context, BiometricsProtoEnums.ACTION_ENUMERATE),
+ biometricContext);
}
@Override
protected RemovalClient<Fingerprint, AidlSession> getRemovalClient(Context context,
Supplier<AidlSession> lazyDaemon, IBinder token, int biometricId, int userId,
String owner, BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
Map<Integer, Long> authenticatorIds) {
return new FingerprintRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
- utils, sensorId, authenticatorIds);
+ utils, sensorId, logger.swapAction(context, BiometricsProtoEnums.ACTION_REMOVE),
+ biometricContext, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
index 06ba6d45b407..a5a832aaaf59 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
@@ -18,12 +18,13 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -40,9 +41,10 @@ class FingerprintInternalEnumerateClient extends InternalEnumerateClient<AidlSes
protected FingerprintInternalEnumerateClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, int userId,
@NonNull String owner, @NonNull List<Fingerprint> enrolledList,
- @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
+ @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
index 1ee32e986ca6..bc02897ef5c4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
@@ -23,6 +23,8 @@ import android.hardware.fingerprint.Fingerprint;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.InvalidationClient;
import java.util.Map;
@@ -33,8 +35,10 @@ public class FingerprintInvalidationClient extends InvalidationClient<Fingerprin
public FingerprintInvalidationClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds, @NonNull IInvalidationCallback callback) {
- super(context, lazyDaemon, userId, sensorId, authenticatorIds, callback);
+ super(context, lazyDaemon, userId, sensorId, logger, biometricContext,
+ authenticatorIds, callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index efc93045f957..221ff94141e2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -26,6 +26,7 @@ import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.TypedArray;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
@@ -53,6 +54,8 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -298,6 +301,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
new FingerprintGetAuthenticatorIdClient(mContext,
mSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
});
@@ -307,6 +313,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mHandler.post(() -> {
final InvalidationRequesterClient<Fingerprint> client =
new InvalidationRequesterClient<>(mContext, userId, sensorId,
+ BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
FingerprintUtils.getInstance(sensorId));
scheduleForSensor(sensorId, client);
});
@@ -317,7 +324,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mHandler.post(() -> {
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(
mContext, mSensors.get(sensorId).getLazySession(), userId,
- mContext.getOpPackageName(), sensorId, hardwareAuthToken,
+ mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), hardwareAuthToken,
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
scheduleForSensor(sensorId, client);
});
@@ -331,7 +341,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
new FingerprintGenerateChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- sensorId);
+ sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance());
scheduleForSensor(sensorId, client);
});
}
@@ -343,7 +355,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintRevokeChallengeClient client =
new FingerprintRevokeChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- userId, opPackageName, sensorId, challenge);
+ userId, opPackageName, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), challenge);
scheduleForSensor(sensorId, client);
});
}
@@ -361,6 +376,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mSensors.get(sensorId).getLazySession(), token, id,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
mSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController, maxTemplatesPerUser, enrollReason);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@@ -399,8 +417,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mSensors.get(sensorId).getLazySession(), token, id, callback, userId,
- opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric,
- statsClient);
+ opPackageName, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ BiometricContext.getInstance(),
+ mUdfpsOverlayController, isStrongBiometric);
scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
@@ -417,7 +437,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
userId, operationId, restricted, opPackageName, cookie,
- false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
+ false /* requireConfirmation */, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ BiometricContext.getInstance(), isStrongBiometric,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication,
mSensors.get(sensorId).getSensorProperties());
@@ -479,6 +501,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
@@ -492,14 +517,22 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintInternalCleanupClient client =
new FingerprintInternalCleanupClient(mContext,
mSensors.get(sensorId).getLazySession(), userId,
- mContext.getOpPackageName(), sensorId, enrolledList,
- FingerprintUtils.getInstance(sensorId),
+ mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
+ enrolledList, FingerprintUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(callback,
mFingerprintStateCallback));
});
}
+ private BiometricLogger createLogger(int statsAction, int statsClient) {
+ return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ statsAction, statsClient);
+ }
+
@Override
public boolean isHardwareDetected(int sensorId) {
return hasHalInstance();
@@ -524,6 +557,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintInvalidationClient client =
new FingerprintInvalidationClient(mContext,
mSensors.get(sensorId).getLazySession(), userId, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
mSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
index fbc1dc08b726..d559bb1d72f1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -19,12 +19,13 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -45,9 +46,10 @@ class FingerprintRemovalClient extends RemovalClient<Fingerprint, AidlSession> {
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
@Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ logger, biometricContext, authenticatorIds);
mBiometricIds = biometricIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 0e64dab5d325..f90cba79dac2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
@@ -26,6 +25,8 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -50,11 +51,11 @@ class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implem
FingerprintResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
@NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, biometricLogger, biometricContext);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
index fd938677105c..afa62e2e3173 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.RevokeChallengeClient;
import java.util.function.Supplier;
@@ -38,8 +40,10 @@ class FingerprintRevokeChallengeClient extends RevokeChallengeClient<AidlSession
FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId, long challenge) {
- super(context, lazyDaemon, token, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ long challenge) {
+ super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
mChallenge = challenge;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
index 9dc06e1e0665..52305a31a644 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
@@ -27,6 +27,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StartUserClient;
@@ -40,9 +42,10 @@ public class FingerprintStartUserClient extends StartUserClient<IFingerprint, IS
public FingerprintStartUserClient(@NonNull Context context,
@NonNull Supplier<IFingerprint> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull ISessionCallback sessionCallback,
@NonNull UserStartedCallback<ISession> callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
mSessionCallback = sessionCallback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
index fac17f2cc16a..2cc1879c0851 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StopUserClient;
@@ -33,8 +35,10 @@ public class FingerprintStopUserClient extends StopUserClient<AidlSession> {
public FingerprintStopUserClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @Nullable IBinder token, int userId,
- int sensorId, @NonNull UserStoppedCallback callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull UserStoppedCallback callback) {
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 22762329926b..27226b30f548 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -39,12 +39,15 @@ import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
@@ -72,7 +75,7 @@ import java.util.function.Supplier;
* {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
*/
@SuppressWarnings("deprecation")
-class Sensor {
+public class Sensor {
private boolean mTestHalEnabled;
@@ -89,7 +92,8 @@ class Sensor {
@Nullable private AidlSession mCurrentSession;
@NonNull private final Supplier<AidlSession> mLazySession;
- static class HalSessionCallback extends ISessionCallback.Stub {
+ @VisibleForTesting
+ public static class HalSessionCallback extends ISessionCallback.Stub {
/**
* Interface to sends results to the HalSessionCallback's owner.
@@ -442,7 +446,9 @@ class Sensor {
@Override
public StopUserClient<?> getStopUserClient(int userId) {
return new FingerprintStopUserClient(mContext, mLazySession, mToken,
- userId, mSensorProperties.sensorId, () -> mCurrentSession = null);
+ userId, mSensorProperties.sensorId,
+ BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ () -> mCurrentSession = null);
}
@NonNull
@@ -478,6 +484,7 @@ class Sensor {
return new FingerprintStartUserClient(mContext, provider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
+ BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
resultController, userStartedCallback);
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 29d460fc1204..6c35c8c91448 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -57,6 +57,8 @@ import com.android.server.biometrics.Utils;
import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
@@ -493,6 +495,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintUpdateActiveUserClient client =
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -536,7 +541,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
// thread.
mHandler.post(() -> {
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
- userId, mContext.getOpPackageName(), sensorId, mLockoutTracker);
+ userId, mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), mLockoutTracker);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -548,7 +556,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintGenerateChallengeClient client =
new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- mSensorProperties.sensorId);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance());
mScheduler.scheduleClientMonitor(client);
});
}
@@ -559,7 +570,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
mContext, mLazyDaemon, token, userId, opPackageName,
- mSensorProperties.sensorId);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance());
mScheduler.scheduleClientMonitor(client);
});
}
@@ -577,7 +591,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
userId, hardwareAuthToken, opPackageName,
FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
- mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(),
+ mUdfpsOverlayController, mSidefpsController,
enrollReason);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -616,8 +634,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mLazyDaemon, token, id, listener, userId, opPackageName,
- mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric,
- statsClient);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ BiometricContext.getInstance(), mUdfpsOverlayController, isStrongBiometric);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
@@ -636,7 +655,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
restricted, opPackageName, cookie, false /* requireConfirmation */,
- mSensorProperties.sensorId, isStrongBiometric, statsClient,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ BiometricContext.getInstance(), isStrongBiometric,
mTaskStackListener, mLockoutTracker,
mUdfpsOverlayController, mSidefpsController,
allowBackgroundAuthentication, mSensorProperties);
@@ -678,7 +699,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId, mAuthenticatorIds);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@@ -695,7 +719,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
0 /* fingerprintId */, userId, opPackageName,
FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId, mAuthenticatorIds);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@@ -709,7 +736,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mSensorProperties.sensorId, userId);
final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
- mSensorProperties.sensorId, enrolledList,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricContext.getInstance(), enrolledList,
FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
});
@@ -722,6 +752,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mFingerprintStateCallback));
}
+ private BiometricLogger createLogger(int statsAction, int statsClient) {
+ return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ statsAction, statsClient);
+ }
+
@Override
public boolean isHardwareDetected(int sensorId) {
return getDaemon() != null;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 589bfcfba992..97fbb5f6e4fe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -23,7 +23,6 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -32,6 +31,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthenticationClient;
@@ -69,7 +70,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
- int sensorId, boolean isStrongBiometric, int statsClient,
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, boolean isStrongBiometric,
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutFrameworkImpl lockoutTracker,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@@ -77,10 +79,9 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
- owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
- lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
- false /* isKeyguardBypassEnabled */);
+ owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication,
+ true /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutFrameworkImpl = lockoutTracker;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 88487462e51a..f10d4e409b0e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricOverlayConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
@@ -30,6 +29,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -59,11 +60,11 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController,
- boolean isStrongBiometric, int statsClient) {
+ int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ true /* shouldVibrate */, biometricLogger, biometricContext);
setRequestId(requestId);
mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
mIsStrongBiometric = isStrongBiometric;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index c69deac371d9..1d478e53fd38 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
@@ -31,6 +30,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -62,12 +63,13 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
@FingerprintManager.EnrollReason int enrollReason) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
- true /* shouldVibrate */);
+ timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger,
+ biometricContext);
setRequestId(requestId);
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
index 591f542396ef..3bb7135a3e06 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -41,8 +43,10 @@ public class FingerprintGenerateChallengeClient
FingerprintGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger,
+ biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
index 403602b8839d..5e7cf3578411 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalCleanupClient;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -42,31 +43,35 @@ class FingerprintInternalCleanupClient
FingerprintInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
- @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList,
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull List<Fingerprint> enrolledList,
@NonNull BiometricUtils<Fingerprint> utils,
@NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId,
- BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
+ super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
+ enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
Context context, Supplier<IBiometricsFingerprint> lazyDaemon, IBinder token,
int userId, String owner, List<Fingerprint> enrolledList,
- BiometricUtils<Fingerprint> utils, int sensorId) {
+ BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId);
+ enrolledList, utils, sensorId, logger, biometricContext);
}
@Override
protected RemovalClient<Fingerprint, IBiometricsFingerprint> getRemovalClient(Context context,
Supplier<IBiometricsFingerprint> lazyDaemon, IBinder token,
int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils,
- int sensorId, Map<Integer, Long> authenticatorIds) {
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
return new FingerprintRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
- sensorId, authenticatorIds);
+ sensorId, logger, biometricContext, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
index def8ed0735f5..0840f1b36903 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -42,9 +43,10 @@ class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiomet
FingerprintInternalEnumerateClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
- @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
+ @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
index 77c201c5ec97..9ec56c2a7dcd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -46,9 +47,10 @@ class FingerprintRemovalClient extends RemovalClient<Fingerprint, IBiometricsFin
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ logger, biometricContext, authenticatorIds);
mBiometricId = biometricId;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
index ed28e3ff481e..559ca0633c42 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
@@ -18,9 +18,10 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -33,10 +34,11 @@ public class FingerprintResetLockoutClient extends BaseClientMonitor {
@NonNull final LockoutFrameworkImpl mLockoutTracker;
public FingerprintResetLockoutClient(@NonNull Context context, int userId,
- @NonNull String owner, int sensorId, @NonNull LockoutFrameworkImpl lockoutTracker) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull LockoutFrameworkImpl lockoutTracker) {
super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */,
- sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ sensorId, logger, biometricContext);
mLockoutTracker = lockoutTracker;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
index 0180a466d7d6..6273417eb0db 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.RevokeChallengeClient;
import java.util.function.Supplier;
@@ -39,8 +41,9 @@ public class FingerprintRevokeChallengeClient
FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index cb9c33eb9956..a4e602553101 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.os.Build;
import android.os.Environment;
@@ -27,6 +26,8 @@ import android.os.SELinux;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -50,12 +51,13 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
FingerprintUpdateActiveUserClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
- @NonNull String owner, int sensorId, Supplier<Integer> currentUserId,
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ Supplier<Integer> currentUserId,
boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
boolean forceUpdateAuthenticatorId) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mCurrentUserId = currentUserId;
mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId;
mHasEnrolledBiometrics = hasEnrolledBiometrics;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7ad497972462..4e88acd11fac 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -141,7 +141,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -1756,10 +1755,6 @@ public final class DisplayManagerService extends SystemService {
void setUserPreferredDisplayModeInternal(int displayId, Display.Mode mode) {
synchronized (mSyncRoot) {
- if (Objects.equals(mUserPreferredMode, mode) && displayId == Display.INVALID_DISPLAY) {
- return;
- }
-
if (mode != null && !isResolutionAndRefreshRateValid(mode)
&& displayId == Display.INVALID_DISPLAY) {
throw new IllegalArgumentException("width, height and refresh rate of mode should "
@@ -1813,7 +1808,15 @@ public final class DisplayManagerService extends SystemService {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, resolutionWidth);
mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
- device.setUserPreferredDisplayModeLocked(mode);
+ // If there is a display specific mode, don't override that
+ final Point deviceUserPreferredResolution =
+ mPersistentDataStore.getUserPreferredResolution(device);
+ final float deviceRefreshRate =
+ mPersistentDataStore.getUserPreferredRefreshRate(device);
+ if (!isValidResolution(deviceUserPreferredResolution)
+ && !isValidRefreshRate(deviceRefreshRate)) {
+ device.setUserPreferredDisplayModeLocked(mode);
+ }
});
}
@@ -3533,6 +3536,14 @@ public final class DisplayManagerService extends SystemService {
&& (brightness <= PowerManager.BRIGHTNESS_MAX);
}
+ private static boolean isValidResolution(Point resolution) {
+ return (resolution != null) && (resolution.x > 0) && (resolution.y > 0);
+ }
+
+ private static boolean isValidRefreshRate(float refreshRate) {
+ return !Float.isNaN(refreshRate) && (refreshRate > 0.0f);
+ }
+
private final class LocalService extends DisplayManagerInternal {
@Override
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 534ed5d8491a..23c17f5af10d 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -521,6 +521,10 @@ class HighBrightnessModeController {
} else if (mIsBlockedByLowPowerMode) {
reason = FrameworkStatsLog
.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_BATTERY_SAVE_ON;
+ } else if (mBrightness <= mHbmData.transitionPoint) {
+ // This must be after external thermal check.
+ reason = FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS;
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index de933cc47005..783a88ca29bf 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2285,14 +2285,8 @@ public class InputManagerService extends IInputManager.Stub
nativeNotifyPortAssociationsChanged(mPtr);
}
- /**
- * Add a runtime association between the input device name and the display unique id.
- * @param inputDeviceName The name of the input device.
- * @param displayUniqueId The unique id of the associated display.
- */
@Override // Binder call
- public void addUniqueIdAssociation(@NonNull String inputDeviceName,
- @NonNull String displayUniqueId) {
+ public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
if (!checkCallingPermission(
android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
"addNameAssociation()")) {
@@ -2300,20 +2294,16 @@ public class InputManagerService extends IInputManager.Stub
"Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
}
- Objects.requireNonNull(inputDeviceName);
+ Objects.requireNonNull(inputPort);
Objects.requireNonNull(displayUniqueId);
synchronized (mAssociationsLock) {
- mUniqueIdAssociations.put(inputDeviceName, displayUniqueId);
+ mUniqueIdAssociations.put(inputPort, displayUniqueId);
}
nativeChangeUniqueIdAssociation(mPtr);
}
- /**
- * Remove the runtime association between the input device and the display.
- * @param inputDeviceName The port of the input device to be cleared.
- */
@Override // Binder call
- public void removeUniqueIdAssociation(@NonNull String inputDeviceName) {
+ public void removeUniqueIdAssociation(@NonNull String inputPort) {
if (!checkCallingPermission(
android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
"removeUniqueIdAssociation()")) {
@@ -2321,9 +2311,9 @@ public class InputManagerService extends IInputManager.Stub
"Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
}
- Objects.requireNonNull(inputDeviceName);
+ Objects.requireNonNull(inputPort);
synchronized (mAssociationsLock) {
- mUniqueIdAssociations.remove(inputDeviceName);
+ mUniqueIdAssociations.remove(inputPort);
}
nativeChangeUniqueIdAssociation(mPtr);
}
@@ -2594,6 +2584,13 @@ public class InputManagerService extends IInputManager.Stub
pw.println(" display: " + v);
});
}
+ if (!mUniqueIdAssociations.isEmpty()) {
+ pw.println("Unique Id Associations:");
+ mUniqueIdAssociations.forEach((k, v) -> {
+ pw.print(" port: " + k);
+ pw.println(" uniqueId: " + v);
+ });
+ }
}
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index c87ca92d632e..6cb3b3b6740d 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -107,9 +107,11 @@ final class IInputMethodInvoker {
@AnyThread
void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
- int configChanges, boolean stylusHwSupported) {
+ int configChanges, boolean stylusHwSupported,
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
try {
- mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported);
+ mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported,
+ shouldShowImeSwitcherWhenImeIsShown);
} catch (RemoteException e) {
logRemoteException(e);
}
@@ -145,9 +147,20 @@ final class IInputMethodInvoker {
@AnyThread
void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
- boolean restarting) {
+ boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) {
try {
- mTarget.startInput(startInputToken, inputContext, attribute, restarting);
+ mTarget.startInput(startInputToken, inputContext, attribute, restarting,
+ shouldShowImeSwitcherWhenImeIsShown);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown) {
+ try {
+ mTarget.onShouldShowImeSwitcherWhenImeIsShownChanged(
+ shouldShowImeSwitcherWhenImeIsShown);
} catch (RemoteException e) {
logRemoteException(e);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 936b1a20e326..c207738a4f88 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2325,10 +2325,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
true /* direct */);
}
+ final boolean shouldShowImeSwitcherWhenImeIsShown =
+ shouldShowImeSwitcherWhenImeIsShownLocked();
final SessionState session = mCurClient.curSession;
setEnabledSessionLocked(session);
- session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting);
-
+ session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting,
+ shouldShowImeSwitcherWhenImeIsShown);
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2528,7 +2530,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ mCurTokenDisplayId);
}
inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token),
- configChanges, supportStylusHw);
+ configChanges, supportStylusHw, shouldShowImeSwitcherWhenImeIsShownLocked());
}
@AnyThread
@@ -2731,6 +2733,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
+ boolean shouldShowImeSwitcherWhenImeIsShownLocked() {
+ return shouldShowImeSwitcherLocked(
+ InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
+ }
+
+ @GuardedBy("ImfLock.class")
private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
if (mMenuController.getSwitchingDialogLocked() != null) return false;
@@ -2990,6 +2998,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
+ sendShouldShowImeSwitcherWhenImeIsShownLocked();
}
@GuardedBy("ImfLock.class")
@@ -4308,6 +4317,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
updateImeWindowStatus(msg.arg1 == 1);
return true;
}
+
// ---------------------------------------------------------
case MSG_UNBIND_CLIENT:
@@ -4368,6 +4378,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// --------------------------------------------------------------
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1);
+ synchronized (ImfLock.class) {
+ sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ }
return true;
case MSG_SYSTEM_UNLOCK_USER: {
final int userId = msg.arg1;
@@ -4638,6 +4651,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
+ sendShouldShowImeSwitcherWhenImeIsShownLocked();
+
// Notify InputMethodListListeners of the new installed InputMethods.
final List<InputMethodInfo> inputMethodList = new ArrayList<>(mMethodList);
mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED,
@@ -4645,6 +4660,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
+ void sendShouldShowImeSwitcherWhenImeIsShownLocked() {
+ final IInputMethodInvoker curMethod = mBindingController.getCurMethod();
+ if (curMethod == null) {
+ // No need to send the data if the IME is not yet bound.
+ return;
+ }
+ curMethod.onShouldShowImeSwitcherWhenImeIsShownChanged(
+ shouldShowImeSwitcherWhenImeIsShownLocked());
+ }
+
+ @GuardedBy("ImfLock.class")
private void updateDefaultVoiceImeIfNeededLocked() {
final String systemSpeechRecognizer =
mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 348bb2d868a2..98bde11ad517 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -203,6 +203,7 @@ final class InputMethodMenuController {
attrs.setTitle("Select input method");
w.setAttributes(attrs);
mService.updateSystemUiLocked();
+ mService.sendShouldShowImeSwitcherWhenImeIsShownLocked();
mSwitchingDialog.show();
}
}
@@ -238,6 +239,7 @@ final class InputMethodMenuController {
mSwitchingDialogTitleView = null;
mService.updateSystemUiLocked();
+ mService.sendShouldShowImeSwitcherWhenImeIsShownLocked();
mDialogBuilder = null;
mIms = null;
}
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index b65338d9691d..9c85d18515af 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -69,6 +69,14 @@ class LocationShellCommand extends BasicShellCommandHandler {
handleSetAdasGnssLocationEnabled();
return 0;
}
+ case "set-automotive-gnss-suspended": {
+ handleSetAutomotiveGnssSuspended();
+ return 0;
+ }
+ case "is-automotive-gnss-suspended": {
+ handleIsAutomotiveGnssSuspended();
+ return 0;
+ }
case "providers": {
String command = getNextArgRequired();
return parseProvidersCommand(command);
@@ -189,6 +197,24 @@ class LocationShellCommand extends BasicShellCommandHandler {
mService.setAdasGnssLocationEnabledForUser(enabled, userId);
}
+ private void handleSetAutomotiveGnssSuspended() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ boolean suspended = Boolean.parseBoolean(getNextArgRequired());
+
+ mService.setAutomotiveGnssSuspended(suspended);
+ }
+
+ private void handleIsAutomotiveGnssSuspended() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ getOutPrintWriter().println(mService.isAutomotiveGnssSuspended());
+ }
+
private void handleAddTestProvider() {
String provider = getNextArgRequired();
@@ -359,6 +385,10 @@ class LocationShellCommand extends BasicShellCommandHandler {
pw.println(" set-adas-gnss-location-enabled true|false [--user <USER_ID>]");
pw.println(" Sets the ADAS GNSS location enabled state. If no user is specified,");
pw.println(" the current user is assumed.");
+ pw.println(" is-automotive-gnss-suspended");
+ pw.println(" Gets the automotive GNSS suspended state.");
+ pw.println(" set-automotive-gnss-suspended true|false");
+ pw.println(" Sets the automotive GNSS suspended state.");
}
pw.println(" providers");
pw.println(" The providers command is followed by a subcommand, as listed below:");
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 583cdd599780..647a89efcbbf 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -104,6 +104,12 @@ public class NotificationComparator
return -1 * Boolean.compare(leftPeople, rightPeople);
}
+ boolean leftSystemMax = isSystemMax(left);
+ boolean rightSystemMax = isSystemMax(right);
+ if (leftSystemMax != rightSystemMax) {
+ return -1 * Boolean.compare(leftSystemMax, rightSystemMax);
+ }
+
if (leftImportance != rightImportance) {
// by importance, high to low
return -1 * Integer.compare(leftImportance, rightImportance);
@@ -173,6 +179,20 @@ public class NotificationComparator
return mMessagingUtil.isImportantMessaging(record.getSbn(), record.getImportance());
}
+ protected boolean isSystemMax(NotificationRecord record) {
+ if (record.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
+ return false;
+ }
+ String packageName = record.getSbn().getPackageName();
+ if ("android".equals(packageName)) {
+ return true;
+ }
+ if ("com.android.systemui".equals(packageName)) {
+ return true;
+ }
+ return false;
+ }
+
private boolean isOngoing(NotificationRecord record) {
final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE;
return (record.getNotification().flags & ongoingFlags) != 0;
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 0cbdbc18ad39..5d18069ea205 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -19,7 +19,7 @@ package com.android.server.notification;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.permission.PermissionManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.Manifest;
import android.annotation.NonNull;
@@ -77,7 +77,8 @@ public final class PermissionHelper {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
try {
- return mPmi.checkUidPermission(uid, NOTIFICATION_PERMISSION) == PERMISSION_GRANTED;
+ return mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(uid)
+ == PERMISSION_GRANTED;
} finally {
Binder.restoreCallingIdentity(callingId);
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 1fa901352c3d..9bcb7242b645 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -285,6 +285,19 @@ public class UserRestrictionsUtils {
);
/**
+ * User restrictions available to a device owner whose type is
+ * {@link android.app.admin.DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}.
+ */
+ private static final Set<String> FINANCED_DEVICE_OWNER_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_SAFE_BOOT,
+ UserManager.DISALLOW_CONFIG_DATE_TIME,
+ UserManager.DISALLOW_OUTGOING_CALLS
+ );
+
+ /**
* Returns whether the given restriction name is valid (and logs it if it isn't).
*/
public static boolean isValidRestriction(@NonNull String restriction) {
@@ -458,6 +471,15 @@ public class UserRestrictionsUtils {
}
/**
+ * @return {@code true} only if the restriction is allowed for financed devices and can be set
+ * by a device owner. Otherwise, {@code false} would be returned.
+ */
+ public static boolean canFinancedDeviceOwnerChange(String restriction) {
+ return FINANCED_DEVICE_OWNER_RESTRICTIONS.contains(restriction)
+ && canDeviceOwnerChange(restriction);
+ }
+
+ /**
* Whether given user restriction should be enforced globally.
*/
public static boolean isGlobal(@UserManagerInternal.OwnerType int restrictionOwnerType,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 317730a9f606..79c5ea2efefe 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.pm.permission;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.UPDATE_APP_OPS_STATS;
import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
@@ -608,6 +609,21 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
+ public int checkPostNotificationsPermissionGrantedOrLegacyAccess(int uid) {
+ int granted = PermissionManagerService.this.checkUidPermission(uid,
+ POST_NOTIFICATIONS);
+ AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
+ if (granted != PermissionManager.PERMISSION_GRANTED) {
+ int flags = PermissionManagerService.this.getPermissionFlags(pkg.getPackageName(),
+ POST_NOTIFICATIONS, UserHandle.getUserId(uid));
+ if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ return PermissionManager.PERMISSION_GRANTED;
+ }
+ }
+ return granted;
+ }
+
+ @Override
public void startShellPermissionIdentityDelegation(int uid, @NonNull String packageName,
@Nullable List<String> permissionNames) {
Objects.requireNonNull(packageName, "packageName");
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index d2c4ec4cc5a5..812d7a04dc13 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -63,6 +63,17 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter
int checkUidPermission(int uid, @NonNull String permissionName);
/**
+ * Check whether a particular UID has been granted the POST_NOTIFICATIONS permission, or if
+ * access should be granted based on legacy access (currently symbolized by the REVIEW_REQUIRED
+ * permission flag
+ *
+ * @param uid the UID
+ * @return {@code PERMISSION_GRANTED} if the permission is granted, or legacy access is granted,
+ * {@code PERMISSION_DENIED} otherwise
+ */
+ int checkPostNotificationsPermissionGrantedOrLegacyAccess(int uid);
+
+ /**
* Adds a listener for runtime permission state (permissions or flags) changes.
*
* @param listener The listener.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index e71ff784dc23..344edbbef9c7 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -47,6 +47,7 @@ import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManager;
@@ -894,6 +895,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
+ public void setBiometicContextListener(IBiometricContextListener listener) {
+ enforceStatusBarService();
+ if (mBar != null) {
+ try {
+ mBar.setBiometicContextListener(listener);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
public void setUdfpsHbmListener(IUdfpsHbmListener listener) {
enforceStatusBarService();
if (mBar != null) {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 9bed24d05f3d..52f7d101ce99 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -122,7 +122,8 @@ public class TrustManagerService extends SystemService {
private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13;
private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14;
private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
- public static final int MSG_USER_REQUESTED_UNLOCK = 16;
+ private static final int MSG_USER_REQUESTED_UNLOCK = 16;
+ private static final int MSG_ENABLE_TRUST_AGENT = 17;
private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except";
@@ -631,6 +632,24 @@ public class TrustManagerService extends SystemService {
}
}
+
+ /**
+ * Uses {@link LockPatternUtils} to enable the setting for trust agent in the specified
+ * component name. This should only be used for testing.
+ */
+ private void enableTrustAgentForUserForTest(@NonNull ComponentName componentName, int userId) {
+ Log.i(TAG,
+ "Enabling trust agent " + componentName.flattenToString() + " for user " + userId);
+ List<ComponentName> agents =
+ new ArrayList<>(mLockPatternUtils.getEnabledTrustAgents(userId));
+ if (!agents.contains(componentName)) {
+ agents.add(componentName);
+ }
+ // Even if the agent was already there, we still call setEnabledTrustAgents to trigger a
+ // refresh of installed agents.
+ mLockPatternUtils.setEnabledTrustAgents(agents, userId);
+ }
+
boolean isDeviceLockedInner(int userId) {
synchronized (mDeviceLockedForUser) {
return mDeviceLockedForUser.get(userId, true);
@@ -929,6 +948,7 @@ public class TrustManagerService extends SystemService {
continue;
}
allowedAgents.add(resolveInfo);
+ if (DEBUG) Slog.d(TAG, "Adding agent " + getComponentName(resolveInfo));
}
return allowedAgents;
}
@@ -1158,6 +1178,13 @@ public class TrustManagerService extends SystemService {
}
@Override
+ public void enableTrustAgentForUserForTest(ComponentName componentName, int userId)
+ throws RemoteException {
+ enforceReportPermission();
+ mHandler.obtainMessage(MSG_ENABLE_TRUST_AGENT, userId, 0, componentName).sendToTarget();
+ }
+
+ @Override
public void reportKeyguardShowingChanged() throws RemoteException {
enforceReportPermission();
// coalesce refresh messages.
@@ -1433,6 +1460,9 @@ public class TrustManagerService extends SystemService {
// This is also called when the security mode of a user changes.
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
+ case MSG_ENABLE_TRUST_AGENT:
+ enableTrustAgentForUserForTest((ComponentName) msg.obj, msg.arg1);
+ break;
case MSG_KEYGUARD_SHOWING_CHANGED:
refreshDeviceLockedForUser(mCurrentUser);
break;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index ded58f48b207..ad4594873cf0 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1039,6 +1039,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
" Rejecting as detached: %s", wc);
continue;
}
+ // The level of transition target should be at least window token.
+ if (wc.asWindowState() != null) continue;
final ChangeInfo changeInfo = changes.get(wc);
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index 43018a900f4c..adc91fc3f2e8 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -186,7 +186,7 @@ static std::map<int, int> KEY_CODE_MAPPING = {
};
/** Creates a new uinput device and assigns a file descriptor. */
-static int openUinput(const char* readableName, jint vendorId, jint productId,
+static int openUinput(const char* readableName, jint vendorId, jint productId, const char* phys,
DeviceType deviceType, jint screenHeight, jint screenWidth) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
if (fd < 0) {
@@ -194,6 +194,8 @@ static int openUinput(const char* readableName, jint vendorId, jint productId,
return -errno;
}
+ ioctl(fd, UI_SET_PHYS, phys);
+
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_EVBIT, EV_SYN);
switch (deviceType) {
@@ -295,28 +297,30 @@ static int openUinput(const char* readableName, jint vendorId, jint productId,
return fd.release();
}
-static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
+static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId, jstring phys,
DeviceType deviceType, int screenHeight, int screenWidth) {
ScopedUtfChars readableName(env, name);
- return openUinput(readableName.c_str(), vendorId, productId, deviceType, screenHeight,
- screenWidth);
+ ScopedUtfChars readablePhys(env, phys);
+ return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType,
+ screenHeight, screenWidth);
}
static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
- jint productId) {
- return openUinputJni(env, name, vendorId, productId, DeviceType::KEYBOARD, /* screenHeight */ 0,
- /* screenWidth */ 0);
+ jint productId, jstring phys) {
+ return openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD,
+ /* screenHeight */ 0, /* screenWidth */ 0);
}
static int nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
- jint productId) {
- return openUinputJni(env, name, vendorId, productId, DeviceType::MOUSE, /* screenHeight */ 0,
- /* screenWidth */ 0);
+ jint productId, jstring phys) {
+ return openUinputJni(env, name, vendorId, productId, phys, DeviceType::MOUSE,
+ /* screenHeight */ 0, /* screenWidth */ 0);
}
static int nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
- jint productId, jint height, jint width) {
- return openUinputJni(env, name, vendorId, productId, DeviceType::TOUCHSCREEN, height, width);
+ jint productId, jstring phys, jint height, jint width) {
+ return openUinputJni(env, name, vendorId, productId, phys, DeviceType::TOUCHSCREEN, height,
+ width);
}
static bool nativeCloseUinput(JNIEnv* env, jobject thiz, jint fd) {
@@ -435,9 +439,11 @@ static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xA
}
static JNINativeMethod methods[] = {
- {"nativeOpenUinputKeyboard", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputKeyboard},
- {"nativeOpenUinputMouse", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputMouse},
- {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IIII)I",
+ {"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)I",
+ (void*)nativeOpenUinputKeyboard},
+ {"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)I",
+ (void*)nativeOpenUinputMouse},
+ {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)I",
(void*)nativeOpenUinputTouchscreen},
{"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput},
{"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent},
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 0da8f7ef0dea..161d7ce350fc 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1504,11 +1504,14 @@ static jboolean android_location_gnss_hal_GnssNative_set_position_mode(
JNIEnv* /* env */, jclass, jint mode, jint recurrence, jint min_interval,
jint preferred_accuracy, jint preferred_time, jboolean low_power_mode) {
if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->setPositionMode(static_cast<IGnssAidl::GnssPositionMode>(mode),
- static_cast<IGnssAidl::GnssPositionRecurrence>(
- recurrence),
- min_interval, preferred_accuracy, preferred_time,
- low_power_mode);
+ IGnssAidl::PositionModeOptions options;
+ options.mode = static_cast<IGnssAidl::GnssPositionMode>(mode);
+ options.recurrence = static_cast<IGnssAidl::GnssPositionRecurrence>(recurrence);
+ options.minIntervalMs = min_interval;
+ options.preferredAccuracyMeters = preferred_accuracy;
+ options.preferredTimeMs = preferred_time;
+ options.lowPowerMode = low_power_mode;
+ auto status = gnssHalAidl->setPositionMode(options);
return checkAidlStatus(status, "IGnssAidl setPositionMode() failed.");
}
diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp
index d760b4d2195e..424ffd463713 100644
--- a/services/core/jni/gnss/AGnssRil.cpp
+++ b/services/core/jni/gnss/AGnssRil.cpp
@@ -41,7 +41,7 @@ jboolean AGnssRil::setCallback(const std::unique_ptr<AGnssRilCallback>& callback
jboolean AGnssRil::setSetId(jint type, const jstring& setid_string) {
JNIEnv* env = getJniEnv();
ScopedJniString jniSetId{env, setid_string};
- auto status = mIAGnssRil->setSetId((IAGnssRil::SetIDType)type, jniSetId.c_str());
+ auto status = mIAGnssRil->setSetId((IAGnssRil::SetIdType)type, jniSetId.c_str());
return checkAidlStatus(status, "IAGnssRilAidl setSetId() failed.");
}
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index fbdeec6b897e..6c0d5d984980 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -359,9 +359,7 @@ void GnssMeasurementCallbackAidl::translateAndSetGnssData(const GnssData& data)
jobjectArray measurementArray = translateAllGnssMeasurements(env, data.measurements);
jobjectArray gnssAgcArray = nullptr;
- if (data.gnssAgcs.has_value()) {
- gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs.value());
- }
+ gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs);
setMeasurementData(env, mCallbacksObj, clock, measurementArray, gnssAgcArray);
env->DeleteLocalRef(clock);
@@ -508,8 +506,8 @@ jobjectArray GnssMeasurementCallbackAidl::translateAllGnssMeasurements(
return gnssMeasurementArray;
}
-jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(
- JNIEnv* env, const std::vector<std::optional<GnssAgc>>& agcs) {
+jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(JNIEnv* env,
+ const std::vector<GnssAgc>& agcs) {
if (agcs.size() == 0) {
return nullptr;
}
@@ -518,10 +516,7 @@ jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(
env->NewObjectArray(agcs.size(), class_gnssAgc, nullptr /* initialElement */);
for (uint16_t i = 0; i < agcs.size(); ++i) {
- if (!agcs[i].has_value()) {
- continue;
- }
- const GnssAgc& gnssAgc = agcs[i].value();
+ const GnssAgc& gnssAgc = agcs[i];
jobject agcBuilderObject = env->NewObject(class_gnssAgcBuilder, method_gnssAgcBuilderCtor);
env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetLevelDb,
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h
index 9b346312db38..17af94939666 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.h
+++ b/services/core/jni/gnss/GnssMeasurementCallback.h
@@ -62,8 +62,8 @@ private:
jobjectArray translateAllGnssMeasurements(
JNIEnv* env, const std::vector<hardware::gnss::GnssMeasurement>& measurements);
- jobjectArray translateAllGnssAgcs(
- JNIEnv* env, const std::vector<std::optional<hardware::gnss::GnssData::GnssAgc>>& agcs);
+ jobjectArray translateAllGnssAgcs(JNIEnv* env,
+ const std::vector<hardware::gnss::GnssData::GnssAgc>& agcs);
void translateAndSetGnssData(const hardware::gnss::GnssData& data);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 733cfcdfaea5..3bda7bf049c4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -56,6 +56,8 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_NETWORK_LOGGING;
import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_ID;
import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE;
@@ -66,9 +68,12 @@ import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATI
import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
@@ -3918,8 +3923,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
- isProfileOwner(caller) || isDeviceOwner(caller) || isSystemUid(caller)
- || isPasswordLimitingAdminTargetingP(caller));
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isSystemUid(caller) || isPasswordLimitingAdminTargetingP(caller));
if (parent) {
Preconditions.checkCallAuthorization(
@@ -4772,7 +4777,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller));
Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
return !isSeparateProfileChallengeEnabled(caller.getUserId());
@@ -4860,12 +4866,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
enforceUserUnlocked(caller.getUserId());
if (parent) {
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller),
"Only profile owner, device owner and system may call this method on parent.");
} else {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY)
- || isDeviceOwner(caller) || isProfileOwner(caller),
+ || isDefaultDeviceOwner(caller) || isProfileOwner(caller),
"Must have " + REQUEST_PASSWORD_COMPLEXITY
+ " permission, or be a profile owner or device owner.");
}
@@ -4888,7 +4894,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
"Provided complexity is not one of the allowed values.");
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
synchronized (getLockObject()) {
@@ -4968,7 +4975,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwner(caller));
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
@@ -5160,7 +5167,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
// If caller has PO (or DO) throw or fail silently depending on its target SDK level.
- if (isDeviceOwner(caller) || isProfileOwner(caller)) {
+ if (isDefaultDeviceOwner(caller) || isProfileOwner(caller)) {
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) {
@@ -5219,7 +5226,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
- boolean callerIsDeviceOwnerAdmin = isDeviceOwner(caller);
+ boolean callerIsDeviceOwnerAdmin = isDefaultDeviceOwner(caller);
boolean doNotAskCredentialsOnBoot =
(flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0;
if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) {
@@ -5406,7 +5413,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkArgument(timeoutMs >= 0, "Timeout must not be a negative number.");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
// timeoutMs with value 0 means that the admin doesn't participate
// timeoutMs is clamped to the interval in case the internal constants change in the future
final long minimumStrongAuthTimeout = getMinimumStrongAuthTimeoutMs();
@@ -5575,7 +5583,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean canManageCaCerts(CallerIdentity caller) {
- return (caller.hasAdminComponent() && (isDeviceOwner(caller) || isProfileOwner(caller)))
+ return (caller.hasAdminComponent() && (isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))
|| hasCallingOrSelfPermission(MANAGE_CA_CERTIFICATES);
}
@@ -5689,7 +5698,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(!isUserSelectable, "The credential "
@@ -5754,7 +5763,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
@@ -5818,12 +5827,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean canInstallCertificates(CallerIdentity caller) {
- return isProfileOwner(caller) || isDeviceOwner(caller)
+ return isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
}
private boolean canChooseCertificates(CallerIdentity caller) {
- return isProfileOwner(caller) || isDeviceOwner(caller)
+ return isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| isCallerDelegate(caller, DELEGATION_CERT_SELECTION);
}
@@ -5871,7 +5880,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_SELECTION)));
final int granteeUid;
@@ -5984,7 +5993,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// If not, fall back to the device owner check.
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ isDefaultDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
}
@VisibleForTesting
@@ -6047,7 +6056,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
enforceIndividualAttestationSupportedIfRequested(attestationUtilsFlags);
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
@@ -6182,7 +6191,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
@@ -6337,14 +6346,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = caller.getUserId();
// Ensure calling process is device/profile owner.
if (!Collections.disjoint(scopes, DEVICE_OWNER_OR_MANAGED_PROFILE_OWNER_DELEGATIONS)) {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| (isProfileOwner(caller) && isManagedProfile(caller.getUserId())));
} else if (!Collections.disjoint(
scopes, DEVICE_OWNER_OR_ORGANIZATION_OWNED_MANAGED_PROFILE_OWNER_DELEGATIONS)) {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| isProfileOwnerOfOrganizationOwnedDevice(caller));
} else {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
synchronized (getLockObject()) {
@@ -6434,7 +6444,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// * Either it's a profile owner / device owner, if componentName is provided
// * Or it's an app querying its own delegation scopes
if (caller.hasAdminComponent()) {
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
} else {
Preconditions.checkCallAuthorization(isPackage(caller, delegatePackage),
String.format("Caller with uid %d is not %s", caller.getUid(),
@@ -6467,7 +6478,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Retrieve the user ID of the calling process.
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
return getDelegatePackagesInternalLocked(scope, caller.getUserId());
}
@@ -6600,7 +6612,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
// Ensure calling process is device/profile owner.
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(caller.getUserId());
@@ -6712,7 +6725,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_ALWAYS_ON_VPN_PACKAGE);
if (vpnPackage == null) {
@@ -6792,7 +6806,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getVpnManager().getAlwaysOnVpnPackageForUser(caller.getUserId()));
@@ -6818,7 +6833,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
caller = getCallerIdentity();
} else {
caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
return mInjector.binderWithCleanCallingIdentity(
@@ -6841,7 +6857,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getVpnManager().getVpnLockdownAllowlist(caller.getUserId()));
@@ -6946,8 +6963,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ "organization-owned device.");
}
if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || calledByProfileOwnerOnOrgOwnedDevice,
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || calledByProfileOwnerOnOrgOwnedDevice
+ || isFinancedDeviceOwner(caller),
"Only device owners or profile owners of organization-owned device can set "
+ "WIPE_RESET_PROTECTION_DATA");
}
@@ -7139,8 +7157,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkNotNull(who, "ComponentName is null");
CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager
.OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY);
@@ -7189,7 +7207,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
UserHandle.getUserId(frpManagementAgentUid));
} else {
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
admin = getProfileOwnerOrDeviceOwnerLocked(caller);
}
}
@@ -7617,7 +7636,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setRecommendedGlobalProxy(ComponentName who, ProxyInfo proxyInfo) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkAllUsersAreAffiliatedWithDevice();
mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getConnectivityManager().setGlobalProxy(proxyInfo));
@@ -7915,7 +7934,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -7934,8 +7954,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isProfileOwner(caller)
- || isDeviceOwner(caller)
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| hasCallingOrSelfPermission(permission.READ_NEARBY_STREAMING_POLICY));
synchronized (getLockObject()) {
@@ -7955,7 +7974,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -7974,8 +7994,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isProfileOwner(caller)
- || isDeviceOwner(caller)
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| hasCallingOrSelfPermission(permission.READ_NEARBY_STREAMING_POLICY));
synchronized (getLockObject()) {
@@ -8067,7 +8086,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, enabled ? 1 : 0));
@@ -8091,7 +8110,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0;
}
@@ -8108,7 +8127,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
@@ -8132,7 +8151,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
}
@@ -8161,7 +8180,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// which could still contain data related to that user. Should we disallow that, e.g. until
// next boot? Might not be needed given that this still requires user consent.
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkAllUsersAreAffiliatedWithDevice();
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REQUEST_BUGREPORT);
@@ -8472,7 +8491,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(packageList, "packageList is null");
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && isDefaultDeviceOwner(caller))
|| (caller.hasPackage()
&& isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_KEEP_UNINSTALLED_PACKAGES);
@@ -8501,7 +8521,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && isDefaultDeviceOwner(caller))
|| (caller.hasPackage()
&& isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)));
@@ -8615,8 +8636,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean hasDeviceOwner() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || canManageUsers(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || canManageUsers(caller) || isFinancedDeviceOwner(caller)
|| hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return mOwners.hasDeviceOwner();
}
@@ -8633,17 +8654,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private boolean isDeviceOwner(CallerIdentity caller) {
+ /**
+ * Returns {@code true} <b>only if</b> the caller is the device owner and the device owner type
+ * is {@link DevicePolicyManager#DEVICE_OWNER_TYPE_DEFAULT}. {@code false} is returned for the
+ * case where the caller is not the device owner, there is no device owner, or the device owner
+ * type is not {@link DevicePolicyManager#DEVICE_OWNER_TYPE_DEFAULT}.
+ *
+ */
+ private boolean isDefaultDeviceOwner(CallerIdentity caller) {
synchronized (getLockObject()) {
- if (!mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != caller.getUserId()) {
- return false;
- }
+ return isDeviceOwnerLocked(caller) && getDeviceOwnerTypeLocked(
+ mOwners.getDeviceOwnerPackageName()) == DEVICE_OWNER_TYPE_DEFAULT;
+ }
+ }
- if (caller.hasAdminComponent()) {
- return mOwners.getDeviceOwnerComponent().equals(caller.getComponentName());
- } else {
- return isUidDeviceOwnerLocked(caller.getUid());
- }
+ private boolean isDeviceOwnerLocked(CallerIdentity caller) {
+ if (!mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != caller.getUserId()) {
+ return false;
+ }
+
+ if (caller.hasAdminComponent()) {
+ return mOwners.getDeviceOwnerComponent().equals(caller.getComponentName());
+ } else {
+ return isUidDeviceOwnerLocked(caller.getUid());
}
}
@@ -9073,8 +9106,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mLockPatternUtils.setDeviceOwnerInfo(info != null ? info.toString() : null));
@@ -9258,7 +9291,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
final int userId = caller.getUserId();
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
Preconditions.checkCallingUser(isManagedProfile(userId));
synchronized (getLockObject()) {
@@ -9289,7 +9323,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
mInjector.binderWithCleanCallingIdentity(() -> {
mUserManager.setUserName(caller.getUserId(), profileName);
@@ -9725,7 +9760,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void enforceCanCallLockTaskLocked(CallerIdentity caller) {
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
final int userId = caller.getUserId();
if (!canUserUseLockTaskLocked(userId)) {
@@ -9982,7 +10018,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ComponentName activity) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
final int userHandle = caller.getUserId();
synchronized (getLockObject()) {
@@ -10009,7 +10046,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
final int userHandle = caller.getUserId();
synchronized (getLockObject()) {
@@ -10030,7 +10068,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
if (parent) {
mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage(
@@ -10070,7 +10108,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String packageName, Bundle settings) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS);
@@ -10168,7 +10206,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_RESTRICTIONS_PROVIDER);
synchronized (getLockObject()) {
@@ -10193,7 +10232,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
int callingUserId = caller.getUserId();
synchronized (getLockObject()) {
long id = mInjector.binderClearCallingIdentity();
@@ -10242,7 +10282,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void clearCrossProfileIntentFilters(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
int callingUserId = caller.getUserId();
synchronized (getLockObject()) {
@@ -10382,7 +10423,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -10495,7 +10537,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ "system input methods when called on the parent instance of an "
+ "organization-owned device");
} else {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
if (packageList != null) {
@@ -10553,7 +10596,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (calledOnParentInstance) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
} else {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
synchronized (getLockObject()) {
@@ -10732,7 +10776,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Only allow the system user to use this method
Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
"createAndManageUser was called from non-system user");
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER);
final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0;
@@ -10974,7 +11018,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_USER);
return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -11008,7 +11052,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean switchUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SWITCH_USER);
boolean switched = false;
@@ -11083,7 +11127,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_START_USER_IN_BACKGROUND);
final int userId = userHandle.getIdentifier();
@@ -11119,7 +11163,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_STOP_USER);
final int userId = userHandle.getIdentifier();
@@ -11135,7 +11179,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public int logoutUser(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_LOGOUT_USER);
final int callingUserId = caller.getUserId();
@@ -11224,7 +11269,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<UserHandle> getSecondaryUsers(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
return mInjector.binderWithCleanCallingIdentity(() -> {
final List<UserInfo> userInfos = mInjector.getUserManager().getAliveUsers();
@@ -11244,7 +11289,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getUserManager().isUserEphemeral(caller.getUserId()));
@@ -11255,7 +11301,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -11306,7 +11352,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(packageNames, "array of packages cannot be null");
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PACKAGES_SUSPENDED);
@@ -11369,7 +11415,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
synchronized (getLockObject()) {
@@ -11390,8 +11436,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<String> listPolicyExemptApps() {
CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS) || isDeviceOwner(caller)
- || isProfileOwner(caller));
+ hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)
+ || isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return listPolicyExemptAppsUnchecked();
}
@@ -11432,12 +11478,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
getProfileOwnerOrDeviceOwnerLocked(caller), parent);
- if (isDeviceOwner(caller)) {
+ if (isDefaultDeviceOwner(caller)) {
if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
throw new SecurityException("Device owner cannot set user restriction " + key);
}
Preconditions.checkArgument(!parent,
"Cannot use the parent instance in Device Owner mode");
+ } else if (isFinancedDeviceOwner(caller)) {
+ if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(key)) {
+ throw new SecurityException("Cannot set user restriction " + key
+ + " when managing a financed device");
+ }
+ Preconditions.checkArgument(!parent,
+ "Cannot use the parent instance in Financed Device Owner mode");
} else {
boolean profileOwnerCanChangeOnItself = !parent
&& UserRestrictionsUtils.canProfileOwnerChange(key, userHandle);
@@ -11546,7 +11599,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller)
|| (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
synchronized (getLockObject()) {
@@ -11561,7 +11615,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
boolean hidden, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
List<String> exemptApps = listPolicyExemptAppsUnchecked();
@@ -11607,7 +11661,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String packageName, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
@@ -11643,7 +11697,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void enableSystemApp(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)));
synchronized (getLockObject()) {
@@ -11687,7 +11741,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public int enableSystemAppWithIntent(ComponentName who, String callerPackage, Intent intent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)));
int numberOfAppsInstalled = 0;
@@ -11756,7 +11810,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage()
&& isCallerDelegate(caller, DELEGATION_INSTALL_EXISTING_PACKAGE)));
@@ -11872,7 +11926,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
boolean uninstallBlocked) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL)));
final int userId = caller.getUserId();
@@ -11913,7 +11968,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (who != null) {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
- isProfileOwner(caller) || isDeviceOwner(caller));
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller));
}
try {
return mIPackageManager.getBlockUninstallForUser(packageName, userId);
@@ -12087,7 +12143,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -12111,7 +12168,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -12136,7 +12194,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Check can set secondary lockscreen enabled
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
"User %d is not allowed to call setSecondaryLockscreenEnabled",
caller.getUserId());
@@ -12325,6 +12384,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userHandle = caller.getUserId();
synchronized (getLockObject()) {
enforceCanCallLockTaskLocked(caller);
+ enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
setLockTaskFeaturesLocked(userHandle, flags);
}
@@ -12373,6 +12433,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
}
+ private void enforceCanSetLockTaskFeaturesOnFinancedDevice(CallerIdentity caller, int flags) {
+ int allowedFlags = LOCK_TASK_FEATURE_SYSTEM_INFO | LOCK_TASK_FEATURE_KEYGUARD
+ | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_GLOBAL_ACTIONS
+ | LOCK_TASK_FEATURE_NOTIFICATIONS;
+
+ if (!isFinancedDeviceOwner(caller)) {
+ return;
+ }
+
+ if ((flags == 0) || ((flags & ~(allowedFlags)) != 0)) {
+ throw new SecurityException(
+ "Permitted lock task features when managing a financed device: "
+ + "LOCK_TASK_FEATURE_SYSTEM_INFO, LOCK_TASK_FEATURE_KEYGUARD, "
+ + "LOCK_TASK_FEATURE_HOME, LOCK_TASK_FEATURE_GLOBAL_ACTIONS, "
+ + "or LOCK_TASK_FEATURE_NOTIFICATIONS");
+ }
+ }
+
@Override
public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
@@ -12413,7 +12491,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setGlobalSetting(ComponentName who, String setting, String value) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_GLOBAL_SETTING)
@@ -12454,7 +12532,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(setting, "String setting is null or empty");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_SETTING);
synchronized (getLockObject()) {
@@ -12476,8 +12555,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkNotNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
@@ -12498,8 +12577,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkNotNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
return mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalGetInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) > 0);
@@ -12510,7 +12589,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkNotNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
UserHandle userHandle = caller.getUserHandle();
if (mIsAutomotive && !locationEnabled) {
@@ -12592,8 +12671,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
// Don't allow set time when auto time is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
@@ -12612,8 +12691,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
// Don't allow set timezone when auto timezone is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
@@ -12633,7 +12712,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setSecureSetting(ComponentName who, String setting, String value) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
int callingUserId = caller.getUserId();
synchronized (getLockObject()) {
@@ -12720,7 +12800,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setMasterVolumeMuted(ComponentName who, boolean on) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_MASTER_VOLUME_MUTED);
synchronized (getLockObject()) {
@@ -12737,7 +12818,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean isMasterVolumeMuted(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
AudioManager audioManager =
@@ -12750,7 +12832,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setUserIcon(ComponentName who, Bitmap icon) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
mInjector.binderWithCleanCallingIdentity(
@@ -12766,7 +12849,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean setKeyguardDisabled(ComponentName who, boolean disabled) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
final int userId = caller.getUserId();
synchronized (getLockObject()) {
@@ -12808,7 +12892,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean setStatusBarDisabled(ComponentName who, boolean disabled) {
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
int userId = caller.getUserId();
synchronized (getLockObject()) {
@@ -13042,7 +13127,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isActiveDeviceOwner(int uid) {
- return isDeviceOwner(new CallerIdentity(uid, null, null));
+ return isDefaultDeviceOwner(new CallerIdentity(uid, null, null));
}
@Override
@@ -13633,7 +13718,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_UPDATE_POLICY);
if (policy == null) {
@@ -13831,7 +13916,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return mOwners.getSystemUpdateInfo();
}
@@ -13840,7 +13926,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setPermissionPolicy(ComponentName admin, String callerPackage, int policy) {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_POLICY);
@@ -13882,11 +13968,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_GRANT_STATE);
synchronized (getLockObject()) {
+ if (isFinancedDeviceOwner(caller)) {
+ enforceCanSetPermissionGrantOnFinancedDevice(packageName, permission);
+ }
long ident = mInjector.binderClearCallingIdentity();
try {
boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
@@ -13946,12 +14036,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private void enforceCanSetPermissionGrantOnFinancedDevice(
+ String packageName, String permission) {
+ if (!Manifest.permission.READ_PHONE_STATE.equals(permission)) {
+ throw new SecurityException("Cannot grant " + permission
+ + " when managing a financed device");
+ } else if (!mOwners.getDeviceOwnerPackageName().equals(packageName)) {
+ throw new SecurityException("Cannot grant permission to a package that is not"
+ + " the device owner");
+ }
+ }
+
@Override
public int getPermissionGrantState(ComponentName admin, String callerPackage,
String packageName, String permission) throws RemoteException {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
Preconditions.checkCallAuthorization(isSystemUid(caller) || (caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
@@ -14231,7 +14332,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void checkIsDeviceOwner(CallerIdentity caller) {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller), caller.getUid()
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller), caller.getUid()
+ " is not device owner");
}
@@ -14263,8 +14364,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
return mInjector.binderWithCleanCallingIdentity(() -> {
String[] macAddresses = mInjector.getWifiManager().getFactoryMacAddresses();
@@ -14299,7 +14400,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return isManagedProfile(caller.getUserId());
}
@@ -14308,7 +14410,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void reboot(ComponentName admin) {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REBOOT);
mInjector.binderWithCleanCallingIdentity(() -> {
// Make sure there are no ongoing calls on the device.
@@ -14542,7 +14644,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || canManageUsers(caller) || isFinancedDeviceOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
return deviceOwnerAdmin == null ? null : deviceOwnerAdmin.organizationName;
@@ -14576,7 +14679,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who);
Objects.requireNonNull(packageNames);
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller),
"Admin %s does not own the profile", caller.getComponentName());
if (!mHasFeature) {
@@ -14627,7 +14731,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return new ArrayList<>();
}
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller),
"Admin %s does not own the profile", caller.getComponentName());
synchronized (getLockObject()) {
@@ -14766,7 +14871,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final Set<String> affiliationIds = new ArraySet<>(ids);
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
final int callingUserId = caller.getUserId();
synchronized (getLockObject()) {
@@ -14797,7 +14903,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin);
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
return new ArrayList<String>(getUserData(caller.getUserId()).mAffiliationIds);
@@ -14891,7 +14998,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (admin != null) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
} else {
// A delegate app passes a null admin component, which is expected
Preconditions.checkCallAuthorization(
@@ -14928,7 +15035,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (admin != null) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
} else {
// A delegate app passes a null admin component, which is expected
Preconditions.checkCallAuthorization(
@@ -14961,7 +15068,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (admin != null) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
} else {
// A delegate app passes a null admin component, which is expected
Preconditions.checkCallAuthorization(
@@ -15007,7 +15114,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (admin != null) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
} else {
// A delegate app passes a null admin component, which is expected
Preconditions.checkCallAuthorization(
@@ -15246,7 +15353,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller) || isFinancedDeviceOwner(caller));
toggleBackupServiceActive(caller.getUserId(), enabled);
}
@@ -15259,7 +15367,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller) || isFinancedDeviceOwner(caller));
return mInjector.binderWithCleanCallingIdentity(() -> {
synchronized (getLockObject()) {
@@ -15334,7 +15443,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin);
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final int callingUserId = caller.getUserId();
@@ -15462,7 +15572,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isManagedProfileOwner = isProfileOwner(caller)
&& isManagedProfile(caller.getUserId());
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isDeviceOwner(caller) || isManagedProfileOwner))
+ && (isDefaultDeviceOwner(caller) || isManagedProfileOwner))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
synchronized (getLockObject()) {
@@ -15622,7 +15732,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isDeviceOwner(caller)
+ && (isDefaultDeviceOwner(caller)
|| (isProfileOwner(caller) && isManagedProfile(caller.getUserId()))))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))
|| hasCallingOrSelfPermission(permission.MANAGE_USERS));
@@ -15654,7 +15764,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isManagedProfileOwner = isProfileOwner(caller)
&& isManagedProfile(caller.getUserId());
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isDeviceOwner(caller) || isManagedProfileOwner))
+ && (isDefaultDeviceOwner(caller) || isManagedProfileOwner))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
if (mOwners.hasDeviceOwner()) {
checkAllUsersAreAffiliatedWithDevice();
@@ -15815,14 +15925,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public long getLastSecurityLogRetrievalTime() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || canManageUsers(caller));
return getUserData(UserHandle.USER_SYSTEM).mLastSecurityLogRetrievalTime;
}
@Override
public long getLastBugReportRequestTime() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || canManageUsers(caller));
return getUserData(UserHandle.USER_SYSTEM).mLastBugReportRequestTime;
}
@@ -15830,7 +15942,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public long getLastNetworkLogRetrievalTime() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| (isProfileOwner(caller) && isManagedProfile(caller.getUserId()))
|| canManageUsers(caller));
final int affectedUserId = getNetworkLoggingAffectedUser();
@@ -15846,7 +15958,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
throw new IllegalArgumentException("token must be at least 32-byte long");
}
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final int userHandle = caller.getUserId();
@@ -15870,7 +15983,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final int userHandle = caller.getUserId();
@@ -15895,7 +16009,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
return isResetPasswordTokenActiveForUserLocked(caller.getUserId());
@@ -15920,7 +16035,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(token);
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(caller.getUserId());
@@ -15945,7 +16061,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isCurrentInputMethodSetByOwner() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| isProfileOwner(caller) || isSystemUid(caller),
"Only profile owner, device owner and system may call this method.");
return getUserData(caller.getUserId()).mCurrentInputMethodSet;
@@ -15956,7 +16072,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = user.getIdentifier();
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization((userId == caller.getUserId())
- || isProfileOwner(caller) || isDeviceOwner(caller)
+ || isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| hasFullCrossUsersPermission(caller, userId));
synchronized (getLockObject()) {
@@ -15973,7 +16089,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(callback, "callback is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CLEAR_APPLICATION_USER_DATA);
long ident = mInjector.binderClearCallingIdentity();
@@ -16005,7 +16122,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOGOUT_ENABLED);
synchronized (getLockObject()) {
@@ -16054,7 +16171,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
"Provided administrator and target have the same package name.");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
final int callingUserId = caller.getUserId();
final DevicePolicyData policy = getUserData(callingUserId);
@@ -16096,7 +16214,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (isUserAffiliatedWithDeviceLocked(callingUserId)) {
notifyAffiliatedProfileTransferOwnershipComplete(callingUserId);
}
- } else if (isDeviceOwner(caller)) {
+ } else if (isDefaultDeviceOwner(caller)) {
ownerType = ADMIN_TYPE_DEVICE_OWNER;
prepareTransfer(admin, target, bundle, callingUserId,
ADMIN_TYPE_DEVICE_OWNER);
@@ -16177,7 +16295,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
final String startUserSessionMessageString =
startUserSessionMessage != null ? startUserSessionMessage.toString() : null;
@@ -16202,7 +16320,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
final String endUserSessionMessageString =
endUserSessionMessage != null ? endUserSessionMessage.toString() : null;
@@ -16227,7 +16345,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
@@ -16242,7 +16360,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
@@ -16258,7 +16376,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Nullable
public PersistableBundle getTransferOwnershipBundle() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final int callingUserId = caller.getUserId();
@@ -16288,7 +16407,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in addOverrideApn");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
if (tm != null) {
@@ -16309,7 +16428,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in updateOverrideApn");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
if (apnId < 0) {
return false;
@@ -16331,7 +16450,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
return removeOverrideApnUnchecked(apnId);
}
@@ -16352,7 +16471,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
return getOverrideApnsUnchecked();
}
@@ -16373,7 +16492,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_OVERRIDE_APNS_ENABLED);
setOverrideApnsEnabledUnchecked(enabled);
@@ -16393,7 +16512,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
Cursor enforceCursor = mInjector.binderWithCleanCallingIdentity(
() -> mContext.getContentResolver().query(
@@ -16476,7 +16595,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkAllUsersAreAffiliatedWithDevice();
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_GLOBAL_PRIVATE_DNS);
@@ -16515,7 +16634,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
final int currentMode = ConnectivitySettingsManager.getPrivateDnsMode(mContext);
switch (currentMode) {
@@ -16537,7 +16656,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
}
@@ -16547,8 +16666,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_SYSTEM_UPDATE);
DevicePolicyEventLogger
@@ -16923,7 +17042,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(packages, "packages is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(
DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES);
@@ -16942,7 +17062,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
synchronized (getLockObject()) {
return mOwners.getDeviceOwnerProtectedPackages(who.getPackageName());
@@ -16954,7 +17075,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "Admin component name must be provided");
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"Common Criteria mode can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
synchronized (getLockObject()) {
@@ -16974,7 +17095,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (who != null) {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"Common Criteria mode can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
@@ -17446,7 +17567,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(callerPackage);
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwner(caller)
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller)
|| isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
synchronized (getLockObject()) {
@@ -17467,7 +17588,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(callerPackage);
// Only the DPC can set this ID.
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || isProfileOwner(caller),
"Only a Device Owner or Profile Owner may set the Enterprise ID.");
// Empty enterprise ID must not be provided in calls to this method.
Preconditions.checkArgument(!TextUtils.isEmpty(organizationId),
@@ -18141,27 +18262,51 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@DeviceOwnerType int deviceOwnerType) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
- verifyDeviceOwnerTypePreconditions(admin);
- final String packageName = admin.getPackageName();
+ synchronized (getLockObject()) {
+ setDeviceOwnerTypeLocked(admin, deviceOwnerType);
+ }
+ }
+
+ private void setDeviceOwnerTypeLocked(ComponentName admin,
+ @DeviceOwnerType int deviceOwnerType) {
+ String packageName = admin.getPackageName();
+
+ verifyDeviceOwnerTypePreconditionsLocked(admin);
Preconditions.checkState(!mOwners.isDeviceOwnerTypeSetForDeviceOwner(packageName),
"The device owner type has already been set for " + packageName);
- synchronized (getLockObject()) {
- mOwners.setDeviceOwnerType(packageName, deviceOwnerType);
- }
+ mOwners.setDeviceOwnerType(packageName, deviceOwnerType);
}
@Override
@DeviceOwnerType
public int getDeviceOwnerType(@NonNull ComponentName admin) {
- verifyDeviceOwnerTypePreconditions(admin);
synchronized (getLockObject()) {
- return mOwners.getDeviceOwnerType(admin.getPackageName());
+ verifyDeviceOwnerTypePreconditionsLocked(admin);
+ return getDeviceOwnerTypeLocked(admin.getPackageName());
+ }
+ }
+
+ @DeviceOwnerType
+ private int getDeviceOwnerTypeLocked(String packageName) {
+ return mOwners.getDeviceOwnerType(packageName);
+ }
+
+ /**
+ * {@code true} is returned <b>only if</b> the caller is the device owner and the device owner
+ * type is {@link DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}. {@code false} is returned for
+ * the case where the caller is not the device owner, there is no device owner, or the device
+ * owner type is not {@link DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}.
+ */
+ private boolean isFinancedDeviceOwner(CallerIdentity caller) {
+ synchronized (getLockObject()) {
+ return isDeviceOwnerLocked(caller) && getDeviceOwnerTypeLocked(
+ mOwners.getDeviceOwnerPackageName()) == DEVICE_OWNER_TYPE_FINANCED;
}
}
- private void verifyDeviceOwnerTypePreconditions(@NonNull ComponentName admin) {
+ private void verifyDeviceOwnerTypePreconditionsLocked(@NonNull ComponentName admin) {
Preconditions.checkState(mOwners.hasDeviceOwner(), "there is no device owner");
Preconditions.checkState(mOwners.getDeviceOwnerComponent().equals(admin),
"admin is not the device owner");
@@ -18172,7 +18317,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"USB data signaling can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
Preconditions.checkState(canUsbDataSignalingBeDisabled(),
@@ -18213,7 +18358,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
// If the caller is an admin, return the policy set by itself. Otherwise
// return the device-wide policy.
- if (isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) {
+ if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) {
return getProfileOwnerOrDeviceOwnerLocked(caller).mUsbDataSignalingEnabled;
} else {
return isUsbDataSignalingEnabledInternalLocked();
@@ -18254,7 +18399,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setMinimumRequiredWifiSecurityLevel(int level) {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"Wi-Fi minimum security level can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
@@ -18284,7 +18429,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setSsidAllowlist(List<String> ssids) {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"SSID allowlist can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
@@ -18306,7 +18451,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<String> getSsidAllowlist() {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
|| isSystemUid(caller),
"SSID allowlist can only be retrieved by a device owner or "
+ "a profile owner on an organization-owned device or a system app.");
@@ -18322,7 +18467,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setSsidDenylist(List<String> ssids) {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"SSID denylist can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
@@ -18344,7 +18489,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<String> getSsidDenylist() {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
|| isSystemUid(caller),
"SSID denylist can only be retrieved by a device owner or "
+ "a profile owner on an organization-owned device or a system app.");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
index 685cf0580a48..598f9e88ac6d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
@@ -34,6 +34,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -41,6 +42,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Binder;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.view.inputmethod.InputMethodInfo;
@@ -91,6 +93,8 @@ public class OverlayPackagesProvider {
List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);
String getActiveApexPackageNameContainingPackage(String packageName);
+
+ String getDeviceManagerRoleHolderPackageName(Context context);
}
private static final class DefaultInjector implements Injector {
@@ -104,6 +108,19 @@ public class OverlayPackagesProvider {
public String getActiveApexPackageNameContainingPackage(String packageName) {
return ApexManager.getInstance().getActiveApexPackageNameContainingPackage(packageName);
}
+
+ @Override
+ public String getDeviceManagerRoleHolderPackageName(Context context) {
+ return Binder.withCleanCallingIdentity(() -> {
+ RoleManager roleManager = context.getSystemService(RoleManager.class);
+ List<String> roleHolders =
+ roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_MANAGER);
+ if (roleHolders.isEmpty()) {
+ return null;
+ }
+ return roleHolders.get(0);
+ });
+ }
}
@VisibleForTesting
@@ -142,9 +159,20 @@ public class OverlayPackagesProvider {
nonRequiredApps.addAll(getDisallowedApps(provisioningAction));
nonRequiredApps.removeAll(
getRequiredAppsMainlineModules(nonRequiredApps, provisioningAction));
+ nonRequiredApps.removeAll(getDeviceManagerRoleHolders());
return nonRequiredApps;
}
+ private Set<String> getDeviceManagerRoleHolders() {
+ HashSet<String> result = new HashSet<>();
+ String deviceManagerRoleHolderPackageName =
+ mInjector.getDeviceManagerRoleHolderPackageName(mContext);
+ if (deviceManagerRoleHolderPackageName != null) {
+ result.add(deviceManagerRoleHolderPackageName);
+ }
+ return result;
+ }
+
/**
* Returns a subset of {@code packageNames} whose packages are mainline modules declared as
* required apps via their app metadata.
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
new file mode 100644
index 000000000000..ec884f59014f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.server.biometrics.log;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.biometrics.IBiometricContextListener;
+import android.hardware.biometrics.common.OperationContext;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.statusbar.IStatusBarService;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+public class BiometricContextProviderTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IStatusBarService mStatusBarService;
+
+ private OperationContext mOpContext = new OperationContext();
+ private IBiometricContextListener mListener;
+ private BiometricContextProvider mProvider;
+
+ @Before
+ public void setup() throws RemoteException {
+ mProvider = new BiometricContextProvider(mStatusBarService, null /* handler */);
+ ArgumentCaptor<IBiometricContextListener> captor =
+ ArgumentCaptor.forClass(IBiometricContextListener.class);
+ verify(mStatusBarService).setBiometicContextListener(captor.capture());
+ mListener = captor.getValue();
+ }
+
+ @Test
+ public void testIsAoD() throws RemoteException {
+ mListener.onDozeChanged(true);
+ assertThat(mProvider.isAoD()).isTrue();
+ mListener.onDozeChanged(false);
+ assertThat(mProvider.isAoD()).isFalse();
+ }
+
+ @Test
+ public void testSubscribesToAoD() throws RemoteException {
+ final List<Boolean> expected = ImmutableList.of(true, false, true, true, false);
+ final List<Boolean> actual = new ArrayList<>();
+
+ mProvider.subscribe(mOpContext, ctx -> {
+ assertThat(ctx).isSameInstanceAs(mOpContext);
+ actual.add(ctx.isAoD);
+ });
+
+ for (boolean v : expected) {
+ mListener.onDozeChanged(v);
+ }
+
+ assertThat(actual).containsExactlyElementsIn(expected).inOrder();
+ }
+
+ @Test
+ public void testUnsubscribes() throws RemoteException {
+ final Consumer<OperationContext> emptyConsumer = mock(Consumer.class);
+ mProvider.subscribe(mOpContext, emptyConsumer);
+ mProvider.unsubscribe(mOpContext);
+
+ mListener.onDozeChanged(true);
+
+ final Consumer<OperationContext> nonEmptyConsumer = mock(Consumer.class);
+ mProvider.subscribe(mOpContext, nonEmptyConsumer);
+ mListener.onDozeChanged(false);
+ mProvider.unsubscribe(mOpContext);
+ mListener.onDozeChanged(true);
+
+ verify(emptyConsumer, never()).accept(any());
+ verify(nonEmptyConsumer).accept(same(mOpContext));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index 2b72fabe7cca..b0eb810b5da9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -44,7 +44,8 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
@Presubmit
@SmallTest
@@ -55,6 +56,9 @@ public class BiometricLoggerTest {
private static final int DEFAULT_CLIENT = BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
@Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Rule
public TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
@Mock
@@ -68,8 +72,6 @@ public class BiometricLoggerTest {
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
-
mContext.addMockSystemService(SensorManager.class, mSensorManager);
when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(
new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
index 9bb722f8a853..2d9d868f2f74 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -34,6 +35,9 @@ import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -92,9 +96,8 @@ public class AcquisitionClientTest {
@NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter callback) {
super(context, lazyDaemon, token, callback, 0 /* userId */, "Test", 0 /* cookie */,
- TEST_SENSOR_ID /* sensorId */, true /* shouldVibrate */, 0 /* statsModality */,
- 0 /* statsAction */,
- 0 /* statsClient */);
+ TEST_SENSOR_ID /* sensorId */, true /* shouldVibrate */,
+ mock(BiometricLogger.class), mock(BiometricContext.class));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
index 51d234d5afeb..8e6d90c839d8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
@@ -30,6 +30,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import org.junit.Before;
@@ -49,6 +50,8 @@ public class BaseClientMonitorTest {
@Mock
private BiometricLogger mLogger;
@Mock
+ private BiometricContext mBiometricContext;
+ @Mock
private ClientMonitorCallback mCallback;
private TestClientMonitor mClientMonitor;
@@ -109,7 +112,7 @@ public class BaseClientMonitorTest {
TestClientMonitor() {
super(mContext, mToken, mListener, 9 /* userId */, "foo" /* owner */, 2 /* cookie */,
- 5 /* sensorId */, mLogger);
+ 5 /* sensorId */, mLogger, mBiometricContext);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index 8751cf3810bf..64be56906d4b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -36,6 +36,9 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,7 +57,8 @@ public class BiometricSchedulerOperationTest {
public abstract static class InterruptableMonitor<T>
extends HalClientMonitor<T> implements Interruptable {
public InterruptableMonitor() {
- super(null, null, null, null, 0, null, 0, 0, 0, 0, 0);
+ super(null, null, null, null, 0, null, 0, 0,
+ mock(BiometricLogger.class), mock(BiometricContext.class));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index ecd9abcb80e6..0fa2b41e8b32 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -51,6 +51,8 @@ import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.nano.BiometricSchedulerProto;
import com.android.server.biometrics.nano.BiometricsProto;
@@ -526,10 +528,10 @@ public class BiometricSchedulerTest {
@NonNull ClientMonitorCallbackConverter listener) {
super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */,
- TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */,
- 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class),
- false /* isKeyguard */, true /* shouldVibrate */,
- false /* isKeyguardBypassEnabled */);
+ TEST_SENSOR_ID, mock(BiometricLogger.class), mock(BiometricContext.class),
+ true /* isStrongBiometric */, null /* taskStackListener */,
+ mock(LockoutTracker.class), false /* isKeyguard */,
+ true /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
}
@Override
@@ -573,8 +575,9 @@ public class BiometricSchedulerTest {
@NonNull ClientMonitorCallbackConverter listener) {
super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
"test" /* owner */, mock(BiometricUtils.class),
- 5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID,
- true /* shouldVibrate */);
+ 5 /* timeoutSec */, TEST_SENSOR_ID,
+ true /* shouldVibrate */,
+ mock(BiometricLogger.class), mock(BiometricContext.class));
}
@Override
@@ -613,8 +616,8 @@ public class BiometricSchedulerTest {
TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
@NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum) {
super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
- TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */,
- 0 /* statsAction */, 0 /* statsClient */);
+ TAG, cookie, TEST_SENSOR_ID,
+ mock(BiometricLogger.class), mock(BiometricContext.class));
mProtoEnum = protoEnum;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
index 587bb600f409..092ca191acba 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
@@ -64,7 +64,7 @@ public class CompositeCallbackTest {
ClientMonitorCompositeCallback callback = new ClientMonitorCompositeCallback(callbacks);
callback.onClientStarted(mClientMonitor);
- final InOrder order = inOrder(expected);
+ final InOrder order = inOrder((Object[]) expected);
for (ClientMonitorCallback cb : expected) {
order.verify(cb).onClientStarted(eq(mClientMonitor));
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 30777cd79a0c..52eee9a55cc7 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -41,6 +41,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,6 +69,10 @@ public class UserAwareBiometricSchedulerTest {
private Context mContext;
@Mock
private IBiometricService mBiometricService;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
@@ -88,7 +95,8 @@ public class UserAwareBiometricSchedulerTest {
@Override
public StopUserClient<?> getStopUserClient(int userId) {
return new TestStopUserClient(mContext, Object::new, mToken, userId,
- TEST_SENSOR_ID, mUserStoppedCallback);
+ TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
+ mUserStoppedCallback);
}
@NonNull
@@ -96,7 +104,8 @@ public class UserAwareBiometricSchedulerTest {
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
mStartUserClientCount++;
return new TestStartUserClient(mContext, Object::new, mToken, newUserId,
- TEST_SENSOR_ID, mUserStartedCallback, mStartOperationsFinish);
+ TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
+ mUserStartedCallback, mStartOperationsFinish);
}
},
CoexCoordinator.getInstance());
@@ -226,8 +235,10 @@ public class UserAwareBiometricSchedulerTest {
private static class TestStopUserClient extends StopUserClient<Object> {
public TestStopUserClient(@NonNull Context context,
@NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId,
- int sensorId, @NonNull UserStoppedCallback callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext,
+ @NonNull UserStoppedCallback callback) {
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
}
@Override
@@ -254,8 +265,10 @@ public class UserAwareBiometricSchedulerTest {
public TestStartUserClient(@NonNull Context context,
@NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId,
- int sensorId, @NonNull UserStartedCallback<Object> callback, boolean shouldFinish) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext,
+ @NonNull UserStartedCallback<Object> callback, boolean shouldFinish) {
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
mShouldFinish = shouldFinish;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
new file mode 100644
index 000000000000..25585dd0cfc3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.server.biometrics.sensors.face.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutCache;
+import com.android.server.biometrics.sensors.face.UsageStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceAuthenticationClientTest {
+
+ private static final int USER_ID = 12;
+ private static final long OP_ID = 32;
+ private static final boolean HAS_AOD = true;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private LockoutCache mLockoutCache;
+ @Mock
+ private UsageStats mUsageStats;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricContext.isAoD()).thenReturn(HAS_AOD);
+ }
+
+ @Test
+ public void authNoContext_v1() throws RemoteException {
+ final FaceAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+
+ verify(mHal).authenticate(eq(OP_ID));
+ verify(mHal, never()).authenticateWithContext(anyLong(), any());
+ }
+
+ @Test
+ public void authWithContext_v2() throws RemoteException {
+ final FaceAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+
+ verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
+ verify(mHal, never()).authenticate(anyLong());
+
+ final OperationContext opContext = mOperationContextCaptor.getValue();
+ assertThat(opContext.isAoD).isEqualTo(HAS_AOD);
+ }
+
+ private FaceAuthenticationClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FaceAuthenticationClient(mContext, () -> aidl, mToken,
+ 2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID,
+ false /* restricted */, "test-owner", 4 /* cookie */,
+ false /* requireConfirmation */, 9 /* sensorId */,
+ mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
+ mUsageStats, mLockoutCache, false /* allowBackgroundAuthentication */,
+ false /* isKeyguardBypassEnabled */, null /* sensorPrivacyManager */);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
new file mode 100644
index 000000000000..6c72ebfd3674
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.server.biometrics.sensors.face.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceDetectClientTest {
+
+ private static final int USER_ID = 12;
+ private static final boolean HAS_AOD = true;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricContext.isAoD()).thenReturn(HAS_AOD);
+ }
+
+ @Test
+ public void detectNoContext_v1() throws RemoteException {
+ final FaceDetectClient client = createClient(1);
+ client.start(mCallback);
+
+ verify(mHal).detectInteraction();
+ verify(mHal, never()).detectInteractionWithContext(any());
+ }
+
+ @Test
+ public void detectWithContext_v2() throws RemoteException {
+ final FaceDetectClient client = createClient(2);
+ client.start(mCallback);
+
+ verify(mHal).detectInteractionWithContext(mOperationContextCaptor.capture());
+ verify(mHal, never()).detectInteraction();
+
+ final OperationContext opContext = mOperationContextCaptor.getValue();
+ assertThat(opContext.isAoD).isEqualTo(HAS_AOD);
+ }
+
+ private FaceDetectClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FaceDetectClient(mContext, () -> aidl, mToken,
+ 99 /* requestId */, mClientMonitorCallbackConverter, USER_ID,
+ "own-it", 5 /* sensorId */, mBiometricLogger, mBiometricContext,
+ false /* isStrongBiometric */, null /* sensorPrivacyManager */);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
new file mode 100644
index 000000000000..22070e9579ee
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.server.biometrics.sensors.face.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyByte;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.face.ISession;
+import android.hardware.face.Face;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceEnrollClientTest {
+
+ private static final byte[] HAT = new byte[69];
+ private static final int USER_ID = 12;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Face> mUtils;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Test
+ public void enrollNoContext_v1() throws RemoteException {
+ final FaceEnrollClient client = createClient(1);
+ client.start(mCallback);
+
+ verify(mHal).enroll(any(), anyByte(), any(), any());
+ verify(mHal, never()).enrollWithContext(any(), anyByte(), any(), any(), any());
+ }
+
+ @Test
+ public void enrollWithContext_v2() throws RemoteException {
+ final FaceEnrollClient client = createClient(2);
+ client.start(mCallback);
+
+ verify(mHal).enrollWithContext(any(), anyByte(), any(), any(), any());
+ verify(mHal, never()).enroll(any(), anyByte(), any(), any());
+ }
+
+ private FaceEnrollClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FaceEnrollClient(mContext, () -> aidl, mToken, mClientMonitorCallbackConverter,
+ USER_ID, HAT, "com.foo.bar", 44 /* requestId */,
+ mUtils, new int[0] /* disabledFeatures */, 6 /* timeoutSec */,
+ null /* previewSurface */, 8 /* sensorId */,
+ mBiometricLogger, mBiometricContext, 5 /* maxTemplatesPerUser */,
+ true /* debugConsent */);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index 61e4776e9c50..b60324e88f15 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -32,6 +32,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.CoexCoordinator;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -66,6 +68,10 @@ public class SensorTest {
private Sensor.HalSessionCallback.Callback mHalSessionCallback;
@Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -102,7 +108,8 @@ public class SensorTest {
mScheduler.scheduleClientMonitor(new FaceResetLockoutClient(mContext,
() -> new AidlSession(1, mSession, USER_ID, mHalCallback),
- USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache, mLockoutResetDispatcher));
+ USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext,
+ HAT, mLockoutCache, mLockoutResetDispatcher));
mLooper.dispatchAll();
verifyNotLocked();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
index 931fad14888e..ec083294c0bd 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
@@ -34,6 +34,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -62,6 +64,10 @@ public class FaceGenerateChallengeClientTest {
private IFaceServiceReceiver mOtherReceiver;
@Mock
private ClientMonitorCallback mMonitorCallback;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
private FaceGenerateChallengeClient mClient;
@@ -75,7 +81,7 @@ public class FaceGenerateChallengeClientTest {
mClient = new FaceGenerateChallengeClient(mContext, () -> mIBiometricsFace, new Binder(),
new ClientMonitorCallbackConverter(mClientReceiver), USER_ID,
- TAG, SENSOR_ID, START_TIME);
+ TAG, SENSOR_ID, mBiometricLogger, mBiometricContext , START_TIME);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
new file mode 100644
index 000000000000..5c360e9147c1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -0,0 +1,265 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutCache;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+public class FingerprintAuthenticationClientTest {
+
+ private static final int USER_ID = 8;
+ private static final long OP_ID = 7;
+ private static final boolean HAS_AOD = true;
+ private static final int POINTER_ID = 0;
+ private static final int TOUCH_X = 8;
+ private static final int TOUCH_Y = 20;
+ private static final float TOUCH_MAJOR = 4.4f;
+ private static final float TOUCH_MINOR = 5.5f;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private LockoutCache mLockoutCache;
+ @Mock
+ private IUdfpsOverlayController mUdfpsOverlayController;
+ @Mock
+ private ISidefpsController mSideFpsController;
+ @Mock
+ private FingerprintSensorPropertiesInternal mSensorProps;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Mock
+ private Probe mLuxProbe;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ @Captor
+ private ArgumentCaptor<PointerContext> mPointerContextCaptor;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i ->
+ new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
+ when(mBiometricContext.isAoD()).thenReturn(HAS_AOD);
+ }
+
+ @Test
+ public void authNoContext_v1() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+
+ verify(mHal).authenticate(eq(OP_ID));
+ verify(mHal, never()).authenticateWithContext(anyLong(), any());
+ }
+
+ @Test
+ public void authWithContext_v2() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+
+ verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
+ verify(mHal, never()).authenticate(anyLong());
+
+ final OperationContext opContext = mOperationContextCaptor.getValue();
+ assertThat(opContext.isAoD).isEqualTo(HAS_AOD);
+ }
+
+ @Test
+ public void pointerUp_v1() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+ client.onPointerUp();
+
+ verify(mHal).onPointerUp(eq(POINTER_ID));
+ verify(mHal, never()).onPointerUpWithContext(any());
+ }
+
+ @Test
+ public void pointerDown_v1() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+
+ verify(mHal).onPointerDown(eq(0),
+ eq(TOUCH_X), eq(TOUCH_Y), eq(TOUCH_MAJOR), eq(TOUCH_MINOR));
+ verify(mHal, never()).onPointerDownWithContext(any());
+ }
+
+ @Test
+ public void pointerUpWithContext_v2() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+ client.onPointerUp();
+
+ verify(mHal).onPointerUpWithContext(mPointerContextCaptor.capture());
+ verify(mHal, never()).onPointerUp(eq(POINTER_ID));
+
+ final PointerContext pContext = mPointerContextCaptor.getValue();
+ assertThat(pContext.pointerId).isEqualTo(POINTER_ID);
+ }
+
+ @Test
+ public void pointerDownWithContext_v2() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+
+ verify(mHal).onPointerDownWithContext(mPointerContextCaptor.capture());
+ verify(mHal, never()).onPointerDown(anyInt(), anyInt(), anyInt(), anyFloat(), anyFloat());
+
+ final PointerContext pContext = mPointerContextCaptor.getValue();
+ assertThat(pContext.pointerId).isEqualTo(POINTER_ID);
+ }
+
+ @Test
+ public void luxProbeWhenFingerDown() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient();
+ client.start(mCallback);
+
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+ verify(mLuxProbe).enable();
+
+ client.onAcquired(2, 0);
+ verify(mLuxProbe, never()).disable();
+
+ client.onPointerUp();
+ verify(mLuxProbe).disable();
+
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+ verify(mLuxProbe, times(2)).enable();
+ }
+
+ @Test
+ public void showHideOverlay_cancel() throws RemoteException {
+ showHideOverlay(c -> c.cancel());
+ }
+
+ @Test
+ public void showHideOverlay_stop() throws RemoteException {
+ showHideOverlay(c -> c.stopHalOperation());
+ }
+
+ @Test
+ public void showHideOverlay_error() throws RemoteException {
+ showHideOverlay(c -> c.onError(0, 0));
+ verify(mCallback).onClientFinished(any(), eq(false));
+ }
+
+ @Test
+ public void showHideOverlay_lockout() throws RemoteException {
+ showHideOverlay(c -> c.onLockoutTimed(5000));
+ }
+
+ @Test
+ public void showHideOverlay_lockoutPerm() throws RemoteException {
+ showHideOverlay(c -> c.onLockoutPermanent());
+ }
+
+ private void showHideOverlay(Consumer<FingerprintAuthenticationClient> block)
+ throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient();
+
+ client.start(mCallback);
+
+ verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any());
+ verify(mSideFpsController).show(anyInt(), anyInt());
+
+ block.accept(client);
+
+ verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
+ verify(mSideFpsController).hide(anyInt());
+ }
+
+ private FingerprintAuthenticationClient createClient() throws RemoteException {
+ return createClient(100);
+ }
+
+ private FingerprintAuthenticationClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken,
+ 2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID,
+ false /* restricted */, "test-owner", 4 /* cookie */, false /* requireConfirmation */,
+ 9 /* sensorId */, mBiometricLogger, mBiometricContext,
+ true /* isStrongBiometric */,
+ null /* taskStackListener */, mLockoutCache,
+ mUdfpsOverlayController, mSideFpsController,
+ false /* allowBackgroundAuthentication */, mSensorProps);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
new file mode 100644
index 000000000000..295fe4766a85
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FingerprintDetectClientTest {
+
+ private static final int USER_ID = 8;
+ private static final boolean HAS_AOD = true;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private IUdfpsOverlayController mUdfpsOverlayController;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricContext.isAoD()).thenReturn(HAS_AOD);
+ }
+
+ @Test
+ public void detectNoContext_v1() throws RemoteException {
+ final FingerprintDetectClient client = createClient(1);
+
+ client.start(mCallback);
+
+ verify(mHal).detectInteraction();
+ verify(mHal, never()).detectInteractionWithContext(any());
+ }
+
+ @Test
+ public void detectNoContext_v2() throws RemoteException {
+ final FingerprintDetectClient client = createClient(2);
+
+ client.start(mCallback);
+
+ verify(mHal).detectInteractionWithContext(mOperationContextCaptor.capture());
+ verify(mHal, never()).detectInteraction();
+ }
+
+ private FingerprintDetectClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FingerprintDetectClient(mContext, () -> aidl, mToken,
+ 6 /* requestId */, mClientMonitorCallbackConverter, 2 /* userId */,
+ "a-test", 1 /* sensorId */, mBiometricLogger, mBiometricContext,
+ mUdfpsOverlayController, true /* isStrongBiometric */);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
new file mode 100644
index 000000000000..7e44eab0a556
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+public class FingerprintEnrollClientTest {
+
+ private static final byte[] HAT = new byte[69];
+ private static final int USER_ID = 8;
+ private static final int POINTER_ID = 0;
+ private static final int TOUCH_X = 8;
+ private static final int TOUCH_Y = 20;
+ private static final float TOUCH_MAJOR = 4.4f;
+ private static final float TOUCH_MINOR = 5.5f;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Fingerprint> mBiometricUtils;
+ @Mock
+ private IUdfpsOverlayController mUdfpsOverlayController;
+ @Mock
+ private ISidefpsController mSideFpsController;
+ @Mock
+ private FingerprintSensorPropertiesInternal mSensorProps;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ @Captor
+ private ArgumentCaptor<PointerContext> mPointerContextCaptor;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Test
+ public void enrollNoContext_v1() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(1);
+
+ client.start(mCallback);
+
+ verify(mHal).enroll(any());
+ verify(mHal, never()).enrollWithContext(any(), any());
+ }
+
+ @Test
+ public void enrollWithContext_v2() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(2);
+
+ client.start(mCallback);
+
+ verify(mHal).enrollWithContext(any(), mOperationContextCaptor.capture());
+ verify(mHal, never()).enroll(any());
+ }
+
+ @Test
+ public void pointerUp_v1() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(1);
+ client.start(mCallback);
+ client.onPointerUp();
+
+ verify(mHal).onPointerUp(eq(POINTER_ID));
+ verify(mHal, never()).onPointerUpWithContext(any());
+ }
+
+ @Test
+ public void pointerDown_v1() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(1);
+ client.start(mCallback);
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+
+ verify(mHal).onPointerDown(eq(0),
+ eq(TOUCH_X), eq(TOUCH_Y), eq(TOUCH_MAJOR), eq(TOUCH_MINOR));
+ verify(mHal, never()).onPointerDownWithContext(any());
+ }
+
+ @Test
+ public void pointerUpWithContext_v2() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(2);
+ client.start(mCallback);
+ client.onPointerUp();
+
+ verify(mHal).onPointerUpWithContext(mPointerContextCaptor.capture());
+ verify(mHal, never()).onPointerUp(eq(POINTER_ID));
+
+ final PointerContext pContext = mPointerContextCaptor.getValue();
+ assertThat(pContext.pointerId).isEqualTo(POINTER_ID);
+ }
+
+ @Test
+ public void pointerDownWithContext_v2() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(2);
+ client.start(mCallback);
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+
+ verify(mHal).onPointerDownWithContext(mPointerContextCaptor.capture());
+ verify(mHal, never()).onPointerDown(anyInt(), anyInt(), anyInt(), anyFloat(), anyFloat());
+
+ final PointerContext pContext = mPointerContextCaptor.getValue();
+ assertThat(pContext.pointerId).isEqualTo(POINTER_ID);
+ }
+
+ @Test
+ public void showHideOverlay_cancel() throws RemoteException {
+ showHideOverlay(c -> c.cancel());
+ }
+
+ @Test
+ public void showHideOverlay_stop() throws RemoteException {
+ showHideOverlay(c -> c.stopHalOperation());
+ }
+
+ @Test
+ public void showHideOverlay_error() throws RemoteException {
+ showHideOverlay(c -> c.onError(0, 0));
+ verify(mCallback).onClientFinished(any(), eq(false));
+ }
+
+ @Test
+ public void showHideOverlay_result() throws RemoteException {
+ showHideOverlay(c -> c.onEnrollResult(new Fingerprint("", 1, 1), 0));
+ }
+
+ private void showHideOverlay(Consumer<FingerprintEnrollClient> block)
+ throws RemoteException {
+ final FingerprintEnrollClient client = createClient();
+
+ client.start(mCallback);
+
+ verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any());
+ verify(mSideFpsController).show(anyInt(), anyInt());
+
+ block.accept(client);
+
+ verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
+ verify(mSideFpsController).hide(anyInt());
+ }
+
+ private FingerprintEnrollClient createClient() throws RemoteException {
+ return createClient(500);
+ }
+
+ private FingerprintEnrollClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FingerprintEnrollClient(mContext, () -> aidl, mToken, 6 /* requestId */,
+ mClientMonitorCallbackConverter, 0 /* userId */,
+ HAT, "owner", mBiometricUtils, 8 /* sensorId */,
+ mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController,
+ mSideFpsController, 6 /* maxTemplatesPerUser */, FingerprintManager.ENROLL_ENROLL);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 8b7b484b8462..e1a4a2d9f969 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -32,6 +32,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.CoexCoordinator;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -66,6 +68,10 @@ public class SensorTest {
private Sensor.HalSessionCallback.Callback mHalSessionCallback;
@Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
+ @Mock
+ private BiometricLogger mLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -102,7 +108,8 @@ public class SensorTest {
mScheduler.scheduleClientMonitor(new FingerprintResetLockoutClient(mContext,
() -> new AidlSession(1, mSession, USER_ID, mHalCallback),
- USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache, mLockoutResetDispatcher));
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT, mLockoutCache,
+ mLockoutResetDispatcher));
mLooper.dispatchAll();
verifyNotLocked();
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index ff1b6f66ef85..83fa7ac02503 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -17,17 +17,23 @@
package com.android.server.companion.virtual;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInputConstants;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.runner.AndroidJUnit4;
@@ -46,18 +52,31 @@ public class InputControllerTest {
@Mock
private InputManagerInternal mInputManagerInternalMock;
@Mock
+ private DisplayManagerInternal mDisplayManagerInternalMock;
+ @Mock
private InputController.NativeWrapper mNativeWrapperMock;
+ @Mock
+ private IInputManager mIInputManagerMock;
private InputController mInputController;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = "uniqueId";
+ doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
+ InputManager.resetInstance(mIInputManagerMock);
+ doNothing().when(mIInputManagerMock).addUniqueIdAssociation(anyString(), anyString());
+ doNothing().when(mIInputManagerMock).removeUniqueIdAssociation(anyString());
mInputController = new InputController(new Object(), mNativeWrapperMock);
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index e36263e247bf..ceb723a407f9 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -57,6 +58,7 @@ import android.os.WorkSource;
import android.platform.test.annotations.Presubmit;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.DisplayInfo;
import android.view.KeyEvent;
import androidx.test.InstrumentationRegistry;
@@ -80,6 +82,8 @@ public class VirtualDeviceManagerServiceTest {
private static final int DISPLAY_ID = 2;
private static final int PRODUCT_ID = 10;
private static final int VENDOR_ID = 5;
+ private static final String UNIQUE_ID = "uniqueid";
+ private static final String PHYS = "phys";
private static final int HEIGHT = 1800;
private static final int WIDTH = 900;
private static final Binder BINDER = new Binder("binder");
@@ -116,6 +120,12 @@ public class VirtualDeviceManagerServiceTest {
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = UNIQUE_ID;
+ doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
doNothing().when(mContext).enforceCallingOrSelfPermission(
eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
@@ -274,7 +284,8 @@ public class VirtualDeviceManagerServiceTest {
BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
- verify(mNativeWrapperMock).openUinputKeyboard(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
+ verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID),
+ eq(PRODUCT_ID), anyString());
}
@Test
@@ -284,7 +295,8 @@ public class VirtualDeviceManagerServiceTest {
BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
- verify(mNativeWrapperMock).openUinputMouse(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
+ verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID),
+ anyString());
}
@Test
@@ -294,8 +306,8 @@ public class VirtualDeviceManagerServiceTest {
BINDER, new Point(WIDTH, HEIGHT));
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
- verify(mNativeWrapperMock).openUinputTouchscreen(DEVICE_NAME, VENDOR_ID, PRODUCT_ID, HEIGHT,
- WIDTH);
+ verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID),
+ eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH));
}
@Test
@@ -315,7 +327,7 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualKeyEvent.ACTION_UP;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 1,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode)
.setAction(action).build());
verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
@@ -340,7 +352,7 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mInputController.mActivePointerDisplayId = 1;
mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
.setButtonCode(buttonCode)
@@ -355,7 +367,7 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
assertThrows(
IllegalStateException.class,
() ->
@@ -381,7 +393,7 @@ public class VirtualDeviceManagerServiceTest {
final float y = 0.7f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mInputController.mActivePointerDisplayId = 1;
mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
.setRelativeX(x).setRelativeY(y).build());
@@ -395,7 +407,7 @@ public class VirtualDeviceManagerServiceTest {
final float y = 0.7f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
assertThrows(
IllegalStateException.class,
() ->
@@ -422,7 +434,7 @@ public class VirtualDeviceManagerServiceTest {
final float y = 1f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mInputController.mActivePointerDisplayId = 1;
mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
.setXAxisMovement(x)
@@ -437,7 +449,7 @@ public class VirtualDeviceManagerServiceTest {
final float y = 1f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
assertThrows(
IllegalStateException.class,
() ->
@@ -470,7 +482,7 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualTouchEvent.ACTION_UP;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
.setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build());
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
@@ -489,7 +501,7 @@ public class VirtualDeviceManagerServiceTest {
final float majorAxisSize = 10.0f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
.setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType)
.setPressure(pressure).setMajorAxisSize(majorAxisSize).build());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 564c4e439d86..64ce6b282f17 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -28,6 +28,14 @@ import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
@@ -101,6 +109,7 @@ import android.app.admin.WifiSsidPolicy;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -138,6 +147,9 @@ import com.android.internal.widget.LockscreenCredential;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener;
+import com.android.server.pm.RestrictionsSet;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserRestrictionsUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
@@ -7764,6 +7776,296 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
@Test
+ public void testSetUserRestriction_financeDo_invalidRestrictions_restrictionNotSet()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ for (String restriction : UserRestrictionsUtils.USER_RESTRICTIONS) {
+ if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(restriction)) {
+ assertNoDeviceOwnerRestrictions();
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.addUserRestriction(admin1, restriction));
+
+ verify(getServices().userManagerInternal, never())
+ .setDevicePolicyUserRestrictions(anyInt(), any(), any(), anyBoolean());
+ }
+ }
+ }
+
+ @Test
+ public void testSetUserRestriction_financeDo_validRestrictions_setsRestriction()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ for (String restriction : UserRestrictionsUtils.USER_RESTRICTIONS) {
+ if (UserRestrictionsUtils.canFinancedDeviceOwnerChange(restriction)) {
+ assertNoDeviceOwnerRestrictions();
+ dpm.addUserRestriction(admin1, restriction);
+
+ Bundle globalRestrictions =
+ dpms.getDeviceOwnerAdminLocked().getGlobalUserRestrictions(
+ UserManagerInternal.OWNER_TYPE_DEVICE_OWNER);
+ RestrictionsSet localRestrictions = new RestrictionsSet();
+ localRestrictions.updateRestrictions(
+ UserHandle.USER_SYSTEM,
+ dpms.getDeviceOwnerAdminLocked().getLocalUserRestrictions(
+ UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+ verify(getServices().userManagerInternal)
+ .setDevicePolicyUserRestrictions(eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(globalRestrictions),
+ MockUtils.checkUserRestrictions(
+ UserHandle.USER_SYSTEM, localRestrictions),
+ eq(true));
+ reset(getServices().userManagerInternal);
+
+ dpm.clearUserRestriction(admin1, restriction);
+ reset(getServices().userManagerInternal);
+ }
+ }
+ }
+
+ @Test
+ public void testSetLockTaskFeatures_financeDo_validLockTaskFeatures_lockTaskFeaturesSet()
+ throws Exception {
+ int validLockTaskFeatures = LOCK_TASK_FEATURE_SYSTEM_INFO | LOCK_TASK_FEATURE_KEYGUARD
+ | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_GLOBAL_ACTIONS
+ | LOCK_TASK_FEATURE_NOTIFICATIONS;
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setLockTaskFeatures(admin1, validLockTaskFeatures);
+
+ verify(getServices().iactivityTaskManager)
+ .updateLockTaskFeatures(eq(UserHandle.USER_SYSTEM), eq(validLockTaskFeatures));
+ }
+
+ @Test
+ public void testSetLockTaskFeatures_financeDo_invalidLockTaskFeatures_throwsException()
+ throws Exception {
+ int invalidLockTaskFeatures = LOCK_TASK_FEATURE_NONE | LOCK_TASK_FEATURE_OVERVIEW
+ | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+ // Called during setup.
+ verify(getServices().iactivityTaskManager).updateLockTaskFeatures(anyInt(), anyInt());
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setLockTaskFeatures(admin1, invalidLockTaskFeatures));
+
+ verifyNoMoreInteractions(getServices().iactivityTaskManager);
+ }
+
+ @Test
+ public void testIsUninstallBlocked_financeDo_success() throws Exception {
+ String packageName = "com.android.foo.package";
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+ when(getServices().ipackageManager.getBlockUninstallForUser(
+ eq(packageName), eq(UserHandle.USER_SYSTEM)))
+ .thenReturn(true);
+
+ assertThat(dpm.isUninstallBlocked(admin1, packageName)).isTrue();
+ }
+
+ @Test
+ public void testSetUninstallBlocked_financeDo_success() throws Exception {
+ String packageName = "com.android.foo.package";
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setUninstallBlocked(admin1, packageName, false);
+
+ verify(getServices().ipackageManager)
+ .setBlockUninstallForUser(eq(packageName), eq(false),
+ eq(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testSetUserControlDisabledPackages_financeDo_success() throws Exception {
+ List<String> packages = new ArrayList<>();
+ packages.add("com.android.foo.package");
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setUserControlDisabledPackages(admin1, packages);
+
+ verify(getServices().packageManagerInternal)
+ .setDeviceOwnerProtectedPackages(eq(admin1.getPackageName()), eq(packages));
+ }
+
+ @Test
+ public void testGetUserControlDisabledPackages_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertThat(dpm.getUserControlDisabledPackages(admin1)).isEmpty();
+ }
+
+ @Test
+ public void testSetOrganizationName_financeDo_success() throws Exception {
+ String organizationName = "Test Organization";
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setOrganizationName(admin1, organizationName);
+
+ assertThat(dpm.getDeviceOwnerOrganizationName()).isEqualTo(organizationName);
+ }
+
+ @Test
+ public void testSetShortSupportMessage_financeDo_success() throws Exception {
+ String supportMessage = "Test short support message";
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setShortSupportMessage(admin1, supportMessage);
+
+ assertThat(dpm.getShortSupportMessage(admin1)).isEqualTo(supportMessage);
+ }
+
+ @Test
+ public void testIsBackupServiceEnabled_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+ when(getServices().ibackupManager.isBackupServiceActive(eq(UserHandle.USER_SYSTEM)))
+ .thenReturn(true);
+
+ assertThat(dpm.isBackupServiceEnabled(admin1)).isTrue();
+ }
+
+ @Test
+ public void testSetBackupServiceEnabled_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setBackupServiceEnabled(admin1, true);
+
+ verify(getServices().ibackupManager)
+ .setBackupServiceActive(eq(UserHandle.USER_SYSTEM), eq(true));
+ }
+
+ @Test
+ public void testIsLockTaskPermitted_financeDo_success() throws Exception {
+ String packageName = "com.android.foo.package";
+ mockPolicyExemptApps(packageName);
+ mockVendorPolicyExemptApps();
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertThat(dpm.isLockTaskPermitted(packageName)).isTrue();
+ }
+
+ @Test
+ public void testSetLockTaskPackages_financeDo_success() throws Exception {
+ String[] packages = {"com.android.foo.package"};
+ mockEmptyPolicyExemptApps();
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setLockTaskPackages(admin1, packages);
+
+ verify(getServices().iactivityManager)
+ .updateLockTaskPackages(eq(UserHandle.USER_SYSTEM), eq(packages));
+ }
+
+ @Test
+ public void testAddPersistentPreferredActivity_financeDo_success() throws Exception {
+ IntentFilter filter = new IntentFilter();
+ ComponentName target = new ComponentName(admin2.getPackageName(), "test.class");
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.addPersistentPreferredActivity(admin1, filter, target);
+
+ verify(getServices().ipackageManager)
+ .addPersistentPreferredActivity(eq(filter), eq(target), eq(UserHandle.USER_SYSTEM));
+ verify(getServices().ipackageManager)
+ .flushPackageRestrictionsAsUser(eq(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testClearPackagePersistentPreferredActvities_financeDo_success() throws Exception {
+ String packageName = admin2.getPackageName();
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.clearPackagePersistentPreferredActivities(admin1, packageName);
+
+ verify(getServices().ipackageManager)
+ .clearPackagePersistentPreferredActivities(
+ eq(packageName), eq(UserHandle.USER_SYSTEM));
+ verify(getServices().ipackageManager)
+ .flushPackageRestrictionsAsUser(eq(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testWipeData_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+ when(getServices().userManager.getUserRestrictionSource(
+ UserManager.DISALLOW_FACTORY_RESET,
+ UserHandle.SYSTEM))
+ .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+ when(mMockContext.getResources()
+ .getString(R.string.work_profile_deleted_description_dpm_wipe))
+ .thenReturn("Test string");
+
+ dpm.wipeData(0);
+
+ verifyRebootWipeUserData(/* wipeEuicc= */ false);
+ }
+
+ @Test
+ public void testIsDeviceOwnerApp_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isTrue();
+ }
+
+ @Test
+ public void testClearDeviceOwnerApp_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.clearDeviceOwnerApp(admin1.getPackageName());
+
+ assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isNull();
+ assertThat(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM)).isFalse();
+ verify(mMockContext.spiedContext, times(2))
+ .sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
+ eq(UserHandle.SYSTEM));
+ }
+
+ @Test
+ public void testSetPermissionGrantState_financeDo_notReadPhoneStatePermission_throwsException()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPermissionGrantState(admin1, admin1.getPackageName(),
+ permission.READ_CALENDAR,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED));
+ }
+
+ @Test
+ public void testSetPermissionGrantState_financeDo_grantPermissionToNonDeviceOwnerPackage_throwsException()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPermissionGrantState(admin1, "com.android.foo.package",
+ permission.READ_PHONE_STATE,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED));
+ }
+
+ @Test
public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() {
assertThrows(SecurityException.class,
() -> dpm.setUsbDataSignalingEnabled(true));
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index 15f3ed1be552..4cb46b4047b0 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -109,6 +109,14 @@ public class MockUtils {
public static Bundle checkUserRestrictions(String... keys) {
final Bundle expected = DpmTestUtils.newRestrictions(
java.util.Objects.requireNonNull(keys));
+ return checkUserRestrictions(expected);
+ }
+
+ public static Bundle checkUserRestrictions(Bundle expected) {
+ return createUserRestrictionsBundleMatcher(expected);
+ }
+
+ private static Bundle createUserRestrictionsBundleMatcher(Bundle expected) {
final Matcher<Bundle> m = new BaseMatcher<Bundle>() {
@Override
public boolean matches(Object item) {
@@ -129,6 +137,15 @@ public class MockUtils {
public static RestrictionsSet checkUserRestrictions(int userId, String... keys) {
final RestrictionsSet expected = DpmTestUtils.newRestrictions(userId,
java.util.Objects.requireNonNull(keys));
+ return checkUserRestrictions(userId, expected);
+ }
+
+ public static RestrictionsSet checkUserRestrictions(int userId, RestrictionsSet expected) {
+ return createUserRestrictionsSetMatcher(userId, expected);
+ }
+
+ private static RestrictionsSet createUserRestrictionsSetMatcher(
+ int userId, RestrictionsSet expected) {
final Matcher<RestrictionsSet> m = new BaseMatcher<RestrictionsSet>() {
@Override
public boolean matches(Object item) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
index a8f24ce5e11d..533fb2d8d214 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
@@ -26,6 +26,7 @@ import static android.app.admin.DevicePolicyManager.REQUIRED_APP_MANAGED_USER;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@@ -76,6 +77,7 @@ public class OverlayPackagesProviderTest {
private static final ComponentName TEST_MDM_COMPONENT_NAME = new ComponentName(
TEST_DPC_PACKAGE_NAME, "pc.package.name.DeviceAdmin");
private static final int TEST_USER_ID = 123;
+ private static final String ROLE_HOLDER_PACKAGE_NAME = "test.role.holder.package.name";
private @Mock Resources mResources;
@@ -305,6 +307,26 @@ public class OverlayPackagesProviderTest {
ACTION_PROVISION_MANAGED_PROFILE, "package1", "package2", "package3");
}
+ @Test
+ public void testGetNonRequiredApps_managedProfile_roleHolder_works() {
+ when(mInjector.getDeviceManagerRoleHolderPackageName(any()))
+ .thenReturn(ROLE_HOLDER_PACKAGE_NAME);
+ setSystemAppsWithLauncher("package1", "package2", ROLE_HOLDER_PACKAGE_NAME);
+
+ verifyAppsAreNonRequired(
+ ACTION_PROVISION_MANAGED_PROFILE, "package1", "package2");
+ }
+
+ @Test
+ public void testGetNonRequiredApps_managedDevice_roleHolder_works() {
+ when(mInjector.getDeviceManagerRoleHolderPackageName(any()))
+ .thenReturn(ROLE_HOLDER_PACKAGE_NAME);
+ setSystemAppsWithLauncher("package1", "package2", ROLE_HOLDER_PACKAGE_NAME);
+
+ verifyAppsAreNonRequired(
+ ACTION_PROVISION_MANAGED_DEVICE, "package1", "package2");
+ }
+
private void setupRegularModulesWithManagedUser(String... regularModules) {
setupRegularModulesWithMetadata(regularModules, REQUIRED_APP_MANAGED_USER);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index b7af010103bc..6203c2f54f07 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -663,6 +663,30 @@ public class HighBrightnessModeControllerTest {
eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING));
}
+ @Test
+ public void tetHbmStats_LowRequestedBrightness() {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+ final int displayStatsId = mDisplayUniqueId.hashCode();
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
+ advanceTime(0);
+ // verify in HBM sunlight mode
+ assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+ // verify HBM_ON_SUNLIGHT
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN);
+ // verify HBM_SV_OFF due to LOW_REQUESTED_BRIGHTNESS
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS));
+ }
+
private void assertState(HighBrightnessModeController hbmc,
float brightnessMin, float brightnessMax, int hbmMode) {
assertEquals(brightnessMin, hbmc.getCurrentBrightnessMin(), EPSILON);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 5eed30be9279..91d4f8f63f38 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -67,6 +67,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
@Mock Vibrator mVibrator;
private final String callPkg = "com.android.server.notification";
+ private final String sysPkg = "android";
private final int callUid = 10;
private String smsPkg;
private final int smsUid = 11;
@@ -79,6 +80,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
private NotificationRecord mRecordHighCall;
private NotificationRecord mRecordHighCallStyle;
private NotificationRecord mRecordEmail;
+ private NotificationRecord mRecordSystemMax;
private NotificationRecord mRecordInlineReply;
private NotificationRecord mRecordSms;
private NotificationRecord mRecordStarredContact;
@@ -191,6 +193,12 @@ public class NotificationComparatorTest extends UiServiceTestCase {
mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT);
mRecordContact.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
+ Notification nSystemMax = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
+ mRecordSystemMax = new NotificationRecord(mContext, new StatusBarNotification(sysPkg,
+ sysPkg, 1, "systemmax", uid2, uid2, nSystemMax, new UserHandle(userId),
+ "", 1244), getDefaultChannel());
+ mRecordSystemMax.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
+
Notification n8 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
@@ -267,6 +275,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
}
expected.add(mRecordStarredContact);
expected.add(mRecordContact);
+ expected.add(mRecordSystemMax);
expected.add(mRecordEmail);
expected.add(mRecordUrgent);
expected.add(mNoMediaSessionMedia);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index fa294dd61ea3..3b6718207c83 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -20,8 +20,8 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.permission.PermissionManager.PERMISSION_GRANTED;
-import static android.permission.PermissionManager.PERMISSION_SOFT_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.google.common.truth.Truth.assertThat;
@@ -130,13 +130,13 @@ public class PermissionHelperTest extends UiServiceTestCase {
@Test
public void testHasPermission() throws Exception {
- when(mPmi.checkUidPermission(anyInt(), eq(Manifest.permission.POST_NOTIFICATIONS)))
+ when(mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(anyInt()))
.thenReturn(PERMISSION_GRANTED);
assertThat(mPermissionHelper.hasPermission(1)).isTrue();
- when(mPmi.checkUidPermission(anyInt(), eq(Manifest.permission.POST_NOTIFICATIONS)))
- .thenReturn(PERMISSION_SOFT_DENIED);
+ when(mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(anyInt()))
+ .thenReturn(PERMISSION_DENIED);
assertThat(mPermissionHelper.hasPermission(1)).isFalse();
}
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index 24fda17dbe79..e65501329cdf 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -69,6 +69,12 @@ class BroadcastResponseStatsTracker {
private SparseArray<SparseArray<UserBroadcastResponseStats>> mUserResponseStats =
new SparseArray<>();
+ private AppStandbyInternal mAppStandby;
+
+ BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby) {
+ mAppStandby = appStandby;
+ }
+
// TODO (206518114): Move all callbacks handling to a handler thread.
void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
UserHandle targetUser, long idForResponseEvent,
@@ -132,8 +138,7 @@ class BroadcastResponseStatsTracker {
if (dispatchTimestampMs >= timestampMs) {
continue;
}
- // TODO (206518114): Make the constant configurable.
- if (elapsedDurationMs <= 2 * 60 * 1000) {
+ if (elapsedDurationMs <= mAppStandby.getBroadcastResponseWindowDurationMs()) {
final BroadcastEvent broadcastEvent = broadcastEvents.valueAt(i);
final BroadcastResponseStats responseStats =
getBroadcastResponseStats(broadcastEvent);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index e90d28a11e1d..6906f20f26c2 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -281,7 +281,7 @@ public class UsageStatsService extends SystemService implements
mHandler = new H(BackgroundThread.get().getLooper());
mAppStandby = mInjector.getAppStandbyController(getContext());
- mResponseStatsTracker = new BroadcastResponseStatsTracker();
+ mResponseStatsTracker = new BroadcastResponseStatsTracker(mAppStandby);
mAppTimeLimit = new AppTimeLimitController(getContext(),
new AppTimeLimitController.TimeLimitCallbackListener() {