summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp5
-rw-r--r--ProtoLibraries.bp1
-rw-r--r--STABILITY_OWNERS2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java8
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java8
-rw-r--r--core/api/current.txt41
-rw-r--r--core/api/system-current.txt9
-rw-r--r--core/api/test-current.txt3
-rw-r--r--core/java/android/app/ActivityManagerInternal.java4
-rw-r--r--core/java/android/app/ApplicationPackageManager.java2
-rw-r--r--core/java/android/app/INotificationManager.aidl16
-rw-r--r--core/java/android/app/NotificationManager.java75
-rw-r--r--core/java/android/app/usage/UsageEventsQuery.java22
-rw-r--r--core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl17
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraCallback.java12
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraConfig.java7
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraMetadata.java61
-rw-r--r--core/java/android/content/pm/ActivityInfo.java12
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl3
-rw-r--r--core/java/android/content/pm/PackageManager.java3
-rw-r--r--core/java/android/content/pm/flags.aconfig8
-rw-r--r--core/java/android/content/pm/multiuser.aconfig8
-rw-r--r--core/java/android/database/CursorWindow.java31
-rw-r--r--core/java/android/database/SQLException.java1
-rw-r--r--core/java/android/database/sqlite/SQLiteClosable.java1
-rw-r--r--core/java/android/database/sqlite/SQLiteException.java1
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java17
-rw-r--r--core/java/android/hardware/face/FaceSensorConfigurations.aidl (renamed from core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl)10
-rw-r--r--core/java/android/hardware/face/FaceSensorConfigurations.java182
-rw-r--r--core/java/android/hardware/face/HidlFaceSensorConfig.java85
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl5
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.aidl (renamed from packages/SystemUI/src/com/android/systemui/common/domain/CommonDomainLayerModule.kt)13
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java184
-rw-r--r--core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java119
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl5
-rw-r--r--core/java/android/provider/ContactsContract.java8
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java52
-rw-r--r--core/java/android/service/voice/VisualQueryDetector.java94
-rw-r--r--core/java/android/view/DisplayEventReceiver.java17
-rw-r--r--core/java/android/view/ViewRootImpl.java29
-rw-r--r--core/java/android/view/WindowManager.java12
-rw-r--r--core/java/android/window/BackMotionEvent.java16
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig7
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig16
-rw-r--r--core/java/com/android/internal/app/SuspendedAppActivity.java12
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java34
-rw-r--r--core/jni/AndroidRuntime.cpp5
-rw-r--r--core/jni/android_view_DisplayEventReceiver.cpp22
-rw-r--r--core/proto/android/server/activitymanagerservice.proto1
-rw-r--r--core/tests/coretests/Android.bp4
-rw-r--r--core/tests/coretests/src/android/database/CursorWindowPerformanceTest.java64
-rw-r--r--core/tests/coretests/src/android/database/CursorWindowTest.java49
-rw-r--r--core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java97
-rw-r--r--core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java102
-rw-r--r--core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java1
-rw-r--r--core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java2
-rw-r--r--data/etc/services.core.protolog.json18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationConstants.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java169
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt119
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java1
-rw-r--r--packages/SettingsLib/Android.bp16
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java13
-rw-r--r--packages/SettingsLib/tests/integ/Android.bp2
-rw-r--r--packages/SettingsLib/tests/robotests/Android.bp2
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt53
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt3
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt5
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt25
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt7
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt40
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt164
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt9
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt10
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeTest.kt63
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt40
-rw-r--r--packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt30
-rw-r--r--packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt110
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelTest.kt83
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt285
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt196
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt)102
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt97
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt54
-rw-r--r--packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml4
-rw-r--r--packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml2
-rw-r--r--packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml2
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml4
-rw-r--r--packages/SystemUI/res/values/config.xml8
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/ids.xml3
-rw-r--r--packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/domain/interactor/ConfigurationInteractor.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt123
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt210
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt (renamed from packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt)44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/AsyncHybridViewInflation.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/DisplaySwitchNotificationsHiderFlag.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarViewBinderConstants.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorKosmos.kt)13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorTest.kt138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt85
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt116
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt61
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt42
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt)0
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt49
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt14
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt62
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorKosmos.kt2
-rw-r--r--ravenwood/ravenwood-annotation-allowed-classes.txt4
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java6
-rw-r--r--services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java4
-rw-r--r--services/core/Android.bp5
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java20
-rw-r--r--services/core/java/com/android/server/BootReceiver.java83
-rw-r--r--services/core/java/com/android/server/VpnManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java103
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java2
-rw-r--r--services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java15
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java2
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java291
-rw-r--r--services/core/java/com/android/server/am/UserController.java4
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig7
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java7
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java111
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java4
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java7
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java3
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java167
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java88
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java114
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java219
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java175
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java20
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java248
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java (renamed from services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java)150
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java85
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java132
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java245
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java203
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java27
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java297
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java (renamed from services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java)80
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java46
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java9
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java91
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java58
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java14
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java4
-rw-r--r--services/core/java/com/android/server/location/altitude/AltitudeService.java40
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java18
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java2
-rw-r--r--services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java1
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java103
-rw-r--r--services/core/java/com/android/server/notification/NotificationShellCmd.java9
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java4
-rw-r--r--services/core/java/com/android/server/os/NativeTombstoneManager.java135
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterImpl.java4
-rw-r--r--services/core/java/com/android/server/pm/CleanUpArgs.java62
-rw-r--r--services/core/java/com/android/server/pm/Computer.java4
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java9
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java6
-rw-r--r--services/core/java/com/android/server/pm/InstallArgs.java23
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java15
-rw-r--r--services/core/java/com/android/server/pm/InstallRequest.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageArchiver.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageHandler.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerInternalBase.java19
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java55
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageRemovedInfo.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java39
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java18
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java5
-rw-r--r--services/core/java/com/android/server/pm/Settings.java52
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java160
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java4
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java23
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java5
-rw-r--r--services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java5
-rw-r--r--services/core/java/com/android/server/utils/WatchedSparseSetArray.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivitySnapshotController.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java160
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java1
-rw-r--r--services/core/java/com/android/server/wm/ClientLifecycleManager.java53
-rw-r--r--services/core/java/com/android/server/wm/CompatModePackages.java55
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java11
-rw-r--r--services/core/java/com/android/server/wm/InsetsControlTarget.java4
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java7
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java28
-rw-r--r--services/core/java/com/android/server/wm/Session.java44
-rw-r--r--services/core/java/com/android/server/wm/Task.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java20
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java67
-rw-r--r--services/core/jni/Android.bp11
-rw-r--r--services/core/jni/OWNERS1
-rw-r--r--services/core/jni/com_android_server_BootReceiver.cpp57
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java4
-rw-r--r--services/manifest_services.xml2
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt11
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java174
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java17
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java9
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt70
-rw-r--r--services/tests/servicestests/src/com/android/server/BootReceiverTest.java97
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java66
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java205
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java235
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java (renamed from services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java)81
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java83
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java301
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java (renamed from services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java)28
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java7
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java6
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java188
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java43
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java2
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java14
-rw-r--r--services/voiceinteraction/Android.bp6
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java130
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java51
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl3
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java18
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java3
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl4
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java2
-rw-r--r--tools/hoststubgen/hoststubgen/Android.bp3
-rw-r--r--tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java159
342 files changed, 9512 insertions, 3306 deletions
diff --git a/Android.bp b/Android.bp
index 96d26cf2f0be..bb9304819e80 100644
--- a/Android.bp
+++ b/Android.bp
@@ -97,6 +97,7 @@ filegroup {
// AIDL sources from external directories
":android.hardware.biometrics.common-V4-java-source",
":android.hardware.biometrics.fingerprint-V3-java-source",
+ ":android.hardware.biometrics.face-V4-java-source",
":android.hardware.gnss-V2-java-source",
":android.hardware.graphics.common-V3-java-source",
":android.hardware.keymaster-V4-java-source",
@@ -206,11 +207,13 @@ java_defaults {
// framework_srcs. These have no or very limited dependency to the framework.
java_library {
name: "framework-internal-utils",
+ defaults: [
+ "android.hardware.power-java_static",
+ ],
static_libs: [
"apex_aidl_interface-java",
"packagemanager_aidl-java",
"framework-protos",
- "libtombstone_proto_java",
"updatable-driver-protos",
"ota_metadata_proto_java",
"android.hidl.base-V1.0-java",
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index d03bbd249b00..e7adf203334e 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -34,6 +34,7 @@ gensrcs {
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
+ ":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
],
diff --git a/STABILITY_OWNERS b/STABILITY_OWNERS
deleted file mode 100644
index a7ecb4dfdd44..000000000000
--- a/STABILITY_OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-gaillard@google.com
-
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 83db4cbb7e43..900c90203f41 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1828,7 +1828,9 @@ public class JobSchedulerService extends com.android.server.SystemService
/* system_measured_source_download_bytes */0,
/* system_measured_source_upload_bytes */ 0,
/* system_measured_calling_download_bytes */0,
- /* system_measured_calling_upload_bytes */ 0);
+ /* system_measured_calling_upload_bytes */ 0,
+ jobStatus.getJob().getIntervalMillis(),
+ jobStatus.getJob().getFlexMillis());
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -2269,7 +2271,9 @@ public class JobSchedulerService extends com.android.server.SystemService
/* system_measured_source_download_bytes */ 0,
/* system_measured_source_upload_bytes */ 0,
/* system_measured_calling_download_bytes */0,
- /* system_measured_calling_upload_bytes */ 0);
+ /* system_measured_calling_upload_bytes */ 0,
+ cancelled.getJob().getIntervalMillis(),
+ cancelled.getJob().getFlexMillis());
}
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 6449edcd3103..3addf9f98db2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -534,7 +534,9 @@ public final class JobServiceContext implements ServiceConnection {
/* system_measured_source_download_bytes */ 0,
/* system_measured_source_upload_bytes */ 0,
/* system_measured_calling_download_bytes */ 0,
- /* system_measured_calling_upload_bytes */ 0);
+ /* system_measured_calling_upload_bytes */ 0,
+ job.getJob().getIntervalMillis(),
+ job.getJob().getFlexMillis());
sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1616,7 +1618,9 @@ public final class JobServiceContext implements ServiceConnection {
TrafficStats.getUidRxBytes(completedJob.getUid())
- mInitialDownloadedBytesFromCalling,
TrafficStats.getUidTxBytes(completedJob.getUid())
- - mInitialUploadedBytesFromCalling);
+ - mInitialUploadedBytesFromCalling,
+ completedJob.getJob().getIntervalMillis(),
+ completedJob.getJob().getFlexMillis());
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
getId());
diff --git a/core/api/current.txt b/core/api/current.txt
index 32ed91045791..d3ed89b347ec 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -35874,19 +35874,19 @@ package android.provider {
field public static final String NAMESPACE = "data2";
}
- public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getProtocolLabel(android.content.res.Resources, int, CharSequence);
- method public static int getProtocolLabelResource(int);
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
- method public static int getTypeLabelResource(int);
- field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
- field public static final String CUSTOM_PROTOCOL = "data6";
+ @Deprecated public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
+ method @Deprecated public static CharSequence getProtocolLabel(android.content.res.Resources, int, CharSequence);
+ method @Deprecated public static int getProtocolLabelResource(int);
+ method @Deprecated public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
+ method @Deprecated public static int getTypeLabelResource(int);
+ field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
+ field @Deprecated public static final String CUSTOM_PROTOCOL = "data6";
field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
field public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
- field public static final String PROTOCOL = "data5";
+ field @Deprecated public static final String PROTOCOL = "data5";
field @Deprecated public static final int PROTOCOL_AIM = 0; // 0x0
- field public static final int PROTOCOL_CUSTOM = -1; // 0xffffffff
+ field @Deprecated public static final int PROTOCOL_CUSTOM = -1; // 0xffffffff
field @Deprecated public static final int PROTOCOL_GOOGLE_TALK = 5; // 0x5
field @Deprecated public static final int PROTOCOL_ICQ = 6; // 0x6
field @Deprecated public static final int PROTOCOL_JABBER = 7; // 0x7
@@ -35895,9 +35895,9 @@ package android.provider {
field @Deprecated public static final int PROTOCOL_QQ = 4; // 0x4
field @Deprecated public static final int PROTOCOL_SKYPE = 3; // 0x3
field @Deprecated public static final int PROTOCOL_YAHOO = 2; // 0x2
- field public static final int TYPE_HOME = 1; // 0x1
- field public static final int TYPE_OTHER = 3; // 0x3
- field public static final int TYPE_WORK = 2; // 0x2
+ field @Deprecated public static final int TYPE_HOME = 1; // 0x1
+ field @Deprecated public static final int TYPE_OTHER = 3; // 0x3
+ field @Deprecated public static final int TYPE_WORK = 2; // 0x2
}
public static final class ContactsContract.CommonDataKinds.Nickname implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
@@ -36012,17 +36012,17 @@ package android.provider {
field public static final int TYPE_SPOUSE = 14; // 0xe
}
- public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
- method public static int getTypeLabelResource(int);
- field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
+ @Deprecated public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
+ method @Deprecated public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
+ method @Deprecated public static int getTypeLabelResource(int);
+ field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
field public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
- field public static final String SIP_ADDRESS = "data1";
- field public static final int TYPE_HOME = 1; // 0x1
- field public static final int TYPE_OTHER = 3; // 0x3
- field public static final int TYPE_WORK = 2; // 0x2
+ field @Deprecated public static final String SIP_ADDRESS = "data1";
+ field @Deprecated public static final int TYPE_HOME = 1; // 0x1
+ field @Deprecated public static final int TYPE_OTHER = 3; // 0x3
+ field @Deprecated public static final int TYPE_WORK = 2; // 0x2
}
public static final class ContactsContract.CommonDataKinds.StructuredName implements android.provider.ContactsContract.DataColumnsWithJoins {
@@ -42801,6 +42801,7 @@ package android.telecom {
method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
+ method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccountsAcrossProfiles();
method public String getDefaultDialerPackage();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 53e9f9fc6965..63b142926653 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3350,7 +3350,7 @@ package android.companion.virtual.camera {
}
@FlaggedApi("android.companion.virtual.flags.virtual_camera") public interface VirtualCameraCallback {
- method public void onProcessCaptureRequest(int, long, @Nullable android.companion.virtual.camera.VirtualCameraMetadata);
+ method public default void onProcessCaptureRequest(int, long);
method public void onStreamClosed(int);
method public void onStreamConfigured(int, @NonNull android.view.Surface, @NonNull android.companion.virtual.camera.VirtualCameraStreamConfig);
}
@@ -3371,12 +3371,6 @@ package android.companion.virtual.camera {
method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setVirtualCameraCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.camera.VirtualCameraCallback);
}
- @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraMetadata implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.camera.VirtualCameraMetadata> CREATOR;
- }
-
@FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraStreamConfig implements android.os.Parcelable {
ctor public VirtualCameraStreamConfig(@IntRange(from=1) int, @IntRange(from=1) int, int);
method public int describeContents();
@@ -13516,6 +13510,7 @@ package android.telecom {
method public java.util.List<android.telecom.PhoneAccount> getAllPhoneAccounts();
method public int getAllPhoneAccountsCount();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
+ method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccountsAcrossProfiles(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}, conditional=true) public int getCallState();
method public android.telecom.PhoneAccountHandle getConnectionManager();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 42daea24593e..aaeba66e4a40 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -369,11 +369,14 @@ package android.app {
}
public class NotificationManager {
+ method @FlaggedApi("android.app.modes_api") @NonNull public String addAutomaticZenRule(@NonNull android.app.AutomaticZenRule, boolean);
method public void cleanUpCallersAfter(long);
method public android.content.ComponentName getEffectsSuppressor();
method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String);
+ method @FlaggedApi("android.app.modes_api") public boolean removeAutomaticZenRule(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean);
+ method @FlaggedApi("android.app.modes_api") public boolean updateAutomaticZenRule(@NonNull String, @NonNull android.app.AutomaticZenRule, boolean);
method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel);
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ea9bb396f83c..37692d363a4b 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -537,8 +537,8 @@ public abstract class ActivityManagerInternal {
/**
* Returns whether the given user requires credential entry at this time. This is used to
- * intercept activity launches for locked work apps due to work challenge being triggered or
- * when the profile user is yet to be unlocked.
+ * intercept activity launches for apps corresponding to locked profiles due to separate
+ * challenge being triggered or when the profile user is yet to be unlocked.
*/
public abstract boolean shouldConfirmCredentials(@UserIdInt int userId);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 87c86df6140d..287d2bd9e6a7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2909,7 +2909,7 @@ public class ApplicationPackageManager extends PackageManager {
try {
return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
launcherExtras, dialogInfo, flags, mContext.getOpPackageName(),
- getUserId());
+ UserHandle.myUserId() /* suspendingUserId */, getUserId() /* targetUserId */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 94385717d349..b7db5f5d8b0c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -167,7 +167,7 @@ interface INotificationManager
void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
int getInterruptionFilterFromListener(in INotificationListener token);
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
- void setInterruptionFilter(String pkg, int interruptionFilter);
+ void setInterruptionFilter(String pkg, int interruptionFilter, boolean fromUser);
void updateNotificationChannelGroupFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannelGroup group);
void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannel channel);
@@ -205,11 +205,11 @@ interface INotificationManager
@UnsupportedAppUsage
ZenModeConfig getZenModeConfig();
NotificationManager.Policy getConsolidatedNotificationPolicy();
- oneway void setZenMode(int mode, in Uri conditionId, String reason);
+ oneway void setZenMode(int mode, in Uri conditionId, String reason, boolean fromUser);
oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
boolean isNotificationPolicyAccessGranted(String pkg);
NotificationManager.Policy getNotificationPolicy(String pkg);
- void setNotificationPolicy(String pkg, in NotificationManager.Policy policy);
+ void setNotificationPolicy(String pkg, in NotificationManager.Policy policy, boolean fromUser);
boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
void setNotificationPolicyAccessGranted(String pkg, boolean granted);
void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
@@ -217,12 +217,12 @@ interface INotificationManager
Map<String, AutomaticZenRule> getAutomaticZenRules();
// TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined.
List<ZenModeConfig.ZenRule> getZenRules();
- String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg);
- boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule);
- boolean removeAutomaticZenRule(String id);
- boolean removeAutomaticZenRules(String packageName);
+ String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg, boolean fromUser);
+ boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule, boolean fromUser);
+ boolean removeAutomaticZenRule(String id, boolean fromUser);
+ boolean removeAutomaticZenRules(String packageName, boolean fromUser);
int getRuleInstanceCount(in ComponentName owner);
- void setAutomaticZenRuleState(String id, in Condition condition);
+ void setAutomaticZenRuleState(String id, in Condition condition, boolean fromUser);
byte[] getBackupPayload(int user);
void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index d23b16d636a7..f76a45b37661 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1184,14 +1184,20 @@ public class NotificationManager {
*/
@UnsupportedAppUsage
public void setZenMode(int mode, Uri conditionId, String reason) {
+ setZenMode(mode, conditionId, reason, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ public void setZenMode(int mode, Uri conditionId, String reason, boolean fromUser) {
INotificationManager service = getService();
try {
- service.setZenMode(mode, conditionId, reason);
+ service.setZenMode(mode, conditionId, reason, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+
/**
* @hide
*/
@@ -1325,9 +1331,19 @@ public class NotificationManager {
* @return The id of the newly created rule; null if the rule could not be created.
*/
public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
+ return addAutomaticZenRule(automaticZenRule, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @NonNull
+ public String addAutomaticZenRule(@NonNull AutomaticZenRule automaticZenRule,
+ boolean fromUser) {
INotificationManager service = getService();
try {
- return service.addAutomaticZenRule(automaticZenRule, mContext.getPackageName());
+ return service.addAutomaticZenRule(automaticZenRule,
+ mContext.getPackageName(), fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1347,9 +1363,17 @@ public class NotificationManager {
* @return Whether the rule was successfully updated.
*/
public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule) {
+ return updateAutomaticZenRule(id, automaticZenRule, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public boolean updateAutomaticZenRule(@NonNull String id,
+ @NonNull AutomaticZenRule automaticZenRule, boolean fromUser) {
INotificationManager service = getService();
try {
- return service.updateAutomaticZenRule(id, automaticZenRule);
+ return service.updateAutomaticZenRule(id, automaticZenRule, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1367,9 +1391,20 @@ public class NotificationManager {
* @param condition The new state of this rule
*/
public void setAutomaticZenRuleState(@NonNull String id, @NonNull Condition condition) {
+ if (Flags.modesApi()) {
+ setAutomaticZenRuleState(id, condition,
+ /* fromUser= */ condition.source == Condition.SOURCE_USER_ACTION);
+ } else {
+ setAutomaticZenRuleState(id, condition, /* fromUser= */ false);
+ }
+ }
+
+ /** @hide */
+ public void setAutomaticZenRuleState(@NonNull String id, @NonNull Condition condition,
+ boolean fromUser) {
INotificationManager service = getService();
try {
- service.setAutomaticZenRuleState(id, condition);
+ service.setAutomaticZenRuleState(id, condition, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1388,9 +1423,16 @@ public class NotificationManager {
* @return Whether the rule was successfully deleted.
*/
public boolean removeAutomaticZenRule(String id) {
+ return removeAutomaticZenRule(id, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public boolean removeAutomaticZenRule(@NonNull String id, boolean fromUser) {
INotificationManager service = getService();
try {
- return service.removeAutomaticZenRule(id);
+ return service.removeAutomaticZenRule(id, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1402,9 +1444,14 @@ public class NotificationManager {
* @hide
*/
public boolean removeAutomaticZenRules(String packageName) {
+ return removeAutomaticZenRules(packageName, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ public boolean removeAutomaticZenRules(String packageName, boolean fromUser) {
INotificationManager service = getService();
try {
- return service.removeAutomaticZenRules(packageName);
+ return service.removeAutomaticZenRules(packageName, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1685,10 +1732,15 @@ public class NotificationManager {
*/
// TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public void setNotificationPolicy(@NonNull Policy policy) {
+ setNotificationPolicy(policy, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ public void setNotificationPolicy(@NonNull Policy policy, boolean fromUser) {
checkRequired("policy", policy);
INotificationManager service = getService();
try {
- service.setNotificationPolicy(mContext.getOpPackageName(), policy);
+ service.setNotificationPolicy(mContext.getOpPackageName(), policy, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2685,9 +2737,16 @@ public class NotificationManager {
*/
// TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public final void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
+ setInterruptionFilter(interruptionFilter, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ public final void setInterruptionFilter(@InterruptionFilter int interruptionFilter,
+ boolean fromUser) {
final INotificationManager service = getService();
try {
- service.setInterruptionFilter(mContext.getOpPackageName(), interruptionFilter);
+ service.setInterruptionFilter(mContext.getOpPackageName(), interruptionFilter,
+ fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/usage/UsageEventsQuery.java b/core/java/android/app/usage/UsageEventsQuery.java
index 8c63d1857865..3cd292392694 100644
--- a/core/java/android/app/usage/UsageEventsQuery.java
+++ b/core/java/android/app/usage/UsageEventsQuery.java
@@ -19,9 +19,11 @@ package android.app.usage;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.usage.UsageEvents.Event;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.util.ArraySet;
import com.android.internal.util.ArrayUtils;
@@ -40,11 +42,13 @@ public final class UsageEventsQuery implements Parcelable {
private final @CurrentTimeMillisLong long mBeginTimeMillis;
private final @CurrentTimeMillisLong long mEndTimeMillis;
private final @Event.EventType int[] mEventTypes;
+ private final @UserIdInt int mUserId;
private UsageEventsQuery(@NonNull Builder builder) {
mBeginTimeMillis = builder.mBeginTimeMillis;
mEndTimeMillis = builder.mEndTimeMillis;
mEventTypes = ArrayUtils.convertToIntArray(builder.mEventTypes);
+ mUserId = builder.mUserId;
}
private UsageEventsQuery(Parcel in) {
@@ -53,6 +57,7 @@ public final class UsageEventsQuery implements Parcelable {
int eventTypesLength = in.readInt();
mEventTypes = new int[eventTypesLength];
in.readIntArray(mEventTypes);
+ mUserId = in.readInt();
}
/**
@@ -87,6 +92,11 @@ public final class UsageEventsQuery implements Parcelable {
return eventTypeSet;
}
+ /** @hide */
+ public @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -98,6 +108,7 @@ public final class UsageEventsQuery implements Parcelable {
dest.writeLong(mEndTimeMillis);
dest.writeInt(mEventTypes.length);
dest.writeIntArray(mEventTypes);
+ dest.writeInt(mUserId);
}
@NonNull
@@ -126,6 +137,7 @@ public final class UsageEventsQuery implements Parcelable {
private final @CurrentTimeMillisLong long mBeginTimeMillis;
private final @CurrentTimeMillisLong long mEndTimeMillis;
private final ArraySet<Integer> mEventTypes = new ArraySet<>();
+ private @UserIdInt int mUserId = UserHandle.USER_NULL;
/**
* Constructor that specifies the period for which to return events.
@@ -169,5 +181,15 @@ public final class UsageEventsQuery implements Parcelable {
}
return this;
}
+
+ /**
+ * Specifices the user id for the query.
+ * @param userId for whom the query should be performed.
+ * @hide
+ */
+ public @NonNull Builder setUserId(@UserIdInt int userId) {
+ mUserId = userId;
+ return this;
+ }
}
}
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl b/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl
index fac44b50024f..44942d67d489 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl
+++ b/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,11 @@
package android.companion.virtual.camera;
import android.companion.virtual.camera.VirtualCameraStreamConfig;
-import android.companion.virtual.camera.VirtualCameraMetadata;
import android.view.Surface;
/**
- * Interface for the virtual camera service and system server to talk back to the virtual camera owner.
+ * Interface for the virtual camera service and system server to talk back to the virtual camera
+ * owner.
*
* @hide
*/
@@ -40,8 +40,7 @@ interface IVirtualCameraCallback {
in VirtualCameraStreamConfig streamConfig);
/**
- * The client application is requesting a camera frame for the given streamId with the provided
- * metadata.
+ * The client application is requesting a camera frame for the given streamId and frameId.
*
* <p>The virtual camera needs to write the frame data in the {@link Surface} corresponding to
* this stream that was provided during the {@link #onStreamConfigured(int, Surface,
@@ -52,16 +51,14 @@ interface IVirtualCameraCallback {
* VirtualCameraStreamConfig)}
* @param frameId The frameId that is being requested. Each request will have a different
* frameId, that will be increasing for each call with a particular streamId.
- * @param metadata The metadata requested for the frame. The virtual camera should do its best
- * to honor the requested metadata.
*/
- oneway void onProcessCaptureRequest(
- int streamId, long frameId, in VirtualCameraMetadata metadata);
+ oneway void onProcessCaptureRequest(int streamId, long frameId);
/**
* The stream previously configured when {@link #onStreamConfigured(int, Surface,
* VirtualCameraStreamConfig)} was called is now being closed and associated resources can be
- * freed. The Surface was disposed on the client side and should not be used anymore by the virtual camera owner
+ * freed. The Surface was disposed on the client side and should not be used anymore by the
+ * virtual camera owner.
*
* @param streamId The id of the stream that was closed.
*/
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraCallback.java b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
index a18ae03555e9..5b658b883217 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package android.companion.virtual.camera;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.companion.virtual.flags.Flags;
import android.view.Surface;
@@ -50,8 +49,7 @@ public interface VirtualCameraCallback {
@NonNull VirtualCameraStreamConfig streamConfig);
/**
- * The client application is requesting a camera frame for the given streamId with the provided
- * metadata.
+ * The client application is requesting a camera frame for the given streamId and frameId.
*
* <p>The virtual camera needs to write the frame data in the {@link Surface} corresponding to
* this stream that was provided during the {@link #onStreamConfigured(int, Surface,
@@ -62,12 +60,8 @@ public interface VirtualCameraCallback {
* VirtualCameraStreamConfig)}
* @param frameId The frameId that is being requested. Each request will have a different
* frameId, that will be increasing for each call with a particular streamId.
- * @param metadata The metadata requested for the frame. The virtual camera should do its best
- * to honor the requested metadata but the consumer won't be informed about the metadata set
- * for a particular frame. If null, the requested frame can be anything the producer sends.
*/
- void onProcessCaptureRequest(
- int streamId, long frameId, @Nullable VirtualCameraMetadata metadata);
+ default void onProcessCaptureRequest(int streamId, long frameId) {}
/**
* The stream previously configured when {@link #onStreamConfigured(int, Surface,
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
index f1eb240301e0..a9392518d0c6 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -224,9 +224,8 @@ public final class VirtualCameraConfig implements Parcelable {
}
@Override
- public void onProcessCaptureRequest(
- int streamId, long frameId, VirtualCameraMetadata metadata) {
- mExecutor.execute(() -> mCallback.onProcessCaptureRequest(streamId, frameId, metadata));
+ public void onProcessCaptureRequest(int streamId, long frameId) {
+ mExecutor.execute(() -> mCallback.onProcessCaptureRequest(streamId, frameId));
}
@Override
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java
deleted file mode 100644
index 1ba36d08cbeb..000000000000
--- a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.companion.virtual.camera;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Data structure used to store camera metadata compatible with VirtualCamera.
- *
- * @hide
- */
-@SystemApi
-@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
-public final class VirtualCameraMetadata implements Parcelable {
-
- /** @hide */
- public VirtualCameraMetadata(@NonNull Parcel in) {}
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {}
-
- @NonNull
- public static final Creator<VirtualCameraMetadata> CREATOR =
- new Creator<>() {
- @Override
- @NonNull
- public VirtualCameraMetadata createFromParcel(Parcel in) {
- return new VirtualCameraMetadata(in);
- }
-
- @Override
- @NonNull
- public VirtualCameraMetadata[] newArray(int size) {
- return new VirtualCameraMetadata[size];
- }
- };
-}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index d13d962015de..12da66515c07 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1393,6 +1393,18 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public static final long OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION = 255940284L;
/**
+ * Enables {@link #SCREEN_ORIENTATION_USER} which overrides any orientation requested
+ * by the activity. Fixed orientation apps can be overridden to fullscreen on large
+ * screen devices with ignoreOrientationRequest enabled with this override.
+ *
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ public static final long OVERRIDE_ANY_ORIENTATION_TO_USER = 310816437L;
+
+ /**
* Compares activity window layout min width/height with require space for multi window to
* determine if it can be put into multi window mode.
*/
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 98623de810c4..6dc8d4738c87 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -300,7 +300,8 @@ interface IPackageManager {
String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
in PersistableBundle appExtras, in PersistableBundle launcherExtras,
- in SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId);
+ in SuspendDialogInfo dialogInfo, int flags, String suspendingPackage,
+ int suspendingUserId, int targetUserId);
String[] getUnsuspendablePackagesForUser(in String[] packageNames, int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 82a8c11f024f..a8638708824b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -10000,6 +10000,9 @@ public abstract class PackageManager {
* device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
* {@link android.Manifest.permission#SUSPEND_APPS}.
*
+ * <p>
+ * <strong>Note:</strong>This API doesn't support cross user suspension and should only be used
+ * for testing.
* @param suspendedPackage The package that has been suspended.
* @return Name of the package that suspended the given package. Returns {@code null} if the
* given package is not currently suspended and the platform package name - i.e.
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index b04b7badbae0..60d5c140e2b3 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -115,3 +115,11 @@ flag {
description: "Feature flag to fix duplicated PackageManager flag values"
bug: "314815969"
}
+
+flag {
+ name: "provide_info_of_apk_in_apex"
+ namespace: "package_manager_service"
+ description: "Feature flag to provide the information of APK-in-APEX"
+ bug: "306329516"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 57025c25f97b..9a1796fd3d97 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -56,3 +56,11 @@ flag {
description: "Add support to lock private space automatically after a time period"
bug: "303201022"
}
+
+flag {
+ name: "avatar_sync"
+ namespace: "multiuser"
+ description: "Implement new Avatar Picker outside of SetttingsLib with ability to select avatars from Google Account and synchronise to any changes."
+ bug: "296829976"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 52bba1484f1a..870546a6fd2b 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -22,14 +22,8 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.database.sqlite.SQLiteClosable;
import android.database.sqlite.SQLiteException;
-import android.os.Binder;
-import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.Process;
-import android.util.Log;
-import android.util.LongSparseArray;
-import android.util.SparseIntArray;
import dalvik.annotation.optimization.FastNative;
import dalvik.system.CloseGuard;
@@ -44,6 +38,9 @@ import dalvik.system.CloseGuard;
* consumer for reading.
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
+ "com.android.hoststubgen.nativesubstitution.CursorWindow_host")
public class CursorWindow extends SQLiteClosable implements Parcelable {
private static final String STATS_TAG = "CursorWindowStats";
@@ -61,7 +58,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
private int mStartPos;
private final String mName;
- private final CloseGuard mCloseGuard = CloseGuard.get();
+ private final CloseGuard mCloseGuard;
// May throw CursorWindowAllocationException
private static native long nativeCreate(String name, int cursorWindowSize);
@@ -147,7 +144,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
if (mWindowPtr == 0) {
throw new AssertionError(); // Not possible, the native code won't return it.
}
- mCloseGuard.open("CursorWindow.close");
+ mCloseGuard = createCloseGuard();
}
/**
@@ -175,7 +172,18 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
throw new AssertionError(); // Not possible, the native code won't return it.
}
mName = nativeGetName(mWindowPtr);
- mCloseGuard.open("CursorWindow.close");
+ mCloseGuard = createCloseGuard();
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private CloseGuard createCloseGuard() {
+ final CloseGuard closeGuard = CloseGuard.get();
+ closeGuard.open("CursorWindow.close");
+ return closeGuard;
+ }
+
+ private CloseGuard createCloseGuard$ravenwood() {
+ return null;
}
@Override
@@ -749,6 +757,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
dispose();
}
+ @android.ravenwood.annotation.RavenwoodReplace
private static int getCursorWindowSize() {
if (sCursorWindowSize < 0) {
// The cursor window size. resource xml file specifies the value in kB.
@@ -759,6 +768,10 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
return sCursorWindowSize;
}
+ private static int getCursorWindowSize$ravenwood() {
+ return 1024;
+ }
+
@Override
public String toString() {
return getName() + " {" + Long.toHexString(mWindowPtr) + "}";
diff --git a/core/java/android/database/SQLException.java b/core/java/android/database/SQLException.java
index 3402026a438a..4026f201ea81 100644
--- a/core/java/android/database/SQLException.java
+++ b/core/java/android/database/SQLException.java
@@ -19,6 +19,7 @@ package android.database;
/**
* An exception that indicates there was an error with SQL parsing or execution.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SQLException extends RuntimeException {
public SQLException() {
}
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index 2fca729f2551..8eb512a2cbc6 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -25,6 +25,7 @@ import java.io.Closeable;
*
* This class implements a primitive reference counting scheme for database objects.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class SQLiteClosable implements Closeable {
@UnsupportedAppUsage
private int mReferenceCount = 1;
diff --git a/core/java/android/database/sqlite/SQLiteException.java b/core/java/android/database/sqlite/SQLiteException.java
index a1d9c9f95a2f..531b40a7014f 100644
--- a/core/java/android/database/sqlite/SQLiteException.java
+++ b/core/java/android/database/sqlite/SQLiteException.java
@@ -21,6 +21,7 @@ import android.database.SQLException;
/**
* A SQLite exception that indicates there was an error with SQL parsing or execution.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SQLiteException extends SQLException {
public SQLiteException() {
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index f71e853a1170..ffd7212a7525 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -715,6 +715,14 @@ public abstract class DisplayManagerInternal {
boolean startOffload();
void stopOffload();
+
+ /**
+ * Called when {@link DisplayOffloadSession} tries to block screen turning on.
+ *
+ * @param unblocker a {@link Runnable} executed upon all work required before screen turning
+ * on is done.
+ */
+ void onBlockingScreenOn(Runnable unblocker);
}
/** A session token that associates a internal display with a {@link DisplayOffloader}. */
@@ -734,6 +742,15 @@ public abstract class DisplayManagerInternal {
*/
void updateBrightness(float brightness);
+ /**
+ * Called while display is turning to state ON to leave a small period for displayoffload
+ * session to finish some work.
+ *
+ * @param unblocker a {@link Runnable} used by displayoffload session to notify
+ * {@link DisplayManager} that it can continue turning screen on.
+ */
+ boolean blockScreenOn(Runnable unblocker);
+
/** Returns whether displayoffload supports the given display state. */
static boolean isSupportedOffloadState(int displayState) {
return Display.isSuspendedState(displayState);
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl b/core/java/android/hardware/face/FaceSensorConfigurations.aidl
index 6c1f0fcd622a..26367b3c3dbf 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
+++ b/core/java/android/hardware/face/FaceSensorConfigurations.aidl
@@ -13,12 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.hardware.face;
-package android.companion.virtual.camera;
-
-/**
- * Data structure used to store {@link android.hardware.camera2.CameraMetadata} compatible with
- * VirtualCamera.
- * @hide
- */
-parcelable VirtualCameraMetadata;
+parcelable FaceSensorConfigurations; \ No newline at end of file
diff --git a/core/java/android/hardware/face/FaceSensorConfigurations.java b/core/java/android/hardware/face/FaceSensorConfigurations.java
new file mode 100644
index 000000000000..6ef692f81069
--- /dev/null
+++ b/core/java/android/hardware/face/FaceSensorConfigurations.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.SensorProps;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+/**
+ * Provides the sensor props for face sensor, if available.
+ * @hide
+ */
+public class FaceSensorConfigurations implements Parcelable {
+ private static final String TAG = "FaceSensorConfigurations";
+
+ private final boolean mResetLockoutRequiresChallenge;
+ private final Map<String, SensorProps[]> mSensorPropsMap;
+
+ public static final Creator<FaceSensorConfigurations> CREATOR =
+ new Creator<FaceSensorConfigurations>() {
+ @Override
+ public FaceSensorConfigurations createFromParcel(Parcel in) {
+ return new FaceSensorConfigurations(in);
+ }
+
+ @Override
+ public FaceSensorConfigurations[] newArray(int size) {
+ return new FaceSensorConfigurations[size];
+ }
+ };
+
+ public FaceSensorConfigurations(boolean resetLockoutRequiresChallenge) {
+ mResetLockoutRequiresChallenge = resetLockoutRequiresChallenge;
+ mSensorPropsMap = new HashMap<>();
+ }
+
+ protected FaceSensorConfigurations(Parcel in) {
+ mResetLockoutRequiresChallenge = in.readByte() != 0;
+ mSensorPropsMap = in.readHashMap(null, String.class, SensorProps[].class);
+ }
+
+ /**
+ * Process AIDL instances to extract sensor props and add it to the sensor map.
+ * @param aidlInstances available face AIDL instances
+ * @param getIFace function that provides the daemon for the specific instance
+ */
+ public void addAidlConfigs(@NonNull String[] aidlInstances,
+ @NonNull Function<String, IFace> getIFace) {
+ for (String aidlInstance : aidlInstances) {
+ final String fqName = IFace.DESCRIPTOR + "/" + aidlInstance;
+ IFace face = getIFace.apply(fqName);
+ try {
+ if (face != null) {
+ mSensorPropsMap.put(aidlInstance, face.getSensorProps());
+ } else {
+ Slog.e(TAG, "Unable to get declared service: " + fqName);
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Unable to get sensor properties!");
+ }
+ }
+ }
+
+ /**
+ * Parse through HIDL configuration and add it to the sensor map.
+ */
+ public void addHidlConfigs(@NonNull String[] hidlConfigStrings,
+ @NonNull Context context) {
+ final List<HidlFaceSensorConfig> hidlFaceSensorConfigs = new ArrayList<>();
+ for (String hidlConfig: hidlConfigStrings) {
+ final HidlFaceSensorConfig hidlFaceSensorConfig = new HidlFaceSensorConfig();
+ try {
+ hidlFaceSensorConfig.parse(hidlConfig, context);
+ } catch (Exception e) {
+ Log.e(TAG, "HIDL sensor configuration format is incorrect.");
+ continue;
+ }
+ if (hidlFaceSensorConfig.getModality() == TYPE_FACE) {
+ hidlFaceSensorConfigs.add(hidlFaceSensorConfig);
+ }
+ }
+ final String hidlHalInstanceName = "defaultHIDL";
+ mSensorPropsMap.put(hidlHalInstanceName, hidlFaceSensorConfigs.toArray(
+ new SensorProps[hidlFaceSensorConfigs.size()]));
+ }
+
+ /**
+ * Returns true if any face sensors have been added.
+ */
+ public boolean hasSensorConfigurations() {
+ return mSensorPropsMap.size() > 0;
+ }
+
+ /**
+ * Returns true if there is only a single face sensor configuration available.
+ */
+ public boolean isSingleSensorConfigurationPresent() {
+ return mSensorPropsMap.size() == 1;
+ }
+
+ /**
+ * Return sensor props for the given instance. If instance is not available,
+ * then null is returned.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPairForInstance(String instance) {
+ if (mSensorPropsMap.containsKey(instance)) {
+ return new Pair<>(instance, mSensorPropsMap.get(instance));
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the first pair of instance and sensor props, which does not correspond to the given
+ * If instance is not available, then null is returned.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPairNotForInstance(String instance) {
+ Optional<String> notAVirtualInstance = mSensorPropsMap.keySet().stream().filter(
+ (instanceName) -> !instanceName.equals(instance)).findFirst();
+ return notAVirtualInstance.map(this::getSensorPairForInstance).orElseGet(
+ this::getSensorPair);
+ }
+
+ /**
+ * Returns the first pair of instance and sensor props that has been added to the map.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPair() {
+ Optional<String> optionalInstance = mSensorPropsMap.keySet().stream().findFirst();
+ return optionalInstance.map(this::getSensorPairForInstance).orElse(null);
+
+ }
+
+ public boolean getResetLockoutRequiresChallenge() {
+ return mResetLockoutRequiresChallenge;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeByte((byte) (mResetLockoutRequiresChallenge ? 1 : 0));
+ dest.writeMap(mSensorPropsMap);
+ }
+}
diff --git a/core/java/android/hardware/face/HidlFaceSensorConfig.java b/core/java/android/hardware/face/HidlFaceSensorConfig.java
new file mode 100644
index 000000000000..cab146d0ff54
--- /dev/null
+++ b/core/java/android/hardware/face/HidlFaceSensorConfig.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.common.SensorStrength;
+import android.hardware.biometrics.face.SensorProps;
+
+import com.android.internal.R;
+
+/**
+ * Parse HIDL face sensor config and map it to SensorProps.aidl to match AIDL.
+ * See core/res/res/values/config.xml config_biometric_sensors
+ * @hide
+ */
+public final class HidlFaceSensorConfig extends SensorProps {
+ private int mSensorId;
+ private int mModality;
+ private int mStrength;
+
+ /**
+ * Parse through the config string and map it to SensorProps.aidl.
+ * @throws IllegalArgumentException when config string has unexpected format
+ */
+ public void parse(@NonNull String config, @NonNull Context context)
+ throws IllegalArgumentException {
+ final String[] elems = config.split(":");
+ if (elems.length < 3) {
+ throw new IllegalArgumentException();
+ }
+ mSensorId = Integer.parseInt(elems[0]);
+ mModality = Integer.parseInt(elems[1]);
+ mStrength = Integer.parseInt(elems[2]);
+ mapHidlToAidlFaceSensorConfigurations(context);
+ }
+
+ @BiometricAuthenticator.Modality
+ public int getModality() {
+ return mModality;
+ }
+
+ private void mapHidlToAidlFaceSensorConfigurations(@NonNull Context context) {
+ commonProps = new CommonProps();
+ commonProps.sensorId = mSensorId;
+ commonProps.sensorStrength = authenticatorStrengthToPropertyStrength(mStrength);
+ halControlsPreview = context.getResources().getBoolean(
+ R.bool.config_faceAuthSupportsSelfIllumination);
+ commonProps.maxEnrollmentsPerUser = context.getResources().getInteger(
+ R.integer.config_faceMaxTemplatesPerUser);
+ commonProps.componentInfo = null;
+ supportsDetectInteraction = false;
+ }
+
+ private byte authenticatorStrengthToPropertyStrength(
+ @BiometricManager.Authenticators.Types int strength) {
+ switch (strength) {
+ case BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE:
+ return SensorStrength.CONVENIENCE;
+ case BiometricManager.Authenticators.BIOMETRIC_WEAK:
+ return SensorStrength.WEAK;
+ case BiometricManager.Authenticators.BIOMETRIC_STRONG:
+ return SensorStrength.STRONG;
+ default:
+ throw new IllegalArgumentException("Unknown strength: " + strength);
+ }
+ }
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 7080133dc597..0096877f548a 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -26,6 +26,7 @@ import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.FaceSensorConfigurations;
import android.view.Surface;
/**
@@ -167,6 +168,10 @@ interface IFaceService {
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void registerAuthenticators(in List<FaceSensorPropertiesInternal> hidlSensors);
+ //Register all available face sensors.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void registerAuthenticatorsLegacy(in FaceSensorConfigurations faceSensorConfigurations);
+
// Adds a callback which gets called when the service registers all of the face
// authenticators. The callback is automatically removed after it's invoked.
void addAuthenticatorsRegisteredCallback(IFaceAuthenticatorsRegisteredCallback callback);
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/CommonDomainLayerModule.kt b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.aidl
index 7be2eaf7b105..ebb05dc88182 100644
--- a/packages/SystemUI/src/com/android/systemui/common/domain/CommonDomainLayerModule.kt
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.aidl
@@ -13,15 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.hardware.fingerprint;
-package com.android.systemui.common.domain
-
-import com.android.systemui.common.domain.interactor.ConfigurationInteractor
-import com.android.systemui.common.domain.interactor.ConfigurationInteractorImpl
-import dagger.Binds
-import dagger.Module
-
-@Module
-abstract class CommonDomainLayerModule {
- @Binds abstract fun bindInteractor(impl: ConfigurationInteractorImpl): ConfigurationInteractor
-}
+parcelable FingerprintSensorConfigurations; \ No newline at end of file
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
new file mode 100644
index 000000000000..f214494a5d7b
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+/**
+ * Provides the sensor props for fingerprint sensor, if available.
+ * @hide
+ */
+
+public class FingerprintSensorConfigurations implements Parcelable {
+ private static final String TAG = "FingerprintSensorConfigurations";
+
+ private final Map<String, SensorProps[]> mSensorPropsMap;
+ private final boolean mResetLockoutRequiresHardwareAuthToken;
+
+ public static final Creator<FingerprintSensorConfigurations> CREATOR =
+ new Creator<>() {
+ @Override
+ public FingerprintSensorConfigurations createFromParcel(Parcel in) {
+ return new FingerprintSensorConfigurations(in);
+ }
+
+ @Override
+ public FingerprintSensorConfigurations[] newArray(int size) {
+ return new FingerprintSensorConfigurations[size];
+ }
+ };
+
+ public FingerprintSensorConfigurations(boolean resetLockoutRequiresHardwareAuthToken) {
+ mResetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+ mSensorPropsMap = new HashMap<>();
+ }
+
+ /**
+ * Process AIDL instances to extract sensor props and add it to the sensor map.
+ * @param aidlInstances available face AIDL instances
+ * @param getIFingerprint function that provides the daemon for the specific instance
+ */
+ public void addAidlSensors(@NonNull String[] aidlInstances,
+ @NonNull Function<String, IFingerprint> getIFingerprint) {
+ for (String aidlInstance : aidlInstances) {
+ try {
+ final String fqName = IFingerprint.DESCRIPTOR + "/" + aidlInstance;
+ final IFingerprint fp = getIFingerprint.apply(fqName);
+ if (fp != null) {
+ SensorProps[] props = fp.getSensorProps();
+ mSensorPropsMap.put(aidlInstance, props);
+ } else {
+ Log.d(TAG, "IFingerprint null for instance " + aidlInstance);
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Unable to get sensor properties!");
+ }
+ }
+ }
+
+ /**
+ * Parse through HIDL configuration and add it to the sensor map.
+ */
+ public void addHidlSensors(@NonNull String[] hidlConfigStrings,
+ @NonNull Context context) {
+ final List<HidlFingerprintSensorConfig> hidlFingerprintSensorConfigs = new ArrayList<>();
+ for (String hidlConfigString : hidlConfigStrings) {
+ final HidlFingerprintSensorConfig hidlFingerprintSensorConfig =
+ new HidlFingerprintSensorConfig();
+ try {
+ hidlFingerprintSensorConfig.parse(hidlConfigString, context);
+ } catch (Exception e) {
+ Log.e(TAG, "HIDL sensor configuration format is incorrect.");
+ continue;
+ }
+ if (hidlFingerprintSensorConfig.getModality() == TYPE_FINGERPRINT) {
+ hidlFingerprintSensorConfigs.add(hidlFingerprintSensorConfig);
+ }
+ }
+ final String hidlHalInstanceName = "defaultHIDL";
+ mSensorPropsMap.put(hidlHalInstanceName,
+ hidlFingerprintSensorConfigs.toArray(
+ new HidlFingerprintSensorConfig[hidlFingerprintSensorConfigs.size()]));
+ }
+
+ protected FingerprintSensorConfigurations(Parcel in) {
+ mResetLockoutRequiresHardwareAuthToken = in.readByte() != 0;
+ mSensorPropsMap = in.readHashMap(null /* loader */, String.class, SensorProps[].class);
+ }
+
+ /**
+ * Returns true if any fingerprint sensors have been added.
+ */
+ public boolean hasSensorConfigurations() {
+ return mSensorPropsMap.size() > 0;
+ }
+
+ /**
+ * Returns true if there is only a single fingerprint sensor configuration available.
+ */
+ public boolean isSingleSensorConfigurationPresent() {
+ return mSensorPropsMap.size() == 1;
+ }
+
+ /**
+ * Return sensor props for the given instance. If instance is not available,
+ * then null is returned.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPairForInstance(String instance) {
+ if (mSensorPropsMap.containsKey(instance)) {
+ return new Pair<>(instance, mSensorPropsMap.get(instance));
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the first pair of instance and sensor props, which does not correspond to the given
+ * If instance is not available, then null is returned.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPairNotForInstance(String instance) {
+ Optional<String> notAVirtualInstance = mSensorPropsMap.keySet().stream().filter(
+ (instanceName) -> !instanceName.equals(instance)).findFirst();
+ return notAVirtualInstance.map(this::getSensorPairForInstance).orElseGet(
+ this::getSensorPair);
+ }
+
+ /**
+ * Returns the first pair of instance and sensor props that has been added to the map.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPair() {
+ Optional<String> optionalInstance = mSensorPropsMap.keySet().stream().findFirst();
+ return optionalInstance.map(this::getSensorPairForInstance).orElse(null);
+
+ }
+
+ public boolean getResetLockoutRequiresHardwareAuthToken() {
+ return mResetLockoutRequiresHardwareAuthToken;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeByte((byte) (mResetLockoutRequiresHardwareAuthToken ? 1 : 0));
+ dest.writeMap(mSensorPropsMap);
+ }
+}
diff --git a/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java b/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java
new file mode 100644
index 000000000000..d481153fc642
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.common.SensorStrength;
+import android.hardware.biometrics.fingerprint.FingerprintSensorType;
+import android.hardware.biometrics.fingerprint.SensorLocation;
+import android.hardware.biometrics.fingerprint.SensorProps;
+
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Parse HIDL fingerprint sensor config and map it to SensorProps.aidl to match AIDL.
+ * See core/res/res/values/config.xml config_biometric_sensors
+ * @hide
+ */
+public final class HidlFingerprintSensorConfig extends SensorProps {
+ private int mSensorId;
+ private int mModality;
+ private int mStrength;
+
+ /**
+ * Parse through the config string and map it to SensorProps.aidl.
+ * @throws IllegalArgumentException when config string has unexpected format
+ */
+ public void parse(@NonNull String config, @NonNull Context context)
+ throws IllegalArgumentException {
+ final String[] elems = config.split(":");
+ if (elems.length < 3) {
+ throw new IllegalArgumentException();
+ }
+ mSensorId = Integer.parseInt(elems[0]);
+ mModality = Integer.parseInt(elems[1]);
+ mStrength = Integer.parseInt(elems[2]);
+ mapHidlToAidlSensorConfiguration(context);
+ }
+
+ @BiometricAuthenticator.Modality
+ public int getModality() {
+ return mModality;
+ }
+
+ private void mapHidlToAidlSensorConfiguration(@NonNull Context context) {
+ commonProps = new CommonProps();
+ commonProps.componentInfo = null;
+ commonProps.sensorId = mSensorId;
+ commonProps.sensorStrength = authenticatorStrengthToPropertyStrength(mStrength);
+ commonProps.maxEnrollmentsPerUser = context.getResources().getInteger(
+ R.integer.config_fingerprintMaxTemplatesPerUser);
+ halControlsIllumination = false;
+ sensorLocations = new SensorLocation[1];
+
+ final int[] udfpsProps = context.getResources().getIntArray(
+ com.android.internal.R.array.config_udfps_sensor_props);
+ final boolean isUdfps = !ArrayUtils.isEmpty(udfpsProps);
+ // config_is_powerbutton_fps indicates whether device has a power button fingerprint sensor.
+ final boolean isPowerbuttonFps = context.getResources().getBoolean(
+ R.bool.config_is_powerbutton_fps);
+
+ if (isUdfps) {
+ sensorType = FingerprintSensorType.UNKNOWN;
+ } else if (isPowerbuttonFps) {
+ sensorType = FingerprintSensorType.POWER_BUTTON;
+ } else {
+ sensorType = FingerprintSensorType.REAR;
+ }
+
+ if (isUdfps && udfpsProps.length == 3) {
+ setSensorLocation(udfpsProps[0], udfpsProps[1], udfpsProps[2]);
+ } else {
+ setSensorLocation(540 /* sensorLocationX */, 1636 /* sensorLocationY */,
+ 130 /* sensorRadius */);
+ }
+
+ }
+
+ private void setSensorLocation(int sensorLocationX,
+ int sensorLocationY, int sensorRadius) {
+ sensorLocations[0] = new SensorLocation();
+ sensorLocations[0].display = "";
+ sensorLocations[0].sensorLocationX = sensorLocationX;
+ sensorLocations[0].sensorLocationY = sensorLocationY;
+ sensorLocations[0].sensorRadius = sensorRadius;
+ }
+
+ private byte authenticatorStrengthToPropertyStrength(
+ @BiometricManager.Authenticators.Types int strength) {
+ switch (strength) {
+ case BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE:
+ return SensorStrength.CONVENIENCE;
+ case BiometricManager.Authenticators.BIOMETRIC_WEAK:
+ return SensorStrength.WEAK;
+ case BiometricManager.Authenticators.BIOMETRIC_STRONG:
+ return SensorStrength.STRONG;
+ default:
+ throw new IllegalArgumentException("Unknown strength: " + strength);
+ }
+ }
+}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index f594c00b0e47..606b171f36ba 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -31,6 +31,7 @@ import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import java.util.List;
/**
@@ -173,6 +174,10 @@ interface IFingerprintService {
@EnforcePermission("MANAGE_FINGERPRINT")
void removeClientActiveCallback(IFingerprintClientActiveCallback callback);
+ //Register all available fingerprint sensors.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void registerAuthenticatorsLegacy(in FingerprintSensorConfigurations fingerprintSensorConfigurations);
+
// Registers all HIDL and AIDL sensors. Only HIDL sensor properties need to be provided, because
// AIDL sensor properties are retrieved directly from the available HALs. If no HIDL HALs exist,
// hidlSensors must be non-null and empty. See AuthService.java
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 4bfff16d973f..7d00b80488a9 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6911,7 +6911,11 @@ public final class ContactsContract {
* <td></td>
* </tr>
* </table>
+ *
+ * @deprecated This field may not be well supported by some contacts apps and is discouraged
+ * to use.
*/
+ @Deprecated
public static final class Im implements DataColumnsWithJoins, CommonColumns, ContactCounts {
/**
* This utility class cannot be instantiated
@@ -7721,7 +7725,11 @@ public final class ContactsContract {
* <td></td>
* </tr>
* </table>
+ *
+ * @deprecated This field may not be well supported by some contacts apps and is discouraged
+ * to use.
*/
+ @Deprecated
public static final class SipAddress implements DataColumnsWithJoins, CommonColumns,
ContactCounts {
/**
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 875031fb0cb3..23c8393dadc3 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -960,8 +960,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
mKeyphraseMetadata = new KeyphraseMetadata(1, mText, fakeSupportedLocales,
AlwaysOnHotwordDetector.RECOGNITION_MODE_VOICE_TRIGGER);
}
+ notifyStateChangedLocked();
}
- notifyStateChanged(availability);
}
/**
@@ -1371,8 +1371,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
mAvailability = STATE_INVALID;
mIsAvailabilityOverriddenByTestApi = false;
+ notifyStateChangedLocked();
}
- notifyStateChanged(STATE_INVALID);
super.destroy();
}
@@ -1402,8 +1402,6 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
*/
// TODO(b/281608561): remove the enrollment flow from AlwaysOnHotwordDetector
void onSoundModelsChanged() {
- boolean notifyError = false;
-
synchronized (mLock) {
if (mAvailability == STATE_INVALID
|| mAvailability == STATE_HARDWARE_UNAVAILABLE
@@ -1444,9 +1442,6 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
// calling stopRecognition where there is no started session.
Log.w(TAG, "Failed to stop recognition after enrollment update: code="
+ result);
-
- // Execute a refresh availability task - which should then notify of a change.
- new RefreshAvailabilityTask().execute();
} catch (Exception e) {
Slog.w(TAG, "Failed to stop recognition after enrollment update", e);
if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
@@ -1455,14 +1450,14 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
+ Log.getStackTraceString(e),
FailureSuggestedAction.RECREATE_DETECTOR));
} else {
- notifyError = true;
+ updateAndNotifyStateChangedLocked(STATE_ERROR);
}
+ return;
}
}
- }
- if (notifyError) {
- updateAndNotifyStateChanged(STATE_ERROR);
+ // Execute a refresh availability task - which should then notify of a change.
+ new RefreshAvailabilityTask().execute();
}
}
@@ -1578,11 +1573,10 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
}
- private void updateAndNotifyStateChanged(int availability) {
- synchronized (mLock) {
- updateAvailabilityLocked(availability);
- }
- notifyStateChanged(availability);
+ @GuardedBy("mLock")
+ private void updateAndNotifyStateChangedLocked(int availability) {
+ updateAvailabilityLocked(availability);
+ notifyStateChangedLocked();
}
@GuardedBy("mLock")
@@ -1596,17 +1590,17 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
}
- private void notifyStateChanged(int newAvailability) {
+ @GuardedBy("mLock")
+ private void notifyStateChangedLocked() {
Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED);
- message.arg1 = newAvailability;
+ message.arg1 = mAvailability;
message.sendToTarget();
}
+ @GuardedBy("mLock")
private void sendUnknownFailure(String failureMessage) {
- synchronized (mLock) {
- // update but do not call onAvailabilityChanged callback for STATE_ERROR
- updateAvailabilityLocked(STATE_ERROR);
- }
+ // update but do not call onAvailabilityChanged callback for STATE_ERROR
+ updateAvailabilityLocked(STATE_ERROR);
Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE, failureMessage).sendToTarget();
}
@@ -1822,17 +1816,19 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
availability = STATE_KEYPHRASE_UNENROLLED;
}
}
+ updateAndNotifyStateChangedLocked(availability);
}
- updateAndNotifyStateChanged(availability);
} catch (Exception e) {
// Any exception here not caught will crash the process because AsyncTask does not
// bubble up the exceptions to the client app, so we must propagate it to the app.
Slog.w(TAG, "Failed to refresh availability", e);
- if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
- sendUnknownFailure(
- "Failed to refresh availability: " + Log.getStackTraceString(e));
- } else {
- updateAndNotifyStateChanged(STATE_ERROR);
+ synchronized (mLock) {
+ if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
+ sendUnknownFailure(
+ "Failed to refresh availability: " + Log.getStackTraceString(e));
+ } else {
+ updateAndNotifyStateChangedLocked(STATE_ERROR);
+ }
}
}
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index b7d97057a08b..adc54f5b5a8c 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -94,7 +94,9 @@ public class VisualQueryDetector {
*/
public void updateState(@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory) {
- mInitializationDelegate.updateState(options, sharedMemory);
+ synchronized (mInitializationDelegate.getLock()) {
+ mInitializationDelegate.updateState(options, sharedMemory);
+ }
}
@@ -116,18 +118,21 @@ public class VisualQueryDetector {
if (DEBUG) {
Slog.i(TAG, "#startRecognition");
}
- // check if the detector is active with the initialization delegate
- mInitializationDelegate.startRecognition();
-
- try {
- mManagerService.startPerceiving(new BinderCallback(mExecutor, mCallback));
- } catch (SecurityException e) {
- Slog.e(TAG, "startRecognition failed: " + e);
- return false;
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ synchronized (mInitializationDelegate.getLock()) {
+ // check if the detector is active with the initialization delegate
+ mInitializationDelegate.startRecognition();
+
+ try {
+ mManagerService.startPerceiving(new BinderCallback(
+ mExecutor, mCallback, mInitializationDelegate.getLock()));
+ } catch (SecurityException e) {
+ Slog.e(TAG, "startRecognition failed: " + e);
+ return false;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return true;
}
- return true;
}
/**
@@ -140,15 +145,17 @@ public class VisualQueryDetector {
if (DEBUG) {
Slog.i(TAG, "#stopRecognition");
}
- // check if the detector is active with the initialization delegate
- mInitializationDelegate.startRecognition();
-
- try {
- mManagerService.stopPerceiving();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ synchronized (mInitializationDelegate.getLock()) {
+ // check if the detector is active with the initialization delegate
+ mInitializationDelegate.stopRecognition();
+
+ try {
+ mManagerService.stopPerceiving();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return true;
}
- return true;
}
/**
@@ -160,12 +167,16 @@ public class VisualQueryDetector {
if (DEBUG) {
Slog.i(TAG, "#destroy");
}
- mInitializationDelegate.destroy();
+ synchronized (mInitializationDelegate.getLock()) {
+ mInitializationDelegate.destroy();
+ }
}
/** @hide */
public void dump(String prefix, PrintWriter pw) {
- // TODO: implement this
+ synchronized (mInitializationDelegate.getLock()) {
+ mInitializationDelegate.dump(prefix, pw);
+ }
}
/** @hide */
@@ -175,7 +186,9 @@ public class VisualQueryDetector {
/** @hide */
void registerOnDestroyListener(Consumer<AbstractDetector> onDestroyListener) {
- mInitializationDelegate.registerOnDestroyListener(onDestroyListener);
+ synchronized (mInitializationDelegate.getLock()) {
+ mInitializationDelegate.registerOnDestroyListener(onDestroyListener);
+ }
}
/**
@@ -282,6 +295,15 @@ public class VisualQueryDetector {
public boolean isUsingSandboxedDetectionService() {
return true;
}
+
+ @Override
+ public void dump(String prefix, PrintWriter pw) {
+ // No-op
+ }
+
+ private Object getLock() {
+ return mLock;
+ }
}
private static class BinderCallback
@@ -289,31 +311,43 @@ public class VisualQueryDetector {
private final Executor mExecutor;
private final VisualQueryDetector.Callback mCallback;
- BinderCallback(Executor executor, VisualQueryDetector.Callback callback) {
+ private final Object mLock;
+
+ BinderCallback(Executor executor, VisualQueryDetector.Callback callback, Object lock) {
this.mExecutor = executor;
this.mCallback = callback;
+ this.mLock = lock;
}
/** Called when the detected result is valid. */
@Override
public void onQueryDetected(@NonNull String partialQuery) {
Slog.v(TAG, "BinderCallback#onQueryDetected");
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(
- () -> mCallback.onQueryDetected(partialQuery)));
+ Binder.withCleanCallingIdentity(() -> {
+ synchronized (mLock) {
+ mCallback.onQueryDetected(partialQuery);
+ }
+ });
}
@Override
public void onQueryFinished() {
Slog.v(TAG, "BinderCallback#onQueryFinished");
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(
- () -> mCallback.onQueryFinished()));
+ Binder.withCleanCallingIdentity(() -> {
+ synchronized (mLock) {
+ mCallback.onQueryFinished();
+ }
+ });
}
@Override
public void onQueryRejected() {
Slog.v(TAG, "BinderCallback#onQueryRejected");
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(
- () -> mCallback.onQueryRejected()));
+ Binder.withCleanCallingIdentity(() -> {
+ synchronized (mLock) {
+ mCallback.onQueryRejected();
+ }
+ });
}
/** Called when the detection fails due to an error. */
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 31d759ea92e6..18080e4478fc 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -287,6 +287,16 @@ public abstract class DisplayEventReceiver {
}
/**
+ * Called when a display hdcp levels change event is received.
+ *
+ * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
+ * @param connectedLevel the new connected HDCP level
+ * @param maxLevel the maximum HDCP level
+ */
+ public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) {
+ }
+
+ /**
* Represents a mapping between a UID and an override frame rate
*/
public static class FrameRateOverride {
@@ -374,4 +384,11 @@ public abstract class DisplayEventReceiver {
onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides);
}
+ // Called from native code.
+ @SuppressWarnings("unused")
+ private void dispatchHdcpLevelsChanged(long physicalDisplayId, int connectedLevel,
+ int maxLevel) {
+ onHdcpLevelsChanged(physicalDisplayId, connectedLevel, maxLevel);
+ }
+
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e83488e2689e..9f6395e1aab4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -528,8 +528,6 @@ public final class ViewRootImpl implements ViewParent,
// Set to true to stop input during an Activity Transition.
boolean mPausedForTransition = false;
- boolean mLastInCompatMode = false;
-
SurfaceHolder.Callback2 mSurfaceHolderCallback;
BaseSurfaceHolder mSurfaceHolder;
boolean mIsCreating;
@@ -1375,11 +1373,6 @@ public final class ViewRootImpl implements ViewParent,
}
if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" + attrs);
- if (!compatibilityInfo.supportsScreen()) {
- attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
- mLastInCompatMode = true;
- }
-
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mAttachInfo.mRootView = view;
@@ -1913,10 +1906,6 @@ public final class ViewRootImpl implements ViewParent,
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags = attrs.flags;
- // Preserve compatible window flag if exists.
- final int compatibleWindowFlag = mWindowAttributes.privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-
// Preserve system UI visibility.
final int systemUiVisibility = mWindowAttributes.systemUiVisibility;
final int subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
@@ -1948,8 +1937,7 @@ public final class ViewRootImpl implements ViewParent,
mWindowAttributes.subtreeSystemUiVisibility = subtreeSystemUiVisibility;
mWindowAttributes.insetsFlags.appearance = appearance;
mWindowAttributes.insetsFlags.behavior = behavior;
- mWindowAttributes.privateFlags |= compatibleWindowFlag
- | appearanceAndBehaviorPrivateFlags;
+ mWindowAttributes.privateFlags |= appearanceAndBehaviorPrivateFlags;
if (mWindowAttributes.preservePreviousSurfaceInsets) {
// Restore old surface insets.
@@ -3150,21 +3138,6 @@ public final class ViewRootImpl implements ViewParent,
final boolean shouldOptimizeMeasure = shouldOptimizeMeasure(lp);
WindowManager.LayoutParams params = null;
- CompatibilityInfo compatibilityInfo =
- mDisplay.getDisplayAdjustments().getCompatibilityInfo();
- if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
- params = lp;
- mFullRedrawNeeded = true;
- mLayoutRequested = true;
- if (mLastInCompatMode) {
- params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
- mLastInCompatMode = false;
- } else {
- params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
- mLastInCompatMode = true;
- }
- }
-
Rect frame = mWinFrame;
if (mFirst) {
mFullRedrawNeeded = true;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1712fd3c3323..07a347ace313 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3143,13 +3143,6 @@ public interface WindowManager extends ViewManager {
@UnsupportedAppUsage
public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 1 << 6;
- /** Window flag: special flag to limit the size of the window to be
- * original size ([320x480] x density). Used to create window for applications
- * running under compatibility mode.
- *
- * {@hide} */
- public static final int PRIVATE_FLAG_COMPATIBLE_WINDOW = 1 << 7;
-
/** Window flag: a special option intended for system dialogs. When
* this flag is set, the window will demand focus unconditionally when
* it is created.
@@ -3345,7 +3338,6 @@ public interface WindowManager extends ViewManager {
SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION,
PRIVATE_FLAG_NO_MOVE_ANIMATION,
- PRIVATE_FLAG_COMPATIBLE_WINDOW,
PRIVATE_FLAG_SYSTEM_ERROR,
PRIVATE_FLAG_OPTIMIZE_MEASURE,
PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
@@ -3399,10 +3391,6 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_NO_MOVE_ANIMATION,
name = "NO_MOVE_ANIMATION"),
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_COMPATIBLE_WINDOW,
- equals = PRIVATE_FLAG_COMPATIBLE_WINDOW,
- name = "COMPATIBLE_WINDOW"),
- @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_SYSTEM_ERROR,
equals = PRIVATE_FLAG_SYSTEM_ERROR,
name = "SYSTEM_ERROR"),
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index c47572313eeb..7cda3a36da95 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -36,6 +36,7 @@ public final class BackMotionEvent implements Parcelable {
private final float mProgress;
private final float mVelocityX;
private final float mVelocityY;
+ private final boolean mTriggerBack;
@BackEvent.SwipeEdge
private final int mSwipeEdge;
@@ -54,6 +55,7 @@ public final class BackMotionEvent implements Parcelable {
* Value in pixels/second. {@link Float#NaN} if was not computed.
* @param velocityY Y velocity computed from the touch point of this event.
* Value in pixels/second. {@link Float#NaN} if was not computed.
+ * @param triggerBack Indicates whether the back arrow is in the triggered state or not
* @param swipeEdge Indicates which edge the swipe starts from.
* @param departingAnimationTarget The remote animation target of the departing
* application window.
@@ -64,6 +66,7 @@ public final class BackMotionEvent implements Parcelable {
float progress,
float velocityX,
float velocityY,
+ boolean triggerBack,
@BackEvent.SwipeEdge int swipeEdge,
@Nullable RemoteAnimationTarget departingAnimationTarget) {
mTouchX = touchX;
@@ -71,6 +74,7 @@ public final class BackMotionEvent implements Parcelable {
mProgress = progress;
mVelocityX = velocityX;
mVelocityY = velocityY;
+ mTriggerBack = triggerBack;
mSwipeEdge = swipeEdge;
mDepartingAnimationTarget = departingAnimationTarget;
}
@@ -81,6 +85,7 @@ public final class BackMotionEvent implements Parcelable {
mProgress = in.readFloat();
mVelocityX = in.readFloat();
mVelocityY = in.readFloat();
+ mTriggerBack = in.readBoolean();
mSwipeEdge = in.readInt();
mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
}
@@ -110,6 +115,7 @@ public final class BackMotionEvent implements Parcelable {
dest.writeFloat(mProgress);
dest.writeFloat(mVelocityX);
dest.writeFloat(mVelocityY);
+ dest.writeBoolean(mTriggerBack);
dest.writeInt(mSwipeEdge);
dest.writeTypedObject(mDepartingAnimationTarget, flags);
}
@@ -157,6 +163,15 @@ public final class BackMotionEvent implements Parcelable {
}
/**
+ * Returns whether the back arrow is in the triggered state or not
+ *
+ * @return boolean indicating whether the back arrow is in the triggered state or not
+ */
+ public boolean getTriggerBack() {
+ return mTriggerBack;
+ }
+
+ /**
* Returns the screen edge that the swipe starts from.
*/
@BackEvent.SwipeEdge
@@ -182,6 +197,7 @@ public final class BackMotionEvent implements Parcelable {
+ ", mProgress=" + mProgress
+ ", mVelocityX=" + mVelocityX
+ ", mVelocityY=" + mVelocityY
+ + ", mTriggerBack=" + mTriggerBack
+ ", mSwipeEdge" + mSwipeEdge
+ ", mDepartingAnimationTarget" + mDepartingAnimationTarget
+ "}";
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 0077dab7ff63..4b5595feb966 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -36,3 +36,10 @@ flag {
bug: "316139088"
is_fixed_read_only: true
}
+
+flag {
+ name: "user_min_aspect_ratio_app_default"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Whether the API PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT is available"
+ bug: "310816437"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 07beb114898d..216acdce38fb 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -15,6 +15,14 @@ flag {
}
flag {
+ name: "enforce_edge_to_edge"
+ namespace: "windowing_frontend"
+ description: "Make app go edge-to-edge when targeting SDK level 35 or greater"
+ bug: "309578419"
+ is_fixed_read_only: true
+}
+
+flag {
name: "defer_display_updates"
namespace: "windowing_frontend"
description: "Feature flag for deferring DisplayManager updates to WindowManager if Shell transition is running"
@@ -58,4 +66,12 @@ flag {
description: "Predictive back for system animations"
bug: "309545085"
is_fixed_read_only: true
+}
+
+flag {
+ name: "activity_snapshot_by_default"
+ namespace: "systemui"
+ description: "Enable record activity snapshot by default"
+ bug: "259497289"
+ is_fixed_read_only: true
} \ No newline at end of file
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index efc1455ecd45..467cd49c2279 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -39,6 +39,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserPackage;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -308,7 +309,8 @@ public class SuspendedAppActivity extends AlertActivity
try {
final String[] errored = ipm.setPackagesSuspendedAsUser(
new String[]{mSuspendedPackage}, false, null, null, null, 0,
- mSuspendingPackage, mUserId);
+ mSuspendingPackage, mUserId /* suspendingUserId */,
+ mUserId /* targetUserId */);
if (ArrayUtils.contains(errored, mSuspendedPackage)) {
Slog.e(TAG, "Could not unsuspend " + mSuspendedPackage);
break;
@@ -350,17 +352,19 @@ public class SuspendedAppActivity extends AlertActivity
}
public static Intent createSuspendedAppInterceptIntent(String suspendedPackage,
- String suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options,
+ UserPackage suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options,
IntentSender onUnsuspend, int userId) {
- return new Intent()
+ Intent intent = new Intent()
.setClassName("android", SuspendedAppActivity.class.getName())
.putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage)
.putExtra(EXTRA_DIALOG_INFO, dialogInfo)
- .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage)
+ .putExtra(EXTRA_SUSPENDING_PACKAGE,
+ suspendingPackage != null ? suspendingPackage.packageName : null)
.putExtra(EXTRA_UNSUSPEND_INTENT, onUnsuspend)
.putExtra(EXTRA_ACTIVITY_OPTIONS, options)
.putExtra(Intent.EXTRA_USER_ID, userId)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ return intent;
}
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 201b23cc172c..31910ac7fce5 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -169,20 +169,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
/**
- * Makes navigation bar color transparent by default if the target SDK is
- * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
- private static final long NAV_BAR_COLOR_DEFAULT_TRANSPARENT = 232195501L;
-
- /**
* Make app go edge-to-edge by default if the target SDK is
* {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
- private static final long EDGE_TO_EDGE_BY_DEFAULT = 309578419;
+ private static final long ENFORCE_EDGE_TO_EDGE = 309578419;
private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
(1 << FEATURE_CUSTOM_TITLE) |
@@ -193,9 +185,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private static final Transition USE_DEFAULT_TRANSITION = new TransitionSet();
/**
- * Since which target SDK version this window should be edge-to-edge by default.
+ * Since which target SDK version this window is enforced to go edge-to-edge.
*/
- private static final int DEFAULT_EDGE_TO_EDGE_SDK_VERSION =
+ private static final int ENFORCE_EDGE_TO_EDGE_SDK_VERSION =
SystemProperties.getInt("persist.wm.debug.default_e2e_since_sdk", Integer.MAX_VALUE);
/**
@@ -376,7 +368,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
boolean mDecorFitsSystemWindows = true;
@VisibleForTesting
- public final boolean mDefaultEdgeToEdge;
+ public final boolean mEdgeToEdgeEnforced;
private final ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher;
@@ -396,11 +388,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mProxyOnBackInvokedDispatcher = new ProxyOnBackInvokedDispatcher(context);
mAllowFloatingWindowsFillScreen = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowFloatingWindowsFillScreen);
- mDefaultEdgeToEdge =
- context.getApplicationInfo().targetSdkVersion >= DEFAULT_EDGE_TO_EDGE_SDK_VERSION
- || (CompatChanges.isChangeEnabled(EDGE_TO_EDGE_BY_DEFAULT)
- && Flags.edgeToEdgeByDefault());
- if (mDefaultEdgeToEdge) {
+ mEdgeToEdgeEnforced =
+ context.getApplicationInfo().targetSdkVersion >= ENFORCE_EDGE_TO_EDGE_SDK_VERSION
+ || (CompatChanges.isChangeEnabled(ENFORCE_EDGE_TO_EDGE)
+ && Flags.enforceEdgeToEdge());
+ if (mEdgeToEdgeEnforced) {
mDecorFitsSystemWindows = false;
}
}
@@ -2472,7 +2464,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
params.setFitInsetsSides(0);
params.setFitInsetsTypes(0);
- if (mDefaultEdgeToEdge) {
+ if (mEdgeToEdgeEnforced) {
params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
}
}
@@ -2562,7 +2554,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
final int statusBarColor = a.getColor(R.styleable.Window_statusBarColor,
statusBarDefaultColor);
- mStatusBarColor = statusBarColor == statusBarDefaultColor && !mDefaultEdgeToEdge
+ mStatusBarColor = statusBarColor == statusBarDefaultColor && !mEdgeToEdgeEnforced
? statusBarCompatibleColor
: statusBarColor;
}
@@ -2574,9 +2566,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mNavigationBarColor =
navBarColor == navBarDefaultColor
- && !mDefaultEdgeToEdge
- && !(CompatChanges.isChangeEnabled(NAV_BAR_COLOR_DEFAULT_TRANSPARENT)
- && Flags.navBarTransparentByDefault())
+ && !mEdgeToEdgeEnforced
&& !context.getResources().getBoolean(
R.bool.config_navBarDefaultTransparent)
? navBarCompatibleColor
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c24d21dda68c..17aad43edb6b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -653,6 +653,8 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
char profileMinSavePeriodOptsBuf[sizeof("-Xps-min-save-period-ms:")-1 + PROPERTY_VALUE_MAX];
char profileMinFirstSaveOptsBuf[sizeof("-Xps-min-first-save-ms:") - 1 + PROPERTY_VALUE_MAX];
+ char profileInlineCacheThresholdOptsBuf[
+ sizeof("-Xps-inline-cache-threshold:") - 1 + PROPERTY_VALUE_MAX];
char madviseWillNeedFileSizeVdex[
sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
char madviseWillNeedFileSizeOdex[
@@ -898,6 +900,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
parseRuntimeOption("dalvik.vm.ps-min-first-save-ms", profileMinFirstSaveOptsBuf,
"-Xps-min-first-save-ms:");
+ parseRuntimeOption("dalvik.vm.ps-inline-cache-threshold", profileInlineCacheThresholdOptsBuf,
+ "-Xps-inline-cache-threshold:");
+
property_get("ro.config.low_ram", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
addOption("-XX:LowMemoryMode");
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index fef8ad7499f7..f007cc5a23bd 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -41,6 +41,7 @@ static struct {
jmethodID dispatchHotplugConnectionError;
jmethodID dispatchModeChanged;
jmethodID dispatchFrameRateOverrides;
+ jmethodID dispatchHdcpLevelsChanged;
struct {
jclass clazz;
@@ -96,6 +97,8 @@ private:
void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {}
+ void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int connectedLevel,
+ int maxLevel) override;
};
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
@@ -294,6 +297,22 @@ void NativeDisplayEventReceiver::dispatchFrameRateOverrides(
mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
}
+void NativeDisplayEventReceiver::dispatchHdcpLevelsChanged(PhysicalDisplayId displayId,
+ int connectedLevel, int maxLevel) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking hdcp levels changed handler.", this);
+ env->CallVoidMethod(receiverObj.get(),
+ gDisplayEventReceiverClassInfo.dispatchHdcpLevelsChanged,
+ displayId.value, connectedLevel, maxLevel);
+ ALOGV("receiver %p ~ Returned from hdcp levels changed handler.", this);
+ }
+
+ mMessageQueue->raiseAndClearException(env, "dispatchHdcpLevelsChanged");
+}
+
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject vsyncEventDataWeak,
jobject messageQueueObj, jint vsyncSource, jint eventRegistration,
jlong layerHandle) {
@@ -385,6 +404,9 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) {
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
"dispatchFrameRateOverrides",
"(JJ[Landroid/view/DisplayEventReceiver$FrameRateOverride;)V");
+ gDisplayEventReceiverClassInfo.dispatchHdcpLevelsChanged =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHdcpLevelsChanged",
+ "(JII)V");
jclass frameRateOverrideClazz =
FindClassOrDie(env, "android/view/DisplayEventReceiver$FrameRateOverride");
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index c5889ba78159..75cfba02120e 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -429,6 +429,7 @@ message ServiceRecordProto {
optional string base_dir = 1;
optional string res_dir = 2;
optional string data_dir = 3;
+ optional int32 targetSdkVersion = 4;
}
optional AppInfo appinfo = 8;
optional ProcessRecordProto app = 9;
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1a3ec27418a6..c0581746e6f6 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -202,12 +202,14 @@ android_ravenwood_test {
"testng",
],
srcs: [
+ "src/android/database/CursorWindowTest.java",
"src/android/os/**/*.java",
- "src/com/android/internal/os/**/*.java",
"src/android/util/**/*.java",
+ "src/com/android/internal/os/**/*.java",
"src/com/android/internal/os/LongArrayMultiStateCounterTest.java",
"src/com/android/internal/util/**/*.java",
"src/com/android/internal/power/EnergyConsumerStatsTest.java",
+
":FrameworksCoreTests{.aapt.srcjar}",
":FrameworksCoreTests-aidl",
":FrameworksCoreTests-helpers",
diff --git a/core/tests/coretests/src/android/database/CursorWindowPerformanceTest.java b/core/tests/coretests/src/android/database/CursorWindowPerformanceTest.java
new file mode 100644
index 000000000000..c6d3ebf0251e
--- /dev/null
+++ b/core/tests/coretests/src/android/database/CursorWindowPerformanceTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database;
+
+import android.test.PerformanceTestCase;
+
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * <pre>
+ * m -j44 FrameworksCoreTests
+ * adb install -r -g \
+ * ${ANDROID_PRODUCT_OUT}/testcases/FrameworksCoreTests/arm64/FrameworksCoreTests.apk
+ * adb shell am instrument -r -e perf true \
+ * -e class 'android.database.CursorWindowPerformanceTest'
+ * -w 'com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner'
+ * </pre>
+ */
+public class CursorWindowPerformanceTest extends TestCase implements PerformanceTestCase {
+
+ private final CursorWindowTest mTest = new CursorWindowTest();
+
+ @Override
+ public boolean isPerformanceOnly() {
+ return true;
+ }
+
+ // These test can only be run once.
+ @Override
+ public int startPerformance(Intermediates intermediates) {
+ return 1;
+ }
+
+ @SmallTest
+ public void testConstructor_WithName() {
+ mTest.testConstructor_WithName();
+ }
+
+ @SmallTest
+ public void testConstructorWithEmptyName() {
+ mTest.testConstructorWithEmptyName();
+ }
+
+ @SmallTest
+ public void testValues() {
+ mTest.testValues();
+ }
+}
diff --git a/core/tests/coretests/src/android/database/CursorWindowTest.java b/core/tests/coretests/src/android/database/CursorWindowTest.java
index 123da3e8703b..255020a84893 100644
--- a/core/tests/coretests/src/android/database/CursorWindowTest.java
+++ b/core/tests/coretests/src/android/database/CursorWindowTest.java
@@ -16,25 +16,29 @@
package android.database;
-import android.test.PerformanceTestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import android.database.sqlite.SQLiteException;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.Arrays;
-public class CursorWindowTest extends TestCase implements PerformanceTestCase {
- public boolean isPerformanceOnly() {
- return false;
- }
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CursorWindowTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
- // These test can only be run once.
- public int startPerformance(Intermediates intermediates) {
- return 1;
- }
-
- @SmallTest
+ @Test
public void testConstructor_WithName() {
CursorWindow window = new CursorWindow("MyWindow");
assertEquals("MyWindow", window.getName());
@@ -42,7 +46,7 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase {
window.close();
}
- @SmallTest
+ @Test
public void testConstructorWithEmptyName() {
CursorWindow window = new CursorWindow("");
assertEquals("<unnamed>", window.getName());
@@ -50,7 +54,7 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase {
window.close();
}
- @SmallTest
+ @Test
public void testConstructorWithNullName() {
CursorWindow window = new CursorWindow(null);
assertEquals("<unnamed>", window.getName());
@@ -58,7 +62,7 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase {
window.close();
}
- @SmallTest
+ @Test
public void testDeprecatedConstructor() {
@SuppressWarnings("deprecation")
CursorWindow window = new CursorWindow(true /*this argument is ignored*/);
@@ -67,7 +71,7 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase {
window.close();
}
- @SmallTest
+ @Test
public void testValues() {
CursorWindow window = new CursorWindow("MyWindow");
doTestValues(window);
@@ -77,17 +81,25 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase {
private void doTestValues(CursorWindow window) {
assertTrue(window.setNumColumns(7));
assertTrue(window.allocRow());
+ assertEquals(window.getType(0, 0), Cursor.FIELD_TYPE_NULL);
+
double db1 = 1.26;
assertTrue(window.putDouble(db1, 0, 0));
+ assertEquals(window.getType(0, 0), Cursor.FIELD_TYPE_FLOAT);
double db2 = window.getDouble(0, 0);
- assertEquals(db1, db2);
+ assertEquals(db1, db2, 0.01);
+ assertEquals(1, window.getInt(0, 0));
+ assertEquals("1.26", window.getString(0, 0));
+ assertThrows(SQLiteException.class, () -> window.getBlob(0, 0));
long int1 = Long.MAX_VALUE;
assertTrue(window.putLong(int1, 0, 1));
+ assertEquals(window.getType(0, 1), Cursor.FIELD_TYPE_INTEGER);
long int2 = window.getLong(0, 1);
assertEquals(int1, int2);
assertTrue(window.putString("1198032740000", 0, 3));
+ assertEquals(window.getType(0, 3), Cursor.FIELD_TYPE_STRING);
assertEquals("1198032740000", window.getString(0, 3));
assertEquals(1198032740000L, window.getLong(0, 3));
@@ -97,13 +109,14 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase {
assertTrue(window.putString(Double.toString(42.0), 0, 4));
assertEquals(Double.toString(42.0), window.getString(0, 4));
- assertEquals(42.0, window.getDouble(0, 4));
+ assertEquals(42.0, window.getDouble(0, 4), 0.01);
// put blob
byte[] blob = new byte[1000];
byte value = 99;
Arrays.fill(blob, value);
assertTrue(window.putBlob(blob, 0, 6));
+ assertEquals(window.getType(0, 6), Cursor.FIELD_TYPE_BLOB);
assertTrue(Arrays.equals(blob, window.getBlob(0, 6)));
}
}
diff --git a/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java
new file mode 100644
index 000000000000..da3a465ade7e
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.SensorProps;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.function.Function;
+
+@Presubmit
+@SmallTest
+public class FaceSensorConfigurationsTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private IFace mFace;
+ @Mock
+ private Function<String, IFace> mGetIFace;
+
+ private final String[] mAidlInstances = new String[]{"default", "virtual"};
+ private String[] mHidlConfigStrings = new String[]{"0:2:15", "0:8:15"};
+ private FaceSensorConfigurations mFaceSensorConfigurations;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mGetIFace.apply(anyString())).thenReturn(mFace);
+ when(mFace.getSensorProps()).thenReturn(new SensorProps[]{});
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void testAidlInstanceSensorProps() {
+ mFaceSensorConfigurations = new FaceSensorConfigurations(false);
+ mFaceSensorConfigurations.addAidlConfigs(mAidlInstances, mGetIFace);
+
+ assertThat(mFaceSensorConfigurations.hasSensorConfigurations()).isTrue();
+ assertThat(!mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFaceSensorConfigurations.getResetLockoutRequiresChallenge())
+ .isFalse();
+ }
+
+ @Test
+ public void testHidlConfigStrings() {
+ mFaceSensorConfigurations = new FaceSensorConfigurations(true);
+ mFaceSensorConfigurations.addHidlConfigs(mHidlConfigStrings, mContext);
+
+ assertThat(mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFaceSensorConfigurations.getResetLockoutRequiresChallenge())
+ .isTrue();
+ }
+
+ @Test
+ public void testHidlConfigStrings_incorrectFormat() {
+ mHidlConfigStrings = new String[]{"0:8:15", "0:2", "0:face:15"};
+ mFaceSensorConfigurations = new FaceSensorConfigurations(true);
+ mFaceSensorConfigurations.addHidlConfigs(mHidlConfigStrings, mContext);
+
+ assertThat(mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFaceSensorConfigurations.getResetLockoutRequiresChallenge())
+ .isTrue();
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java
new file mode 100644
index 000000000000..613089c8777d
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.function.Function;
+
+
+@Presubmit
+@SmallTest
+public class FingerprintSensorConfigurationsTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private IFingerprint mFingerprint;
+ @Mock
+ private Function<String, IFingerprint> mGetIFingerprint;
+
+ private final String[] mAidlInstances = new String[]{"default", "virtual"};
+ private String[] mHidlConfigStrings = new String[]{"0:2:15", "0:8:15"};
+ private FingerprintSensorConfigurations mFingerprintSensorConfigurations;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mGetIFingerprint.apply(anyString())).thenReturn(mFingerprint);
+ when(mFingerprint.getSensorProps()).thenReturn(new SensorProps[]{});
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void testAidlInstanceSensorProps() {
+ mFingerprintSensorConfigurations = new FingerprintSensorConfigurations(
+ true /* resetLockoutRequiresHardwareAuthToken */);
+ mFingerprintSensorConfigurations.addAidlSensors(mAidlInstances, mGetIFingerprint);
+
+ assertThat(mFingerprintSensorConfigurations.hasSensorConfigurations()).isTrue();
+ assertThat(!mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent())
+ .isTrue();
+ assertThat(mFingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken())
+ .isTrue();
+ }
+
+ @Test
+ public void testHidlConfigStrings() {
+ mFingerprintSensorConfigurations = new FingerprintSensorConfigurations(
+ false /* resetLockoutRequiresHardwareAuthToken */);
+ mFingerprintSensorConfigurations.addHidlSensors(mHidlConfigStrings, mContext);
+
+ assertThat(mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken())
+ .isFalse();
+ }
+
+ @Test
+ public void testHidlConfigStrings_incorrectFormat() {
+ mHidlConfigStrings = new String[]{"0:8:15", "0:2", "0:fingerprint:15"};
+ mFingerprintSensorConfigurations = new FingerprintSensorConfigurations(
+ false /* resetLockoutRequiresHardwareAuthToken */);
+ mFingerprintSensorConfigurations.addHidlSensors(mHidlConfigStrings, mContext);
+
+ assertThat(mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken())
+ .isFalse();
+ }
+}
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 68c0693fb23a..a709d7be898b 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -84,6 +84,7 @@ public class WindowOnBackInvokedDispatcherTest {
/* progress = */ 0,
/* velocityX = */ 0,
/* velocityY = */ 0,
+ /* triggerBack = */ false,
/* swipeEdge = */ BackEvent.EDGE_LEFT,
/* departingAnimationTarget = */ null);
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
index 6bdc06af2c5c..de55b0759edd 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -63,7 +63,7 @@ public final class PhoneWindowTest {
createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeUnset);
installDecor();
- if (mPhoneWindow.mDefaultEdgeToEdge && !mPhoneWindow.isFloating()) {
+ if (mPhoneWindow.mEdgeToEdgeEnforced && !mPhoneWindow.isFloating()) {
assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode,
is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS));
} else {
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index aaddf0e50ddd..742d5a2627eb 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -379,12 +379,6 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
- "-1770075711": {
- "message": "Adding window client %s that is dead, aborting.",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"-1768557332": {
"message": "removeWallpaperAnimation()",
"level": "DEBUG",
@@ -3253,6 +3247,12 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
+ "723575093": {
+ "message": "Attempted to add window with a client %s that is dead. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"726205185": {
"message": "Moving to DESTROYED: %s (destroy skipped)",
"level": "VERBOSE",
@@ -4237,12 +4237,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1720696061": {
- "message": "Adding window to Display that has been removed.",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"1730300180": {
"message": "PendingStartTransaction found",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationConstants.java
index e06d3ef4e1ab..5b0de5070a60 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationConstants.java
@@ -21,5 +21,4 @@ package com.android.wm.shell.back;
*/
class BackAnimationConstants {
static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.20f;
- static final float PROGRESS_COMMIT_THRESHOLD = 0.1f;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index d8c691b01b61..a49823648d01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -70,9 +70,11 @@ import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -124,6 +126,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private final Context mContext;
private final ContentResolver mContentResolver;
private final ShellController mShellController;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mShellExecutor;
private final Handler mBgHandler;
@@ -180,7 +183,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull @ShellBackgroundThread Handler backgroundHandler,
Context context,
@NonNull BackAnimationBackground backAnimationBackground,
- ShellBackAnimationRegistry shellBackAnimationRegistry) {
+ ShellBackAnimationRegistry shellBackAnimationRegistry,
+ ShellCommandHandler shellCommandHandler) {
this(
shellInit,
shellController,
@@ -190,7 +194,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
context,
context.getContentResolver(),
backAnimationBackground,
- shellBackAnimationRegistry);
+ shellBackAnimationRegistry,
+ shellCommandHandler);
}
@VisibleForTesting
@@ -203,7 +208,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
Context context,
ContentResolver contentResolver,
@NonNull BackAnimationBackground backAnimationBackground,
- ShellBackAnimationRegistry shellBackAnimationRegistry) {
+ ShellBackAnimationRegistry shellBackAnimationRegistry,
+ ShellCommandHandler shellCommandHandler) {
mShellController = shellController;
mShellExecutor = shellExecutor;
mActivityTaskManager = activityTaskManager;
@@ -219,6 +225,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
.build();
mShellBackAnimationRegistry = shellBackAnimationRegistry;
mLatencyTracker = LatencyTracker.getInstance(mContext);
+ mShellCommandHandler = shellCommandHandler;
}
private void onInit() {
@@ -227,6 +234,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
createAdapter();
mShellController.addExternalInterface(KEY_EXTRA_SHELL_BACK_ANIMATION,
this::createExternalInterface, this);
+ mShellCommandHandler.addDumpCallback(this::dump, this);
}
private void setupAnimationDeveloperSettingsObserver(
@@ -968,4 +976,20 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
};
mBackAnimationAdapter = new BackAnimationAdapter(runner);
}
+
+ /**
+ * Description of current BackAnimationController state.
+ */
+ private void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "BackAnimationController state:");
+ pw.println(prefix + " mEnableAnimations=" + mEnableAnimations.get());
+ pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted);
+ pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress);
+ pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent);
+ pw.println(prefix + " mCurrentTracker state:");
+ mCurrentTracker.dump(pw, prefix + " ");
+ pw.println(prefix + " mQueuedTracker state:");
+ mQueuedTracker.dump(pw, prefix + " ");
+ }
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
index 215a6cc99e58..30d5edb59c85 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
@@ -18,9 +18,9 @@ package com.android.wm.shell.back;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.window.BackEvent.EDGE_RIGHT;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY;
-import static com.android.wm.shell.back.BackAnimationConstants.PROGRESS_COMMIT_THRESHOLD;
import static com.android.wm.shell.back.BackAnimationConstants.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
@@ -91,7 +91,7 @@ public class CrossActivityBackAnimation extends ShellBackAnimation {
}
};
private static final float MIN_WINDOW_ALPHA = 0.01f;
- private static final float WINDOW_X_SHIFT_DP = 96;
+ private static final float WINDOW_X_SHIFT_DP = 48;
private static final int SCALE_FACTOR = 100;
// TODO(b/264710590): Use the progress commit threshold from ViewConfiguration once it exists.
private static final float TARGET_COMMIT_PROGRESS = 0.5f;
@@ -126,6 +126,8 @@ public class CrossActivityBackAnimation extends ShellBackAnimation {
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mBackInProgress = false;
+ private boolean mIsRightEdge;
+ private boolean mTriggerBack = false;
private PointF mTouchPos = new PointF();
private IRemoteAnimationFinishedCallback mFinishCallback;
@@ -209,6 +211,7 @@ public class CrossActivityBackAnimation extends ShellBackAnimation {
private void finishAnimation() {
if (mEnteringTarget != null) {
+ mTransaction.setCornerRadius(mEnteringTarget.leash, 0);
mEnteringTarget.leash.release();
mEnteringTarget = null;
}
@@ -241,14 +244,15 @@ public class CrossActivityBackAnimation extends ShellBackAnimation {
private void onGestureProgress(@NonNull BackEvent backEvent) {
if (!mBackInProgress) {
+ mIsRightEdge = backEvent.getSwipeEdge() == EDGE_RIGHT;
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
mBackInProgress = true;
}
mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
float progress = backEvent.getProgress();
- float springProgress = (progress > PROGRESS_COMMIT_THRESHOLD
- ? mapLinear(progress, 0.1f, 1, TARGET_COMMIT_PROGRESS, 1)
+ float springProgress = (mTriggerBack
+ ? mapLinear(progress, 0f, 1, TARGET_COMMIT_PROGRESS, 1)
: mapLinear(progress, 0, 1f, 0, TARGET_COMMIT_PROGRESS)) * SCALE_FACTOR;
mLeavingProgressSpring.animateToFinalPosition(springProgress);
mEnteringProgressSpring.animateToFinalPosition(springProgress);
@@ -312,7 +316,7 @@ public class CrossActivityBackAnimation extends ShellBackAnimation {
transformWithProgress(
mEnteringProgress,
Math.max(
- smoothstep(ENTER_ALPHA_THRESHOLD, 1, mEnteringProgress),
+ smoothstep(ENTER_ALPHA_THRESHOLD, 0.7f, mEnteringProgress),
MIN_WINDOW_ALPHA), /* alpha */
mEnteringTarget.leash,
mEnteringRect,
@@ -337,14 +341,13 @@ public class CrossActivityBackAnimation extends ShellBackAnimation {
mClosingTarget.leash,
mClosingRect,
0,
- mWindowXShift
+ mIsRightEdge ? 0 : mWindowXShift
);
}
}
private void transformWithProgress(float progress, float alpha, SurfaceControl surface,
RectF targetRect, float deltaXMin, float deltaXMax) {
- final float touchY = mTouchPos.y;
final int width = mStartTaskRect.width();
final int height = mStartTaskRect.height();
@@ -376,12 +379,14 @@ public class CrossActivityBackAnimation extends ShellBackAnimation {
private final class Callback extends IOnBackInvokedCallback.Default {
@Override
public void onBackStarted(BackMotionEvent backEvent) {
+ mTriggerBack = backEvent.getTriggerBack();
mProgressAnimator.onBackStarted(backEvent,
CrossActivityBackAnimation.this::onGestureProgress);
}
@Override
public void onBackProgressed(@NonNull BackMotionEvent backEvent) {
+ mTriggerBack = backEvent.getTriggerBack();
mProgressAnimator.onBackProgressed(backEvent);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 4bd56d460818..6213f628dfd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -24,6 +24,8 @@ import android.view.RemoteAnimationTarget;
import android.window.BackEvent;
import android.window.BackMotionEvent;
+import java.io.PrintWriter;
+
/**
* Helper class to record the touch location for gesture and generate back events.
*/
@@ -129,6 +131,7 @@ class TouchTracker {
/* progress = */ 0,
/* velocityX = */ 0,
/* velocityY = */ 0,
+ /* triggerBack = */ mTriggerBack,
/* swipeEdge = */ mSwipeEdge,
/* departingAnimationTarget = */ target);
}
@@ -204,6 +207,7 @@ class TouchTracker {
/* progress = */ progress,
/* velocityX = */ mLatestVelocityX,
/* velocityY = */ mLatestVelocityY,
+ /* triggerBack = */ mTriggerBack,
/* swipeEdge = */ mSwipeEdge,
/* departingAnimationTarget = */ null);
}
@@ -219,6 +223,12 @@ class TouchTracker {
mNonLinearFactor = nonLinearFactor;
}
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "TouchTracker state:");
+ pw.println(prefix + " mState=" + mState);
+ pw.println(prefix + " mTriggerBack=" + mTriggerBack);
+ }
+
enum TouchTrackerState {
INITIAL, ACTIVE, FINISHED
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 7f34ee0cdd3d..f794fef48f27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -23,6 +23,8 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.util.Log;
+import android.util.Size;
+import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
@@ -33,6 +35,7 @@ import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
+import com.android.wm.shell.common.magnetictarget.MagnetizedObject.MagneticTarget;
/**
* Helper class to animate a {@link BubbleBarExpandedView} on a bubble.
@@ -44,6 +47,13 @@ public class BubbleBarAnimationHelper {
private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f;
private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f;
private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
+ private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 100;
+ /**
+ * Additional scale applied to expanded view when it is positioned inside a magnetic target.
+ */
+ private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.75f;
+ private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
+ private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
/** Spring config for the expanded view scale-in animation. */
private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
@@ -181,7 +191,8 @@ public class BubbleBarAnimationHelper {
Log.w(TAG, "Trying to animate collapse without a bubble");
return;
}
-
+ bbev.setScaleX(1f);
+ bbev.setScaleY(1f);
mExpandedViewContainerMatrix.setScaleX(1f);
mExpandedViewContainerMatrix.setScaleY(1f);
@@ -209,11 +220,124 @@ public class BubbleBarAnimationHelper {
}
/**
+ * Animates dismissal of currently expanded bubble
+ *
+ * @param endRunnable a runnable to run at the end of the animation
+ */
+ public void animateDismiss(Runnable endRunnable) {
+ mIsExpanded = false;
+ final BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
+ Log.w(TAG, "Trying to animate dismiss without a bubble");
+ return;
+ }
+
+ int[] location = bbev.getLocationOnScreen();
+ int diffFromBottom = mPositioner.getScreenRect().bottom - location[1];
+
+ bbev.animate()
+ // 2x distance from bottom so the view flies out
+ .translationYBy(diffFromBottom * 2)
+ .setDuration(EXPANDED_VIEW_DISMISS_DURATION)
+ .withEndAction(endRunnable)
+ .start();
+ }
+
+ /**
+ * Animate current expanded bubble back to its rest position
+ */
+ public void animateToRestPosition() {
+ BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
+ Log.w(TAG, "Trying to animate expanded view to rest position without a bubble");
+ return;
+ }
+ Point restPoint = getExpandedViewRestPosition(getExpandedViewSize());
+ bbev.animate()
+ .x(restPoint.x)
+ .y(restPoint.y)
+ .scaleX(1f)
+ .scaleY(1f)
+ .setDuration(EXPANDED_VIEW_ANIMATE_POSITION_DURATION)
+ .setInterpolator(Interpolators.EMPHASIZED_DECELERATE)
+ .withStartAction(() -> bbev.setAnimating(true))
+ .withEndAction(() -> bbev.setAnimating(false))
+ .start();
+ }
+
+ /**
+ * Animates currently expanded bubble into the given {@link MagneticTarget}.
+ *
+ * @param target magnetic target to snap to
+ * @param endRunnable a runnable to run at the end of the animation
+ */
+ public void animateIntoTarget(MagneticTarget target, @Nullable Runnable endRunnable) {
+ BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
+ Log.w(TAG, "Trying to snap the expanded view to target without a bubble");
+ return;
+ }
+ Point expandedViewCenter = getViewCenterOnScreen(bbev);
+
+ // Calculate the difference between the target's center coordinates and the object's.
+ // Animating the object's x/y properties by these values will center the object on top
+ // of the magnetic target.
+ float xDiff = target.getCenterOnScreen().x - expandedViewCenter.x;
+ float yDiff = target.getCenterOnScreen().y - expandedViewCenter.y;
+
+ // Calculate scale of expanded view so it fits inside the magnetic target
+ float bbevMaxSide = Math.max(bbev.getWidth(), bbev.getHeight());
+ float targetMaxSide = Math.max(target.getTargetView().getWidth(),
+ target.getTargetView().getHeight());
+ float scale = (targetMaxSide * EXPANDED_VIEW_IN_TARGET_SCALE) / bbevMaxSide;
+
+ bbev.animate()
+ .translationX(bbev.getTranslationX() + xDiff)
+ .translationY(bbev.getTranslationY() + yDiff)
+ .scaleX(scale)
+ .scaleY(scale)
+ .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
+ .setInterpolator(Interpolators.EMPHASIZED)
+ .withStartAction(() -> bbev.setAnimating(true))
+ .withEndAction(() -> {
+ bbev.setAnimating(false);
+ if (endRunnable != null) {
+ endRunnable.run();
+ }
+ })
+ .start();
+ }
+
+ /**
+ * Animate currently expanded view when it is released from dismiss view
+ */
+ public void animateUnstuckFromDismissView() {
+ BubbleBarExpandedView expandedView = getExpandedView();
+ if (expandedView == null) {
+ Log.w(TAG, "Trying to unsnap the expanded view from dismiss without a bubble");
+ return;
+ }
+ expandedView
+ .animate()
+ .scaleX(1f)
+ .scaleY(1f)
+ .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
+ .setInterpolator(Interpolators.EMPHASIZED)
+ .withStartAction(() -> expandedView.setAnimating(true))
+ .withEndAction(() -> expandedView.setAnimating(false))
+ .start();
+ }
+
+ /**
* Cancel current animations
*/
public void cancelAnimations() {
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
mExpandedViewAlphaAnimator.cancel();
+ BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev != null) {
+ bbev.animate().cancel();
+ }
}
private @Nullable BubbleBarExpandedView getExpandedView() {
@@ -231,21 +355,42 @@ public class BubbleBarAnimationHelper {
return;
}
- boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
- final int padding = mPositioner.getBubbleBarExpandedViewPadding();
- final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
- final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
+ final Size size = getExpandedViewSize();
+ Point position = getExpandedViewRestPosition(size);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) bbev.getLayoutParams();
- lp.width = width;
- lp.height = height;
+ lp.width = size.getWidth();
+ lp.height = size.getHeight();
bbev.setLayoutParams(lp);
+ bbev.setX(position.x);
+ bbev.setY(position.y);
+ bbev.updateLocation();
+ bbev.maybeShowOverflow();
+ }
+
+ private Point getExpandedViewRestPosition(Size size) {
+ final int padding = mPositioner.getBubbleBarExpandedViewPadding();
+ Point point = new Point();
if (mLayerView.isOnLeft()) {
- bbev.setX(mPositioner.getInsets().left + padding);
+ point.x = mPositioner.getInsets().left + padding;
} else {
- bbev.setX(mPositioner.getAvailableRect().width() - width - padding);
+ point.x = mPositioner.getAvailableRect().width() - size.getWidth() - padding;
}
- bbev.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
- bbev.updateLocation();
- bbev.maybeShowOverflow();
+ point.y = mPositioner.getExpandedViewBottomForBubbleBar() - size.getHeight();
+ return point;
+ }
+
+ private Size getExpandedViewSize() {
+ boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
+ final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
+ final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
+ return new Size(width, height);
+ }
+
+ private Point getViewCenterOnScreen(View view) {
+ Point center = new Point();
+ int[] onScreenLocation = view.getLocationOnScreen();
+ center.x = (int) (onScreenLocation[0] + (view.getWidth() / 2f));
+ center.y = (int) (onScreenLocation[1] + (view.getHeight() / 2f));
+ return center;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index 4ea18f78f5b2..d21545079cc2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -16,70 +16,67 @@
package com.android.wm.shell.bubbles.bar
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.graphics.PointF
-import android.graphics.Rect
+import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.View
-import com.android.wm.shell.animation.Interpolators
import com.android.wm.shell.common.bubbles.DismissView
import com.android.wm.shell.common.bubbles.RelativeTouchListener
+import com.android.wm.shell.common.magnetictarget.MagnetizedObject
/** Controller for handling drag interactions with [BubbleBarExpandedView] */
+@SuppressLint("ClickableViewAccessibility")
class BubbleBarExpandedViewDragController(
private val expandedView: BubbleBarExpandedView,
private val dismissView: DismissView,
+ private val animationHelper: BubbleBarAnimationHelper,
private val onDismissed: () -> Unit
) {
+ var isStuckToDismiss: Boolean = false
+ private set
+
+ private var expandedViewInitialTranslationX = 0f
+ private var expandedViewInitialTranslationY = 0f
+ private val magnetizedExpandedView: MagnetizedObject<BubbleBarExpandedView> =
+ MagnetizedObject.magnetizeView(expandedView)
+ private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget
+
init {
- expandedView.handleView.setOnTouchListener(HandleDragListener())
- }
+ magnetizedExpandedView.magnetListener = MagnetListener()
+ magnetizedExpandedView.animateStuckToTarget =
+ {
+ target: MagnetizedObject.MagneticTarget,
+ _: Float,
+ _: Float,
+ _: Boolean,
+ after: (() -> Unit)? ->
+ animationHelper.animateIntoTarget(target, after)
+ }
- private fun finishDrag(x: Float, y: Float, viewInitialX: Float, viewInitialY: Float) {
- val dismissCircleBounds = Rect().apply { dismissView.circle.getBoundsOnScreen(this) }
- if (dismissCircleBounds.contains(x.toInt(), y.toInt())) {
- onDismissed()
- } else {
- resetExpandedViewPosition(viewInitialX, viewInitialY)
- }
- dismissView.hide()
- }
+ magnetizedDismissTarget =
+ MagnetizedObject.MagneticTarget(dismissView.circle, dismissView.circle.width)
+ magnetizedExpandedView.addTarget(magnetizedDismissTarget)
- private fun resetExpandedViewPosition(initialX: Float, initialY: Float) {
- val listener =
- object : AnimatorListenerAdapter() {
- override fun onAnimationStart(animation: Animator) {
- expandedView.isAnimating = true
- }
+ val dragMotionEventHandler = HandleDragListener()
- override fun onAnimationEnd(animation: Animator) {
- expandedView.isAnimating = false
- }
+ expandedView.handleView.setOnTouchListener { view, event ->
+ if (event.actionMasked == MotionEvent.ACTION_DOWN) {
+ expandedViewInitialTranslationX = expandedView.translationX
+ expandedViewInitialTranslationY = expandedView.translationY
}
- expandedView
- .animate()
- .translationX(initialX)
- .translationY(initialY)
- .setDuration(RESET_POSITION_ANIM_DURATION)
- .setInterpolator(Interpolators.EMPHASIZED_DECELERATE)
- .setListener(listener)
- .start()
+ val magnetConsumed = magnetizedExpandedView.maybeConsumeMotionEvent(event)
+ // Move events can be consumed by the magnetized object
+ if (event.actionMasked == MotionEvent.ACTION_MOVE && magnetConsumed) {
+ return@setOnTouchListener true
+ }
+ return@setOnTouchListener dragMotionEventHandler.onTouch(view, event) || magnetConsumed
+ }
}
private inner class HandleDragListener : RelativeTouchListener() {
-
- private val expandedViewRestPosition = PointF()
-
override fun onDown(v: View, ev: MotionEvent): Boolean {
// While animating, don't allow new touch events
- if (expandedView.isAnimating) {
- return false
- }
- expandedViewRestPosition.x = expandedView.translationX
- expandedViewRestPosition.y = expandedView.translationY
- return true
+ return !expandedView.isAnimating
}
override fun onMove(
@@ -90,8 +87,8 @@ class BubbleBarExpandedViewDragController(
dx: Float,
dy: Float
) {
- expandedView.translationX = expandedViewRestPosition.x + dx
- expandedView.translationY = expandedViewRestPosition.y + dy
+ expandedView.translationX = expandedViewInitialTranslationX + dx
+ expandedView.translationY = expandedViewInitialTranslationY + dy
dismissView.show()
}
@@ -105,16 +102,40 @@ class BubbleBarExpandedViewDragController(
velX: Float,
velY: Float
) {
- finishDrag(ev.rawX, ev.rawY, expandedViewRestPosition.x, expandedViewRestPosition.y)
+ finishDrag()
}
override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) {
- resetExpandedViewPosition(expandedViewRestPosition.x, expandedViewRestPosition.y)
- dismissView.hide()
+ finishDrag()
+ }
+
+ private fun finishDrag() {
+ if (!isStuckToDismiss) {
+ animationHelper.animateToRestPosition()
+ dismissView.hide()
+ }
}
}
- companion object {
- const val RESET_POSITION_ANIM_DURATION = 300L
+ private inner class MagnetListener : MagnetizedObject.MagnetListener {
+ override fun onStuckToTarget(target: MagnetizedObject.MagneticTarget) {
+ isStuckToDismiss = true
+ }
+
+ override fun onUnstuckFromTarget(
+ target: MagnetizedObject.MagneticTarget,
+ velX: Float,
+ velY: Float,
+ wasFlungOut: Boolean
+ ) {
+ isStuckToDismiss = false
+ animationHelper.animateUnstuckFromDismissView()
+ }
+
+ override fun onReleasedInTarget(target: MagnetizedObject.MagneticTarget) {
+ onDismissed()
+ dismissView.hide()
+ }
}
}
+
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index bdb0e206e490..12114519d086 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.bubbles.bar;
import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_GESTURE;
import android.annotation.Nullable;
import android.content.Context;
@@ -36,7 +37,6 @@ import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
-import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.bubbles.DeviceConfig;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.bubbles.DismissView;
@@ -206,10 +206,13 @@ public class BubbleBarLayerView extends FrameLayout
}
});
- mDragController = new BubbleBarExpandedViewDragController(mExpandedView, mDismissView,
+ mDragController = new BubbleBarExpandedViewDragController(
+ mExpandedView,
+ mDismissView,
+ mAnimationHelper,
() -> {
mBubbleController.dismissBubble(mExpandedBubble.getKey(),
- Bubbles.DISMISS_USER_GESTURE);
+ DISMISS_USER_GESTURE);
return Unit.INSTANCE;
});
@@ -241,7 +244,11 @@ public class BubbleBarLayerView extends FrameLayout
mIsExpanded = false;
final BubbleBarExpandedView viewToRemove = mExpandedView;
mEducationViewController.hideEducation(/* animated = */ true);
- mAnimationHelper.animateCollapse(() -> removeView(viewToRemove));
+ if (mDragController != null && mDragController.isStuckToDismiss()) {
+ mAnimationHelper.animateDismiss(() -> removeView(viewToRemove));
+ } else {
+ mAnimationHelper.animateCollapse(() -> removeView(viewToRemove));
+ }
mBubbleController.getSysuiProxy().onStackExpandChanged(false);
mExpandedView = null;
mDragController = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 3c6bc1754c5c..fc97c7988a0f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -363,7 +363,8 @@ public abstract class WMShellBaseModule {
@ShellMainThread ShellExecutor shellExecutor,
@ShellBackgroundThread Handler backgroundHandler,
BackAnimationBackground backAnimationBackground,
- Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry) {
+ Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry,
+ ShellCommandHandler shellCommandHandler) {
if (BackAnimationController.IS_ENABLED) {
return shellBackAnimationRegistry.map(
(animations) ->
@@ -374,7 +375,8 @@ public abstract class WMShellBaseModule {
backgroundHandler,
context,
backAnimationBackground,
- animations));
+ animations,
+ shellCommandHandler));
}
return Optional.empty();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index ae21c4bf5450..f58aeac918b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -277,11 +277,6 @@ public class SplashscreenContentDrawer {
params.token = appToken;
params.packageName = activityInfo.packageName;
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-
- if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
- params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
- }
-
params.setTitle("Splash Screen " + title);
return params;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 771876f7ce5d..9ded6ea1d187 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -63,6 +63,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.sysui.ShellSharedConstants;
@@ -110,6 +111,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
@Mock
private InputManager mInputManager;
+ @Mock
+ private ShellCommandHandler mShellCommandHandler;
private BackAnimationController mController;
private TestableContentResolver mContentResolver;
@@ -145,7 +148,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
mContext,
mContentResolver,
mAnimationBackground,
- mShellBackAnimationRegistry);
+ mShellBackAnimationRegistry,
+ mShellCommandHandler);
mShellInit.init();
mShellExecutor.flushAll();
}
@@ -298,7 +302,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
mContext,
mContentResolver,
mAnimationBackground,
- mShellBackAnimationRegistry);
+ mShellBackAnimationRegistry,
+ mShellCommandHandler);
shellInit.init();
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 874ef80c29f0..91503b1c3619 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -53,6 +53,7 @@ public class BackProgressAnimatorTest {
/* progress = */ progress,
/* velocityX = */ 0,
/* velocityY = */ 0,
+ /* triggerBack = */ false,
/* swipeEdge = */ BackEvent.EDGE_LEFT,
/* departingAnimationTarget = */ null);
}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index b9dc618510af..2501869341ad 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -48,6 +48,7 @@ android_library {
"SettingsLibTwoTargetPreference",
"SettingsLibUsageProgressBarPreference",
"SettingsLibUtils",
+ "settingslib_media_flags_lib",
"settingslib_flags_lib",
],
@@ -78,6 +79,19 @@ aconfig_declarations {
}
java_aconfig_library {
- name: "settingslib_flags_lib",
+ name: "settingslib_media_flags_lib",
aconfig_declarations: "settingslib_media_flags",
}
+
+aconfig_declarations {
+ name: "settingslib_flags",
+ package: "com.android.settingslib.flags",
+ srcs: [
+ "aconfig/settingslib.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "settingslib_flags_lib",
+ aconfig_declarations: "settingslib_flags",
+}
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
new file mode 100644
index 000000000000..fd2f9bd345d2
--- /dev/null
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.settingslib.flags"
+
+flag {
+ name: "new_status_bar_icons"
+ namespace: "systemui"
+ description: "Enable new status bar system icons"
+ bug: "314812750"
+}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index 562d20d05429..7fb959ae4d76 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
+import android.app.Flags;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
@@ -143,9 +144,16 @@ public class EnableZenModeDialog {
Slog.d(TAG, "Invalid manual condition: " + tag.condition);
}
// always triggers priority-only dnd with chosen condition
- mNotificationManager.setZenMode(
- Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
- getRealConditionId(tag.condition), TAG);
+ if (Flags.modesApi()) {
+ mNotificationManager.setZenMode(
+ Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ getRealConditionId(tag.condition), TAG,
+ /* fromUser= */ true);
+ } else {
+ mNotificationManager.setZenMode(
+ Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ getRealConditionId(tag.condition), TAG);
+ }
}
});
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index 9084aa2dc71a..e83b9bc25799 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -22,6 +22,7 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
+import android.multiuser.Flags;
import android.net.Uri;
import android.util.Log;
import android.widget.ImageView;
@@ -59,6 +60,9 @@ public class EditUserPhotoController {
private static final String IMAGES_DIR = "multi_user";
private static final String NEW_USER_PHOTO_FILE_NAME = "NewUserPhoto.png";
+ private static final String AVATAR_PICKER_ACTION = "com.android.avatarpicker"
+ + ".FULL_SCREEN_ACTIVITY";
+
private final Activity mActivity;
private final ActivityStarter mActivityStarter;
private final ImageView mImageView;
@@ -105,7 +109,6 @@ public class EditUserPhotoController {
onPhotoCropped(data.getData());
return true;
}
-
}
return false;
}
@@ -115,7 +118,13 @@ public class EditUserPhotoController {
}
private void showAvatarPicker() {
- Intent intent = new Intent(mImageView.getContext(), AvatarPickerActivity.class);
+ Intent intent;
+ if (Flags.avatarSync()) {
+ intent = new Intent(AVATAR_PICKER_ACTION);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ } else {
+ intent = new Intent(mImageView.getContext(), AvatarPickerActivity.class);
+ }
intent.putExtra(AvatarPickerActivity.EXTRA_FILE_AUTHORITY, mFileAuthority);
mActivityStarter.startActivityForResult(intent, REQUEST_CODE_PICK_AVATAR);
}
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 4b4caf5a620e..644b72c383ac 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -56,7 +56,7 @@ android_test {
"SettingsLibDeviceStateRotationLock",
"SettingsLibSettingsSpinner",
"SettingsLibUsageProgressBarPreference",
- "settingslib_flags_lib",
+ "settingslib_media_flags_lib",
],
dxflags: ["--multi-dex"],
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 732c33615dd0..f4ddd0ac59df 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -50,7 +50,7 @@ android_robolectric_test {
"androidx.test.core",
"androidx.core_core",
"flag-junit",
- "settingslib_flags_lib",
+ "settingslib_media_flags_lib",
"testng", // TODO: remove once JUnit on Android provides assertThrows
],
java_resource_dirs: ["config"],
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 21ca613bca34..ab4fe76a58c7 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -239,3 +239,12 @@ flag {
description: "Enable special visual and haptic effects for quick settings tiles with long-press actions"
bug: "229856884"
}
+
+flag {
+ name: "status_bar_static_inout_indicators"
+ namespace: "systemui"
+ description: "(Upstream request) Always show the network activity inout indicators and "
+ "prefer using alpha to distinguish network activity."
+ bug: "310715220"
+}
+
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 5dc1079e8b56..a85d9bff283e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import android.graphics.Picture
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
@@ -66,12 +67,21 @@ internal class Element(val key: ElementKey) {
* The movable content of this element, if this element is composed using
* [SceneScope.MovableElement].
*/
- val movableContent by
- // This is only accessed from the composition (main) thread, so no need to use the default
- // lock of lazy {} to synchronize.
- lazy(mode = LazyThreadSafetyMode.NONE) {
- movableContentOf { content: @Composable () -> Unit -> content() }
- }
+ private var _movableContent: (@Composable (@Composable () -> Unit) -> Unit)? = null
+ val movableContent: @Composable (@Composable () -> Unit) -> Unit
+ get() =
+ _movableContent
+ ?: movableContentOf { content: @Composable () -> Unit -> content() }
+ .also { _movableContent = it }
+
+ /**
+ * The [Picture] to which we save the last drawing commands of this element, if it is movable.
+ * This is necessary because the content of this element might not be composed in the scene it
+ * should currently be drawn.
+ */
+ private var _picture: Picture? = null
+ val picture: Picture
+ get() = _picture ?: Picture().also { _picture = it }
override fun toString(): String {
return "Element(key=$key)"
@@ -235,18 +245,16 @@ internal class ElementNode(
}
override fun ContentDrawScope.draw() {
- if (shouldDrawElement(layoutImpl, scene, element)) {
- val drawScale = getDrawScale(layoutImpl, element, scene, sceneValues)
- if (drawScale == Scale.Default) {
- drawContent()
- } else {
- scale(
- drawScale.scaleX,
- drawScale.scaleY,
- if (drawScale.pivot.isUnspecified) center else drawScale.pivot,
- ) {
- this@draw.drawContent()
- }
+ val drawScale = getDrawScale(layoutImpl, element, scene, sceneValues)
+ if (drawScale == Scale.Default) {
+ drawContent()
+ } else {
+ scale(
+ drawScale.scaleX,
+ drawScale.scaleY,
+ if (drawScale.pivot.isUnspecified) center else drawScale.pivot,
+ ) {
+ this@draw.drawContent()
}
}
}
@@ -516,7 +524,7 @@ private fun IntermediateMeasureScope.place(
with(placementScope) {
// Update the offset (relative to the SceneTransitionLayout) this element has in this scene
// when idle.
- val coords = coordinates!!
+ val coords = coordinates ?: error("Element ${element.key} does not have any coordinates")
val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords)
if (targetOffsetInScene != sceneValues.targetOffset) {
// TODO(b/290930950): Better handle when this changes to avoid instant offset jumps.
@@ -545,6 +553,13 @@ private fun IntermediateMeasureScope.place(
lastSharedValues.offset = targetOffset
lastValues.offset = targetOffset
+ // No need to place the element in this scene if we don't want to draw it anyways. Note that
+ // it's still important to compute the target offset and update lastValues, otherwise it
+ // will be out of date.
+ if (!shouldDrawElement(layoutImpl, scene, element)) {
+ return
+ }
+
val offset = (targetOffset - currentOffset).round()
if (isElementOpaque(layoutImpl, element, scene, sceneValues)) {
// TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index 306f27626e19..49df2f6b6062 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -16,7 +16,6 @@
package com.android.compose.animation.scene
-import android.graphics.Picture
import android.util.Log
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
@@ -60,7 +59,7 @@ internal fun MovableElement(
// The [Picture] to which we save the last drawing commands of this element. This is
// necessary because the content of this element might not be composed in this scene, in
// which case we still need to draw it.
- val picture = remember { Picture() }
+ val picture = element.picture
// Whether we should compose the movable element here. The scene picker logic to know in
// which scene we should compose/draw a movable element might depend on the current
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 6a7a3a00d4fe..30e50a972230 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -34,6 +34,7 @@ import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.zIndex
+import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions
/** A scene in a [SceneTransitionLayout]. */
@Stable
@@ -152,4 +153,8 @@ private class SceneScopeImpl(
bounds: ElementKey,
shape: Shape
): Modifier = punchHole(layoutImpl, element, bounds, shape)
+
+ override fun Modifier.noResizeDuringTransitions(): Modifier {
+ return noResizeDuringTransitions(layoutState = layoutImpl.state)
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 3608e374fdbc..5eb339e4a5e4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -65,11 +65,11 @@ fun SceneTransitionLayout(
SceneTransitionLayoutForTesting(
currentScene,
onChangeScene,
+ modifier,
transitions,
state,
edgeDetector,
transitionInterceptionThreshold,
- modifier,
onLayoutImpl = null,
scenes,
)
@@ -205,6 +205,12 @@ interface SceneScope {
* the result.
*/
fun Modifier.punchHole(element: ElementKey, bounds: ElementKey, shape: Shape): Modifier
+
+ /**
+ * Don't resize during transitions. This can for instance be used to make sure that scrollable
+ * lists keep a constant size during transitions even if its elements are growing/shrinking.
+ */
+ fun Modifier.noResizeDuringTransitions(): Modifier
}
// TODO(b/291053742): Add animateSharedValueAsState(targetValue) without any ValueKey and ElementKey
@@ -257,12 +263,12 @@ enum class SwipeDirection(val orientation: Orientation) {
internal fun SceneTransitionLayoutForTesting(
currentScene: SceneKey,
onChangeScene: (SceneKey) -> Unit,
- transitions: SceneTransitions,
- state: SceneTransitionLayoutState,
- edgeDetector: EdgeDetector,
- transitionInterceptionThreshold: Float,
- modifier: Modifier,
- onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)?,
+ modifier: Modifier = Modifier,
+ transitions: SceneTransitions = transitions {},
+ state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
+ edgeDetector: EdgeDetector = DefaultEdgeDetector,
+ transitionInterceptionThreshold: Float = 0f,
+ onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null,
scenes: SceneTransitionLayoutScope.() -> Unit,
) {
val density = LocalDensity.current
@@ -280,6 +286,10 @@ internal fun SceneTransitionLayoutForTesting(
.also { onLayoutImpl?.invoke(it) }
}
+ // TODO(b/317014852): Move this into the SideEffect {} again once STLImpl.scenes is not a
+ // SnapshotStateMap anymore.
+ layoutImpl.updateScenes(scenes)
+
val targetSceneChannel = remember { Channel<SceneKey>(Channel.CONFLATED) }
SideEffect {
if (state != layoutImpl.state) {
@@ -293,7 +303,6 @@ internal fun SceneTransitionLayoutForTesting(
(state as SceneTransitionLayoutStateImpl).transitions = transitions
layoutImpl.density = density
layoutImpl.edgeDetector = edgeDetector
- layoutImpl.updateScenes(scenes)
state.transitions = transitions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index c99c3250bbb1..45e1a0fa8f77 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -46,7 +46,12 @@ internal class SceneTransitionLayoutImpl(
builder: SceneTransitionLayoutScope.() -> Unit,
coroutineScope: CoroutineScope,
) {
- internal val scenes = mutableMapOf<SceneKey, Scene>()
+ /**
+ * The map of [Scene]s.
+ *
+ * TODO(b/317014852): Make this a normal MutableMap instead.
+ */
+ internal val scenes = SnapshotStateMap<SceneKey, Scene>()
/**
* The map of [Element]s.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt
new file mode 100644
index 000000000000..bd36cb8655ac
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.modifiers
+
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.unit.Constraints
+import com.android.compose.animation.scene.SceneTransitionLayoutState
+
+@OptIn(ExperimentalComposeUiApi::class)
+internal fun Modifier.noResizeDuringTransitions(layoutState: SceneTransitionLayoutState): Modifier {
+ return intermediateLayout { measurable, constraints ->
+ if (layoutState.currentTransition == null) {
+ return@intermediateLayout measurable.measure(constraints).run {
+ layout(width, height) { place(0, 0) }
+ }
+ }
+
+ // Make sure that this layout node has the same size than when we are at rest.
+ val sizeAtRest = lookaheadSize
+ measurable.measure(Constraints.fixed(sizeAtRest.width, sizeAtRest.height)).run {
+ layout(width, height) { place(0, 0) }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 439dc00d2e8e..da5a0a04ed63 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
@@ -30,16 +31,21 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.subjects.DpOffsetSubject
+import com.android.compose.test.subjects.assertThat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -116,7 +122,13 @@ class ElementTest {
toSceneContent = {
Box(Modifier.size(layoutSize)) {
// Shared element.
- Element(TestElements.Foo, elementSize, elementOffset)
+ Element(
+ TestElements.Foo,
+ elementSize,
+ elementOffset,
+ onLayout = { fooLayouts++ },
+ onPlacement = { fooPlacements++ },
+ )
}
},
transition = {
@@ -127,21 +139,25 @@ class ElementTest {
scaleSize(TestElements.Bar, width = 1f, height = 1f)
},
) {
- var numberOfLayoutsAfterOneAnimationFrame = 0
- var numberOfPlacementsAfterOneAnimationFrame = 0
+ var fooLayoutsAfterOneAnimationFrame = 0
+ var fooPlacementsAfterOneAnimationFrame = 0
+ var barLayoutsAfterOneAnimationFrame = 0
+ var barPlacementsAfterOneAnimationFrame = 0
fun assertNumberOfLayoutsAndPlacements() {
- assertThat(fooLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
- assertThat(fooPlacements).isEqualTo(numberOfPlacementsAfterOneAnimationFrame)
- assertThat(barLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
- assertThat(barPlacements).isEqualTo(numberOfPlacementsAfterOneAnimationFrame)
+ assertThat(fooLayouts).isEqualTo(fooLayoutsAfterOneAnimationFrame)
+ assertThat(fooPlacements).isEqualTo(fooPlacementsAfterOneAnimationFrame)
+ assertThat(barLayouts).isEqualTo(barLayoutsAfterOneAnimationFrame)
+ assertThat(barPlacements).isEqualTo(barPlacementsAfterOneAnimationFrame)
}
at(16) {
// Capture the number of layouts and placements that happened after 1 animation
// frame.
- numberOfLayoutsAfterOneAnimationFrame = fooLayouts
- numberOfPlacementsAfterOneAnimationFrame = fooPlacements
+ fooLayoutsAfterOneAnimationFrame = fooLayouts
+ fooPlacementsAfterOneAnimationFrame = fooPlacements
+ barLayoutsAfterOneAnimationFrame = barLayouts
+ barPlacementsAfterOneAnimationFrame = barPlacements
}
repeat(nFrames - 2) { i ->
// Ensure that all animation frames (except the final one) don't relayout or replace
@@ -187,7 +203,13 @@ class ElementTest {
toSceneContent = {
Box(Modifier.size(layoutSize)) {
// Shared element.
- Element(TestElements.Foo, elementSize, offset = 20.dp)
+ Element(
+ TestElements.Foo,
+ elementSize,
+ offset = 20.dp,
+ onLayout = { fooLayouts++ },
+ onPlacement = { fooPlacements++ },
+ )
}
},
transition = {
@@ -198,25 +220,30 @@ class ElementTest {
scaleSize(TestElements.Bar, width = 1f, height = 1f)
},
) {
- var numberOfLayoutsAfterOneAnimationFrame = 0
- var lastNumberOfPlacements = 0
+ var fooLayoutsAfterOneAnimationFrame = 0
+ var barLayoutsAfterOneAnimationFrame = 0
+ var lastFooPlacements = 0
+ var lastBarPlacements = 0
fun assertNumberOfLayoutsAndPlacements() {
// The number of layouts have not changed.
- assertThat(fooLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
- assertThat(barLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
+ assertThat(fooLayouts).isEqualTo(fooLayoutsAfterOneAnimationFrame)
+ assertThat(barLayouts).isEqualTo(barLayoutsAfterOneAnimationFrame)
// The number of placements have increased.
- assertThat(fooPlacements).isGreaterThan(lastNumberOfPlacements)
- assertThat(barPlacements).isGreaterThan(lastNumberOfPlacements)
- lastNumberOfPlacements = fooPlacements
+ assertThat(fooPlacements).isGreaterThan(lastFooPlacements)
+ assertThat(barPlacements).isGreaterThan(lastBarPlacements)
+ lastFooPlacements = fooPlacements
+ lastBarPlacements = barPlacements
}
at(16) {
// Capture the number of layouts and placements that happened after 1 animation
// frame.
- numberOfLayoutsAfterOneAnimationFrame = fooLayouts
- lastNumberOfPlacements = fooPlacements
+ fooLayoutsAfterOneAnimationFrame = fooLayouts
+ barLayoutsAfterOneAnimationFrame = barLayouts
+ lastFooPlacements = fooPlacements
+ lastBarPlacements = barPlacements
}
repeat(nFrames - 2) { i ->
// Ensure that all animation frames (except the final one) only replaced the
@@ -238,11 +265,6 @@ class ElementTest {
SceneTransitionLayoutForTesting(
currentScene = currentScene,
onChangeScene = { currentScene = it },
- transitions = remember { transitions {} },
- state = remember { SceneTransitionLayoutState(currentScene) },
- edgeDetector = DefaultEdgeDetector,
- modifier = Modifier,
- transitionInterceptionThreshold = 0f,
onLayoutImpl = { nullableLayoutImpl = it },
) {
scene(TestScenes.SceneA) { /* Nothing */}
@@ -408,11 +430,6 @@ class ElementTest {
SceneTransitionLayoutForTesting(
currentScene = TestScenes.SceneA,
onChangeScene = {},
- transitions = remember { transitions {} },
- state = remember { SceneTransitionLayoutState(TestScenes.SceneA) },
- edgeDetector = DefaultEdgeDetector,
- modifier = Modifier,
- transitionInterceptionThreshold = 0f,
onLayoutImpl = { nullableLayoutImpl = it },
) {
scene(TestScenes.SceneA) { Box(Modifier.element(key)) }
@@ -463,11 +480,6 @@ class ElementTest {
SceneTransitionLayoutForTesting(
currentScene = TestScenes.SceneA,
onChangeScene = {},
- transitions = remember { transitions {} },
- state = remember { SceneTransitionLayoutState(TestScenes.SceneA) },
- edgeDetector = DefaultEdgeDetector,
- modifier = Modifier,
- transitionInterceptionThreshold = 0f,
onLayoutImpl = { nullableLayoutImpl = it },
) {
scene(TestScenes.SceneA) {
@@ -553,4 +565,86 @@ class ElementTest {
after { assertThat(fooCompositions).isEqualTo(1) }
}
}
+
+ @Test
+ fun sharedElementOffsetIsUpdatedEvenWhenNotPlaced() {
+ var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
+ var density: Density? = null
+
+ fun layoutImpl() = nullableLayoutImpl ?: error("nullableLayoutImpl was not set")
+
+ fun density() = density ?: error("density was not set")
+
+ fun Offset.toDpOffset() = with(density()) { DpOffset(x.toDp(), y.toDp()) }
+
+ fun foo() = layoutImpl().elements[TestElements.Foo] ?: error("Foo not in elements map")
+
+ fun Element.lastSharedOffset() = lastSharedValues.offset.toDpOffset()
+
+ fun Element.lastOffsetIn(scene: SceneKey) =
+ (sceneValues[scene] ?: error("$scene not in sceneValues map"))
+ .lastValues
+ .offset
+ .toDpOffset()
+
+ rule.testTransition(
+ from = TestScenes.SceneA,
+ to = TestScenes.SceneB,
+ transitionLayout = { currentScene, onChangeScene ->
+ density = LocalDensity.current
+
+ SceneTransitionLayoutForTesting(
+ currentScene = currentScene,
+ onChangeScene = onChangeScene,
+ onLayoutImpl = { nullableLayoutImpl = it },
+ transitions =
+ transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ spec = tween(durationMillis = 4 * 16, easing = LinearEasing)
+ }
+ }
+ ) {
+ scene(TestScenes.SceneA) { Box(Modifier.element(TestElements.Foo)) }
+ scene(TestScenes.SceneB) {
+ Box(Modifier.offset(x = 40.dp, y = 80.dp).element(TestElements.Foo))
+ }
+ }
+ }
+ ) {
+ val tolerance = DpOffsetSubject.DefaultTolerance
+
+ before {
+ val expected = DpOffset(0.dp, 0.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
+ }
+
+ at(16) {
+ val expected = DpOffset(10.dp, 20.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
+ }
+
+ at(32) {
+ val expected = DpOffset(20.dp, 40.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
+ }
+
+ at(48) {
+ val expected = DpOffset(30.dp, 60.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
+ }
+
+ after {
+ val expected = DpOffset(40.dp, 80.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
index 83af630ab098..3cd65cde274e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
@@ -30,6 +30,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasParent
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
@@ -92,15 +93,15 @@ class MovableElementTest {
}
at(32) {
- // In the middle of the transition, there are 2 copies of the counter: the previous
- // one from scene A (equal to 3) and the new one from scene B (equal to 0).
+ // In the middle of the transition, 2 copies of the counter are composed but only
+ // the one in scene B is placed/drawn.
rule
.onNode(
hasText("count: 3") and
hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneA))
)
- .assertIsDisplayed()
- .assertSizeIsEqualTo(75.dp, 75.dp)
+ .assertExists()
+ .assertIsNotDisplayed()
rule
.onNode(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 321cf637824a..ebbd5006be55 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -40,9 +40,7 @@ import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.assertWidthIsEqualTo
import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onChild
-import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.unit.Dp
@@ -251,9 +249,9 @@ class SceneTransitionLayoutTest {
// Advance to the middle of the animation.
rule.mainClock.advanceTimeBy(TestTransitionDuration / 2)
- // We need to use onAllNodesWithTag().onFirst() here given that shared elements are
- // composed and laid out in both scenes (but drawn only in one).
- sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.testTag).onFirst()
+ // Foo is shared between Scene A and Scene B, and is therefore placed/drawn in Scene B given
+ // that B has a higher zIndex than A.
+ sharedFoo = rule.onNode(isElement(TestElements.Foo, TestScenes.SceneB))
// In scene B, foo is at the top start (x = 0, y = 0) of the layout and has a size of
// 100.dp. We pause at the middle of the transition, so it should now be 75.dp given that we
@@ -287,7 +285,7 @@ class SceneTransitionLayoutTest {
val expectedLeft = 0.dp
val expectedSize = 100.dp + (150.dp - 100.dp) * interpolatedProgress
- sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.testTag).onFirst()
+ sharedFoo = rule.onNode(isElement(TestElements.Foo, TestScenes.SceneC))
assertThat((layoutState.transitionState as TransitionState.Transition).progress)
.isEqualTo(interpolatedProgress)
sharedFoo.assertWidthIsEqualTo(expectedSize)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeTest.kt
new file mode 100644
index 000000000000..2c159d15fa66
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.modifiers
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestElements
+import com.android.compose.animation.scene.element
+import com.android.compose.animation.scene.testTransition
+import com.android.compose.test.assertSizeIsEqualTo
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SizeTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun noResizeDuringTransitions() {
+ // The tag for the parent of the shared Foo element.
+ val parentTag = "parent"
+
+ rule.testTransition(
+ fromSceneContent = { Box(Modifier.element(TestElements.Foo).size(100.dp)) },
+ toSceneContent = {
+ // Don't resize the parent of Foo during transitions so that it's always the same
+ // size as when there is no transition (200dp).
+ Box(Modifier.noResizeDuringTransitions().testTag(parentTag)) {
+ Box(Modifier.element(TestElements.Foo).size(200.dp))
+ }
+ },
+ transition = { spec = tween(durationMillis = 4 * 16, easing = LinearEasing) },
+ ) {
+ at(16) { rule.onNodeWithTag(parentTag).assertSizeIsEqualTo(200.dp, 200.dp) }
+ at(32) { rule.onNodeWithTag(parentTag).assertSizeIsEqualTo(200.dp, 200.dp) }
+ at(48) { rule.onNodeWithTag(parentTag).assertSizeIsEqualTo(200.dp, 200.dp) }
+ after { rule.onNodeWithTag(parentTag).assertSizeIsEqualTo(200.dp, 200.dp) }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
index e94eff32c30c..8001f418a12c 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
@@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.dp
@@ -33,7 +34,6 @@ import com.android.compose.animation.scene.TestScenes
import com.android.compose.animation.scene.inScene
import com.android.compose.animation.scene.testTransition
import com.android.compose.test.assertSizeIsEqualTo
-import com.android.compose.test.onEach
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -63,28 +63,34 @@ class SharedElementTest {
onElement(TestElements.Foo).assertSizeIsEqualTo(20.dp, 80.dp)
}
at(0) {
- onSharedElement(TestElements.Foo).onEach {
- assertPositionInRootIsEqualTo(10.dp, 50.dp)
- assertSizeIsEqualTo(20.dp, 80.dp)
- }
+ // Shared elements are by default placed and drawn only in the scene with highest
+ // zIndex.
+ onElement(TestElements.Foo, TestScenes.SceneA).assertIsNotDisplayed()
+
+ onElement(TestElements.Foo, TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(10.dp, 50.dp)
+ .assertSizeIsEqualTo(20.dp, 80.dp)
}
at(16) {
- onSharedElement(TestElements.Foo).onEach {
- assertPositionInRootIsEqualTo(20.dp, 55.dp)
- assertSizeIsEqualTo(17.5.dp, 70.dp)
- }
+ onElement(TestElements.Foo, TestScenes.SceneA).assertIsNotDisplayed()
+
+ onElement(TestElements.Foo, TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(20.dp, 55.dp)
+ .assertSizeIsEqualTo(17.5.dp, 70.dp)
}
at(32) {
- onSharedElement(TestElements.Foo).onEach {
- assertPositionInRootIsEqualTo(30.dp, 60.dp)
- assertSizeIsEqualTo(15.dp, 60.dp)
- }
+ onElement(TestElements.Foo, TestScenes.SceneA).assertIsNotDisplayed()
+
+ onElement(TestElements.Foo, TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(30.dp, 60.dp)
+ .assertSizeIsEqualTo(15.dp, 60.dp)
}
at(48) {
- onSharedElement(TestElements.Foo).onEach {
- assertPositionInRootIsEqualTo(40.dp, 65.dp)
- assertSizeIsEqualTo(12.5.dp, 50.dp)
- }
+ onElement(TestElements.Foo, TestScenes.SceneA).assertIsNotDisplayed()
+
+ onElement(TestElements.Foo, TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(40.dp, 65.dp)
+ .assertSizeIsEqualTo(12.5.dp, 50.dp)
}
after {
onElement(TestElements.Foo).assertPositionInRootIsEqualTo(50.dp, 70.dp)
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
new file mode 100644
index 000000000000..e743c7885c14
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.hasParent
+import androidx.compose.ui.test.hasTestTag
+
+/** A [SemanticsMatcher] that matches [element], optionally restricted to scene [scene]. */
+fun isElement(element: ElementKey, scene: SceneKey? = null): SemanticsMatcher {
+ return if (scene == null) {
+ hasTestTag(element.testTag)
+ } else {
+ hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag))
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
index 06de2965f716..6d6d57506a3a 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
@@ -21,13 +21,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.SemanticsNodeInteractionCollection
-import androidx.compose.ui.test.hasParent
-import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.junit4.ComposeContentTestRule
-import androidx.compose.ui.test.onAllNodesWithTag
@DslMarker annotation class TransitionTestDsl
@@ -62,23 +57,15 @@ interface TransitionTestBuilder {
@TransitionTestDsl
interface TransitionTestAssertionScope {
- fun isElement(element: ElementKey, scene: SceneKey? = null): SemanticsMatcher
-
/**
* Assert on [element].
*
* Note that presence/value assertions on the returned [SemanticsNodeInteraction] will fail if 0
* or more than 1 elements matched [element]. If you need to assert on a shared element that
- * will be present multiple times in the layout during transitions, either specify the [scene]
- * in which you are matching or use [onSharedElement] instead.
+ * will be present multiple times in the layout during transitions, specify the [scene] in which
+ * you are matching.
*/
fun onElement(element: ElementKey, scene: SceneKey? = null): SemanticsNodeInteraction
-
- /**
- * Assert on a shared [element]. This will throw if [element] is not shared and present only in
- * one scene during a transition.
- */
- fun onSharedElement(element: ElementKey): SemanticsNodeInteractionCollection
}
/**
@@ -131,29 +118,12 @@ fun ComposeContentTestRule.testTransition(
val test = transitionTest(builder)
val assertionScope =
object : TransitionTestAssertionScope {
- override fun isElement(element: ElementKey, scene: SceneKey?): SemanticsMatcher {
- return if (scene == null) {
- hasTestTag(element.testTag)
- } else {
- hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag))
- }
- }
-
override fun onElement(
element: ElementKey,
scene: SceneKey?
): SemanticsNodeInteraction {
return onNode(isElement(element, scene))
}
-
- override fun onSharedElement(element: ElementKey): SemanticsNodeInteractionCollection {
- val interaction = onAllNodesWithTag(element.testTag)
- val matches = interaction.fetchSemanticsNodes(atLeastOneRootRequired = false).size
- if (matches < 2) {
- error("Element $element is not shared ($matches matches)")
- }
- return interaction
- }
}
var currentScene by mutableStateOf(from)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 0870122594b1..adf4fc6c8ae3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -32,18 +32,15 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class AlternateBouncerInteractorTest : SysuiTestCase() {
@@ -167,7 +164,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
}
@Test
- @Ignore("b/287599719")
fun canShowAlternateBouncerForFingerprint_rearFps() {
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
initializeUnderTest()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt
new file mode 100644
index 000000000000..9daf1860ebb8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AlternateBouncerToAodTransitionViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
+ private val biometricSettingsRepository = kosmos.biometricSettingsRepository
+ private val underTest = kosmos.alternateBouncerToAodTransitionViewModel
+
+ @Test
+ fun deviceEntryParentViewAppear() =
+ testScope.runTest {
+ fingerprintPropertyRepository.setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.UDFPS_OPTICAL,
+ sensorLocations = emptyMap(),
+ )
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(0.1f),
+ step(0.2f),
+ step(0.3f),
+ step(1f),
+ ),
+ testScope,
+ )
+
+ values.forEach { assertThat(it).isEqualTo(1f) }
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewDisappear() =
+ testScope.runTest {
+ val values by collectValues(underTest.deviceEntryBackgroundViewAlpha)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(0.1f),
+ step(0.2f),
+ step(0.3f),
+ step(1f),
+ ),
+ testScope,
+ )
+
+ assertThat(values.size).isEqualTo(6)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+ }
+
+ private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = AOD,
+ value = value,
+ transitionState = state,
+ ownerName = "AlternateBouncerToAodTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelTest.kt
new file mode 100644
index 000000000000..3f7e0df427c6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelTest.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AlternateBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
+ val kosmos =
+ testKosmos().apply {
+ fakeFeatureFlagsClassic.apply {
+ set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ }
+ }
+ private val testScope = kosmos.testScope
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val underTest = kosmos.alternateBouncerToGoneTransitionViewModel
+
+ @Test
+ fun deviceEntryParentViewDisappear() =
+ testScope.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(0.1f),
+ step(0.2f),
+ step(0.3f),
+ step(1f),
+ ),
+ testScope,
+ )
+
+ values.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = GONE,
+ value = value,
+ transitionState = state,
+ ownerName = "AlternateBouncerToGoneTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
index cf076c557765..a84b9fa32d85 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -24,16 +24,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
import com.android.systemui.qs.tiles.impl.custom.commons.copy
+import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
+import com.android.systemui.qs.tiles.impl.custom.tileSpec
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -44,194 +47,260 @@ import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
class CustomTileRepositoryTest : SysuiTestCase() {
- private val testScope = TestScope()
-
- private val persister = FakeCustomTileStatePersister()
-
+ private val kosmos = Kosmos().apply { tileSpec = TileSpec.create(TEST_COMPONENT) }
private val underTest: CustomTileRepository =
- CustomTileRepositoryImpl(
- TileSpec.create(TEST_COMPONENT),
- persister,
- testScope.testScheduler,
- )
+ with(kosmos) {
+ CustomTileRepositoryImpl(
+ tileSpec,
+ customTileStatePersister,
+ packageManagerAdapterFacade.packageManagerAdapter,
+ testScope.testScheduler,
+ )
+ }
@Test
fun persistableTileIsRestoredForUser() =
- testScope.runTest {
- persister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
- persister.persistState(TEST_TILE_KEY_2, TEST_TILE_2)
+ with(kosmos) {
+ testScope.runTest {
+ customTileStatePersister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
+ customTileStatePersister.persistState(TEST_TILE_KEY_2, TEST_TILE_2)
- underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
- runCurrent()
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
+ runCurrent()
- assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
- assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ }
}
@Test
fun notPersistableTileIsNotRestored() =
- testScope.runTest {
- persister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
- val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+ with(kosmos) {
+ testScope.runTest {
+ customTileStatePersister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
- underTest.restoreForTheUserIfNeeded(TEST_USER_1, false)
- runCurrent()
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, false)
+ runCurrent()
- assertThat(tiles()).isEmpty()
+ assertThat(tiles()).isEmpty()
+ }
}
@Test
fun emptyPersistedStateIsHandled() =
- testScope.runTest {
- val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+ with(kosmos) {
+ testScope.runTest {
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
- underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
- runCurrent()
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
+ runCurrent()
- assertThat(tiles()).isEmpty()
+ assertThat(tiles()).isEmpty()
+ }
}
@Test
fun updatingWithPersistableTilePersists() =
- testScope.runTest {
- underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
- runCurrent()
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
- assertThat(persister.readState(TEST_TILE_KEY_1)).isEqualTo(TEST_TILE_1)
+ assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1))
+ .isEqualTo(TEST_TILE_1)
+ }
}
@Test
fun updatingWithNotPersistableTileDoesntPersist() =
- testScope.runTest {
- underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, false)
- runCurrent()
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, false)
+ runCurrent()
- assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1)).isNull()
+ }
}
@Test
fun updateWithTileEmits() =
- testScope.runTest {
- underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
- runCurrent()
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
- assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
- assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ }
}
@Test
fun updatingPeristableWithDefaultsPersists() =
- testScope.runTest {
- underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
- runCurrent()
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
- assertThat(persister.readState(TEST_TILE_KEY_1)).isEqualTo(TEST_TILE_1)
+ assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1))
+ .isEqualTo(TEST_TILE_1)
+ }
}
@Test
fun updatingNotPersistableWithDefaultsDoesntPersist() =
- testScope.runTest {
- underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, false)
- runCurrent()
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, false)
+ runCurrent()
- assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1)).isNull()
+ }
}
@Test
fun updatingPeristableWithErrorDefaultsDoesntPersist() =
- testScope.runTest {
- underTest.updateWithDefaults(TEST_USER_1, CustomTileDefaults.Error, true)
- runCurrent()
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, CustomTileDefaults.Error, true)
+ runCurrent()
- assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1)).isNull()
+ }
}
@Test
fun updateWithDefaultsEmits() =
- testScope.runTest {
- underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
- runCurrent()
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
- assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
- assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ }
}
@Test
fun getTileForAnotherUserReturnsNull() =
- testScope.runTest {
- underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
- runCurrent()
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
- assertThat(underTest.getTile(TEST_USER_2)).isNull()
+ assertThat(underTest.getTile(TEST_USER_2)).isNull()
+ }
}
@Test
fun getTilesForAnotherUserEmpty() =
- testScope.runTest {
- val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+ with(kosmos) {
+ testScope.runTest {
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
- underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
- runCurrent()
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
- assertThat(tiles()).isEmpty()
+ assertThat(tiles()).isEmpty()
+ }
}
@Test
fun updatingWithTileForTheSameUserAddsData() =
- testScope.runTest {
- underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
- runCurrent()
-
- underTest.updateWithTile(TEST_USER_1, Tile().apply { subtitle = "test_subtitle" }, true)
- runCurrent()
-
- val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
- assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
- assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ underTest.updateWithTile(
+ TEST_USER_1,
+ Tile().apply { subtitle = "test_subtitle" },
+ true
+ )
+ runCurrent()
+
+ val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ }
}
@Test
fun updatingWithTileForAnotherUserOverridesTile() =
- testScope.runTest {
- underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
- runCurrent()
-
- val tiles = collectValues(underTest.getTiles(TEST_USER_2))
- underTest.updateWithTile(TEST_USER_2, TEST_TILE_2, true)
- runCurrent()
-
- assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
- assertThat(tiles()).hasSize(1)
- assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+ underTest.updateWithTile(TEST_USER_2, TEST_TILE_2, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ }
}
@Test
fun updatingWithDefaultsForTheSameUserAddsData() =
- testScope.runTest {
- underTest.updateWithTile(TEST_USER_1, Tile().apply { subtitle = "test_subtitle" }, true)
- runCurrent()
-
- underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
- runCurrent()
-
- val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
- assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
- assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithTile(
+ TEST_USER_1,
+ Tile().apply { subtitle = "test_subtitle" },
+ true
+ )
+ runCurrent()
+
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ }
}
@Test
fun updatingWithDefaultsForAnotherUserOverridesTile() =
- testScope.runTest {
- underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
- runCurrent()
+ with(kosmos) {
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+ underTest.updateWithDefaults(TEST_USER_2, TEST_DEFAULTS_2, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ }
+ }
- val tiles = collectValues(underTest.getTiles(TEST_USER_2))
- underTest.updateWithDefaults(TEST_USER_2, TEST_DEFAULTS_2, true)
- runCurrent()
+ @Test
+ fun isActiveFollowsPackageManagerAdapter() =
+ with(kosmos) {
+ testScope.runTest {
+ packageManagerAdapterFacade.setIsActive(false)
+ assertThat(underTest.isTileActive()).isFalse()
+
+ packageManagerAdapterFacade.setIsActive(true)
+ assertThat(underTest.isTileActive()).isTrue()
+ }
+ }
- assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
- assertThat(tiles()).hasSize(1)
- assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ @Test
+ fun isToggleableFollowsPackageManagerAdapter() =
+ with(kosmos) {
+ testScope.runTest {
+ packageManagerAdapterFacade.setIsToggleable(false)
+ assertThat(underTest.isTileToggleable()).isFalse()
+
+ packageManagerAdapterFacade.setIsToggleable(true)
+ assertThat(underTest.isTileToggleable()).isTrue()
+ }
}
private companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
index eebb145ef384..90779cb1c0b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
@@ -25,157 +25,159 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.external.TileServiceKey
-import com.android.systemui.qs.external.TileServiceManager
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.customTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.customTileRepository
+import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
-import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
-import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileRepository
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.qs.tiles.impl.custom.tileSpec
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class CustomTileInteractorTest : SysuiTestCase() {
- @Mock private lateinit var tileServiceManager: TileServiceManager
+ private val kosmos = Kosmos().apply { tileSpec = TileSpec.create(TEST_COMPONENT) }
- private val testScope = TestScope()
-
- private val defaultsRepository = FakeCustomTileDefaultsRepository()
- private val customTileStatePersister = FakeCustomTileStatePersister()
- private val customTileRepository =
- FakeCustomTileRepository(
- TEST_TILE_SPEC,
- customTileStatePersister,
- testScope.testScheduler,
- )
-
- private lateinit var underTest: CustomTileInteractor
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
-
- underTest =
+ private val underTest: CustomTileInteractor =
+ with(kosmos) {
CustomTileInteractor(
- TEST_USER,
- defaultsRepository,
+ customTileDefaultsRepository,
customTileRepository,
- tileServiceManager,
testScope.backgroundScope,
testScope.testScheduler,
)
- }
+ }
@Test
fun activeTileIsAvailableAfterRestored() =
- testScope.runTest {
- whenever(tileServiceManager.isActiveTile).thenReturn(true)
- customTileStatePersister.persistState(
- TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
- TEST_TILE,
- )
-
- underTest.init()
-
- assertThat(underTest.tile).isEqualTo(TEST_TILE)
- assertThat(underTest.tiles.first()).isEqualTo(TEST_TILE)
+ with(kosmos) {
+ testScope.runTest {
+ customTileRepository.setTileActive(true)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+
+ underTest.initForUser(TEST_USER)
+
+ assertThat(underTest.getTile(TEST_USER)).isEqualTo(TEST_TILE)
+ assertThat(underTest.getTiles(TEST_USER).first()).isEqualTo(TEST_TILE)
+ }
}
@Test
fun notActiveTileIsAvailableAfterUpdated() =
- testScope.runTest {
- whenever(tileServiceManager.isActiveTile).thenReturn(false)
- customTileStatePersister.persistState(
- TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
- TEST_TILE,
- )
- val tiles = collectValues(underTest.tiles)
- val initJob = launch { underTest.init() }
-
- underTest.updateTile(TEST_TILE)
- runCurrent()
- initJob.join()
-
- assertThat(tiles()).hasSize(1)
- assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ with(kosmos) {
+ testScope.runTest {
+ customTileRepository.setTileActive(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.getTiles(TEST_USER))
+ val initJob = launch { underTest.initForUser(TEST_USER) }
+
+ underTest.updateTile(TEST_TILE)
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ }
}
@Test
fun notActiveTileIsAvailableAfterDefaultsUpdated() =
- testScope.runTest {
- whenever(tileServiceManager.isActiveTile).thenReturn(false)
- customTileStatePersister.persistState(
- TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
- TEST_TILE,
- )
- val tiles = collectValues(underTest.tiles)
- val initJob = launch { underTest.init() }
-
- defaultsRepository.putDefaults(TEST_USER, TEST_COMPONENT, TEST_DEFAULTS)
- defaultsRepository.requestNewDefaults(TEST_USER, TEST_COMPONENT)
- runCurrent()
- initJob.join()
-
- assertThat(tiles()).hasSize(1)
- assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ with(kosmos) {
+ testScope.runTest {
+ customTileRepository.setTileActive(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.getTiles(TEST_USER))
+ val initJob = launch { underTest.initForUser(TEST_USER) }
+
+ customTileDefaultsRepository.putDefaults(TEST_USER, TEST_COMPONENT, TEST_DEFAULTS)
+ customTileDefaultsRepository.requestNewDefaults(TEST_USER, TEST_COMPONENT)
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ }
}
@Test(expected = IllegalStateException::class)
- fun getTileBeforeInitThrows() = testScope.runTest { underTest.tile }
+ fun getTileBeforeInitThrows() =
+ with(kosmos) { testScope.runTest { underTest.getTile(TEST_USER) } }
@Test
fun initSuspendsForActiveTileNotRestoredAndNotUpdated() =
- testScope.runTest {
- whenever(tileServiceManager.isActiveTile).thenReturn(true)
- val tiles = collectValues(underTest.tiles)
+ with(kosmos) {
+ testScope.runTest {
+ customTileRepository.setTileActive(true)
+ val tiles = collectValues(underTest.getTiles(TEST_USER))
- val initJob = backgroundScope.launch { underTest.init() }
- advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
+ val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER) }
+ advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
- // Is still suspended
- assertThat(initJob.isActive).isTrue()
- assertThat(tiles()).isEmpty()
+ // Is still suspended
+ assertThat(initJob.isActive).isTrue()
+ assertThat(tiles()).isEmpty()
+ }
}
@Test
fun initSuspendedForNotActiveTileWithoutUpdates() =
- testScope.runTest {
- whenever(tileServiceManager.isActiveTile).thenReturn(false)
- customTileStatePersister.persistState(
- TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
- TEST_TILE,
- )
- val tiles = collectValues(underTest.tiles)
-
- val initJob = backgroundScope.launch { underTest.init() }
- advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
+ with(kosmos) {
+ testScope.runTest {
+ customTileRepository.setTileActive(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.getTiles(TEST_USER))
+
+ val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER) }
+ advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
+
+ // Is still suspended
+ assertThat(initJob.isActive).isTrue()
+ assertThat(tiles()).isEmpty()
+ }
+ }
- // Is still suspended
- assertThat(initJob.isActive).isTrue()
- assertThat(tiles()).isEmpty()
+ @Test
+ fun toggleableFollowsTheRepository() {
+ with(kosmos) {
+ testScope.runTest {
+ customTileRepository.setTileToggleable(false)
+ assertThat(underTest.isTileToggleable()).isFalse()
+
+ customTileRepository.setTileToggleable(true)
+ assertThat(underTest.isTileToggleable()).isTrue()
+ }
}
+ }
private companion object {
val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
- val TEST_TILE_SPEC = TileSpec.create(TEST_COMPONENT)
val TEST_USER = UserHandle.of(1)!!
val TEST_TILE =
Tile().apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt
index 886c61ae29dd..bf33010a055b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt
@@ -27,7 +27,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
-import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
+import com.android.systemui.smartspace.filters.LockscreenTargetFilter
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -52,55 +52,52 @@ import org.mockito.MockitoAnnotations
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidJUnit4::class)
-class LockscreenAndDreamTargetFilterTest : SysuiTestCase() {
- @Mock
- private lateinit var secureSettings: SecureSettings
+class LockscreenTargetFilterTest : SysuiTestCase() {
+ @Mock private lateinit var secureSettings: SecureSettings
- @Mock
- private lateinit var userTracker: UserTracker
+ @Mock private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var execution: Execution
+ @Mock private lateinit var execution: Execution
- @Mock
- private lateinit var handler: Handler
+ @Mock private lateinit var handler: Handler
- @Mock
- private lateinit var contentResolver: ContentResolver
+ @Mock private lateinit var contentResolver: ContentResolver
- @Mock
- private lateinit var uiExecution: Executor
+ @Mock private lateinit var uiExecution: Executor
- @Mock
- private lateinit var userHandle: UserHandle
+ @Mock private lateinit var userHandle: UserHandle
- @Mock
- private lateinit var listener: SmartspaceTargetFilter.Listener
+ @Mock private lateinit var listener: SmartspaceTargetFilter.Listener
- @Mock
- private lateinit var lockScreenAllowPrivateNotificationsUri: Uri
+ @Mock private lateinit var lockScreenAllowPrivateNotificationsUri: Uri
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(userTracker.userHandle).thenReturn(userHandle)
- `when`(secureSettings
- .getUriFor(eq(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS)))
- .thenReturn(lockScreenAllowPrivateNotificationsUri)
+ `when`(
+ secureSettings.getUriFor(
+ eq(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS)
+ )
+ )
+ .thenReturn(lockScreenAllowPrivateNotificationsUri)
}
- /**
- * Ensures {@link Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS} is
- * tracked.
- */
+ /** Ensures {@link Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS} is tracked. */
@Test
fun testLockscreenAllowPrivateNotifications() {
var setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
- `when`(secureSettings
- .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
- .thenReturn(0)
- var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler,
- contentResolver, uiExecution)
+ `when`(secureSettings.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
+ .thenReturn(0)
+ var filter =
+ LockscreenTargetFilter(
+ secureSettings,
+ userTracker,
+ execution,
+ handler,
+ contentResolver,
+ uiExecution
+ )
filter.addListener(listener)
var smartspaceTarget = mock(SmartspaceTarget::class.java)
@@ -110,12 +107,16 @@ class LockscreenAndDreamTargetFilterTest : SysuiTestCase() {
var settingCaptor = ArgumentCaptor.forClass(ContentObserver::class.java)
- verify(contentResolver).registerContentObserver(eq(lockScreenAllowPrivateNotificationsUri),
- anyBoolean(), settingCaptor.capture(), anyInt())
+ verify(contentResolver)
+ .registerContentObserver(
+ eq(lockScreenAllowPrivateNotificationsUri),
+ anyBoolean(),
+ settingCaptor.capture(),
+ anyInt()
+ )
- `when`(secureSettings
- .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
- .thenReturn(1)
+ `when`(secureSettings.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
+ .thenReturn(1)
clearInvocations(listener)
settingCaptor.value.onChange(false, mock(Uri::class.java))
@@ -123,23 +124,28 @@ class LockscreenAndDreamTargetFilterTest : SysuiTestCase() {
assertThat(filter.filterSmartspaceTarget(smartspaceTarget)).isTrue()
}
- /**
- * Ensures user switches are tracked.
- */
+ /** Ensures user switches are tracked. */
@Test
fun testUserSwitchCallback() {
var setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
- `when`(secureSettings
- .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
- .thenReturn(0)
- var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler,
- contentResolver, uiExecution)
+ `when`(secureSettings.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
+ .thenReturn(0)
+ var filter =
+ LockscreenTargetFilter(
+ secureSettings,
+ userTracker,
+ execution,
+ handler,
+ contentResolver,
+ uiExecution
+ )
filter.addListener(listener)
- var userTrackerCallback = withArgCaptor<UserTracker.Callback> {
- verify(userTracker).addCallback(capture(), any())
- }
+ var userTrackerCallback =
+ withArgCaptor<UserTracker.Callback> {
+ verify(userTracker).addCallback(capture(), any())
+ }
clearInvocations(listener)
userTrackerCallback.onUserChanged(0, context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
new file mode 100644
index 000000000000..f67c70ce783f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.app.Notification
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationContentDescriptionTest : SysuiTestCase() {
+
+ private val TITLE = "this is a title"
+ private val TEXT = "this is text"
+ private val TICKER = "this is a ticker"
+
+ @Test
+ fun notificationWithAllDifferentFields_descriptionIsTitle() {
+ val n = createNotification(TITLE, TEXT, TICKER)
+ val description = contentDescForNotification(context, n)
+ assertThat(description).isEqualTo(createDescriptionText(n, TITLE))
+ }
+
+ @Test
+ fun notificationWithAllDifferentFields_titleMatchesAppName_descriptionIsText() {
+ val n = createNotification(getTestAppName(), TEXT, TICKER)
+ val description = contentDescForNotification(context, n)
+ assertThat(description).isEqualTo(createDescriptionText(n, TEXT))
+ }
+
+ @Test
+ fun notificationWithAllDifferentFields_titleMatchesAppNameNoText_descriptionIsTicker() {
+ val n = createNotification(getTestAppName(), null, TICKER)
+ val description = contentDescForNotification(context, n)
+ assertThat(description).isEqualTo(createDescriptionText(n, TICKER))
+ }
+
+ @Test
+ fun notificationWithAllDifferentFields_titleMatchesAppNameNoTextNoTicker_descriptionEmpty() {
+ val appName = getTestAppName()
+ val n = createNotification(appName, null, null)
+ val description = contentDescForNotification(context, n)
+ assertThat(description).isEqualTo(createDescriptionText(n, ""))
+ }
+
+ @Test
+ fun nullNotification_descriptionIsAppName() {
+ val description = contentDescForNotification(context, null)
+ assertThat(description).isEqualTo(createDescriptionText(null, ""))
+ }
+
+ private fun createNotification(
+ title: String? = null,
+ text: String? = null,
+ ticker: String? = null
+ ): Notification =
+ Notification.Builder(context)
+ .setContentTitle(title)
+ .setContentText(text)
+ .setTicker(ticker)
+ .build()
+
+ private fun getTestAppName(): String {
+ return getAppName(createNotification("", "", ""))
+ }
+
+ private fun getAppName(n: Notification?) =
+ n?.let {
+ val builder = Notification.Builder.recoverBuilder(context, it)
+ builder.loadHeaderAppName()
+ }
+ ?: ""
+
+ private fun createDescriptionText(n: Notification?, desc: String?): String {
+ val appName = getAppName(n)
+ return context.getString(R.string.accessibility_desc_notification_icon, appName, desc)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 791a028eef4e..123734742820 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -16,9 +16,12 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
+import com.android.systemui.Flags.FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.coroutines.collectLastValue
@@ -183,7 +186,8 @@ class WifiViewModelTest : SysuiTestCase() {
}
@Test
- fun activity_nullSsid_outputsFalse() =
+ @DisableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
+ fun activity_nullSsid_outputsFalse_staticFlagOff() =
testScope.runTest {
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
@@ -207,6 +211,33 @@ class WifiViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
+ fun activity_nullSsid_outputsFalse_staticFlagOn() =
+ testScope.runTest {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1)
+ )
+
+ val activityIn by collectLastValue(underTest.isActivityInViewVisible)
+ val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
+ val activityContainer by collectLastValue(underTest.isActivityContainerVisible)
+
+ // WHEN we update the repo to have activity
+ val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+ wifiRepository.setWifiActivity(activity)
+
+ // THEN we still output false because our network's SSID is null
+ assertThat(activityIn).isFalse()
+ assertThat(activityOut).isFalse()
+
+ // THEN the inout indicators are sill showing due to the config being true
+ assertThat(activityContainer).isTrue()
+ }
+
+ @Test
fun activity_allLocationViewModelsReceiveSameData() =
testScope.runTest {
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
@@ -335,7 +366,8 @@ class WifiViewModelTest : SysuiTestCase() {
}
@Test
- fun activityContainer_inAndOutFalse_outputsFalse() =
+ @DisableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
+ fun activityContainer_inAndOutFalse_outputsTrue_staticFlagOff() =
testScope.runTest {
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
@@ -350,6 +382,24 @@ class WifiViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
+ fun activityContainer_inAndOutFalse_outputsTrue_staticFlagOn() =
+ testScope.runTest {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+
+ val latest by collectLastValue(underTest.isActivityContainerVisible)
+
+ val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ wifiRepository.setWifiActivity(activity)
+
+ // The activity container should always be visible, since activity is
+ // shown in UI by changing opacity of the indicators.
+ assertThat(latest).isTrue()
+ }
+
+ @Test
fun airplaneSpacer_notAirplaneMode_outputsFalse() =
testScope.runTest {
val latest by collectLastValue(underTest.isAirplaneSpacerVisible)
diff --git a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
index 7b9027004eea..50d560728fb7 100644
--- a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
@@ -15,7 +15,7 @@
~
-->
-<FrameLayout
+<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:id="@+id/alternate_bouncer"
@@ -32,4 +32,4 @@
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
-</FrameLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
index a8017f65c810..25cadfbb0a79 100644
--- a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
+++ b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
@@ -39,7 +39,6 @@
android:adjustViewBounds="true"
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_down"
- android:visibility="gone"
android:paddingEnd="2dp"
/>
<ImageView
@@ -49,7 +48,6 @@
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_up"
android:paddingEnd="2dp"
- android:visibility="gone"
/>
</FrameLayout>
<FrameLayout
diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
index 4c5cd7dda0c0..e8b8922a138d 100644
--- a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
+++ b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
@@ -37,7 +37,6 @@
android:adjustViewBounds="true"
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_down"
- android:visibility="gone"
android:paddingEnd="2dp"
/>
<ImageView
@@ -47,7 +46,6 @@
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_up"
android:paddingEnd="2dp"
- android:visibility="gone"
/>
</FrameLayout>
<FrameLayout
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 87c31c8b8aae..dca84b9fab7c 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -116,6 +116,10 @@
android:inflatedId="@+id/multi_shade"
android:layout="@layout/multi_shade" />
+ <include layout="@layout/alternate_bouncer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
<com.android.systemui.biometrics.AuthRippleView
android:id="@+id/auth_ripple"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index deed6c8c7f25..90cc1fbb1fba 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -966,14 +966,6 @@
<bool name="config_edgeToEdgeBottomSheetDialog">true</bool>
<!--
- Whether the scene container framework is enabled.
-
- The scene container framework is a newer (2023) way to organize the various "scenes" between the
- bouncer, lockscreen, shade, and quick settings.
- -->
- <bool name="config_sceneContainerFrameworkEnabled">true</bool>
-
- <!--
Time in milliseconds the user has to touch the side FPS sensor to successfully authenticate
TODO(b/302332976) Get this value from the HAL if they can provide an API for it.
-->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b9478018d7d3..0267454c9161 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1905,4 +1905,7 @@
<!-- Bouncer user switcher margins -->
<dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">0dp</dimen>
<dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">0dp</dimen>
+
+ <!-- UDFPS view attributes -->
+ <dimen name="udfps_icon_size">6mm</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index cf63cc74521d..6cb2d457daea 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -259,4 +259,7 @@
<item type="id" name="device_entry_icon_view" />
<item type="id" name="device_entry_icon_fg" />
<item type="id" name="device_entry_icon_bg" />
+
+ <!--Id for the device-entry UDFPS icon that lives in the alternate bouncer. -->
+ <item type="id" name="alternate_bouncer_udfps_icon_view" />
</resources>
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
index 8ad32b4f1695..a00cdfa05726 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
@@ -30,6 +30,10 @@ enum class FingerprintSensorType {
fun isUdfps(): Boolean {
return (this == UDFPS_OPTICAL) || (this == UDFPS_ULTRASONIC)
}
+
+ fun isPowerButton(): Boolean {
+ return this == POWER_BUTTON
+ }
}
/** Convert [this] to corresponding [FingerprintSensorType] */
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index 38043b4020e5..f4a2811a1b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics.domain.interactor
+import android.content.Context
import android.view.MotionEvent
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
@@ -23,12 +24,15 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Encapsulates business logic for interacting with the UDFPS overlay. */
@@ -36,10 +40,12 @@ import kotlinx.coroutines.flow.stateIn
class UdfpsOverlayInteractor
@Inject
constructor(
+ @Application context: Context,
private val authController: AuthController,
private val selectedUserInteractor: SelectedUserInteractor,
@Application scope: CoroutineScope
) {
+ private val iconSize: Int = context.resources.getDimensionPixelSize(R.dimen.udfps_icon_size)
/** Whether a touch is within the under-display fingerprint sensor area */
fun isTouchWithinUdfpsArea(ev: MotionEvent): Boolean {
@@ -70,6 +76,14 @@ constructor(
}
.stateIn(scope, started = SharingStarted.Eagerly, initialValue = UdfpsOverlayParams())
+ // Padding between the fingerprint icon and its bounding box in pixels.
+ val iconPadding: Flow<Int> =
+ udfpsOverlayParams.map { params ->
+ val sensorWidth = params.nativeSensorBounds.right - params.nativeSensorBounds.left
+ val nativePadding = (sensorWidth - iconSize) / 2
+ (nativePadding * params.scaleFactor).toInt()
+ }
+
companion object {
private const val TAG = "UdfpsOverlayInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt
index c19ea19833d2..710667438299 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics.ui.viewmodel
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.hideAffordancesRequest
@@ -26,22 +27,30 @@ import kotlinx.coroutines.flow.combine
/**
* View model for the UdfpsTouchOverlay for when UDFPS is being requested for device entry. Handles
- * touches as long as the device entry views are visible.
+ * touches as long as the device entry view is visible (the lockscreen or the alternate bouncer
+ * view).
*/
@ExperimentalCoroutinesApi
class DeviceEntryUdfpsTouchOverlayViewModel
@Inject
constructor(
deviceEntryIconViewModel: DeviceEntryIconViewModel,
+ alternateBouncerInteractor: AlternateBouncerInteractor,
systemUIDialogManager: SystemUIDialogManager,
) : UdfpsTouchOverlayViewModel {
- // TODO (b/305234447): AlternateBouncer showing overrides sysuiDialogHideAffordancesRequest
- override val shouldHandleTouches: Flow<Boolean> =
+ private val showingUdfpsAffordance: Flow<Boolean> =
combine(
deviceEntryIconViewModel.deviceEntryViewAlpha,
+ alternateBouncerInteractor.isVisible,
+ ) { deviceEntryViewAlpha, alternateBouncerVisible ->
+ deviceEntryViewAlpha > ALLOW_TOUCH_ALPHA_THRESHOLD || alternateBouncerVisible
+ }
+ override val shouldHandleTouches: Flow<Boolean> =
+ combine(
+ showingUdfpsAffordance,
systemUIDialogManager.hideAffordancesRequest,
- ) { deviceEntryViewAlpha, dialogRequestingHideAffordances ->
- deviceEntryViewAlpha > ALLOW_TOUCH_ALPHA_THRESHOLD && !dialogRequestingHideAffordances
+ ) { showingUdfpsAffordance, dialogRequestingHideAffordances ->
+ showingUdfpsAffordance && !dialogRequestingHideAffordances
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 84f7dd5edecf..af32eb534155 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -29,9 +29,10 @@ import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/** Encapsulates business logic for interacting with the lock-screen alternate bouncer. */
@SysUISingleton
@@ -52,19 +53,13 @@ constructor(
private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet()
private val alternateBouncerSupported: StateFlow<Boolean> =
if (DeviceEntryUdfpsRefactor.isEnabled) {
- // The device entry udfps refactor doesn't currently support the alternate bouncer.
- // TODO: Re-enable when b/287599719 is ready.
- MutableStateFlow(false).asStateFlow()
- // fingerprintPropertyRepository.sensorType
- // .map { sensorType ->
- // sensorType.isUdfps() || sensorType ==
- // FingerprintSensorType.POWER_BUTTON
- // }
- // .stateIn(
- // scope = scope,
- // started = SharingStarted.Eagerly,
- // initialValue = false,
- // )
+ fingerprintPropertyRepository.sensorType
+ .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
} else {
bouncerRepository.alternateBouncerUIAvailable
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/ConfigurationInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/ConfigurationInteractor.kt
deleted file mode 100644
index 89053d1d05fa..000000000000
--- a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/ConfigurationInteractor.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.systemui.common.domain.interactor
-
-import android.content.res.Configuration
-import android.graphics.Rect
-import android.view.Surface
-import com.android.systemui.common.ui.data.repository.ConfigurationRepository
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
-
-interface ConfigurationInteractor {
- /**
- * Returns screen size adjusted to rotation, so returned screen sizes are stable across all
- * rotations, could be useful if you need to react to screen resize (e.g. fold/unfold on
- * foldable devices)
- */
- val naturalMaxBounds: Flow<Rect>
-}
-
-class ConfigurationInteractorImpl
-@Inject
-constructor(private val repository: ConfigurationRepository) : ConfigurationInteractor {
-
- override val naturalMaxBounds: Flow<Rect>
- get() = repository.configurationValues.map { it.naturalScreenBounds }.distinctUntilChanged()
-
- /**
- * Returns screen size adjusted to rotation, so returned screen size is stable across all
- * rotations
- */
- private val Configuration.naturalScreenBounds: Rect
- get() {
- val rotation = windowConfiguration.displayRotation
- val maxBounds = windowConfiguration.maxBounds
- return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Rect(0, 0, maxBounds.width(), maxBounds.height())
- } else {
- Rect(0, 0, maxBounds.height(), maxBounds.width())
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
index 3648f3b2c3b9..13539850a598 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
@@ -17,17 +17,45 @@
package com.android.systemui.common.ui.domain.interactor
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.view.Surface
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onStart
/** Business logic related to configuration changes. */
@SysUISingleton
class ConfigurationInteractor @Inject constructor(private val repository: ConfigurationRepository) {
+ /**
+ * Returns screen size adjusted to rotation, so returned screen size is stable across all
+ * rotations
+ */
+ private val Configuration.naturalScreenBounds: Rect
+ get() {
+ val rotation = windowConfiguration.displayRotation
+ val maxBounds = windowConfiguration.maxBounds
+ return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
+ Rect(0, 0, maxBounds.width(), maxBounds.height())
+ } else {
+ Rect(0, 0, maxBounds.height(), maxBounds.width())
+ }
+ }
+
+ /**
+ * Returns screen size adjusted to rotation, so returned screen sizes are stable across all
+ * rotations, could be useful if you need to react to screen resize (e.g. fold/unfold on
+ * foldable devices)
+ */
+ val naturalMaxBounds: Flow<Rect> =
+ repository.configurationValues.map { it.naturalScreenBounds }.distinctUntilChanged()
+
/** Given [resourceId], emit the dimension pixel size on config change */
fun dimensionPixelSize(resourceId: Int): Flow<Int> {
return onAnyConfigurationChange.mapLatest { repository.getDimensionPixelSize(resourceId) }
@@ -43,4 +71,7 @@ class ConfigurationInteractor @Inject constructor(private val repository: Config
/** Emit an event on any config change */
val onAnyConfigurationChange: Flow<Unit> =
repository.onAnyConfigurationChange.onStart { emit(Unit) }
+
+ /** Emits the current resolution scaling factor */
+ val scaleForResolution: Flow<Float> = repository.scaleForResolution
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
index c5610c877f57..75f9d809cc69 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
@@ -30,9 +30,9 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_GLANCEABLE_HUB
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
-import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION
-import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.LOCKSCREEN_SMARTSPACE_PRECONDITION
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.LOCKSCREEN_SMARTSPACE_TARGET_FILTER
import com.android.systemui.util.concurrency.Execution
import java.util.Optional
import java.util.concurrent.Executor
@@ -48,8 +48,8 @@ constructor(
private val smartspaceManager: SmartspaceManager?,
private val execution: Execution,
@Main private val uiExecutor: Executor,
- @Named(DREAM_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition,
- @Named(DREAM_SMARTSPACE_TARGET_FILTER)
+ @Named(LOCKSCREEN_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition,
+ @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
private val optionalTargetFilter: Optional<SmartspaceTargetFilter>,
@Named(GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index ca8268dc89a3..a25c78871115 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -43,7 +43,6 @@ import com.android.systemui.bouncer.ui.BouncerViewModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
import com.android.systemui.common.data.CommonDataLayerModule;
-import com.android.systemui.common.domain.CommonDomainLayerModule;
import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.complication.dagger.ComplicationComponent;
import com.android.systemui.controls.dagger.ControlsModule;
@@ -179,7 +178,6 @@ import javax.inject.Named;
ClockRegistryModule.class,
CommunalModule.class,
CommonDataLayerModule.class,
- CommonDomainLayerModule.class,
ConnectivityModule.class,
ControlsModule.class,
CoroutinesModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
index ca4ef0d0bd8f..4a8c040e33c1 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
@@ -34,9 +34,9 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_DREAM
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_DATA_PLUGIN
-import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION
-import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.LOCKSCREEN_SMARTSPACE_PRECONDITION
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.LOCKSCREEN_SMARTSPACE_TARGET_FILTER
import com.android.systemui.smartspace.dagger.SmartspaceViewComponent
import com.android.systemui.util.concurrency.Execution
import java.util.Optional
@@ -54,8 +54,8 @@ class DreamSmartspaceController @Inject constructor(
private val execution: Execution,
@Main private val uiExecutor: Executor,
private val smartspaceViewComponentFactory: SmartspaceViewComponent.Factory,
- @Named(DREAM_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition,
- @Named(DREAM_SMARTSPACE_TARGET_FILTER)
+ @Named(LOCKSCREEN_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition,
+ @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
private val optionalTargetFilter: Optional<SmartspaceTargetFilter>,
@Named(DREAM_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>,
@Named(DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN)
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 5ec51f4c3dad..15404238099d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -25,7 +25,9 @@ import com.android.server.notification.Flags.vibrateWhileUnlocked
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
@@ -36,20 +38,28 @@ import javax.inject.Inject
class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, handler: Handler) :
FlagDependenciesBase(featureFlags, handler) {
override fun defineDependencies() {
+ // Internal notification backend dependencies
+ crossAppPoliteNotifications dependsOn politeNotifications
+ vibrateWhileUnlockedToken dependsOn politeNotifications
+
+ // Internal notification frontend dependencies
NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token
FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token
- val keyguardBottomAreaRefactor = FlagToken(
- FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor())
+ // Internal keyguard dependencies
KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor
- val crossAppPoliteNotifToken =
- FlagToken(FLAG_CROSS_APP_POLITE_NOTIFICATIONS, crossAppPoliteNotifications())
- val politeNotifToken = FlagToken(FLAG_POLITE_NOTIFICATIONS, politeNotifications())
- crossAppPoliteNotifToken dependsOn politeNotifToken
-
- val vibrateWhileUnlockedToken =
- FlagToken(FLAG_VIBRATE_WHILE_UNLOCKED, vibrateWhileUnlocked())
- vibrateWhileUnlockedToken dependsOn politeNotifToken
+ // SceneContainer dependencies
+ SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta }
+ SceneContainerFlag.getMainStaticFlag() dependsOn MIGRATE_KEYGUARD_STATUS_BAR_VIEW
}
+
+ private inline val politeNotifications
+ get() = FlagToken(FLAG_POLITE_NOTIFICATIONS, politeNotifications())
+ private inline val crossAppPoliteNotifications
+ get() = FlagToken(FLAG_CROSS_APP_POLITE_NOTIFICATIONS, crossAppPoliteNotifications())
+ private inline val vibrateWhileUnlockedToken: FlagToken
+ get() = FlagToken(FLAG_VIBRATE_WHILE_UNLOCKED, vibrateWhileUnlocked())
+ private inline val keyguardBottomAreaRefactor
+ get() = FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor())
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
index e96660fe4f79..6560ee3408d4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
@@ -133,6 +133,7 @@ constructor(
.setContentTitle(title)
.setContentText(details)
.setStyle(Notification.BigTextStyle().bigText(details))
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
.build()
notifManager.createNotificationChannel(channel)
notifManager.notify("flags", 0, notification)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 7e360cfad66d..52f2759fe63d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -141,5 +141,6 @@ constructor(
companion object {
val TRANSITION_DURATION_MS = 300.milliseconds
val TO_GONE_DURATION = 500.milliseconds
+ val TO_AOD_DURATION = TRANSITION_DURATION_MS
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
new file mode 100644
index 000000000000..d12d193aa43b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.content.res.ColorStateList
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+object AlternateBouncerUdfpsViewBinder {
+
+ /** Updates UI for the UDFPS icon on the alternate bouncer. */
+ @JvmStatic
+ fun bind(
+ view: DeviceEntryIconView,
+ viewModel: AlternateBouncerUdfpsIconViewModel,
+ ) {
+ if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
+ return
+ }
+ val fgIconView = view.iconView
+ val bgView = view.bgView
+
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.accessibilityDelegateHint.collect { hint ->
+ view.accessibilityHintType = hint
+ }
+ }
+ }
+
+ fgIconView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.fgViewModel.collect { fgViewModel ->
+ fgIconView.setImageState(
+ view.getIconState(fgViewModel.type, fgViewModel.useAodVariant),
+ /* merge */ false
+ )
+ fgIconView.imageTintList = ColorStateList.valueOf(fgViewModel.tint)
+ fgIconView.setPadding(
+ fgViewModel.padding,
+ fgViewModel.padding,
+ fgViewModel.padding,
+ fgViewModel.padding,
+ )
+ }
+ }
+ }
+
+ bgView.visibility = View.VISIBLE
+ bgView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.bgViewModel.collect { bgViewModel ->
+ bgView.alpha = bgViewModel.alpha
+ bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index d1d323fff87f..78906acd9cea 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -17,47 +17,49 @@
package com.android.systemui.keyguard.ui.binder
import android.view.View
-import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.classifier.Classifier
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.scrim.ScrimView
-import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.gesture.TapGestureDetector
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
-/** Binds the alternate bouncer view to its view-model. */
+/**
+ * Binds the alternate bouncer view to its view-model.
+ *
+ * For devices that support UDFPS, this includes a UDFPS view.
+ */
@ExperimentalCoroutinesApi
object AlternateBouncerViewBinder {
/** Binds the view to the view-model, continuing to update the former based on the latter. */
@JvmStatic
fun bind(
- view: ViewGroup,
+ view: ConstraintLayout,
viewModel: AlternateBouncerViewModel,
- scope: CoroutineScope,
- notificationShadeWindowController: NotificationShadeWindowController,
falsingManager: FalsingManager,
swipeUpAnywhereGestureHandler: SwipeUpAnywhereGestureHandler,
tapGestureDetector: TapGestureDetector,
+ alternateBouncerUdfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
) {
- if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) return
- scope.launch {
- // forcePluginOpen is necessary to show over occluded apps.
- // This cannot be tied to the view's lifecycle because setting this allows the view
- // to be started in the first place.
- viewModel.forcePluginOpen.collect {
- notificationShadeWindowController.setForcePluginOpen(it, this)
- }
+ if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
+ return
}
+ optionallyAddUdfpsView(
+ view = view,
+ alternateBouncerUdfpsIconViewModel = alternateBouncerUdfpsIconViewModel,
+ )
val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView
view.repeatWhenAttached { alternateBouncerViewContainer ->
@@ -99,6 +101,52 @@ object AlternateBouncerViewBinder {
}
}
}
+
+ private fun optionallyAddUdfpsView(
+ view: ConstraintLayout,
+ alternateBouncerUdfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
+ ) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ alternateBouncerUdfpsIconViewModel.iconLocation.collect { iconLocation ->
+ val viewId = R.id.alternate_bouncer_udfps_icon_view
+ var udfpsView = view.getViewById(viewId)
+ if (udfpsView == null) {
+ udfpsView =
+ DeviceEntryIconView(view.context, null).apply { id = viewId }
+ view.addView(udfpsView)
+ AlternateBouncerUdfpsViewBinder.bind(
+ udfpsView,
+ alternateBouncerUdfpsIconViewModel,
+ )
+ }
+
+ val constraintSet = ConstraintSet().apply { clone(view) }
+ constraintSet.apply {
+ constrainWidth(viewId, iconLocation.width)
+ constrainHeight(viewId, iconLocation.height)
+ connect(
+ viewId,
+ ConstraintSet.TOP,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.TOP,
+ iconLocation.top,
+ )
+ connect(
+ viewId,
+ ConstraintSet.START,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.START,
+ iconLocation.left
+ )
+ }
+ constraintSet.applyTo(view)
+ }
+ }
+ }
+ }
+ }
}
private const val swipeTag = "AlternateBouncer-SWIPE"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 01a1ca3eeb93..08e2a8fb6d77 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -393,7 +393,6 @@ object KeyguardRootViewBinder {
iconsAppearTranslationPx: Int,
screenOffAnimationController: ScreenOffAnimationController,
) {
- val statusViewMigrated = KeyguardShadeMigrationNssl.isEnabled
animate().cancel()
val animatorListener =
object : AnimatorListenerAdapter() {
@@ -404,13 +403,13 @@ object KeyguardRootViewBinder {
when {
!isVisible.isAnimating -> {
alpha = 1f
- if (!statusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
translationY = 0f
}
visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE
}
newAodTransition() -> {
- animateInIconTranslation(statusViewMigrated)
+ animateInIconTranslation()
if (isVisible.value) {
CrossFadeHelper.fadeIn(this, animatorListener)
} else {
@@ -419,7 +418,7 @@ object KeyguardRootViewBinder {
}
!isVisible.value -> {
// Let's make sure the icon are translated to 0, since we cancelled it above
- animateInIconTranslation(statusViewMigrated)
+ animateInIconTranslation()
CrossFadeHelper.fadeOut(this, animatorListener)
}
visibility != View.VISIBLE -> {
@@ -429,13 +428,12 @@ object KeyguardRootViewBinder {
appearIcons(
animate = screenOffAnimationController.shouldAnimateAodIcons(),
iconsAppearTranslationPx,
- statusViewMigrated,
animatorListener,
)
}
else -> {
// Let's make sure the icons are translated to 0, since we cancelled it above
- animateInIconTranslation(statusViewMigrated)
+ animateInIconTranslation()
// We were fading out, let's fade in instead
CrossFadeHelper.fadeIn(this, animatorListener)
}
@@ -445,11 +443,10 @@ object KeyguardRootViewBinder {
private fun View.appearIcons(
animate: Boolean,
iconAppearTranslation: Int,
- statusViewMigrated: Boolean,
animatorListener: Animator.AnimatorListener,
) {
if (animate) {
- if (!statusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
translationY = -iconAppearTranslation.toFloat()
}
alpha = 0f
@@ -457,19 +454,19 @@ object KeyguardRootViewBinder {
.alpha(1f)
.setInterpolator(Interpolators.LINEAR)
.setDuration(AOD_ICONS_APPEAR_DURATION)
- .apply { if (statusViewMigrated) animateInIconTranslation() }
+ .apply { if (KeyguardShadeMigrationNssl.isEnabled) animateInIconTranslation() }
.setListener(animatorListener)
.start()
} else {
alpha = 1.0f
- if (!statusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
translationY = 0f
}
}
}
- private fun View.animateInIconTranslation(statusViewMigrated: Boolean) {
- if (!statusViewMigrated) {
+ private fun View.animateInIconTranslation() {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index 9d557bb8a3b2..920fc04168a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -15,6 +15,8 @@
*/
package com.android.systemui.keyguard.ui.transitions
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
@@ -39,6 +41,18 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
abstract class DeviceEntryIconTransitionModule {
@Binds
@IntoSet
+ abstract fun alternateBouncerToAod(
+ impl: AlternateBouncerToAodTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun alternateBouncerToGone(
+ impl: AlternateBouncerToGoneTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
abstract fun aodToLockscreen(
impl: AodToLockscreenTransitionViewModel
): DeviceEntryIconTransition
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index a693ec9317d0..0bf9ad02eb58 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -23,7 +23,6 @@ import android.graphics.Rect
import android.util.DisplayMetrics
import android.view.View
import android.view.WindowManager
-import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
@@ -32,31 +31,24 @@ import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
-import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder
import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
-import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.gesture.TapGestureDetector
import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-/** Includes both the device entry icon and the alternate bouncer scrim. */
+/** Includes the device entry icon. */
@ExperimentalCoroutinesApi
class DefaultDeviceEntrySection
@Inject
@@ -72,27 +64,15 @@ constructor(
private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
- private val alternateBouncerViewModel: Lazy<AlternateBouncerViewModel>,
- private val notificationShadeWindowController: Lazy<NotificationShadeWindowController>,
- @Application private val scope: CoroutineScope,
- private val swipeUpAnywhereGestureHandler: Lazy<SwipeUpAnywhereGestureHandler>,
- private val tapGestureDetector: Lazy<TapGestureDetector>,
private val vibratorHelper: Lazy<VibratorHelper>,
) : KeyguardSection() {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
- private val alternateBouncerViewId = R.id.alternate_bouncer
override fun addViews(constraintLayout: ConstraintLayout) {
if (!keyguardBottomAreaRefactor() && !DeviceEntryUdfpsRefactor.isEnabled) {
return
}
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- // The alternate bouncer scrim needs to be below the device entry icon view, so
- // we add the view here before adding the device entry icon view.
- View.inflate(context, R.layout.alternate_bouncer, constraintLayout)
- }
-
notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
notificationPanelView.removeView(it)
}
@@ -119,17 +99,6 @@ constructor(
vibratorHelper.get(),
)
}
- constraintLayout.findViewById<FrameLayout?>(alternateBouncerViewId)?.let {
- AlternateBouncerViewBinder.bind(
- it,
- alternateBouncerViewModel.get(),
- scope,
- notificationShadeWindowController.get(),
- falsingManager.get(),
- swipeUpAnywhereGestureHandler.get(),
- tapGestureDetector.get(),
- )
- }
} else {
constraintLayout.findViewById<LockIconView?>(R.id.lock_icon_view)?.let {
lockIconViewController.get().setLockIconView(it)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
new file mode 100644
index 000000000000..a8e3be79fc5a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Breaks down ALTERNATE BOUNCER->AOD transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class AlternateBouncerToAodTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ animationFlow.setup(
+ duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
+ stepFlow = interactor.transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD),
+ )
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
+ onStep = { 1 - it },
+ onFinish = { 0f },
+ )
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled
+ ->
+ if (udfpsEnrolledAndEnabled) {
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
index 023d16cab013..5d6b0cebf959 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
@@ -18,8 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -33,10 +37,20 @@ import kotlinx.coroutines.flow.Flow
class AlternateBouncerToGoneTransitionViewModel
@Inject
constructor(
+ interactor: KeyguardTransitionInteractor,
bouncerToGoneFlows: BouncerToGoneFlows,
-) {
+ animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ animationFlow.setup(
+ duration = TO_GONE_DURATION,
+ stepFlow = interactor.transition(ALTERNATE_BOUNCER, KeyguardState.GONE),
+ )
/** Scrim alpha values */
val scrimAlpha: Flow<ScrimAlpha> =
bouncerToGoneFlows.scrimAlpha(TO_GONE_DURATION, ALTERNATE_BOUNCER)
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
new file mode 100644
index 000000000000..e18893a381f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.content.Context
+import android.hardware.biometrics.SensorLocationInternal
+import com.android.settingslib.Utils
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/** Models the UI state for the UDFPS icon view in the alternate bouncer view. */
+@ExperimentalCoroutinesApi
+class AlternateBouncerUdfpsIconViewModel
+@Inject
+constructor(
+ val context: Context,
+ configurationInteractor: ConfigurationInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ fingerprintPropertyRepository: FingerprintPropertyRepository,
+) {
+ private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
+ val iconLocation: Flow<IconLocation> =
+ isSupported.flatMapLatest { supportsUI ->
+ if (supportsUI) {
+ fingerprintPropertyRepository.sensorLocations.map { sensorLocations ->
+ val sensorLocation =
+ sensorLocations.getOrDefault("", SensorLocationInternal.DEFAULT)
+ IconLocation(
+ left = sensorLocation.sensorLocationX - sensorLocation.sensorRadius,
+ top = sensorLocation.sensorLocationY - sensorLocation.sensorRadius,
+ right = sensorLocation.sensorLocationX + sensorLocation.sensorRadius,
+ bottom = sensorLocation.sensorLocationY + sensorLocation.sensorRadius,
+ )
+ }
+ } else {
+ emptyFlow()
+ }
+ }
+ val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
+ flowOf(DeviceEntryIconView.AccessibilityHintType.ENTER)
+
+ private val fgIconColor: Flow<Int> =
+ configurationInteractor.onAnyConfigurationChange
+ .map { Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) }
+ .onStart {
+ emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary))
+ }
+ private val fgIconPadding: Flow<Int> =
+ configurationInteractor.scaleForResolution.map { scale ->
+ (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+ .roundToInt()
+ }
+ val fgViewModel: Flow<DeviceEntryForegroundViewModel.ForegroundIconViewModel> =
+ combine(
+ fgIconColor,
+ fgIconPadding,
+ ) { color, padding ->
+ DeviceEntryForegroundViewModel.ForegroundIconViewModel(
+ type = DeviceEntryIconView.IconType.FINGERPRINT,
+ useAodVariant = false,
+ tint = color,
+ padding = padding,
+ )
+ }
+
+ private val bgColor: Flow<Int> =
+ configurationInteractor.onAnyConfigurationChange
+ .map {
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorSurface)
+ }
+ .onStart {
+ emit(
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.colorSurface
+ )
+ )
+ }
+ val bgViewModel: Flow<DeviceEntryBackgroundViewModel.BackgroundViewModel> =
+ bgColor.map { color ->
+ DeviceEntryBackgroundViewModel.BackgroundViewModel(
+ alpha = 1f,
+ tint = color,
+ )
+ }
+
+ data class IconLocation(
+ val left: Int,
+ val top: Int,
+ val right: Int,
+ val bottom: Int,
+ ) {
+ val width = right - left
+ val height = bottom - top
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 3e8bbb3b4c34..c45caf0b18fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -42,6 +42,7 @@ constructor(
occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+ alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
) {
private val color: Flow<Int> =
configurationRepository.onAnyConfigurationChange
@@ -65,6 +66,7 @@ constructor(
occludedToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
occludedToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
dreamingToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ alternateBouncerToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
)
.merge()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 9a50d8370525..fe0b3656c3d7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
import com.android.settingslib.Utils
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -26,7 +27,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.res.R
import javax.inject.Inject
-import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -45,6 +45,7 @@ constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
transitionInteractor: KeyguardTransitionInteractor,
deviceEntryIconViewModel: DeviceEntryIconViewModel,
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
private val isShowingAod: Flow<Boolean> =
transitionInteractor.startedKeyguardState.map { keyguardState ->
@@ -73,11 +74,7 @@ constructor(
isTransitionToAod && isUdfps
}
.distinctUntilChanged()
- private val padding: Flow<Int> =
- configurationRepository.scaleForResolution.map { scale ->
- (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
- .roundToInt()
- }
+ private val padding: Flow<Int> = udfpsOverlayInteractor.iconPadding
val viewModel: Flow<ForegroundIconViewModel> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index f95713bf1802..eacaa40de821 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -29,6 +29,7 @@ import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.kotlin.sample
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -52,15 +53,12 @@ constructor(
transitionInteractor: KeyguardTransitionInteractor,
val keyguardInteractor: KeyguardInteractor,
val viewModel: AodToLockscreenTransitionViewModel,
- val shadeDependentFlows: ShadeDependentFlows,
private val sceneContainerFlags: SceneContainerFlags,
private val keyguardViewController: Lazy<KeyguardViewController>,
private val deviceEntryInteractor: DeviceEntryInteractor,
) {
private val intEvaluator = IntEvaluator()
private val floatEvaluator = FloatEvaluator()
- private val toAodFromState: Flow<KeyguardState> =
- transitionInteractor.transitionStepsToState(KeyguardState.AOD).map { it.from }
private val showingAlternateBouncer: Flow<Boolean> =
transitionInteractor.startedKeyguardState.map { keyguardState ->
keyguardState == KeyguardState.ALTERNATE_BOUNCER
@@ -95,13 +93,31 @@ constructor(
fullyDozingBurnInProgress,
)
}
+
+ private val dozeAmount: Flow<Float> =
+ combine(
+ transitionInteractor.startedKeyguardTransitionStep,
+ merge(
+ transitionInteractor.transitionStepsFromState(KeyguardState.AOD).map {
+ 1f - it.value
+ },
+ transitionInteractor.transitionStepsToState(KeyguardState.AOD).map { it.value }
+ ),
+ ) { startedKeyguardTransitionStep, aodTransitionAmount ->
+ if (
+ startedKeyguardTransitionStep.to == KeyguardState.AOD ||
+ startedKeyguardTransitionStep.from == KeyguardState.AOD
+ ) {
+ aodTransitionAmount
+ } else {
+ // in case a new transition (ie: to occluded) cancels a transition to or from
+ // aod, then we want to make sure the doze amount is still updated to 0
+ 0f
+ }
+ }
// Burn-in offsets that animate based on the transition amount to AOD
private val animatedBurnInOffsets: Flow<BurnInOffsets> =
- combine(
- nonAnimatedBurnInOffsets,
- transitionInteractor.transitionStepsToState(KeyguardState.AOD)
- ) { burnInOffsets, transitionStepsToAod ->
- val dozeAmount = transitionStepsToAod.value
+ combine(nonAnimatedBurnInOffsets, dozeAmount) { burnInOffsets, dozeAmount ->
BurnInOffsets(
intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.x),
intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.y),
@@ -120,22 +136,35 @@ constructor(
val burnInOffsets: Flow<BurnInOffsets> =
deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolled ->
if (udfpsEnrolled) {
- toAodFromState.flatMapLatest { fromState ->
- when (fromState) {
- KeyguardState.AOD,
- KeyguardState.GONE,
- KeyguardState.OCCLUDED,
- KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
- KeyguardState.OFF,
- KeyguardState.DOZING,
- KeyguardState.DREAMING,
- KeyguardState.PRIMARY_BOUNCER -> nonAnimatedBurnInOffsets
- KeyguardState.ALTERNATE_BOUNCER -> animatedBurnInOffsets
- KeyguardState.LOCKSCREEN ->
- shadeDependentFlows.transitionFlow(
- flowWhenShadeIsExpanded = nonAnimatedBurnInOffsets,
- flowWhenShadeIsNotExpanded = animatedBurnInOffsets,
- )
+ combine(
+ transitionInteractor.startedKeyguardTransitionStep.sample(
+ shadeInteractor.isAnyFullyExpanded,
+ ::Pair
+ ),
+ animatedBurnInOffsets,
+ nonAnimatedBurnInOffsets,
+ ) {
+ (startedTransitionStep, shadeExpanded),
+ animatedBurnInOffsets,
+ nonAnimatedBurnInOffsets ->
+ if (startedTransitionStep.to == KeyguardState.AOD) {
+ when (startedTransitionStep.from) {
+ KeyguardState.ALTERNATE_BOUNCER -> animatedBurnInOffsets
+ KeyguardState.LOCKSCREEN ->
+ if (shadeExpanded) {
+ nonAnimatedBurnInOffsets
+ } else {
+ animatedBurnInOffsets
+ }
+ else -> nonAnimatedBurnInOffsets
+ }
+ } else if (startedTransitionStep.from == KeyguardState.AOD) {
+ when (startedTransitionStep.to) {
+ KeyguardState.LOCKSCREEN -> animatedBurnInOffsets
+ else -> BurnInOffsets(x = 0, y = 0, progress = 0f)
+ }
+ } else {
+ BurnInOffsets(x = 0, y = 0, progress = 0f)
}
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index a99c51c2e557..be9393655c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -405,12 +405,15 @@ constructor(
}
widgetGroupIds.forEach { id ->
squishedViewState.widgetStates.get(id)?.let { state ->
- state.alpha =
- calculateAlpha(
- squishFraction,
- startPosition / nonsquishedHeight,
- endPosition / nonsquishedHeight
- )
+ // Don't modify alpha for elements that should be invisible (e.g. disabled seekbar)
+ if (state.alpha != 0f) {
+ state.alpha =
+ calculateAlpha(
+ squishFraction,
+ startPosition / nonsquishedHeight,
+ endPosition / nonsquishedHeight
+ )
+ }
}
}
return groupTop // used for the widget group above this group
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
index 898298c58b32..77279f225d83 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.util
import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
/** Helper for reading or using the media_in_scene_container flag state. */
@@ -25,6 +26,10 @@ object MediaInSceneContainerFlag {
/** The aconfig flag name */
const val FLAG_NAME = Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
/** Is the flag enabled? */
@JvmStatic
inline val isEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
index ca5302e13545..c932cee92a30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
@@ -16,11 +16,15 @@
package com.android.systemui.qs.tiles.impl.custom.data.repository
+import android.content.pm.PackageManager
+import android.content.pm.ServiceInfo
import android.graphics.drawable.Icon
import android.os.UserHandle
import android.service.quicksettings.Tile
+import android.service.quicksettings.TileService
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.external.CustomTileStatePersister
+import com.android.systemui.qs.external.PackageManagerAdapter
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.impl.custom.commons.copy
@@ -63,6 +67,12 @@ interface CustomTileRepository {
*/
fun getTile(user: UserHandle): Tile?
+ /** @see [com.android.systemui.qs.external.TileLifecycleManager.isActiveTile] */
+ suspend fun isTileActive(): Boolean
+
+ /** @see [com.android.systemui.qs.external.TileLifecycleManager.isToggleableTile] */
+ suspend fun isTileToggleable(): Boolean
+
/**
* Updates tile with the non-null values from [newTile]. Overwrites the current cache when
* [user] differs from the cached one. [isPersistable] tile will be persisted to be possibly
@@ -92,6 +102,7 @@ class CustomTileRepositoryImpl
constructor(
private val tileSpec: TileSpec.CustomTileSpec,
private val customTileStatePersister: CustomTileStatePersister,
+ private val packageManagerAdapter: PackageManagerAdapter,
@Background private val backgroundContext: CoroutineContext,
) : CustomTileRepository {
@@ -149,6 +160,34 @@ constructor(
}
}
+ override suspend fun isTileActive(): Boolean =
+ withContext(backgroundContext) {
+ try {
+ val info: ServiceInfo =
+ packageManagerAdapter.getServiceInfo(
+ tileSpec.componentName,
+ META_DATA_QUERY_FLAGS
+ )
+ info.metaData?.getBoolean(TileService.META_DATA_ACTIVE_TILE, false) == true
+ } catch (e: PackageManager.NameNotFoundException) {
+ false
+ }
+ }
+
+ override suspend fun isTileToggleable(): Boolean =
+ withContext(backgroundContext) {
+ try {
+ val info: ServiceInfo =
+ packageManagerAdapter.getServiceInfo(
+ tileSpec.componentName,
+ META_DATA_QUERY_FLAGS
+ )
+ info.metaData?.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false) == true
+ } catch (e: PackageManager.NameNotFoundException) {
+ false
+ }
+ }
+
private suspend fun updateTile(
user: UserHandle,
isPersistable: Boolean,
@@ -193,4 +232,12 @@ constructor(
private fun UserHandle.getKey() = TileServiceKey(tileSpec.componentName, this.identifier)
private data class TileWithUser(val user: UserHandle, val tile: Tile)
+
+ private companion object {
+ const val META_DATA_QUERY_FLAGS =
+ (PackageManager.GET_META_DATA or
+ PackageManager.MATCH_UNINSTALLED_PACKAGES or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE or
+ PackageManager.MATCH_DIRECT_BOOT_AWARE)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
index 351bba538463..10b012d30fcd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
@@ -19,10 +19,9 @@ package com.android.systemui.qs.tiles.impl.custom.domain.interactor
import android.os.UserHandle
import android.service.quicksettings.Tile
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.external.TileServiceManager
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
-import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundScope
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
@@ -35,15 +34,13 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
/** Manages updates of the [Tile] assigned for the current custom tile. */
-@CustomTileBoundScope
+@QSTileScope
class CustomTileInteractor
@Inject
constructor(
- private val user: UserHandle,
private val defaultsRepository: CustomTileDefaultsRepository,
private val customTileRepository: CustomTileRepository,
- private val tileServiceManager: TileServiceManager,
- @CustomTileBoundScope private val boundScope: CoroutineScope,
+ @QSTileScope private val tileScope: CoroutineScope,
@Background private val backgroundContext: CoroutineContext,
) {
@@ -51,8 +48,7 @@ constructor(
MutableSharedFlow<Tile>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
/** [Tile] updates. [updateTile] to emit a new one. */
- val tiles: Flow<Tile>
- get() = customTileRepository.getTiles(user)
+ fun getTiles(user: UserHandle): Flow<Tile> = customTileRepository.getTiles(user)
/**
* Current [Tile]
@@ -61,10 +57,14 @@ constructor(
* the tile hasn't been updated for the current user. Can happen when this is accessed before
* [init] returns.
*/
- val tile: Tile
- get() =
- customTileRepository.getTile(user)
- ?: throw IllegalStateException("Attempt to get a tile for a wrong user")
+ fun getTile(user: UserHandle): Tile =
+ customTileRepository.getTile(user)
+ ?: throw IllegalStateException("Attempt to get a tile for a wrong user")
+
+ /**
+ * True if the tile is toggleable like a switch and false if it operates as a clickable button.
+ */
+ suspend fun isTileToggleable(): Boolean = customTileRepository.isTileToggleable()
/**
* Initializes the repository for the current user. Suspends until it's safe to call [tile]
@@ -73,36 +73,36 @@ constructor(
* - receive tile update in [updateTile];
* - restoration happened for a persisted tile.
*/
- suspend fun init() {
- launchUpdates()
- customTileRepository.restoreForTheUserIfNeeded(user, tileServiceManager.isActiveTile)
+ suspend fun initForUser(user: UserHandle) {
+ launchUpdates(user)
+ customTileRepository.restoreForTheUserIfNeeded(user, customTileRepository.isTileActive())
// Suspend to make sure it gets the tile from one of the sources: restoration, defaults, or
// tile update.
customTileRepository.getTiles(user).firstOrNull()
}
- private fun launchUpdates() {
+ private fun launchUpdates(user: UserHandle) {
tileUpdates
.onEach {
customTileRepository.updateWithTile(
user,
it,
- tileServiceManager.isActiveTile,
+ customTileRepository.isTileActive(),
)
}
.flowOn(backgroundContext)
- .launchIn(boundScope)
+ .launchIn(tileScope)
defaultsRepository
.defaults(user)
.onEach {
customTileRepository.updateWithDefaults(
user,
it,
- tileServiceManager.isActiveTile,
+ customTileRepository.isTileActive(),
)
}
.flowOn(backgroundContext)
- .launchIn(boundScope)
+ .launchIn(tileScope)
}
/** Updates current [Tile]. Emits a new event in [tiles]. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index 580c42122a85..ef3df482ea45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -22,8 +22,8 @@ import kotlinx.coroutines.flow.StateFlow
/**
* Represents tiles behaviour logic. This ViewModel is a connection between tile view and data
- * layers. All direct inheritors must be added to the [QSTileViewModelInterfaceComplianceTest] class
- * to pass compliance tests.
+ * layers. All direct inheritors must be added to the
+ * [com.android.systemui.qs.tiles.viewmodel.QSTileViewModelTest] class to pass interface tests.
*
* All methods of this view model should be considered running on the main thread. This means no
* synchronous long running operations are permitted in any method.
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 1156250666f3..8c3e4a5d0be2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -14,30 +14,94 @@
* limitations under the License.
*/
+@file:Suppress("NOTHING_TO_INLINE")
+
package com.android.systemui.scene.shared.flag
-import android.content.Context
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags.sceneContainer
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flag
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.ReleasedFlag
-import com.android.systemui.flags.ResourceBooleanFlag
-import com.android.systemui.flags.UnreleasedFlag
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.Flags.SCENE_CONTAINER_ENABLED
+import com.android.systemui.flags.RefactorFlagUtils
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.media.controls.util.MediaInSceneContainerFlag
-import com.android.systemui.res.R
import dagger.Module
import dagger.Provides
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+
+/** Helper for reading or using the scene container flag state. */
+object SceneContainerFlag {
+ /** The flag description -- not an aconfig flag name */
+ const val DESCRIPTION = "SceneContainerFlag"
+
+ inline val isEnabled
+ get() =
+ SCENE_CONTAINER_ENABLED && // mainStaticFlag
+ sceneContainer() && // mainAconfigFlag
+ keyguardBottomAreaRefactor() &&
+ KeyguardShadeMigrationNssl.isEnabled &&
+ MediaInSceneContainerFlag.isEnabled &&
+ ComposeFacade.isComposeAvailable()
+
+ /**
+ * The main static flag, SCENE_CONTAINER_ENABLED. This is an explicit static flag check that
+ * helps with downstream optimizations (like unused code stripping) in builds where aconfig
+ * flags are still writable. Do not remove!
+ */
+ inline fun getMainStaticFlag() =
+ FlagToken("Flags.SCENE_CONTAINER_ENABLED", SCENE_CONTAINER_ENABLED)
+
+ /** The main aconfig flag. */
+ inline fun getMainAconfigFlag() = FlagToken(FLAG_SCENE_CONTAINER, sceneContainer())
+
+ /** The set of secondary flags which must be enabled for scene container to work properly */
+ inline fun getSecondaryFlags(): Sequence<FlagToken> =
+ sequenceOf(
+ FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()),
+ KeyguardShadeMigrationNssl.token,
+ MediaInSceneContainerFlag.token,
+ )
+
+ /** The full set of requirements for SceneContainer */
+ inline fun getAllRequirements(): Sequence<FlagToken> {
+ val composeRequirement =
+ FlagToken("ComposeFacade.isComposeAvailable()", ComposeFacade.isComposeAvailable())
+ return sequenceOf(getMainStaticFlag(), getMainAconfigFlag()) +
+ getSecondaryFlags() +
+ composeRequirement
+ }
+
+ /** Return all dependencies of this flag in pairs where [Pair.first] depends on [Pair.second] */
+ inline fun getFlagDependencies(): Sequence<Pair<FlagToken, FlagToken>> {
+ val mainStaticFlag = getMainStaticFlag()
+ val mainAconfigFlag = getMainAconfigFlag()
+ return sequence {
+ // The static and aconfig flags should be equal; make them co-dependent
+ yield(mainAconfigFlag to mainStaticFlag)
+ yield(mainStaticFlag to mainAconfigFlag)
+ // all other flags depend on the static flag for brevity
+ } + getSecondaryFlags().map { mainStaticFlag to it }
+ }
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, DESCRIPTION)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, DESCRIPTION)
+}
/**
* Defines interface for classes that can check whether the scene container framework feature is
@@ -52,133 +116,25 @@ interface SceneContainerFlags {
fun requirementDescription(): String
}
-class SceneContainerFlagsImpl
-@AssistedInject
-constructor(
- @Application private val context: Context,
- private val featureFlagsClassic: FeatureFlagsClassic,
- @Assisted private val isComposeAvailable: Boolean,
-) : SceneContainerFlags {
-
- companion object {
- @VisibleForTesting
- val classicFlagTokens: List<Flag<Boolean>> =
- listOf(
- Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW,
- )
- }
-
- /** The list of requirements, all must be met for the feature to be enabled. */
- private val requirements =
- listOf(
- AconfigFlagMustBeEnabled(
- flagName = AConfigFlags.FLAG_SCENE_CONTAINER,
- flagValue = sceneContainer(),
- ),
- AconfigFlagMustBeEnabled(
- flagName = AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
- flagValue = keyguardBottomAreaRefactor(),
- ),
- AconfigFlagMustBeEnabled(
- flagName = KeyguardShadeMigrationNssl.FLAG_NAME,
- flagValue = KeyguardShadeMigrationNssl.isEnabled,
- ),
- AconfigFlagMustBeEnabled(
- flagName = MediaInSceneContainerFlag.FLAG_NAME,
- flagValue = MediaInSceneContainerFlag.isEnabled,
- ),
- ) +
- classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
- listOf(
- ComposeMustBeAvailable(),
- CompileTimeFlagMustBeEnabled(),
- ResourceConfigMustBeEnabled()
- )
+class SceneContainerFlagsImpl : SceneContainerFlags {
override fun isEnabled(): Boolean {
- // SCENE_CONTAINER_ENABLED is an explicit static flag check that helps with downstream
- // optimizations, e.g., unused code stripping. Do not remove!
- return Flags.SCENE_CONTAINER_ENABLED && requirements.all { it.isMet() }
+ return SceneContainerFlag.isEnabled
}
override fun requirementDescription(): String {
return buildString {
- requirements.forEach { requirement ->
+ SceneContainerFlag.getAllRequirements().forEach { requirement ->
append('\n')
- append(if (requirement.isMet()) " [MET]" else "[NOT MET]")
+ append(if (requirement.isEnabled) " [MET]" else "[NOT MET]")
append(" ${requirement.name}")
}
}
}
-
- private interface Requirement {
- val name: String
-
- fun isMet(): Boolean
- }
-
- private inner class ComposeMustBeAvailable : Requirement {
- override val name = "Jetpack Compose must be available"
-
- override fun isMet(): Boolean {
- return isComposeAvailable
- }
- }
-
- private inner class CompileTimeFlagMustBeEnabled : Requirement {
- override val name = "Flags.SCENE_CONTAINER_ENABLED must be enabled in code"
-
- override fun isMet(): Boolean {
- return Flags.SCENE_CONTAINER_ENABLED
- }
- }
-
- private inner class FlagMustBeEnabled<FlagType : Flag<*>>(
- private val flag: FlagType,
- ) : Requirement {
- override val name = "Flag ${flag.name} must be enabled"
-
- override fun isMet(): Boolean {
- return when (flag) {
- is ResourceBooleanFlag -> featureFlagsClassic.isEnabled(flag)
- is ReleasedFlag -> featureFlagsClassic.isEnabled(flag)
- is UnreleasedFlag -> featureFlagsClassic.isEnabled(flag)
- else -> error("Unsupported flag type ${flag.javaClass}")
- }
- }
- }
-
- private inner class AconfigFlagMustBeEnabled(
- flagName: String,
- private val flagValue: Boolean,
- ) : Requirement {
- override val name: String = "Aconfig flag $flagName must be enabled"
-
- override fun isMet(): Boolean {
- return flagValue
- }
- }
-
- private inner class ResourceConfigMustBeEnabled : Requirement {
- override val name: String = "R.bool.config_sceneContainerFrameworkEnabled must be true"
-
- override fun isMet(): Boolean {
- return context.resources.getBoolean(R.bool.config_sceneContainerFrameworkEnabled)
- }
- }
-
- @AssistedFactory
- interface Factory {
- fun create(isComposeAvailable: Boolean): SceneContainerFlagsImpl
- }
}
@Module
object SceneContainerFlagsModule {
- @Provides
- @SysUISingleton
- fun impl(factory: SceneContainerFlagsImpl.Factory): SceneContainerFlags {
- return factory.create(ComposeFacade.isComposeAvailable())
- }
+ @Provides @SysUISingleton fun impl(): SceneContainerFlags = SceneContainerFlagsImpl()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 95f7c94a235f..878e6faf32e7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1096,7 +1096,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
@Override
- public void onPulseExpansionChanged(boolean expandingChanged) {
+ public void onPulseExpansionAmountChanged(boolean expandingChanged) {
if (mKeyguardBypassController.getBypassEnabled()) {
// Position the notifications while dragging down while pulsing
requestScrollerTopPaddingUpdate(false /* animate */);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 07ce57735dc7..3cf468f302b2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -54,8 +54,13 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler;
+import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder;
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.log.BouncerLogger;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.res.R;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.DragDownHelper;
@@ -64,6 +69,7 @@ import com.android.systemui.statusbar.NotificationInsetsController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.gesture.TapGestureDetector;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -76,14 +82,19 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.SystemClock;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.Optional;
import java.util.function.Consumer;
import javax.inject.Inject;
+import kotlinx.coroutines.ExperimentalCoroutinesApi;
+
/**
* Controller for {@link NotificationShadeWindowView}.
*/
@@ -149,6 +160,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
};
private final SystemClock mClock;
+ @ExperimentalCoroutinesApi
@Inject
public NotificationShadeWindowViewController(
LockscreenShadeTransitionController transitionController,
@@ -190,7 +202,13 @@ public class NotificationShadeWindowViewController implements Dumpable {
QuickSettingsController quickSettingsController,
PrimaryBouncerInteractor primaryBouncerInteractor,
AlternateBouncerInteractor alternateBouncerInteractor,
- SelectedUserInteractor selectedUserInteractor) {
+ SelectedUserInteractor selectedUserInteractor,
+ Lazy<JavaAdapter> javaAdapter,
+ Lazy<AlternateBouncerViewModel> alternateBouncerViewModel,
+ Lazy<FalsingManager> falsingManager,
+ Lazy<SwipeUpAnywhereGestureHandler> swipeUpAnywhereGestureHandler,
+ Lazy<TapGestureDetector> tapGestureDetector,
+ Lazy<AlternateBouncerUdfpsIconViewModel> alternateBouncerUdfpsIconViewModel) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -235,6 +253,25 @@ public class NotificationShadeWindowViewController implements Dumpable {
featureFlagsClassic,
selectedUserInteractor);
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
+ AlternateBouncerViewBinder.bind(
+ mView.findViewById(R.id.alternate_bouncer),
+ alternateBouncerViewModel.get(),
+ falsingManager.get(),
+ swipeUpAnywhereGestureHandler.get(),
+ tapGestureDetector.get(),
+ alternateBouncerUdfpsIconViewModel.get()
+ );
+ javaAdapter.get().alwaysCollectFlow(
+ alternateBouncerViewModel.get().getForcePluginOpen(),
+ forcePluginOpen ->
+ mNotificationShadeWindowController.setForcePluginOpen(
+ forcePluginOpen,
+ alternateBouncerViewModel.get()
+ )
+ );
+ }
+
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
mLockscreenToDreamingTransition);
collectFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index d26fded19cc1..2f58b35ae182 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -41,14 +41,14 @@ abstract class SmartspaceModule {
const val DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN = "dream_weather_smartspace_data_plugin"
/**
- * The dream smartspace target filter.
+ * The target filter for smartspace over lockscreen.
*/
- const val DREAM_SMARTSPACE_TARGET_FILTER = "dream_smartspace_target_filter"
+ const val LOCKSCREEN_SMARTSPACE_TARGET_FILTER = "lockscreen_smartspace_target_filter"
/**
- * The precondition for dream smartspace
+ * The precondition for smartspace over lockscreen
*/
- const val DREAM_SMARTSPACE_PRECONDITION = "dream_smartspace_precondition"
+ const val LOCKSCREEN_SMARTSPACE_PRECONDITION = "lockscreen_smartspace_precondition"
/**
* The BcSmartspaceDataPlugin for the standalone date (+alarm+dnd).
@@ -67,7 +67,7 @@ abstract class SmartspaceModule {
}
@BindsOptionalOf
- @Named(DREAM_SMARTSPACE_TARGET_FILTER)
+ @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
abstract fun optionalDreamSmartspaceTargetFilter(): SmartspaceTargetFilter?
@BindsOptionalOf
@@ -79,7 +79,7 @@ abstract class SmartspaceModule {
abstract fun optionalDreamWeatherSmartspaceDataPlugin(): BcSmartspaceDataPlugin?
@Binds
- @Named(DREAM_SMARTSPACE_PRECONDITION)
+ @Named(LOCKSCREEN_SMARTSPACE_PRECONDITION)
abstract fun bindSmartspacePrecondition(
lockscreenPrecondition: LockscreenPrecondition?
): SmartspacePrecondition?
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt
index aa4cf75a7a6e..5fb85e59ad91 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt
@@ -31,10 +31,10 @@ import com.android.systemui.util.settings.SecureSettings
import java.util.concurrent.Executor
import javax.inject.Inject
-/**
- * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen and dreams.
- */
-class LockscreenAndDreamTargetFilter @Inject constructor(
+/** {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen. */
+class LockscreenTargetFilter
+@Inject
+constructor(
private val secureSettings: SecureSettings,
private val userTracker: UserTracker,
private val execution: Execution,
@@ -60,12 +60,13 @@ class LockscreenAndDreamTargetFilter @Inject constructor(
}
}
- private val settingsObserver = object : ContentObserver(handler) {
- override fun onChange(selfChange: Boolean, uri: Uri?) {
- execution.assertIsMainThread()
- updateUserContentSettings()
+ private val settingsObserver =
+ object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ execution.assertIsMainThread()
+ updateUserContentSettings()
+ }
}
- }
private var managedUserHandle: UserHandle? = null
@@ -79,10 +80,10 @@ class LockscreenAndDreamTargetFilter @Inject constructor(
userTracker.addCallback(userTrackerCallback, uiExecutor)
contentResolver.registerContentObserver(
- secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
- true,
- settingsObserver,
- UserHandle.USER_ALL
+ secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true,
+ settingsObserver,
+ UserHandle.USER_ALL
)
updateUserContentSettings()
@@ -110,7 +111,7 @@ class LockscreenAndDreamTargetFilter @Inject constructor(
// Only the primary user can have an associated managed profile, so only show
// content for the managed profile if the primary user is active
userTracker.userHandle.identifier == UserHandle.USER_SYSTEM &&
- (!t.isSensitive || showSensitiveContentForManagedUser)
+ (!t.isSensitive || showSensitiveContentForManagedUser)
}
else -> {
false
@@ -118,12 +119,13 @@ class LockscreenAndDreamTargetFilter @Inject constructor(
}
}
- private val userTrackerCallback = object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- execution.assertIsMainThread()
- updateUserContentSettings()
+ private val userTrackerCallback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ execution.assertIsMainThread()
+ updateUserContentSettings()
+ }
}
- }
private fun getWorkProfileUser(): UserHandle? {
for (userInfo in userTracker.userProfiles) {
@@ -138,13 +140,13 @@ class LockscreenAndDreamTargetFilter @Inject constructor(
val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
showSensitiveContentForCurrentUser =
- secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1
+ secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1
managedUserHandle = getWorkProfileUser()
val managedId = managedUserHandle?.identifier
if (managedId != null) {
showSensitiveContentForManagedUser =
- secureSettings.getIntForUser(setting, 0, managedId) == 1
+ secureSettings.getIntForUser(setting, 0, managedId) == 1
}
listeners.forEach { it.onCriteriaChanged() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 843a454d73a0..984fcad469a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -27,7 +27,6 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -59,6 +58,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.NotificationContentDescription;
import com.android.systemui.statusbar.notification.NotificationDozeHelper;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
@@ -350,9 +350,22 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
}
public void setNotification(StatusBarNotification notification) {
- mNotification = notification;
+ CharSequence contentDescription = null;
if (notification != null) {
- setContentDescription(notification.getNotification());
+ contentDescription = NotificationContentDescription
+ .contentDescForNotification(mContext, notification.getNotification());
+ }
+ setNotification(notification, contentDescription);
+ }
+
+ /**
+ * Sets the notification with a pre-set content description.
+ */
+ public void setNotification(@Nullable StatusBarNotification notification,
+ @Nullable CharSequence notificationContentDescription) {
+ mNotification = notification;
+ if (!TextUtils.isEmpty(notificationContentDescription)) {
+ setContentDescription(notificationContentDescription);
}
maybeUpdateIconScaleDimens();
}
@@ -621,15 +634,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
mNumberBackground.setBounds(w-dw, h-dh, w, h);
}
- private void setContentDescription(Notification notification) {
- if (notification != null) {
- String d = contentDescForNotification(mContext, notification);
- if (!TextUtils.isEmpty(d)) {
- setContentDescription(d);
- }
- }
- }
-
@Override
public String toString() {
return "StatusBarIconView("
@@ -647,35 +651,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
return mSlot;
}
-
- public static String contentDescForNotification(Context c, Notification n) {
- String appName = "";
- try {
- Notification.Builder builder = Notification.Builder.recoverBuilder(c, n);
- appName = builder.loadHeaderAppName();
- } catch (RuntimeException e) {
- Log.e(TAG, "Unable to recover builder", e);
- // Trying to get the app name from the app info instead.
- ApplicationInfo appInfo = n.extras.getParcelable(
- Notification.EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
- if (appInfo != null) {
- appName = String.valueOf(appInfo.loadLabel(c.getPackageManager()));
- }
- }
-
- CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
- CharSequence text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
- CharSequence ticker = n.tickerText;
-
- // Some apps just put the app name into the title
- CharSequence titleOrText = TextUtils.equals(title, appName) ? text : title;
-
- CharSequence desc = !TextUtils.isEmpty(titleOrText) ? titleOrText
- : !TextUtils.isEmpty(ticker) ? ticker : "";
-
- return c.getString(R.string.accessibility_desc_notification_icon, appName, desc);
- }
-
/**
* Set the color that is used to draw decoration like the overflow dot. This will not be applied
* to the drawable.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
new file mode 100644
index 000000000000..e7012ea51caf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("NotificationContentDescription")
+
+package com.android.systemui.statusbar.notification
+
+import android.app.Notification
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.text.TextUtils
+import android.util.Log
+import androidx.annotation.MainThread
+import com.android.systemui.res.R
+
+/**
+ * Returns accessibility content description for a given notification.
+ *
+ * NOTE: This is a relatively slow call.
+ */
+@MainThread
+fun contentDescForNotification(c: Context, n: Notification?): CharSequence {
+ var appName = ""
+ try {
+ val builder = Notification.Builder.recoverBuilder(c, n)
+ appName = builder.loadHeaderAppName()
+ } catch (e: RuntimeException) {
+ Log.e("ContentDescription", "Unable to recover builder", e)
+ // Trying to get the app name from the app info instead.
+ val appInfo =
+ n?.extras?.getParcelable(
+ Notification.EXTRA_BUILDER_APPLICATION_INFO,
+ ApplicationInfo::class.java
+ )
+ if (appInfo != null) {
+ appName = appInfo.loadLabel(c.packageManager).toString()
+ }
+ }
+ val title = n?.extras?.getCharSequence(Notification.EXTRA_TITLE)
+ val text = n?.extras?.getCharSequence(Notification.EXTRA_TEXT)
+ val ticker = n?.tickerText
+
+ // Some apps just put the app name into the title
+ val titleOrText = if (TextUtils.equals(title, appName)) text else title
+ val desc =
+ if (!TextUtils.isEmpty(titleOrText)) titleOrText
+ else if (!TextUtils.isEmpty(ticker)) ticker else ""
+ return c.getString(R.string.accessibility_desc_notification_icon, appName, desc)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 8d1e8d0ab524..0c67279c1660 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -20,9 +20,9 @@ import android.util.FloatProperty
import android.view.animation.Interpolator
import androidx.annotation.VisibleForTesting
import androidx.core.animation.ObjectAnimator
-import com.android.systemui.Dumpable
import com.android.app.animation.Interpolators
import com.android.app.animation.InterpolatorsAndroidX
+import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -31,6 +31,7 @@ import com.android.systemui.shade.ShadeExpansionListener
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.DozeParameters
@@ -206,8 +207,15 @@ constructor(
val nowExpanding = isPulseExpanding()
val changed = nowExpanding != pulseExpanding
pulseExpanding = nowExpanding
- for (listener in wakeUpListeners) {
- listener.onPulseExpansionChanged(changed)
+ if (!NotificationIconContainerRefactor.isEnabled) {
+ for (listener in wakeUpListeners) {
+ listener.onPulseExpansionAmountChanged(changed)
+ }
+ }
+ if (changed) {
+ for (listener in wakeUpListeners) {
+ listener.onPulseExpandingChanged(pulseExpanding)
+ }
}
}
}
@@ -620,13 +628,20 @@ constructor(
*
* @param expandingChanged if the user has started or stopped expanding
*/
- fun onPulseExpansionChanged(expandingChanged: Boolean) {}
+ @Deprecated(
+ message = "Use onPulseExpandedChanged instead.",
+ replaceWith = ReplaceWith("onPulseExpandedChanged"),
+ )
+ fun onPulseExpansionAmountChanged(expandingChanged: Boolean) {}
/**
* Called when the animator started by [scheduleDelayedDozeAmountAnimation] begins running
* after the start delay, or after it ends/is cancelled.
*/
fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
+
+ /** Called whenever a pulse has started or stopped expanding. */
+ fun onPulseExpandingChanged(isPulseExpanding: Boolean) {}
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
index cf03d1c5addc..2cc1403a80a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
@@ -62,8 +62,8 @@ constructor(
override val isPulseExpanding: Flow<Boolean> = conflatedCallbackFlow {
val listener =
object : NotificationWakeUpCoordinator.WakeUpListener {
- override fun onPulseExpansionChanged(expandingChanged: Boolean) {
- trySend(expandingChanged)
+ override fun onPulseExpandingChanged(isPulseExpanding: Boolean) {
+ trySend(isPulseExpanding)
}
}
trySend(wakeUpCoordinator.isPulseExpanding())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
index afc123faba79..319b49972bd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
@@ -20,6 +20,7 @@ import android.app.Notification
import android.content.Context
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.contentDescForNotification
import javax.inject.Inject
/**
@@ -36,6 +37,6 @@ class IconBuilder @Inject constructor(
}
fun getIconContentDescription(n: Notification): CharSequence {
- return StatusBarIconView.contentDescForNotification(context, n)
+ return contentDescForNotification(context, n)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 9e8654a66bde..76e5fd3bd4f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -26,15 +26,15 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
+import com.android.app.tracing.traceSection
import com.android.internal.statusbar.StatusBarIcon
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.InflationException
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.app.tracing.traceSection
import javax.inject.Inject
/**
@@ -112,15 +112,6 @@ class IconManager @Inject constructor(
aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
aodIcon.setIncreasedSize(true)
- // Construct the centered icon view.
- val centeredIcon = if (entry.sbn.notification.isMediaNotification) {
- iconBuilder.createIconView(entry).apply {
- scaleType = ImageView.ScaleType.CENTER_INSIDE
- }
- } else {
- null
- }
-
// Set the icon views' icons
val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
@@ -128,10 +119,7 @@ class IconManager @Inject constructor(
setIcon(entry, normalIconDescriptor, sbIcon)
setIcon(entry, sensitiveIconDescriptor, shelfIcon)
setIcon(entry, sensitiveIconDescriptor, aodIcon)
- if (centeredIcon != null) {
- setIcon(entry, normalIconDescriptor, centeredIcon)
- }
- entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, centeredIcon, entry.icons)
+ entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons)
} catch (e: InflationException) {
entry.icons = IconPack.buildEmptyPack(entry.icons)
throw e
@@ -153,24 +141,22 @@ class IconManager @Inject constructor(
entry.icons.peopleAvatarDescriptor = null
val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+ val notificationContentDescription = entry.sbn.notification?.let {
+ iconBuilder.getIconContentDescription(it)
+ }
entry.icons.statusBarIcon?.let {
- it.notification = entry.sbn
+ it.setNotification(entry.sbn, notificationContentDescription)
setIcon(entry, normalIconDescriptor, it)
}
entry.icons.shelfIcon?.let {
- it.notification = entry.sbn
+ it.setNotification(entry.sbn, notificationContentDescription)
setIcon(entry, normalIconDescriptor, it)
}
entry.icons.aodIcon?.let {
- it.notification = entry.sbn
- setIcon(entry, sensitiveIconDescriptor, it)
- }
-
- entry.icons.centeredIcon?.let {
- it.notification = entry.sbn
+ it.setNotification(entry.sbn, notificationContentDescription)
setIcon(entry, sensitiveIconDescriptor, it)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
index 054e381f096a..442c0978fd77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
@@ -31,7 +31,6 @@ public final class IconPack {
@Nullable private final StatusBarIconView mStatusBarIcon;
@Nullable private final StatusBarIconView mShelfIcon;
@Nullable private final StatusBarIconView mAodIcon;
- @Nullable private final StatusBarIconView mCenteredIcon;
@Nullable private StatusBarIcon mSmallIconDescriptor;
@Nullable private StatusBarIcon mPeopleAvatarDescriptor;
@@ -43,7 +42,7 @@ public final class IconPack {
* haven't been inflated yet or there was an error while inflating them).
*/
public static IconPack buildEmptyPack(@Nullable IconPack fromSource) {
- return new IconPack(false, null, null, null, null, fromSource);
+ return new IconPack(false, null, null, null, fromSource);
}
/**
@@ -53,9 +52,8 @@ public final class IconPack {
@NonNull StatusBarIconView statusBarIcon,
@NonNull StatusBarIconView shelfIcon,
@NonNull StatusBarIconView aodIcon,
- @Nullable StatusBarIconView centeredIcon,
@Nullable IconPack source) {
- return new IconPack(true, statusBarIcon, shelfIcon, aodIcon, centeredIcon, source);
+ return new IconPack(true, statusBarIcon, shelfIcon, aodIcon, source);
}
private IconPack(
@@ -63,12 +61,10 @@ public final class IconPack {
@Nullable StatusBarIconView statusBarIcon,
@Nullable StatusBarIconView shelfIcon,
@Nullable StatusBarIconView aodIcon,
- @Nullable StatusBarIconView centeredIcon,
@Nullable IconPack source) {
mAreIconsAvailable = areIconsAvailable;
mStatusBarIcon = statusBarIcon;
mShelfIcon = shelfIcon;
- mCenteredIcon = centeredIcon;
mAodIcon = aodIcon;
if (source != null) {
mIsImportantConversation = source.mIsImportantConversation;
@@ -91,11 +87,6 @@ public final class IconPack {
return mShelfIcon;
}
- @Nullable
- public StatusBarIconView getCenteredIcon() {
- return mCenteredIcon;
- }
-
/** The version of the icon that's shown when pulsing (in AOD). */
@Nullable
public StatusBarIconView getAodIcon() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt
index 2624363c7dec..db33f92c1848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt
@@ -17,12 +17,17 @@
package com.android.systemui.statusbar.notification.interruption
import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
/** Helper for reading or using the visual interruptions refactor flag state. */
object VisualInterruptionRefactor {
const val FLAG_NAME = Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
/** Whether the refactor is enabled */
@JvmStatic
inline val isEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/AsyncHybridViewInflation.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/AsyncHybridViewInflation.kt
index 24e7f05a9c1d..9556c2e3159c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/AsyncHybridViewInflation.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/AsyncHybridViewInflation.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row.shared
import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
/** Helper for reading or using the async hybrid view inflation flag state. */
@@ -24,6 +25,10 @@ import com.android.systemui.flags.RefactorFlagUtils
object AsyncHybridViewInflation {
const val FLAG_NAME = Flags.FLAG_NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
/** Is async hybrid (single-line) view inflation enabled */
@JvmStatic
inline val isEnabled
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 3bbdfd164ba7..85e63e58bc7a 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
@@ -94,9 +94,7 @@ import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.res.R;
-import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.TouchLogger;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
@@ -109,7 +107,6 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -146,8 +143,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private static final String TAG = "StackScroller";
private static final boolean SPEW = Log.isLoggable(TAG, Log.VERBOSE);
- // Delay in milli-seconds before shade closes for clear all.
- private static final int DELAY_BEFORE_SHADE_CLOSE = 200;
private boolean mShadeNeedsToClose = false;
@VisibleForTesting
@@ -319,7 +314,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
};
private NotificationStackScrollLogger mLogger;
- private NotificationsController mNotificationsController;
+ private Runnable mResetUserExpandedStatesRunnable;
private ActivityStarter mActivityStarter;
private final int[] mTempInt2 = new int[2];
private final HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
@@ -482,7 +477,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final Rect mTmpRect = new Rect();
private ClearAllListener mClearAllListener;
private ClearAllAnimationListener mClearAllAnimationListener;
- private ShadeController mShadeController;
+ private Runnable mClearAllFinishedWhilePanelExpandedRunnable;
private Consumer<Boolean> mOnStackYChanged;
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
@@ -4114,7 +4109,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mAmbientState.setExpansionChanging(false);
if (!mIsExpanded) {
resetScrollPosition();
- mNotificationsController.resetUserExpandedStates();
+ mResetUserExpandedStatesRunnable.run();
clearTemporaryViews();
clearUserLockedViews();
resetAllSwipeState();
@@ -4316,21 +4311,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mShadeNeedsToClose) {
mShadeNeedsToClose = false;
if (mIsExpanded) {
- collapseShadeDelayed();
+ mClearAllFinishedWhilePanelExpandedRunnable.run();
}
}
}
}
- private void collapseShadeDelayed() {
- postDelayed(
- () -> {
- mShadeController.animateCollapseShade(
- CommandQueue.FLAG_EXCLUDE_NONE);
- },
- DELAY_BEFORE_SHADE_CLOSE /* delayMillis */);
- }
-
private void clearHeadsUpDisappearRunning() {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
@@ -4747,8 +4733,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return max + getStackTranslation();
}
- public void setNotificationsController(NotificationsController notificationsController) {
- this.mNotificationsController = notificationsController;
+ public void setResetUserExpandedStatesRunnable(Runnable runnable) {
+ this.mResetUserExpandedStatesRunnable = runnable;
}
public void setActivityStarter(ActivityStarter activityStarter) {
@@ -5681,8 +5667,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mFooterClearAllListener = listener;
}
- void setShadeController(ShadeController shadeController) {
- mShadeController = shadeController;
+ void setClearAllFinishedWhilePanelExpandedRunnable(Runnable runnable) {
+ mClearAllFinishedWhilePanelExpandedRunnable = runnable;
}
/**
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 e6315fd159d5..d2fca8fca837 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
@@ -81,6 +81,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -152,6 +153,8 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private static final String TAG = "StackScrollerController";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
private static final String HIGH_PRIORITY = "high_priority";
+ /** Delay in milli-seconds before shade closes for clear all. */
+ private static final int DELAY_BEFORE_SHADE_CLOSE = 200;
private final boolean mAllowLongPress;
private final NotificationGutsManager mNotificationGutsManager;
@@ -754,7 +757,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mView.setController(this);
mView.setLogger(mLogger);
mView.setTouchHandler(new TouchHandler());
- mView.setNotificationsController(mNotificationsController);
+ mView.setResetUserExpandedStatesRunnable(mNotificationsController::resetUserExpandedStates);
mView.setActivityStarter(mActivityStarter);
mView.setClearAllAnimationListener(this::onAnimationEnd);
mView.setClearAllListener((selection) -> mUiEventLogger.log(
@@ -770,7 +773,11 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mView.setIsRemoteInputActive(active);
}
});
- mView.setShadeController(mShadeController);
+ mView.setClearAllFinishedWhilePanelExpandedRunnable(()-> {
+ final Runnable doCollapseRunnable = () ->
+ mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
+ mView.postDelayed(doCollapseRunnable, /* delayMillis = */ DELAY_BEFORE_SHADE_CLOSE);
+ });
mDumpManager.registerDumpable(mView);
mKeyguardBypassController.registerOnBypassStateChangedListener(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
index 4de3a7fd9ff1..3a650aa19eb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor
import android.graphics.Rect
import android.util.Log
import com.android.app.tracing.FlowTracing.traceEach
-import com.android.systemui.common.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
@@ -28,6 +28,7 @@ import com.android.systemui.util.kotlin.WithPrev
import com.android.systemui.util.kotlin.area
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.race
+import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.flow.Flow
@@ -38,7 +39,6 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withTimeout
-import javax.inject.Inject
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/DisplaySwitchNotificationsHiderFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/DisplaySwitchNotificationsHiderFlag.kt
index 98c173402109..3cd9a8f598a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/DisplaySwitchNotificationsHiderFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/DisplaySwitchNotificationsHiderFlag.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.stack.shared
import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
/** Helper for reading or using the DisplaySwitchNotificationsHider flag state. */
@@ -24,6 +25,10 @@ import com.android.systemui.flags.RefactorFlagUtils
object DisplaySwitchNotificationsHiderFlag {
const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HIDE_ON_DISPLAY_SWITCH
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
/** Is the hiding enabled? */
@JvmStatic
inline val isEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
index a62a1ed9f0c0..e79f3ff19031 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -608,7 +608,7 @@ public class LegacyNotificationIconAreaControllerImpl implements
}
@Override
- public void onPulseExpansionChanged(boolean expandingChanged) {
+ public void onPulseExpansionAmountChanged(boolean expandingChanged) {
if (expandingChanged) {
updateAodIconsVisibility(true /* animate */, false /* force */);
}
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 0dabafbdecb0..f34a44a5c4b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -284,11 +284,22 @@ public class NotificationIconContainer extends ViewGroup {
@Override
public String toString() {
- return "NotificationIconContainer("
- + "dozing=" + mDozing + " onLockScreen=" + mOnLockScreen
- + " overrideIconColor=" + mOverrideIconColor
- + " speedBumpIndex=" + mSpeedBumpIndex
- + " themedTextColorPrimary=#" + Integer.toHexString(mThemedTextColorPrimary) + ')';
+ if (NotificationIconContainerRefactor.isEnabled()) {
+ return super.toString()
+ + " {"
+ + " overrideIconColor=" + mOverrideIconColor
+ + ", maxIcons=" + mMaxIcons
+ + ", isStaticLayout=" + mIsStaticLayout
+ + ", themedTextColorPrimary=#" + Integer.toHexString(mThemedTextColorPrimary)
+ + " }";
+ } else {
+ return "NotificationIconContainer("
+ + "dozing=" + mDozing + " onLockScreen=" + mOnLockScreen
+ + " overrideIconColor=" + mOverrideIconColor
+ + " speedBumpIndex=" + mSpeedBumpIndex
+ + " themedTextColorPrimary=#" + Integer.toHexString(mThemedTextColorPrimary)
+ + ')';
+ }
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index a14e87cb8d7c..5475528152ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -30,6 +30,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settingslib.graph.SignalDrawable
+import com.android.systemui.Flags.statusBarStaticInoutIndicators
import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -41,6 +42,8 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding
import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewVisibilityHelper
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarViewBinderConstants.ALPHA_ACTIVE
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarViewBinderConstants.ALPHA_INACTIVE
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -190,10 +193,29 @@ object MobileIconBinder {
}
}
- // Set the activity indicators
- launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } }
+ if (statusBarStaticInoutIndicators()) {
+ // Set the opacity of the activity indicators
+ launch {
+ viewModel.activityInVisible.collect { visible ->
+ activityIn.imageAlpha =
+ (if (visible) ALPHA_ACTIVE else ALPHA_INACTIVE)
+ }
+ }
- launch { viewModel.activityOutVisible.collect { activityOut.isVisible = it } }
+ launch {
+ viewModel.activityOutVisible.collect { visible ->
+ activityOut.imageAlpha =
+ (if (visible) ALPHA_ACTIVE else ALPHA_INACTIVE)
+ }
+ }
+ } else {
+ // Set the activity indicators
+ launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } }
+
+ launch {
+ viewModel.activityOutVisible.collect { activityOut.isVisible = it }
+ }
+ }
launch {
viewModel.activityContainerVisible.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index d88c9efdac76..60c662da6aec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
+import com.android.systemui.Flags.statusBarStaticInoutIndicators
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.flags.FeatureFlagsClassic
@@ -199,7 +200,10 @@ class MobileIconViewModel(
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val activityContainerVisible: Flow<Boolean> =
- activity
- .map { it != null && (it.hasActivityIn || it.hasActivityOut) }
+ if (statusBarStaticInoutIndicators()) {
+ flowOf(constants.shouldShowActivityConfig)
+ } else {
+ activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) }
+ }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorKosmos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarViewBinderConstants.kt
index 94d9a72f0215..7348d5f83e11 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarViewBinderConstants.kt
@@ -13,13 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.systemui.statusbar.pipeline.shared.ui.binder
-package com.android.systemui.common.domain.interactor
-
-import com.android.systemui.common.ui.data.repository.configurationRepository
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-
-val Kosmos.configurationInteractor by Fixture {
- ConfigurationInteractorImpl(repository = configurationRepository)
+object StatusBarViewBinderConstants {
+ /** Opacity for the traffic indicator icons */
+ const val ALPHA_ACTIVE: Int = (1.0 * 255).toInt() // Fully opaque
+ const val ALPHA_INACTIVE: Int = (0.3 * 255).toInt() // 30% opaque -> 70% transparency
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 60055279055c..95124905e576 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -23,6 +23,7 @@ import android.widget.ImageView
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags.statusBarStaticInoutIndicators
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
@@ -30,6 +31,8 @@ import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding
import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewVisibilityHelper
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarViewBinderConstants.ALPHA_ACTIVE
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarViewBinderConstants.ALPHA_INACTIVE
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import kotlinx.coroutines.InternalCoroutinesApi
@@ -118,15 +121,36 @@ object WifiViewBinder {
launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } }
- launch {
- viewModel.isActivityInViewVisible.distinctUntilChanged().collect { visible ->
- activityInView.isVisible = visible
+ if (statusBarStaticInoutIndicators()) {
+ // Set the opacity of the activity indicators
+ launch {
+ viewModel.isActivityInViewVisible.distinctUntilChanged().collect { visible
+ ->
+ activityInView.imageAlpha =
+ (if (visible) ALPHA_ACTIVE else ALPHA_INACTIVE)
+ }
}
- }
- launch {
- viewModel.isActivityOutViewVisible.distinctUntilChanged().collect { visible ->
- activityOutView.isVisible = visible
+ launch {
+ viewModel.isActivityOutViewVisible.distinctUntilChanged().collect { visible
+ ->
+ activityOutView.imageAlpha =
+ (if (visible) ALPHA_ACTIVE else ALPHA_INACTIVE)
+ }
+ }
+ } else {
+ launch {
+ viewModel.isActivityInViewVisible.distinctUntilChanged().collect { visible
+ ->
+ activityInView.isVisible = visible
+ }
+ }
+
+ launch {
+ viewModel.isActivityOutViewVisible.distinctUntilChanged().collect { visible
+ ->
+ activityOutView.isVisible = visible
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index d099c8eb1c9d..fa243da8c00b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import android.content.Context
+import com.android.systemui.Flags.statusBarStaticInoutIndicators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
@@ -38,7 +39,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -101,42 +101,36 @@ constructor(
)
/** The wifi activity status. Null if we shouldn't display the activity status. */
- private val activity: Flow<DataActivityModel> = run {
- val default = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ private val activity: Flow<DataActivityModel?> =
if (!connectivityConstants.shouldShowActivityConfig) {
- flowOf(default)
+ flowOf(null)
} else {
combine(interactor.activity, interactor.ssid) { activity, ssid ->
when (ssid) {
- null -> default
+ null -> null
else -> activity
}
}
}
- .distinctUntilChanged()
- .logDiffsForTable(
- wifiTableLogBuffer,
- columnPrefix = "VM.activity",
- initialValue = default,
- )
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = default)
- }
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
override val isActivityInViewVisible: Flow<Boolean> =
activity
- .map { it.hasActivityIn }
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+ .map { it?.hasActivityIn ?: false }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val isActivityOutViewVisible: Flow<Boolean> =
activity
- .map { it.hasActivityOut }
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+ .map { it?.hasActivityOut ?: false }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val isActivityContainerVisible: Flow<Boolean> =
- combine(isActivityInViewVisible, isActivityOutViewVisible) { activityIn, activityOut ->
- activityIn || activityOut
+ if (statusBarStaticInoutIndicators()) {
+ flowOf(connectivityConstants.shouldShowActivityConfig)
+ } else {
+ activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) }
}
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
// TODO(b/238425913): It isn't ideal for the wifi icon to need to know about whether the
// airplane icon is visible. Instead, we should have a parent StatusBarSystemIconsViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index fa0cb5c939ab..66bf527f5047 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import android.app.AlarmManager;
+import android.app.Flags;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -191,7 +192,11 @@ public class ZenModeControllerImpl implements ZenModeController, Dumpable {
@Override
public void setZen(int zen, Uri conditionId, String reason) {
- mNoMan.setZenMode(zen, conditionId, reason);
+ if (Flags.modesApi()) {
+ mNoMan.setZenMode(zen, conditionId, reason, /* fromUser= */ true);
+ } else {
+ mNoMan.setZenMode(zen, conditionId, reason);
+ }
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 91219f02818c..0ee09390d03a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -151,6 +151,7 @@ open class AuthContainerViewTest : SysuiTestCase() {
)
udfpsOverlayInteractor =
UdfpsOverlayInteractor(
+ context,
authController,
selectedUserInteractor,
testScope.backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index bbf471c34cc7..6a686726b959 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -108,6 +108,7 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {
private fun createUdpfsOverlayInteractor() {
underTest =
UdfpsOverlayInteractor(
+ context,
authController,
selectedUserInteractor,
testScope.backgroundScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
index 863d9ebb41e4..fd5a584935ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.biometrics.ui.viewmodel
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -64,7 +65,8 @@ class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
init {
kosmos.deviceEntryIconViewModelTransitionsMock.add(testDeviceEntryIconTransition)
}
- val systemUIDialogManager = kosmos.systemUIDialogManager
+ private val systemUIDialogManager = kosmos.systemUIDialogManager
+ private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
private val underTest = kosmos.deviceEntryUdfpsTouchOverlayViewModel
@Captor
@@ -116,4 +118,16 @@ class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
assertThat(shouldHandleTouches).isTrue()
}
+
+ @Test
+ fun deviceEntryViewAlphaZero_alternateBouncerVisible_shouldHandleTouchesTrue() =
+ testScope.runTest {
+ val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
+
+ testDeviceEntryIconTransitionAlpha.value = 0f
+ runCurrent()
+
+ bouncerRepository.setAlternateVisible(true)
+ assertThat(shouldHandleTouches).isTrue()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 7475235cfeae..6170e0c2db1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -128,6 +128,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
)
udfpsOverlayInteractor =
UdfpsOverlayInteractor(
+ context,
authController,
selectedUserInteractor,
testScope.backgroundScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorTest.kt
deleted file mode 100644
index bfa36412ceb5..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorTest.kt
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.systemui.common.domain.interactor
-
-import android.content.res.Configuration
-import android.graphics.Rect
-import android.testing.AndroidTestingRunner
-import android.view.Surface.ROTATION_0
-import android.view.Surface.ROTATION_90
-import android.view.Surface.Rotation
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
-import com.android.systemui.coroutines.collectValues
-import com.android.systemui.statusbar.policy.FakeConfigurationController
-import com.android.systemui.util.mockito.mock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-open class ConfigurationInteractorTest : SysuiTestCase() {
-
- private val testScope = TestScope()
-
- private val configurationController = FakeConfigurationController()
- private val configurationRepository =
- ConfigurationRepositoryImpl(
- configurationController,
- context,
- testScope.backgroundScope,
- mock()
- )
-
- private lateinit var configuration: Configuration
- private lateinit var underTest: ConfigurationInteractor
-
- @Before
- fun setUp() {
- configuration = context.resources.configuration
-
- val testableResources = context.getOrCreateTestableResources()
- testableResources.overrideConfiguration(configuration)
-
- underTest = ConfigurationInteractorImpl(configurationRepository)
- }
-
- @Test
- fun maxBoundsChange_emitsMaxBoundsChange() =
- testScope.runTest {
- val values by collectValues(underTest.naturalMaxBounds)
-
- updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
- runCurrent()
- updateDisplay(width = DISPLAY_WIDTH * 2, height = DISPLAY_HEIGHT * 3)
- runCurrent()
-
- assertThat(values)
- .containsExactly(
- Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT),
- Rect(0, 0, DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 3),
- )
- .inOrder()
- }
-
- @Test
- fun maxBoundsSameOnConfigChange_doesNotEmitMaxBoundsChange() =
- testScope.runTest {
- val values by collectValues(underTest.naturalMaxBounds)
-
- updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
- runCurrent()
- updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
- runCurrent()
-
- assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT))
- }
-
- @Test
- fun firstMaxBoundsChange_emitsMaxBoundsChange() =
- testScope.runTest {
- val values by collectValues(underTest.naturalMaxBounds)
-
- updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
- runCurrent()
-
- assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT))
- }
-
- @Test
- fun displayRotatedButMaxBoundsTheSame_doesNotEmitNewMaxBoundsChange() =
- testScope.runTest {
- val values by collectValues(underTest.naturalMaxBounds)
-
- updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
- runCurrent()
- updateDisplay(width = DISPLAY_HEIGHT, height = DISPLAY_WIDTH, rotation = ROTATION_90)
- runCurrent()
-
- assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT))
- }
-
- private fun updateDisplay(
- width: Int = DISPLAY_WIDTH,
- height: Int = DISPLAY_HEIGHT,
- @Rotation rotation: Int = ROTATION_0
- ) {
- configuration.windowConfiguration.maxBounds.set(Rect(0, 0, width, height))
- configuration.windowConfiguration.displayRotation = rotation
-
- configurationController.onConfigurationChanged(configuration)
- }
-
- private companion object {
- private const val DISPLAY_WIDTH = 100
- private const val DISPLAY_HEIGHT = 200
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
index c5c02080fb80..9e007e970a92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
@@ -16,14 +16,19 @@
package com.android.systemui.common.ui.domain.interactor
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -35,13 +40,16 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidJUnit4::class)
class ConfigurationInteractorTest : SysuiTestCase() {
private lateinit var testScope: TestScope
+ private lateinit var configuration: Configuration
private lateinit var underTest: ConfigurationInteractor
private lateinit var configurationRepository: FakeConfigurationRepository
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
+ configuration = context.resources.configuration
+ val testableResources = context.getOrCreateTestableResources()
+ testableResources.overrideConfiguration(configuration)
configurationRepository = FakeConfigurationRepository()
testScope = TestScope()
underTest = ConfigurationInteractor(configurationRepository)
@@ -79,4 +87,79 @@ class ConfigurationInteractorTest : SysuiTestCase() {
assertThat(dimensionPixelSizes!![resourceId1]).isEqualTo(pixelSize1)
assertThat(dimensionPixelSizes!![resourceId2]).isEqualTo(pixelSize2)
}
+
+ @Test
+ fun maxBoundsChange_emitsMaxBoundsChange() =
+ testScope.runTest {
+ val values by collectValues(underTest.naturalMaxBounds)
+
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+ updateDisplay(width = DISPLAY_WIDTH * 2, height = DISPLAY_HEIGHT * 3)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT),
+ Rect(0, 0, DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 3),
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun maxBoundsSameOnConfigChange_doesNotEmitMaxBoundsChange() =
+ testScope.runTest {
+ val values by collectValues(underTest.naturalMaxBounds)
+
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+
+ assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT))
+ }
+
+ @Test
+ fun firstMaxBoundsChange_emitsMaxBoundsChange() =
+ testScope.runTest {
+ val values by collectValues(underTest.naturalMaxBounds)
+
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+
+ assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT))
+ }
+
+ @Test
+ fun displayRotatedButMaxBoundsTheSame_doesNotEmitNewMaxBoundsChange() =
+ testScope.runTest {
+ val values by collectValues(underTest.naturalMaxBounds)
+
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+ updateDisplay(
+ width = DISPLAY_HEIGHT,
+ height = DISPLAY_WIDTH,
+ rotation = Surface.ROTATION_90
+ )
+ runCurrent()
+
+ assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT))
+ }
+
+ private fun updateDisplay(
+ width: Int = DISPLAY_WIDTH,
+ height: Int = DISPLAY_HEIGHT,
+ @Surface.Rotation rotation: Int = Surface.ROTATION_0
+ ) {
+ configuration.windowConfiguration.maxBounds.set(Rect(0, 0, width, height))
+ configuration.windowConfiguration.displayRotation = rotation
+
+ configurationRepository.onConfigurationChange(configuration)
+ }
+
+ private companion object {
+ private const val DISPLAY_WIDTH = 100
+ private const val DISPLAY_HEIGHT = 200
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 22569e27d02a..c864704f6997 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -30,20 +30,15 @@ import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
-import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -87,11 +82,6 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() {
{ mock(DeviceEntryForegroundViewModel::class.java) },
{ mock(DeviceEntryBackgroundViewModel::class.java) },
{ falsingManager },
- { mock(AlternateBouncerViewModel::class.java) },
- { mock(NotificationShadeWindowController::class.java) },
- TestScope().backgroundScope,
- { mock(SwipeUpAnywhereGestureHandler::class.java) },
- { mock(TapGestureDetector::class.java) },
{ mock(VibratorHelper::class.java) },
)
}
@@ -177,21 +167,4 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() {
assertThat(constraint.layout.topMargin).isEqualTo(5)
assertThat(constraint.layout.startMargin).isEqualTo(4)
}
-
- @Test
- fun deviceEntryIconViewIsAboveAlternateBouncerView() {
- mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-
- val constraintLayout = ConstraintLayout(context, null)
- underTest.addViews(constraintLayout)
- assertThat(constraintLayout.childCount).isGreaterThan(0)
- val deviceEntryIconView = constraintLayout.getViewById(R.id.device_entry_icon_view)
- val alternateBouncerView = constraintLayout.getViewById(R.id.alternate_bouncer)
- assertThat(deviceEntryIconView).isNotNull()
- assertThat(alternateBouncerView).isNotNull()
-
- // device entry icon is above the alternate bouncer
- assertThat(constraintLayout.indexOfChild(deviceEntryIconView))
- .isGreaterThan(constraintLayout.indexOfChild(alternateBouncerView))
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
index 1f99303f10ed..0a464e6047d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
@@ -22,11 +22,11 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.res.R
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionViewState
@@ -37,6 +37,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.floatThat
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -183,10 +184,12 @@ class MediaViewControllerTest : SysuiTestCase() {
// detail widgets occupy [90, 100]
whenever(detailWidgetState.y).thenReturn(90F)
whenever(detailWidgetState.height).thenReturn(10)
+ whenever(detailWidgetState.alpha).thenReturn(1F)
// control widgets occupy [150, 170]
whenever(controlWidgetState.y).thenReturn(150F)
whenever(controlWidgetState.height).thenReturn(20)
- // in current beizer, when the progress reach 0.38, the result will be 0.5
+ whenever(controlWidgetState.alpha).thenReturn(1F)
+ // in current bezier, when the progress reach 0.38, the result will be 0.5
mediaViewController.squishViewState(mockViewState, 181.4F / 200F)
verify(controlWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
verify(detailWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
@@ -196,6 +199,34 @@ class MediaViewControllerTest : SysuiTestCase() {
}
@Test
+ fun testSquishViewState_applySquishFraction_toTransitionViewState_alpha_invisibleElements() {
+ whenever(mockViewState.copy()).thenReturn(mockCopiedState)
+ whenever(mockCopiedState.widgetStates)
+ .thenReturn(
+ mutableMapOf(
+ R.id.media_progress_bar to controlWidgetState,
+ R.id.header_artist to detailWidgetState
+ )
+ )
+ whenever(mockCopiedState.measureHeight).thenReturn(200)
+ // detail widgets occupy [90, 100]
+ whenever(detailWidgetState.y).thenReturn(90F)
+ whenever(detailWidgetState.height).thenReturn(10)
+ whenever(detailWidgetState.alpha).thenReturn(0F)
+ // control widgets occupy [150, 170]
+ whenever(controlWidgetState.y).thenReturn(150F)
+ whenever(controlWidgetState.height).thenReturn(20)
+ whenever(controlWidgetState.alpha).thenReturn(0F)
+ // Verify that alpha remains 0 throughout squishing
+ mediaViewController.squishViewState(mockViewState, 181.4F / 200F)
+ verify(controlWidgetState, never()).alpha = floatThat { it > 0 }
+ verify(detailWidgetState, never()).alpha = floatThat { it > 0 }
+ mediaViewController.squishViewState(mockViewState, 200F / 200F)
+ verify(controlWidgetState, never()).alpha = floatThat { it > 0 }
+ verify(detailWidgetState, never()).alpha = floatThat { it > 0 }
+ }
+
+ @Test
fun testSquishViewState_applySquishFraction_toTransitionViewState_alpha_forRecommendation() {
whenever(mockViewState.copy()).thenReturn(mockCopiedState)
whenever(mockCopiedState.widgetStates)
@@ -210,12 +241,15 @@ class MediaViewControllerTest : SysuiTestCase() {
// media container widgets occupy [20, 300]
whenever(mediaContainerWidgetState.y).thenReturn(20F)
whenever(mediaContainerWidgetState.height).thenReturn(280)
+ whenever(mediaContainerWidgetState.alpha).thenReturn(1F)
// media title widgets occupy [320, 330]
whenever(mediaTitleWidgetState.y).thenReturn(320F)
whenever(mediaTitleWidgetState.height).thenReturn(10)
+ whenever(mediaTitleWidgetState.alpha).thenReturn(1F)
// media subtitle widgets occupy [340, 350]
whenever(mediaSubTitleWidgetState.y).thenReturn(340F)
whenever(mediaSubTitleWidgetState.height).thenReturn(10)
+ whenever(mediaSubTitleWidgetState.alpha).thenReturn(1F)
// in current beizer, when the progress reach 0.38, the result will be 0.5
mediaViewController.squishViewState(mockViewState, 307.6F / 360F)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index b90ccc0e3d7e..994166172aff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,35 +16,23 @@
package com.android.systemui.scene.shared.flag
-import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.FakeFeatureFlagsImpl
-import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.compose.ComposeFacade
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.ReleasedFlag
-import com.android.systemui.flags.ResourceBooleanFlag
-import com.android.systemui.flags.UnreleasedFlag
+import com.android.systemui.flags.setFlagValue
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.media.controls.util.MediaInSceneContainerFlag
-import com.android.systemui.res.R
-import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth
+import org.junit.Assume
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
@SmallTest
-@RunWith(Parameterized::class)
-internal class SceneContainerFlagsTest(
- private val testCase: TestCase,
-) : SysuiTestCase() {
-
- @Rule @JvmField val setFlagsRule: SetFlagsRule = SetFlagsRule()
-
- private lateinit var underTest: SceneContainerFlags
+@RunWith(AndroidJUnit4::class)
+internal class SceneContainerFlagsTest : SysuiTestCase() {
@Before
fun setUp() {
@@ -52,83 +40,39 @@ internal class SceneContainerFlagsTest(
// Flags.SCENE_CONTAINER_ENABLED is no longer needed.
val field = Flags::class.java.getField("SCENE_CONTAINER_ENABLED")
field.isAccessible = true
- field.set(null, true)
-
- val featureFlags =
- FakeFeatureFlagsClassic().apply {
- SceneContainerFlagsImpl.classicFlagTokens.forEach { flagToken ->
- when (flagToken) {
- is ResourceBooleanFlag -> set(flagToken, testCase.areAllFlagsSet)
- is ReleasedFlag -> set(flagToken, testCase.areAllFlagsSet)
- is UnreleasedFlag -> set(flagToken, testCase.areAllFlagsSet)
- else -> error("Unsupported flag type ${flagToken.javaClass}")
- }
- }
- }
- // TODO(b/306421592): get the aconfig FeatureFlags from the SetFlagsRule.
- val aconfigFlags = FakeFeatureFlagsImpl()
+ field.set(null, true) // note: this does not work with multivalent tests
+ }
+ private fun setAconfigFlagsEnabled(enabled: Boolean) {
listOf(
- AconfigFlags.FLAG_SCENE_CONTAINER,
- AconfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
+ com.android.systemui.Flags.FLAG_SCENE_CONTAINER,
+ com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
KeyguardShadeMigrationNssl.FLAG_NAME,
MediaInSceneContainerFlag.FLAG_NAME,
)
- .forEach { flagToken ->
- setFlagsRule.enableFlags(flagToken)
- aconfigFlags.setFlag(flagToken, testCase.areAllFlagsSet)
- overrideResource(
- R.bool.config_sceneContainerFrameworkEnabled,
- testCase.isResourceConfigEnabled
- )
- }
-
- underTest =
- SceneContainerFlagsImpl(
- context = context,
- featureFlagsClassic = featureFlags,
- isComposeAvailable = testCase.isComposeAvailable,
- )
+ .forEach { flagName -> mSetFlagsRule.setFlagValue(flagName, enabled) }
}
@Test
- fun isEnabled() {
- assertThat(underTest.isEnabled()).isEqualTo(testCase.expectedEnabled)
+ fun isNotEnabled_withoutAconfigFlags() {
+ setAconfigFlagsEnabled(false)
+ Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(false)
+ Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(false)
}
- internal data class TestCase(
- val isComposeAvailable: Boolean,
- val areAllFlagsSet: Boolean,
- val isResourceConfigEnabled: Boolean,
- val expectedEnabled: Boolean,
- ) {
- override fun toString(): String {
- return "(compose=$isComposeAvailable + flags=$areAllFlagsSet) + XML" +
- " config=$isResourceConfigEnabled -> expected=$expectedEnabled"
- }
+ @Test
+ fun isEnabled_withAconfigFlags_withCompose() {
+ Assume.assumeTrue(ComposeFacade.isComposeAvailable())
+ setAconfigFlagsEnabled(true)
+ Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(true)
+ Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(true)
}
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun testCases() = buildList {
- repeat(8) { combination ->
- val isComposeAvailable = combination and 0b100 != 0
- val areAllFlagsSet = combination and 0b010 != 0
- val isResourceConfigEnabled = combination and 0b001 != 0
-
- val expectedEnabled =
- isComposeAvailable && areAllFlagsSet && isResourceConfigEnabled
-
- add(
- TestCase(
- isComposeAvailable = isComposeAvailable,
- areAllFlagsSet = areAllFlagsSet,
- expectedEnabled = expectedEnabled,
- isResourceConfigEnabled = isResourceConfigEnabled,
- )
- )
- }
- }
+ @Test
+ fun isNotEnabled_withAconfigFlags_withoutCompose() {
+ Assume.assumeFalse(ComposeFacade.isComposeAvailable())
+ setAconfigFlagsEnabled(true)
+ Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(false)
+ Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 9d997dae6836..86d8d54684ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -33,7 +33,6 @@ import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -68,9 +67,12 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteracto
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
-import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
@@ -79,6 +81,7 @@ import com.android.systemui.statusbar.NotificationInsetsController
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -93,6 +96,7 @@ import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
@@ -126,8 +130,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock private lateinit var centralSurfaces: CentralSurfaces
@Mock private lateinit var dozeServiceHost: DozeServiceHost
@Mock private lateinit var dozeScrimController: DozeScrimController
- @Mock private lateinit var backActionInteractor: BackActionInteractor
- @Mock private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var dockManager: DockManager
@Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
@Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
@@ -275,6 +277,12 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
primaryBouncerInteractor,
alternateBouncerInteractor,
mSelectedUserInteractor,
+ { mock (JavaAdapter::class.java )},
+ { mock(AlternateBouncerViewModel::class.java) },
+ { mock(FalsingManager::class.java) },
+ { mock(SwipeUpAnywhereGestureHandler::class.java) },
+ { mock(TapGestureDetector::class.java) },
+ { mock(AlternateBouncerUdfpsIconViewModel::class.java) },
)
underTest.setupExpandedStatusBar()
underTest.setDragDownHelper(dragDownHelper)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 9750f60c7b19..d9ff892145c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -55,8 +55,12 @@ import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepo
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
+import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
@@ -65,6 +69,7 @@ import com.android.systemui.statusbar.NotificationInsetsController
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -79,12 +84,14 @@ import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -99,6 +106,7 @@ import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+@ExperimentalCoroutinesApi
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
@@ -259,6 +267,12 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
primaryBouncerInteractor,
alternateBouncerInteractor,
mSelectedUserInteractor,
+ { Mockito.mock(JavaAdapter::class.java) },
+ { Mockito.mock(AlternateBouncerViewModel::class.java) },
+ { Mockito.mock(FalsingManager::class.java) },
+ { Mockito.mock(SwipeUpAnywhereGestureHandler::class.java) },
+ { Mockito.mock(TapGestureDetector::class.java) },
+ { Mockito.mock(AlternateBouncerUdfpsIconViewModel::class.java) },
)
controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index a6180ec8ea0e..f178046b665a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -54,6 +54,7 @@ import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationContentDescription;
import org.junit.Before;
import org.junit.Rule;
@@ -183,7 +184,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
.build();
// should be ApplicationInfo
n.extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, new Bundle());
- StatusBarIconView.contentDescForNotification(mContext, n);
+ NotificationContentDescription.contentDescForNotification(mContext, n);
// no crash, good
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
index f3094cdd4faf..170f651aed91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
@@ -80,7 +80,7 @@ class NotificationsKeyguardViewStateRepositoryTest : SysuiTestCase() {
assertThat(isPulseExpanding).isFalse()
withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) }
- .onPulseExpansionChanged(true)
+ .onPulseExpandingChanged(true)
runCurrent()
assertThat(isPulseExpanding).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index ba5ba2c31a42..ad7dee33b6d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -82,7 +82,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -116,7 +115,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
private AmbientState mAmbientState;
private TestableResources mTestableResources;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
- @Mock private NotificationsController mNotificationsController;
@Mock private SysuiStatusBarStateController mBarState;
@Mock private GroupMembershipManager mGroupMembershipManger;
@Mock private GroupExpansionManager mGroupExpansionManager;
@@ -193,7 +191,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
mNotificationStackSizeCalculator);
mStackScroller = spy(mStackScrollerInternal);
- mStackScroller.setNotificationsController(mNotificationsController);
+ mStackScroller.setResetUserExpandedStatesRunnable(()->{});
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNotificationRoundnessManager())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
index ac20683b4f49..4bfd7e3bef83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
@@ -23,8 +23,8 @@ import android.view.Surface.ROTATION_0
import android.view.Surface.ROTATION_90
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.coroutines.collectValues
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -81,7 +81,7 @@ open class HideNotificationsInteractorTest : SysuiTestCase() {
testScope.backgroundScope,
mock()
)
- private val configurationInteractor = ConfigurationInteractorImpl(configurationRepository)
+ private val configurationInteractor = ConfigurationInteractor(configurationRepository)
private lateinit var configuration: Configuration
private lateinit var underTest: HideNotificationsInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 21774aa2f8d7..c17a8ef62a4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -28,7 +28,6 @@ import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.TestMocksModule
import com.android.systemui.collectLastValue
-import com.android.systemui.common.domain.CommonDomainLayerModule
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -69,7 +68,6 @@ class NotificationListViewModelTest : SysuiTestCase() {
[
SysUITestModule::class,
ActivatableNotificationViewModelModule::class,
- CommonDomainLayerModule::class,
FooterViewModelModule::class,
HeadlessSystemUserModeModule::class,
UnfoldTransitionModule.Bindings::class,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index b0305a1b2056..44fa13283991 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
@@ -24,6 +26,7 @@ import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.TelephonyIcons.G
import com.android.settingslib.mobile.TelephonyIcons.THREE_G
import com.android.settingslib.mobile.TelephonyIcons.UNKNOWN
+import com.android.systemui.Flags.FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -557,7 +560,8 @@ class MobileIconViewModelTest : SysuiTestCase() {
}
@Test
- fun dataActivity_configOn_testIndicators() =
+ @DisableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
+ fun dataActivity_configOn_testIndicators_staticFlagOff() =
testScope.runTest {
// Create a new view model here so the constants are properly read
whenever(constants.shouldShowActivityConfig).thenReturn(true)
@@ -611,6 +615,61 @@ class MobileIconViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
+ fun dataActivity_configOn_testIndicators_staticFlagOn() =
+ testScope.runTest {
+ // Create a new view model here so the constants are properly read
+ whenever(constants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+
+ var inVisible: Boolean? = null
+ val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
+
+ var outVisible: Boolean? = null
+ val outJob = underTest.activityOutVisible.onEach { outVisible = it }.launchIn(this)
+
+ var containerVisible: Boolean? = null
+ val containerJob =
+ underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
+
+ repository.dataActivityDirection.value =
+ DataActivityModel(
+ hasActivityIn = true,
+ hasActivityOut = false,
+ )
+
+ yield()
+
+ assertThat(inVisible).isTrue()
+ assertThat(outVisible).isFalse()
+ assertThat(containerVisible).isTrue()
+
+ repository.dataActivityDirection.value =
+ DataActivityModel(
+ hasActivityIn = false,
+ hasActivityOut = true,
+ )
+
+ assertThat(inVisible).isFalse()
+ assertThat(outVisible).isTrue()
+ assertThat(containerVisible).isTrue()
+
+ repository.dataActivityDirection.value =
+ DataActivityModel(
+ hasActivityIn = false,
+ hasActivityOut = false,
+ )
+
+ assertThat(inVisible).isFalse()
+ assertThat(outVisible).isFalse()
+ assertThat(containerVisible).isTrue()
+
+ inJob.cancel()
+ outJob.cancel()
+ containerJob.cancel()
+ }
+
+ @Test
fun netTypeBackground_flagOff_isNull() =
testScope.runTest {
flags.set(NEW_NETWORK_SLICE_UI, false)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelKosmos.kt
index 54756590ee83..8b1a1d99978c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelKosmos.kt
@@ -16,14 +16,18 @@
package com.android.systemui.biometrics.ui.viewmodel
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.keyguard.ui.viewmodel.deviceEntryIconViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.statusbar.phone.systemUIDialogManager
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+@ExperimentalCoroutinesApi
val Kosmos.deviceEntryUdfpsTouchOverlayViewModel by Fixture {
DeviceEntryUdfpsTouchOverlayViewModel(
deviceEntryIconViewModel = deviceEntryIconViewModel,
+ alternateBouncerInteractor = alternateBouncerInteractor,
systemUIDialogManager = systemUIDialogManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
new file mode 100644
index 000000000000..86a4509b4d62
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.domain.interactor
+
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.fakeSystemClock
+
+var Kosmos.alternateBouncerInteractor by
+ Kosmos.Fixture {
+ AlternateBouncerInteractor(
+ statusBarStateController = statusBarStateController,
+ keyguardStateController = mock<KeyguardStateControllerImpl>(),
+ bouncerRepository = keyguardBouncerRepository,
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ biometricSettingsRepository = biometricSettingsRepository,
+ systemClock = fakeSystemClock,
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
+ scope = testScope.backgroundScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
index 6b38d6ea315a..050c2c9793f3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
@@ -37,7 +37,8 @@ class FakeConfigurationRepository @Inject constructor() : ConfigurationRepositor
MutableSharedFlow<Unit>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val onConfigurationChange: Flow<Unit> = _onConfigurationChange.asSharedFlow()
- private val _configurationChangeValues = MutableSharedFlow<Configuration>()
+ private val _configurationChangeValues =
+ MutableSharedFlow<Configuration>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val configurationValues: Flow<Configuration> =
_configurationChangeValues.asSharedFlow()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
index f4f05d85540e..f4f05d85540e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt
new file mode 100644
index 000000000000..d9c6e4f1f605
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.alternateBouncerToAodTransitionViewModel by Fixture {
+ AlternateBouncerToAodTransitionViewModel(
+ interactor = keyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ animationFlow = keyguardTransitionAnimationFlow,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt
new file mode 100644
index 000000000000..e4821b04fcef
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.alternateBouncerToGoneTransitionViewModel by Fixture {
+ AlternateBouncerToGoneTransitionViewModel(
+ interactor = keyguardTransitionInteractor,
+ bouncerToGoneFlows = bouncerToGoneFlows,
+ animationFlow = keyguardTransitionAnimationFlow,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
index 6557bcfd8337..67e9289f5d92 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
@@ -41,7 +41,6 @@ val Kosmos.deviceEntryIconViewModel by Fixture {
transitionInteractor = keyguardTransitionInteractor,
keyguardInteractor = keyguardInteractor,
viewModel = aodToLockscreenTransitionViewModel,
- shadeDependentFlows = shadeDependentFlows,
sceneContainerFlags = sceneContainerFlags,
keyguardViewController = { statusBarKeyguardViewManager },
deviceEntryInteractor = deviceEntryInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
new file mode 100644
index 000000000000..d70524840145
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTilePackageUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakePackageManagerAdapterFacade
+
+var Kosmos.tileSpec: TileSpec.CustomTileSpec by Kosmos.Fixture()
+
+val Kosmos.customTileStatePersister: FakeCustomTileStatePersister by
+ Kosmos.Fixture { FakeCustomTileStatePersister() }
+
+val Kosmos.customTileRepository: FakeCustomTileRepository by
+ Kosmos.Fixture {
+ FakeCustomTileRepository(
+ customTileStatePersister,
+ packageManagerAdapterFacade,
+ testScope.testScheduler,
+ )
+ }
+
+val Kosmos.customTileDefaultsRepository: FakeCustomTileDefaultsRepository by
+ Kosmos.Fixture { FakeCustomTileDefaultsRepository() }
+
+val Kosmos.customTilePackagesUpdatesRepository: FakeCustomTilePackageUpdatesRepository by
+ Kosmos.Fixture { FakeCustomTilePackageUpdatesRepository() }
+
+val Kosmos.packageManagerAdapterFacade: FakePackageManagerAdapterFacade by
+ Kosmos.Fixture { FakePackageManagerAdapterFacade(tileSpec) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
index ccf03911495f..ba803d8baef0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
@@ -19,21 +19,21 @@ package com.android.systemui.qs.tiles.impl.custom.data.repository
import android.os.UserHandle
import android.service.quicksettings.Tile
import com.android.systemui.qs.external.FakeCustomTileStatePersister
-import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
class FakeCustomTileRepository(
- tileSpec: TileSpec.CustomTileSpec,
customTileStatePersister: FakeCustomTileStatePersister,
+ private val packageManagerAdapterFacade: FakePackageManagerAdapterFacade,
testBackgroundContext: CoroutineContext,
) : CustomTileRepository {
private val realDelegate: CustomTileRepository =
CustomTileRepositoryImpl(
- tileSpec,
+ packageManagerAdapterFacade.tileSpec,
customTileStatePersister,
+ packageManagerAdapterFacade.packageManagerAdapter,
testBackgroundContext,
)
@@ -44,6 +44,10 @@ class FakeCustomTileRepository(
override fun getTile(user: UserHandle): Tile? = realDelegate.getTile(user)
+ override suspend fun isTileActive(): Boolean = realDelegate.isTileActive()
+
+ override suspend fun isTileToggleable(): Boolean = realDelegate.isTileToggleable()
+
override suspend fun updateWithTile(
user: UserHandle,
newTile: Tile,
@@ -55,4 +59,8 @@ class FakeCustomTileRepository(
defaults: CustomTileDefaults,
isPersistable: Boolean,
) = realDelegate.updateWithDefaults(user, defaults, isPersistable)
+
+ fun setTileActive(isActive: Boolean) = packageManagerAdapterFacade.setIsActive(isActive)
+
+ fun setTileToggleable(isActive: Boolean) = packageManagerAdapterFacade.setIsToggleable(isActive)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
new file mode 100644
index 000000000000..c9a7655b5571
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.data.repository
+
+import android.content.pm.ServiceInfo
+import android.os.Bundle
+import com.android.systemui.qs.external.PackageManagerAdapter
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+class FakePackageManagerAdapterFacade(
+ val tileSpec: TileSpec.CustomTileSpec,
+ val packageManagerAdapter: PackageManagerAdapter = mock {},
+) {
+
+ private var isToggleable: Boolean = false
+ private var isActive: Boolean = false
+
+ init {
+ whenever(packageManagerAdapter.getServiceInfo(eq(tileSpec.componentName), any()))
+ .thenAnswer {
+ ServiceInfo().apply {
+ metaData =
+ Bundle().apply {
+ putBoolean(
+ android.service.quicksettings.TileService.META_DATA_TOGGLEABLE_TILE,
+ isToggleable
+ )
+ putBoolean(
+ android.service.quicksettings.TileService.META_DATA_ACTIVE_TILE,
+ isActive
+ )
+ }
+ }
+ }
+ }
+
+ fun setIsActive(isActive: Boolean) {
+ this.isActive = isActive
+ }
+
+ fun setIsToggleable(isToggleable: Boolean) {
+ this.isToggleable = isToggleable
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorKosmos.kt
index baca8b2ef476..4232b274f94f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorKosmos.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
-import com.android.systemui.common.domain.interactor.configurationInteractor
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.power.domain.interactor.powerInteractor
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 13908f1732e1..7744fcaf032a 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -90,6 +90,7 @@ android.database.ContentObserver
android.database.Cursor
android.database.CursorIndexOutOfBoundsException
android.database.CursorJoiner
+android.database.CursorWindow
android.database.CursorWrapper
android.database.DataSetObservable
android.database.DataSetObserver
@@ -97,6 +98,9 @@ android.database.MatrixCursor
android.database.MatrixCursor$RowBuilder
android.database.MergeCursor
android.database.Observable
+android.database.SQLException
+android.database.sqlite.SQLiteClosable
+android.database.sqlite.SQLiteException
android.text.TextUtils
android.text.TextUtils$SimpleStringSplitter
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 77a5e3db2aba..a4b28967e3b2 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -72,6 +72,7 @@ import android.content.pm.ServiceInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -559,10 +560,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId);
} else if (provider.maskedBySuspendedPackage) {
showBadge = mUserManager.hasBadge(appUserId);
- final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
+ final UserPackage suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
appInfo.packageName, appUserId);
// TODO(b/281839596): don't rely on platform always meaning suspended by admin.
- if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+ if (suspendingPackage != null
+ && PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) {
onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
appUserId, true);
} else {
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
index 6940ffe40a51..f24c4cc59336 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,7 +70,7 @@ public final class VirtualCameraConversionUtil {
@Override
public void onProcessCaptureRequest(int streamId, int frameId) throws RemoteException {
- camera.onProcessCaptureRequest(streamId, frameId, /*metadata=*/ null);
+ camera.onProcessCaptureRequest(streamId, frameId);
}
@Override
diff --git a/services/core/Android.bp b/services/core/Android.bp
index f5a80d80f271..5111b08a1812 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -100,10 +100,9 @@ java_library_static {
name: "services.core.unboosted",
defaults: [
"platform_service_defaults",
- "android.hardware.power-java_static",
+ "android.hardware.power-java_shared",
],
srcs: [
- ":android.hardware.biometrics.face-V4-java-source",
":android.hardware.tv.hdmi.connection-V1-java-source",
":android.hardware.tv.hdmi.earc-V1-java-source",
":statslog-art-java-gen",
@@ -153,7 +152,7 @@ java_library_static {
],
static_libs: [
- "android.frameworks.location.altitude-V1-java", // AIDL
+ "android.frameworks.location.altitude-V2-java", // AIDL
"android.frameworks.vibrator-V1-java", // AIDL
"android.hardware.authsecret-V1.0-java",
"android.hardware.authsecret-V1-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 136692eb90d5..cac2efba1c89 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -289,11 +289,11 @@ public abstract class PackageManagerInternal {
*
* @param suspendedPackage The package that has been suspended.
* @param userId The user for which to check.
- * @return Name of the package that suspended the given package. Returns {@code null} if the
- * given package is not currently suspended and the platform package name - i.e.
- * {@code "android"} - if the package was suspended by a device admin.
+ * @return User id and package name of the package that suspended the given package. Returns
+ * {@code null} if the given package is not currently suspended and the platform package name
+ * - i.e. {@code "android"} - if the package was suspended by a device admin.
*/
- public abstract String getSuspendingPackage(String suspendedPackage, int userId);
+ public abstract UserPackage getSuspendingPackage(String suspendedPackage, int userId);
/**
* Suspend or unsuspend packages upon admin request.
@@ -312,13 +312,13 @@ public abstract class PackageManagerInternal {
* suspended application.
*
* @param suspendedPackage The package that has been suspended.
- * @param suspendingPackage
+ * @param suspendingPackage The package responsible for suspension.
* @param userId The user for which to check.
* @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
*/
@Nullable
public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
- String suspendingPackage, int userId);
+ UserPackage suspendingPackage, int userId);
/**
* Gets any distraction flags set via
@@ -1168,14 +1168,14 @@ public abstract class PackageManagerInternal {
public abstract void clearBlockUninstallForUser(@UserIdInt int userId);
/**
- * Unsuspends all packages suspended by the given package for the user.
+ * Unsuspends all packages suspended by an admin for the user.
*/
- public abstract void unsuspendForSuspendingPackage(String suspendingPackage, int userId);
+ public abstract void unsuspendAdminSuspendedPackages(int userId);
/**
- * Returns {@code true} if the package is suspending any packages for the user.
+ * Returns {@code true} if an admin is suspending any packages for the user.
*/
- public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
+ public abstract boolean isAdminSuspendingAnyPackages(int userId);
/**
* Register to listen for loading progress of an installed package.
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 329aac6f3a6a..9f279b1ba3fe 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -48,8 +48,6 @@ import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.am.DropboxRateLimiter;
-import com.android.server.os.TombstoneProtos;
-import com.android.server.os.TombstoneProtos.Tombstone;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -62,14 +60,11 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermissions;
-import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
/**
* Performs a number of miscellaneous, non-system-critical actions
@@ -337,12 +332,12 @@ public class BootReceiver extends BroadcastReceiver {
*
* @param ctx Context
* @param tombstone path to the tombstone
- * @param tombstoneProto the parsed proto tombstone
+ * @param proto whether the tombstone is stored as proto
* @param processName the name of the process corresponding to the tombstone
* @param tmpFileLock the lock for reading/writing tmp files
*/
public static void addTombstoneToDropBox(
- Context ctx, File tombstone, Tombstone tombstoneProto, String processName,
+ Context ctx, File tombstone, boolean proto, String processName,
ReentrantLock tmpFileLock) {
final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
if (db == null) {
@@ -352,33 +347,31 @@ public class BootReceiver extends BroadcastReceiver {
// Check if we should rate limit and abort early if needed.
DropboxRateLimiter.RateLimitResult rateLimitResult =
- sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE_PROTO_WITH_HEADERS, processName);
+ sDropboxRateLimiter.shouldRateLimit(
+ proto ? TAG_TOMBSTONE_PROTO_WITH_HEADERS : TAG_TOMBSTONE, processName);
if (rateLimitResult.shouldRateLimit()) return;
HashMap<String, Long> timestamps = readTimestamps();
try {
- // Remove the memory data from the proto.
- Tombstone tombstoneProtoWithoutMemory = removeMemoryFromTombstone(tombstoneProto);
-
- final byte[] tombstoneBytes = tombstoneProtoWithoutMemory.toByteArray();
-
- // Use JNI to call the c++ proto to text converter and add the headers to the tombstone.
- String tombstoneWithoutMemory = new StringBuilder(getBootHeadersToLogAndUpdate())
- .append(rateLimitResult.createHeader())
- .append(getTombstoneText(tombstoneBytes))
- .toString();
-
- // Add the tombstone without memory data to dropbox.
- db.addText(TAG_TOMBSTONE, tombstoneWithoutMemory);
-
- // Add the tombstone proto to dropbox.
- if (recordFileTimestamp(tombstone, timestamps)) {
- tmpFileLock.lock();
- try {
- addAugmentedProtoToDropbox(tombstone, tombstoneBytes, db, rateLimitResult);
- } finally {
- tmpFileLock.unlock();
+ if (proto) {
+ if (recordFileTimestamp(tombstone, timestamps)) {
+ // We need to attach the count indicating the number of dropped dropbox entries
+ // due to rate limiting. Do this by enclosing the proto tombsstone in a
+ // container proto that has the dropped entry count and the proto tombstone as
+ // bytes (to avoid the complexity of reading and writing nested protos).
+ tmpFileLock.lock();
+ try {
+ addAugmentedProtoToDropbox(tombstone, db, rateLimitResult);
+ } finally {
+ tmpFileLock.unlock();
+ }
}
+ } else {
+ // Add the header indicating how many events have been dropped due to rate limiting.
+ final String headers = getBootHeadersToLogAndUpdate()
+ + rateLimitResult.createHeader();
+ addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
+ TAG_TOMBSTONE);
}
} catch (IOException e) {
Slog.e(TAG, "Can't log tombstone", e);
@@ -387,8 +380,11 @@ public class BootReceiver extends BroadcastReceiver {
}
private static void addAugmentedProtoToDropbox(
- File tombstone, byte[] tombstoneBytes, DropBoxManager db,
+ File tombstone, DropBoxManager db,
DropboxRateLimiter.RateLimitResult rateLimitResult) throws IOException {
+ // Read the proto tombstone file as bytes.
+ final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());
+
final File tombstoneProtoWithHeaders = File.createTempFile(
tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
Files.setPosixFilePermissions(
@@ -421,8 +417,6 @@ public class BootReceiver extends BroadcastReceiver {
}
}
- private static native String getTombstoneText(byte[] tombstoneBytes);
-
private static void addLastkToDropBox(
DropBoxManager db, HashMap<String, Long> timestamps,
String headers, String footers, String filename, int maxSize,
@@ -440,31 +434,6 @@ public class BootReceiver extends BroadcastReceiver {
addFileWithFootersToDropBox(db, timestamps, headers, footers, filename, maxSize, tag);
}
- /** Removes memory information from the Tombstone proto. */
- @VisibleForTesting
- public static Tombstone removeMemoryFromTombstone(Tombstone tombstoneProto) {
- Tombstone.Builder tombstoneBuilder = tombstoneProto.toBuilder()
- .clearMemoryMappings()
- .clearThreads()
- .putAllThreads(tombstoneProto.getThreadsMap().entrySet()
- .stream()
- .map(BootReceiver::clearMemoryDump)
- .collect(Collectors.toMap(e->e.getKey(), e->e.getValue())));
-
- if (tombstoneProto.hasSignalInfo()) {
- tombstoneBuilder.setSignalInfo(
- tombstoneProto.getSignalInfo().toBuilder().clearFaultAdjacentMetadata());
- }
-
- return tombstoneBuilder.build();
- }
-
- private static AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread> clearMemoryDump(
- Map.Entry<Integer, TombstoneProtos.Thread> e) {
- return new AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread>(
- e.getKey(), e.getValue().toBuilder().clearMemoryDump().build());
- }
-
private static void addFileToDropBox(
DropBoxManager db, HashMap<String, Long> timestamps,
String headers, String filename, int maxSize, String tag) throws IOException {
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 2ba3a1d751d0..1d1e2d994787 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -30,7 +30,6 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
-import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.IVpnManager;
import android.net.Network;
@@ -89,8 +88,6 @@ public class VpnManagerService extends IVpnManager.Stub {
private final Context mUserAllContext;
private final Dependencies mDeps;
-
- private final ConnectivityManager mCm;
private final VpnProfileStore mVpnProfileStore;
private final INetworkManagementService mNMS;
private final INetd mNetd;
@@ -164,7 +161,6 @@ public class VpnManagerService extends IVpnManager.Stub {
mHandler = mHandlerThread.getThreadHandler();
mVpnProfileStore = mDeps.getVpnProfileStore();
mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
- mCm = mContext.getSystemService(ConnectivityManager.class);
mNMS = mDeps.getINetworkManagementService();
mNetd = mDeps.getNetd();
mUserManager = mContext.getSystemService(UserManager.class);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index df8f17ac9d7c..3ae5527153b7 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -70,7 +70,6 @@ import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMI
import static android.os.PowerExemptionManager.REASON_OPT_OUT_REQUESTED;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_PLATFORM_VPN;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_VPN;
-import static android.os.PowerExemptionManager.REASON_OTHER;
import static android.os.PowerExemptionManager.REASON_PACKAGE_INSTALLER;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
@@ -127,7 +126,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.Manifest;
-import android.Manifest.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1085,7 +1083,7 @@ public final class ActiveServices {
// Use that as a shortcut if possible to avoid having to recheck all the conditions.
final boolean whileInUseAllowsUiJobScheduling =
ActivityManagerService.doesReasonCodeAllowSchedulingUserInitiatedJobs(
- r.getFgsAllowWIU());
+ r.getFgsAllowWiu_forStart());
r.updateAllowUiJobScheduling(whileInUseAllowsUiJobScheduling
|| mAm.canScheduleUserInitiatedJobs(callingUid, callingPid, callingPackage));
} else {
@@ -2320,7 +2318,7 @@ public final class ActiveServices {
// If the foreground service is not started from TOP process, do not allow it to
// have while-in-use location/camera/microphone access.
- if (!r.isFgsAllowedWIU()) {
+ if (!r.isFgsAllowedWiu_forCapabilities()) {
Slog.w(TAG,
"Foreground service started from background can not have "
+ "location/camera/microphone access: service "
@@ -2436,7 +2434,7 @@ public final class ActiveServices {
// mAllowWhileInUsePermissionInFgs.
r.mAllowStartForegroundAtEntering = r.getFgsAllowStart();
r.mAllowWhileInUsePermissionInFgsAtEntering =
- r.isFgsAllowedWIU();
+ r.isFgsAllowedWiu_forCapabilities();
r.mStartForegroundCount++;
r.mFgsEnterTime = SystemClock.uptimeMillis();
if (!stopProcStatsOp) {
@@ -2632,7 +2630,7 @@ public final class ActiveServices {
policy.getForegroundServiceTypePolicyInfo(type, defaultToType);
final @ForegroundServicePolicyCheckCode int code = policy.checkForegroundServiceTypePolicy(
mAm.mContext, r.packageName, r.app.uid, r.app.getPid(),
- r.isFgsAllowedWIU(), policyInfo);
+ r.isFgsAllowedWiu_forStart(), policyInfo);
RuntimeException exception = null;
switch (code) {
case FGS_TYPE_POLICY_CHECK_DEPRECATED: {
@@ -7580,78 +7578,76 @@ public final class ActiveServices {
* @param callingUid caller app's uid.
* @param intent intent to start/bind service.
* @param r the service to start.
- * @param isBindService True if it's called from bindService().
+ * @param inBindService True if it's called from bindService().
* @param forBoundFgs set to true if it's called from Service.startForeground() for a
* service that's not started but bound.
- * @return true if allow, false otherwise.
*/
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
- BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean inBindService,
boolean forBoundFgs) {
- @ReasonCode int allowWIU;
+ @ReasonCode int allowWiu;
@ReasonCode int allowStart;
// If called from bindService(), do not update the actual fields, but instead
// keep it in a separate set of fields.
- if (isBindService) {
- allowWIU = r.mAllowWIUInBindService;
- allowStart = r.mAllowStartInBindService;
+ if (inBindService) {
+ allowWiu = r.mAllowWiu_inBindService;
+ allowStart = r.mAllowStart_inBindService;
} else {
- allowWIU = r.mAllowWhileInUsePermissionInFgsReasonNoBinding;
- allowStart = r.mAllowStartForegroundNoBinding;
+ allowWiu = r.mAllowWiu_noBinding;
+ allowStart = r.mAllowStart_noBinding;
}
- // Check DeviceConfig flag.
- if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
- if (allowWIU == REASON_DENIED) {
- // BGFGS start restrictions are disabled. We're allowing while-in-use permissions.
- // Note REASON_OTHER since there's no other suitable reason.
- allowWIU = REASON_OTHER;
- }
- }
-
- if ((allowWIU == REASON_DENIED)
- || (allowStart == REASON_DENIED)) {
+ if ((allowWiu == REASON_DENIED) || (allowStart == REASON_DENIED)) {
@ReasonCode final int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges);
// We store them to compare the old and new while-in-use logics to each other.
// (They're not used for any other purposes.)
- if (allowWIU == REASON_DENIED) {
- allowWIU = allowWhileInUse;
+ if (allowWiu == REASON_DENIED) {
+ allowWiu = allowWhileInUse;
}
if (allowStart == REASON_DENIED) {
allowStart = shouldAllowFgsStartForegroundWithBindingCheckLocked(
allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
- backgroundStartPrivileges, isBindService);
+ backgroundStartPrivileges, inBindService);
}
}
- if (isBindService) {
- r.mAllowWIUInBindService = allowWIU;
- r.mAllowStartInBindService = allowStart;
+ if (inBindService) {
+ r.mAllowWiu_inBindService = allowWiu;
+ r.mAllowStart_inBindService = allowStart;
} else {
if (!forBoundFgs) {
- // This is for "normal" situation.
- r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU;
- r.mAllowStartForegroundNoBinding = allowStart;
+ // This is for "normal" situation -- either:
+ // - in Context.start[Foreground]Service()
+ // - or, in Service.startForeground() on a started service.
+ r.mAllowWiu_noBinding = allowWiu;
+ r.mAllowStart_noBinding = allowStart;
} else {
- // This logic is only for logging, so we only update the "by-binding" fields.
- if (r.mAllowWIUByBindings == REASON_DENIED) {
- r.mAllowWIUByBindings = allowWIU;
+ // Service.startForeground() is called on a service that's not started, but bound.
+ // In this case, we set them to "byBindings", not "noBinding", because
+ // we don't want to use them when we calculate the "legacy" code.
+ //
+ // We don't want to set them to "no binding" codes, because on U-QPR1 and below,
+ // we didn't call setFgsRestrictionLocked() in the code path which sets
+ // forBoundFgs to true, and we wanted to preserve the original behavior in other
+ // places to compare the legacy and new logic.
+ if (r.mAllowWiu_byBindings == REASON_DENIED) {
+ r.mAllowWiu_byBindings = allowWiu;
}
- if (r.mAllowStartByBindings == REASON_DENIED) {
- r.mAllowStartByBindings = allowStart;
+ if (r.mAllowStart_byBindings == REASON_DENIED) {
+ r.mAllowStart_byBindings = allowStart;
}
}
// Also do a binding client check, unless called from bindService().
- if (r.mAllowWIUByBindings == REASON_DENIED) {
- r.mAllowWIUByBindings =
+ if (r.mAllowWiu_byBindings == REASON_DENIED) {
+ r.mAllowWiu_byBindings =
shouldAllowFgsWhileInUsePermissionByBindingsLocked(callingUid);
}
- if (r.mAllowStartByBindings == REASON_DENIED) {
- r.mAllowStartByBindings = r.mAllowWIUByBindings;
+ if (r.mAllowStart_byBindings == REASON_DENIED) {
+ r.mAllowStart_byBindings = r.mAllowWiu_byBindings;
}
}
}
@@ -7660,13 +7656,13 @@ public final class ActiveServices {
* Reset various while-in-use and BFSL related information.
*/
void resetFgsRestrictionLocked(ServiceRecord r) {
- r.clearFgsAllowWIU();
+ r.clearFgsAllowWiu();
r.clearFgsAllowStart();
r.mInfoAllowStartForeground = null;
r.mInfoTempFgsAllowListReason = null;
r.mLoggedInfoAllowStartForeground = false;
- r.updateAllowUiJobScheduling(r.isFgsAllowedWIU());
+ r.updateAllowUiJobScheduling(r.isFgsAllowedWiu_forStart());
}
boolean canStartForegroundServiceLocked(int callingPid, int callingUid, String callingPackage) {
@@ -8284,7 +8280,8 @@ public final class ActiveServices {
allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgsAtEntering;
fgsStartReasonCode = r.mAllowStartForegroundAtEntering;
} else {
- allowWhileInUsePermissionInFgs = r.isFgsAllowedWIU();
+ // TODO: Also log "forStart"
+ allowWhileInUsePermissionInFgs = r.isFgsAllowedWiu_forCapabilities();
fgsStartReasonCode = r.getFgsAllowStart();
}
final int callerTargetSdkVersion = r.mRecentCallerApplicationInfo != null
@@ -8323,12 +8320,12 @@ public final class ActiveServices {
mAm.getUidProcessCapabilityLocked(r.mRecentCallingUid),
0,
0,
- r.mAllowWhileInUsePermissionInFgsReasonNoBinding,
- r.mAllowWIUInBindService,
- r.mAllowWIUByBindings,
- r.mAllowStartForegroundNoBinding,
- r.mAllowStartInBindService,
- r.mAllowStartByBindings,
+ r.mAllowWiu_noBinding,
+ r.mAllowWiu_inBindService,
+ r.mAllowWiu_byBindings,
+ r.mAllowStart_noBinding,
+ r.mAllowStart_inBindService,
+ r.mAllowStart_byBindings,
fgsStartApi,
fgsRestrictionRecalculated);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index d0ab287785e3..626b70b51093 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -275,7 +275,7 @@ public final class CachedAppOptimizer {
@VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
@VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 10_000L;
- @VisibleForTesting static final boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = true;
+ @VisibleForTesting static final boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = false;
@VisibleForTesting static final boolean DEFAULT_FREEZER_BINDER_ENABLED = true;
@VisibleForTesting static final long DEFAULT_FREEZER_BINDER_DIVISOR = 4;
@VisibleForTesting static final int DEFAULT_FREEZER_BINDER_OFFSET = 500;
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index fc8ad6bc94e7..05303f6fef63 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -507,7 +507,8 @@ public class ForegroundServiceTypeLoggerModule {
r.appInfo.uid,
r.shortInstanceName,
fgsState, // FGS State
- r.isFgsAllowedWIU(), // allowWhileInUsePermissionInFgs
+ // TODO: Also log "forStart"
+ r.isFgsAllowedWiu_forCapabilities(), // allowWhileInUsePermissionInFgs
r.getFgsAllowStart(), // fgsStartReasonCode
r.appInfo.targetSdkVersion,
r.mRecentCallingUid,
@@ -535,12 +536,12 @@ public class ForegroundServiceTypeLoggerModule {
ActivityManager.PROCESS_CAPABILITY_NONE,
apiDurationBeforeFgsStart,
apiDurationAfterFgsEnd,
- r.mAllowWhileInUsePermissionInFgsReasonNoBinding,
- r.mAllowWIUInBindService,
- r.mAllowWIUByBindings,
- r.mAllowStartForegroundNoBinding,
- r.mAllowStartInBindService,
- r.mAllowStartByBindings,
+ r.mAllowWiu_noBinding,
+ r.mAllowWiu_inBindService,
+ r.mAllowWiu_byBindings,
+ r.mAllowStart_noBinding,
+ r.mAllowStart_inBindService,
+ r.mAllowStart_byBindings,
FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
false
);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 3424578a78d2..b507a604343e 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2254,7 +2254,7 @@ public class OomAdjuster {
if (s.isForeground) {
final int fgsType = s.foregroundServiceType;
- if (s.isFgsAllowedWIU()) {
+ if (s.isFgsAllowedWiu_forCapabilities()) {
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
!= 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 0fba73998bb9..08b129eeb8e5 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -37,6 +37,9 @@ import android.app.IApplicationThread;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -81,6 +84,37 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
// Maximum number of times it can fail during execution before giving up.
static final int MAX_DONE_EXECUTING_COUNT = 6;
+
+ // Compat IDs for the new FGS logic. For now, we just disable all of them.
+ // TODO: Enable them at some point, but only for V+ builds.
+
+ /**
+ * Compat ID to enable the new FGS start logic, for permission calculation used for
+ * per-FGS-type eligibility calculation.
+ * (See also android.app.ForegroundServiceTypePolicy)
+ */
+ @ChangeId
+ // @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Disabled
+ static final long USE_NEW_WIU_LOGIC_FOR_START = 311208629L;
+
+ /**
+ * Compat ID to enable the new FGS start logic, for capability calculation.
+ */
+ @ChangeId
+ // Always enabled
+ @Disabled
+ static final long USE_NEW_WIU_LOGIC_FOR_CAPABILITIES = 313677553L;
+
+ /**
+ * Compat ID to enable the new FGS start logic, for deciding whether to allow FGS start from
+ * the background.
+ */
+ @ChangeId
+ // @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Disabled
+ static final long USE_NEW_BFSL_LOGIC = 311208749L;
+
final ActivityManagerService ams;
final ComponentName name; // service component.
final ComponentName instanceName; // service component's per-instance name.
@@ -178,7 +212,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
// If it's not DENIED, while-in-use permissions are allowed.
// while-in-use permissions in FGS started from background might be restricted.
@PowerExemptionManager.ReasonCode
- int mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED;
+ int mAllowWiu_noBinding = REASON_DENIED;
// A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
boolean mAllowWhileInUsePermissionInFgsAtEntering;
@@ -208,7 +242,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
// allow the service becomes foreground service? Service started from background may not be
// allowed to become a foreground service.
@PowerExemptionManager.ReasonCode
- int mAllowStartForegroundNoBinding = REASON_DENIED;
+ int mAllowStart_noBinding = REASON_DENIED;
// A copy of mAllowStartForeground's value when the service is entering FGS state.
@PowerExemptionManager.ReasonCode
int mAllowStartForegroundAtEntering = REASON_DENIED;
@@ -220,72 +254,172 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
boolean mLoggedInfoAllowStartForeground;
@PowerExemptionManager.ReasonCode
- int mAllowWIUInBindService = REASON_DENIED;
+ int mAllowWiu_inBindService = REASON_DENIED;
+
+ @PowerExemptionManager.ReasonCode
+ int mAllowWiu_byBindings = REASON_DENIED;
+
+ @PowerExemptionManager.ReasonCode
+ int mAllowStart_inBindService = REASON_DENIED;
+
+ @PowerExemptionManager.ReasonCode
+ int mAllowStart_byBindings = REASON_DENIED;
+
+ /**
+ * Whether to use the new "while-in-use permission" logic for FGS start
+ */
+ private boolean useNewWiuLogic_forStart() {
+ return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
+ && CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_START, appInfo.uid);
+ }
+
+ /**
+ * Whether to use the new "while-in-use permission" logic for capabilities
+ */
+ private boolean useNewWiuLogic_forCapabilities() {
+ return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
+ && CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_CAPABILITIES, appInfo.uid);
+ }
+
+ /**
+ * Whether to use the new "FGS BG start exemption" logic.
+ */
+ private boolean useNewBfslLogic() {
+ return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
+ && CompatChanges.isChangeEnabled(USE_NEW_BFSL_LOGIC, appInfo.uid);
+ }
+
+
+ @PowerExemptionManager.ReasonCode
+ private int getFgsAllowWiu_legacy() {
+ // In the legacy mode (U-), we use mAllowWiu_inBindService and mAllowWiu_noBinding.
+ return reasonOr(
+ mAllowWiu_noBinding,
+ mAllowWiu_inBindService
+ );
+ }
@PowerExemptionManager.ReasonCode
- int mAllowWIUByBindings = REASON_DENIED;
+ private int getFgsAllowWiu_new() {
+ // In the new mode, use by-bindings instead of in-bind-service
+ return reasonOr(
+ mAllowWiu_noBinding,
+ mAllowWiu_byBindings
+ );
+ }
+ /**
+ * We use this logic for ForegroundServiceTypePolicy and UIDT eligibility check.
+ */
@PowerExemptionManager.ReasonCode
- int mAllowStartInBindService = REASON_DENIED;
+ int getFgsAllowWiu_forStart() {
+ if (useNewWiuLogic_forStart()) {
+ return getFgsAllowWiu_new();
+ } else {
+ return getFgsAllowWiu_legacy();
+ }
+ }
+ /**
+ * We use this logic for the capability calculation in oom-adjuster.
+ */
@PowerExemptionManager.ReasonCode
- int mAllowStartByBindings = REASON_DENIED;
+ int getFgsAllowWiu_forCapabilities() {
+ if (useNewWiuLogic_forCapabilities()) {
+ return getFgsAllowWiu_new();
+ } else {
+ // If alwaysUseNewLogicForWiuCapabilities() isn't set, just use the same logic as
+ // getFgsAllowWiu_forStart().
+ return getFgsAllowWiu_forStart();
+ }
+ }
+
+ /**
+ * We use this logic for ForegroundServiceTypePolicy and UIDT eligibility check.
+ */
+ boolean isFgsAllowedWiu_forStart() {
+ return getFgsAllowWiu_forStart() != REASON_DENIED;
+ }
+
+ /**
+ * We use this logic for the capability calculation in oom-adjuster.
+ */
+ boolean isFgsAllowedWiu_forCapabilities() {
+ return getFgsAllowWiu_forCapabilities() != REASON_DENIED;
+ }
@PowerExemptionManager.ReasonCode
- int getFgsAllowWIU() {
- return mAllowWhileInUsePermissionInFgsReasonNoBinding != REASON_DENIED
- ? mAllowWhileInUsePermissionInFgsReasonNoBinding
- : mAllowWIUInBindService;
+ private int getFgsAllowStart_legacy() {
+ // This is used for BFSL (background FGS launch) exemption.
+ // Originally -- on U-QPR1 and before -- we only used [in-bind-service] + [no-binding].
+ // This would exclude certain "valid" situations, so in U-QPR2, we started
+ // using [by-bindings] too.
+ return reasonOr(
+ mAllowStart_noBinding,
+ mAllowStart_inBindService,
+ mAllowStart_byBindings
+ );
}
- boolean isFgsAllowedWIU() {
- return getFgsAllowWIU() != REASON_DENIED;
+ @PowerExemptionManager.ReasonCode
+ private int getFgsAllowStart_new() {
+ // In the new mode, we don't use [in-bind-service].
+ return reasonOr(
+ mAllowStart_noBinding,
+ mAllowStart_byBindings
+ );
}
+ /**
+ * Calculate a BFSL exemption code.
+ */
@PowerExemptionManager.ReasonCode
int getFgsAllowStart() {
- return mAllowStartForegroundNoBinding != REASON_DENIED
- ? mAllowStartForegroundNoBinding
- : (mAllowStartByBindings != REASON_DENIED
- ? mAllowStartByBindings
- : mAllowStartInBindService);
+ if (useNewBfslLogic()) {
+ return getFgsAllowStart_new();
+ } else {
+ return getFgsAllowStart_legacy();
+ }
}
+ /**
+ * Return whether BFSL is allowed or not.
+ */
boolean isFgsAllowedStart() {
return getFgsAllowStart() != REASON_DENIED;
}
- void clearFgsAllowWIU() {
- mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED;
- mAllowWIUInBindService = REASON_DENIED;
- mAllowWIUByBindings = REASON_DENIED;
+ void clearFgsAllowWiu() {
+ mAllowWiu_noBinding = REASON_DENIED;
+ mAllowWiu_inBindService = REASON_DENIED;
+ mAllowWiu_byBindings = REASON_DENIED;
}
void clearFgsAllowStart() {
- mAllowStartForegroundNoBinding = REASON_DENIED;
- mAllowStartInBindService = REASON_DENIED;
- mAllowStartByBindings = REASON_DENIED;
+ mAllowStart_noBinding = REASON_DENIED;
+ mAllowStart_inBindService = REASON_DENIED;
+ mAllowStart_byBindings = REASON_DENIED;
}
@PowerExemptionManager.ReasonCode
- int reasonOr(@PowerExemptionManager.ReasonCode int first,
+ static int reasonOr(
+ @PowerExemptionManager.ReasonCode int first,
@PowerExemptionManager.ReasonCode int second) {
return first != REASON_DENIED ? first : second;
}
- boolean allowedChanged(@PowerExemptionManager.ReasonCode int first,
- @PowerExemptionManager.ReasonCode int second) {
- return (first == REASON_DENIED) != (second == REASON_DENIED);
+ @PowerExemptionManager.ReasonCode
+ static int reasonOr(
+ @PowerExemptionManager.ReasonCode int first,
+ @PowerExemptionManager.ReasonCode int second,
+ @PowerExemptionManager.ReasonCode int third) {
+ return first != REASON_DENIED ? first : reasonOr(second, third);
}
- String changeMessage(@PowerExemptionManager.ReasonCode int first,
- @PowerExemptionManager.ReasonCode int second) {
- return reasonOr(first, second) == REASON_DENIED ? "DENIED"
- : ("ALLOWED ("
- + reasonCodeToString(first)
- + "+"
- + reasonCodeToString(second)
- + ")");
+ boolean allowedChanged(
+ @PowerExemptionManager.ReasonCode int legacyCode,
+ @PowerExemptionManager.ReasonCode int newCode) {
+ return (legacyCode == REASON_DENIED) != (newCode == REASON_DENIED);
}
private String getFgsInfoForWtf() {
@@ -295,15 +429,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
}
void maybeLogFgsLogicChange() {
- final int origWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding,
- mAllowWIUInBindService);
- final int newWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding,
- mAllowWIUByBindings);
- final int origStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartInBindService);
- final int newStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartByBindings);
+ final int wiuLegacy = getFgsAllowWiu_legacy();
+ final int wiuNew = getFgsAllowWiu_new();
+
+ final int startLegacy = getFgsAllowStart_legacy();
+ final int startNew = getFgsAllowStart_new();
- final boolean wiuChanged = allowedChanged(origWiu, newWiu);
- final boolean startChanged = allowedChanged(origStart, newStart);
+ final boolean wiuChanged = allowedChanged(wiuLegacy, wiuNew);
+ final boolean startChanged = allowedChanged(startLegacy, startNew);
if (!wiuChanged && !startChanged) {
return;
@@ -311,15 +444,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
final String message = "FGS logic changed:"
+ (wiuChanged ? " [WIU changed]" : "")
+ (startChanged ? " [BFSL changed]" : "")
- + " OW:" // Orig-WIU
- + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding,
- mAllowWIUInBindService)
- + " NW:" // New-WIU
- + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding, mAllowWIUByBindings)
- + " OS:" // Orig-start
- + changeMessage(mAllowStartForegroundNoBinding, mAllowStartInBindService)
- + " NS:" // New-start
- + changeMessage(mAllowStartForegroundNoBinding, mAllowStartByBindings)
+ + " Orig WIU:"
+ + reasonCodeToString(wiuLegacy)
+ + " New WIU:"
+ + reasonCodeToString(wiuNew)
+ + " Orig BFSL:"
+ + reasonCodeToString(startLegacy)
+ + " New BFSL:"
+ + reasonCodeToString(startNew)
+ getFgsInfoForWtf();
Slog.wtf(TAG_SERVICE, message);
}
@@ -587,6 +719,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
proto.write(ServiceRecordProto.AppInfo.RES_DIR, appInfo.publicSourceDir);
}
proto.write(ServiceRecordProto.AppInfo.DATA_DIR, appInfo.dataDir);
+ proto.write(ServiceRecordProto.AppInfo.TARGET_SDK_VERSION, appInfo.targetSdkVersion);
proto.end(appInfoToken);
}
if (app != null) {
@@ -611,8 +744,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
ProtoUtils.toDuration(proto, ServiceRecordProto.LAST_ACTIVITY_TIME, lastActivity, now);
ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now);
proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg);
+
+ // TODO: Log "forStart" too.
proto.write(ServiceRecordProto.ALLOW_WHILE_IN_USE_PERMISSION_IN_FGS,
- isFgsAllowedWIU());
+ isFgsAllowedWiu_forCapabilities());
if (startRequested || delayedStop || lastStartId != 0) {
long startToken = proto.start(ServiceRecordProto.START);
@@ -691,15 +826,25 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
proto.end(shortFgsToken);
}
+ // TODO: Dump all the mAllowWiu* and mAllowStart* fields as needed.
+
proto.end(token);
}
+ void dumpReasonCode(PrintWriter pw, String prefix, String fieldName, int code) {
+ pw.print(prefix);
+ pw.print(fieldName);
+ pw.print("=");
+ pw.println(PowerExemptionManager.reasonCodeToString(code));
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("intent={");
pw.print(intent.getIntent().toShortString(false, true, false, false));
pw.println('}');
pw.print(prefix); pw.print("packageName="); pw.println(packageName);
pw.print(prefix); pw.print("processName="); pw.println(processName);
+ pw.print(prefix); pw.print("targetSdkVersion="); pw.println(appInfo.targetSdkVersion);
if (permission != null) {
pw.print(prefix); pw.print("permission="); pw.println(permission);
}
@@ -727,26 +872,38 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
pw.println(mBackgroundStartPrivilegesByStartMerged);
}
- pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason=");
- pw.println(PowerExemptionManager.reasonCodeToString(
- mAllowWhileInUsePermissionInFgsReasonNoBinding));
- pw.print(prefix); pw.print("mAllowWIUInBindService=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUInBindService));
- pw.print(prefix); pw.print("mAllowWIUByBindings=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUByBindings));
+ pw.print(prefix); pw.print("useNewWiuLogic_forCapabilities()=");
+ pw.println(useNewWiuLogic_forCapabilities());
+ pw.print(prefix); pw.print("useNewWiuLogic_forStart()=");
+ pw.println(useNewWiuLogic_forStart());
+ pw.print(prefix); pw.print("useNewBfslLogic()=");
+ pw.println(useNewBfslLogic());
+
+ dumpReasonCode(pw, prefix, "mAllowWiu_noBinding", mAllowWiu_noBinding);
+ dumpReasonCode(pw, prefix, "mAllowWiu_inBindService", mAllowWiu_inBindService);
+ dumpReasonCode(pw, prefix, "mAllowWiu_byBindings", mAllowWiu_byBindings);
+
+ dumpReasonCode(pw, prefix, "getFgsAllowWiu_legacy", getFgsAllowWiu_legacy());
+ dumpReasonCode(pw, prefix, "getFgsAllowWiu_new", getFgsAllowWiu_new());
+
+ dumpReasonCode(pw, prefix, "getFgsAllowWiu_forStart", getFgsAllowWiu_forStart());
+ dumpReasonCode(pw, prefix, "getFgsAllowWiu_forCapabilities",
+ getFgsAllowWiu_forCapabilities());
pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
pw.print(prefix); pw.print("recentCallingPackage=");
pw.println(mRecentCallingPackage);
pw.print(prefix); pw.print("recentCallingUid=");
pw.println(mRecentCallingUid);
- pw.print(prefix); pw.print("allowStartForeground=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartForegroundNoBinding));
- pw.print(prefix); pw.print("mAllowStartInBindService=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartInBindService));
- pw.print(prefix); pw.print("mAllowStartByBindings=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartByBindings));
+
+ dumpReasonCode(pw, prefix, "mAllowStart_noBinding", mAllowStart_noBinding);
+ dumpReasonCode(pw, prefix, "mAllowStart_inBindService", mAllowStart_inBindService);
+ dumpReasonCode(pw, prefix, "mAllowStart_byBindings", mAllowStart_byBindings);
+
+ dumpReasonCode(pw, prefix, "getFgsAllowStart_legacy", getFgsAllowStart_legacy());
+ dumpReasonCode(pw, prefix, "getFgsAllowStart_new", getFgsAllowStart_new());
+ dumpReasonCode(pw, prefix, "getFgsAllowStart", getFgsAllowStart());
pw.print(prefix); pw.print("startForegroundCount=");
pw.println(mStartForegroundCount);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a6b532cdef09..badd7f085e56 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -3050,8 +3050,8 @@ class UserController implements Handler.Callback {
/**
* Returns whether the given user requires credential entry at this time. This is used to
- * intercept activity launches for locked work apps due to work challenge being triggered
- * or when the profile user is yet to be unlocked.
+ * intercept activity launches for apps corresponding to locked profiles due to separate
+ * challenge being triggered or when the profile user is yet to be unlocked.
*/
protected boolean shouldConfirmCredentials(@UserIdInt int userId) {
if (getStartedUserState(userId) == null) {
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index d9e8dddddae4..654aebd89de2 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -28,3 +28,10 @@ flag {
description: "Restrict network access for certain applications in BFGS process state"
bug: "304347838"
}
+# Whether to use the new while-in-use / BG-FGS-start logic
+flag {
+ namespace: "backstage_power"
+ name: "new_fgs_restriction_logic"
+ description: "Enable the new FGS restriction logic"
+ bug: "276963716"
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 80917533cce1..dada72eb51d9 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -56,6 +56,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.PrintWriterPrinter;
import com.android.internal.annotations.GuardedBy;
@@ -1640,7 +1641,7 @@ public class AudioDeviceBroker {
return mBtHelper.getLeAudioDeviceGroupId(device);
}
- /*package*/ List<String> getLeAudioGroupAddresses(int groupId) {
+ /*package*/ List<Pair<String, String>> getLeAudioGroupAddresses(int groupId) {
return mBtHelper.getLeAudioGroupAddresses(groupId);
}
@@ -2651,9 +2652,9 @@ public class AudioDeviceBroker {
}
}
- List<String> getDeviceAddresses(AudioDeviceAttributes device) {
+ List<String> getDeviceIdentityAddresses(AudioDeviceAttributes device) {
synchronized (mDeviceStateLock) {
- return mDeviceInventory.getDeviceAddresses(device);
+ return mDeviceInventory.getDeviceIdentityAddresses(device);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index d138f24e7dc7..e05824a5f1ab 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -566,23 +566,40 @@ public class AudioDeviceInventory {
final int mDeviceType;
final @NonNull String mDeviceName;
final @NonNull String mDeviceAddress;
+ @NonNull String mDeviceIdentityAddress;
int mDeviceCodecFormat;
- @NonNull String mPeerDeviceAddress;
final int mGroupId;
+ @NonNull String mPeerDeviceAddress;
+ @NonNull String mPeerIdentityDeviceAddress;
/** Disabled operating modes for this device. Use a negative logic so that by default
* an empty list means all modes are allowed.
* See BluetoothAdapter.AUDIO_MODE_DUPLEX and BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY */
@NonNull ArraySet<String> mDisabledModes = new ArraySet(0);
- DeviceInfo(int deviceType, String deviceName, String deviceAddress,
- int deviceCodecFormat, String peerDeviceAddress, int groupId) {
+ DeviceInfo(int deviceType, String deviceName, String address,
+ String identityAddress, int codecFormat,
+ int groupId, String peerAddress, String peerIdentityAddress) {
mDeviceType = deviceType;
- mDeviceName = deviceName == null ? "" : deviceName;
- mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
- mDeviceCodecFormat = deviceCodecFormat;
- mPeerDeviceAddress = peerDeviceAddress == null ? "" : peerDeviceAddress;
+ mDeviceName = TextUtils.emptyIfNull(deviceName);
+ mDeviceAddress = TextUtils.emptyIfNull(address);
+ mDeviceIdentityAddress = TextUtils.emptyIfNull(identityAddress);
+ mDeviceCodecFormat = codecFormat;
mGroupId = groupId;
+ mPeerDeviceAddress = TextUtils.emptyIfNull(peerAddress);
+ mPeerIdentityDeviceAddress = TextUtils.emptyIfNull(peerIdentityAddress);
+ }
+
+ /** Constructor for all devices except A2DP sink and LE Audio */
+ DeviceInfo(int deviceType, String deviceName, String address) {
+ this(deviceType, deviceName, address, null, AudioSystem.AUDIO_FORMAT_DEFAULT);
+ }
+
+ /** Constructor for A2DP sink devices */
+ DeviceInfo(int deviceType, String deviceName, String address,
+ String identityAddress, int codecFormat) {
+ this(deviceType, deviceName, address, identityAddress, codecFormat,
+ BluetoothLeAudio.GROUP_ID_INVALID, null, null);
}
void setModeDisabled(String mode) {
@@ -601,25 +618,20 @@ public class AudioDeviceInventory {
return isModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX);
}
- DeviceInfo(int deviceType, String deviceName, String deviceAddress,
- int deviceCodecFormat) {
- this(deviceType, deviceName, deviceAddress, deviceCodecFormat,
- null, BluetoothLeAudio.GROUP_ID_INVALID);
- }
-
- DeviceInfo(int deviceType, String deviceName, String deviceAddress) {
- this(deviceType, deviceName, deviceAddress, AudioSystem.AUDIO_FORMAT_DEFAULT);
- }
-
@Override
public String toString() {
return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
+ " (" + AudioSystem.getDeviceName(mDeviceType)
+ ") name:" + mDeviceName
+ " addr:" + Utils.anonymizeBluetoothAddress(mDeviceType, mDeviceAddress)
+ + " identity addr:"
+ + Utils.anonymizeBluetoothAddress(mDeviceType, mDeviceIdentityAddress)
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
- + " peer addr:" + mPeerDeviceAddress
+ " group:" + mGroupId
+ + " peer addr:"
+ + Utils.anonymizeBluetoothAddress(mDeviceType, mPeerDeviceAddress)
+ + " peer identity addr:"
+ + Utils.anonymizeBluetoothAddress(mDeviceType, mPeerIdentityDeviceAddress)
+ " disabled modes: " + mDisabledModes + "]";
}
@@ -947,20 +959,44 @@ public class AudioDeviceInventory {
}
+ /**
+ * Goes over all connected LE Audio devices in the provided group ID and
+ * update:
+ * - the peer address according to the addres of other device in the same
+ * group (can also clear the peer address is not anymore in the group)
+ * - The dentity address if not yet set.
+ * LE Audio buds in a pair are in the same group.
+ * @param groupId the LE Audio group to update
+ */
/*package*/ void onUpdateLeAudioGroupAddresses(int groupId) {
synchronized (mDevicesLock) {
+ // <address, identy address>
+ List<Pair<String, String>> addresses = new ArrayList<>();
for (DeviceInfo di : mConnectedDevices.values()) {
if (di.mGroupId == groupId) {
- List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ if (addresses.isEmpty()) {
+ addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ }
if (di.mPeerDeviceAddress.equals("")) {
- for (String addr : addresses) {
- if (!addr.equals(di.mDeviceAddress)) {
- di.mPeerDeviceAddress = addr;
+ for (Pair<String, String> addr : addresses) {
+ if (!addr.first.equals(di.mDeviceAddress)) {
+ di.mPeerDeviceAddress = addr.first;
+ di.mPeerIdentityDeviceAddress = addr.second;
break;
}
}
- } else if (!addresses.contains(di.mPeerDeviceAddress)) {
+ } else if (!addresses.contains(
+ new Pair(di.mPeerDeviceAddress, di.mPeerIdentityDeviceAddress))) {
di.mPeerDeviceAddress = "";
+ di.mPeerIdentityDeviceAddress = "";
+ }
+ if (di.mDeviceIdentityAddress.equals("")) {
+ for (Pair<String, String> addr : addresses) {
+ if (addr.first.equals(di.mDeviceAddress)) {
+ di.mDeviceIdentityAddress = addr.second;
+ break;
+ }
+ }
}
}
}
@@ -1964,7 +2000,7 @@ public class AudioDeviceInventory {
mDeviceBroker.clearA2dpSuspended(true /* internalOnly */);
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
- address, codec);
+ address, btInfo.mDevice.getIdentityAddress(), codec);
final String diKey = di.getKey();
mConnectedDevices.put(diKey, di);
// on a connection always overwrite the device seen by AudioPolicy, see comment above when
@@ -2381,12 +2417,15 @@ public class AudioDeviceInventory {
// Find LE Group ID and peer headset address if available
final int groupId = mDeviceBroker.getLeAudioDeviceGroupId(btInfo.mDevice);
String peerAddress = "";
+ String peerIdentityAddress = "";
if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
- List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ List<Pair<String, String>> addresses =
+ mDeviceBroker.getLeAudioGroupAddresses(groupId);
if (addresses.size() > 1) {
- for (String addr : addresses) {
- if (!addr.equals(address)) {
- peerAddress = addr;
+ for (Pair<String, String> addr : addresses) {
+ if (!addr.first.equals(address)) {
+ peerAddress = addr.first;
+ peerIdentityAddress = addr.second;
break;
}
}
@@ -2420,8 +2459,9 @@ public class AudioDeviceInventory {
// Reset LEA suspend state each time a new sink is connected
mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
- new DeviceInfo(device, name, address, codec,
- peerAddress, groupId));
+ new DeviceInfo(device, name, address,
+ btInfo.mDevice.getIdentityAddress(), codec,
+ groupId, peerAddress, peerIdentityAddress));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
addAudioDeviceInInventoryIfNeeded(device, address, peerAddress,
@@ -2806,18 +2846,19 @@ public class AudioDeviceInventory {
mDevRoleCapturePresetDispatchers.finishBroadcast();
}
- List<String> getDeviceAddresses(AudioDeviceAttributes device) {
+ List<String> getDeviceIdentityAddresses(AudioDeviceAttributes device) {
List<String> addresses = new ArrayList<String>();
final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(),
device.getAddress());
synchronized (mDevicesLock) {
DeviceInfo di = mConnectedDevices.get(key);
if (di != null) {
- if (!di.mDeviceAddress.isEmpty()) {
- addresses.add(di.mDeviceAddress);
+ if (!di.mDeviceIdentityAddress.isEmpty()) {
+ addresses.add(di.mDeviceIdentityAddress);
}
- if (!di.mPeerDeviceAddress.isEmpty()) {
- addresses.add(di.mPeerDeviceAddress);
+ if (!di.mPeerIdentityDeviceAddress.isEmpty()
+ && !di.mPeerIdentityDeviceAddress.equals(di.mDeviceIdentityAddress)) {
+ addresses.add(di.mPeerIdentityDeviceAddress);
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0c782318fe89..56ae2bfcb38f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -13775,8 +13775,8 @@ public class AudioService extends IAudioService.Stub
return activeAssistantUids;
}
- List<String> getDeviceAddresses(AudioDeviceAttributes device) {
- return mDeviceBroker.getDeviceAddresses(device);
+ List<String> getDeviceIdentityAddresses(AudioDeviceAttributes device) {
+ return mDeviceBroker.getDeviceIdentityAddresses(device);
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 8075618be200..a818c30c2e28 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -58,6 +58,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.server.utils.EventLogger;
@@ -1077,8 +1078,8 @@ public class BtHelper {
return mLeAudio.getGroupId(device);
}
- /*package*/ List<String> getLeAudioGroupAddresses(int groupId) {
- List<String> addresses = new ArrayList<String>();
+ /*package*/ List<Pair<String, String>> getLeAudioGroupAddresses(int groupId) {
+ List<Pair<String, String>> addresses = new ArrayList<>();
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null || mLeAudio == null) {
return addresses;
@@ -1086,7 +1087,7 @@ public class BtHelper {
List<BluetoothDevice> activeDevices = adapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
for (BluetoothDevice device : activeDevices) {
if (device != null && mLeAudio.getGroupId(device) == groupId) {
- addresses.add(device.getAddress());
+ addresses.add(new Pair(device.getAddress(), device.getIdentityAddress()));
}
}
return addresses;
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 4f7f31dfa7dc..8428f127e77d 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -1639,8 +1639,7 @@ public class SpatializerHelper {
return -1;
}
final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0);
- List<String> deviceAddresses = mAudioService.getDeviceAddresses(currentDevice);
-
+ List<String> deviceAddresses = mAudioService.getDeviceIdentityAddresses(currentDevice);
// We limit only to Sensor.TYPE_HEAD_TRACKER here to avoid confusion
// with gaming sensors. (Note that Sensor.TYPE_ROTATION_VECTOR
// and Sensor.TYPE_GAME_ROTATION_VECTOR are supported internally by
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index dafea9a199fd..d5d8fd22314b 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -51,9 +51,13 @@ import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorPropertiesInternal;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintService;
@@ -122,8 +126,6 @@ public class AuthService extends SystemService {
/**
* Allows to test with various device sensor configurations.
- * @param context
- * @return
*/
@VisibleForTesting
public String[] getConfiguration(Context context) {
@@ -131,6 +133,30 @@ public class AuthService extends SystemService {
}
/**
+ * Allows to test with various device sensor configurations.
+ */
+ @VisibleForTesting
+ public String[] getFingerprintConfiguration(Context context) {
+ return getConfiguration(context);
+ }
+
+ /**
+ * Allows to test with various device sensor configurations.
+ */
+ @VisibleForTesting
+ public String[] getFaceConfiguration(Context context) {
+ return getConfiguration(context);
+ }
+
+ /**
+ * Allows to test with various device sensor configurations.
+ */
+ @VisibleForTesting
+ public String[] getIrisConfiguration(Context context) {
+ return getConfiguration(context);
+ }
+
+ /**
* Allows us to mock FingerprintService for testing
*/
@VisibleForTesting
@@ -173,6 +199,22 @@ public class AuthService extends SystemService {
}
return false;
}
+
+ /**
+ * Allows to test with various fingerprint aidl instances.
+ */
+ @VisibleForTesting
+ public String[] getFingerprintAidlInstances() {
+ return ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+ }
+
+ /**
+ * Allows to test with various face aidl instances.
+ */
+ @VisibleForTesting
+ public String[] getFaceAidlInstances() {
+ return ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
+ }
}
private final class AuthServiceImpl extends IAuthService.Stub {
@@ -717,12 +759,129 @@ public class AuthService extends SystemService {
hidlConfigs = null;
}
- // Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
- registerAuthenticators(hidlConfigs);
+ if (com.android.server.biometrics.Flags.deHidl()) {
+ Slog.d(TAG, "deHidl flag is on.");
+ registerAuthenticators();
+ } else {
+ // Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
+ registerAuthenticators(hidlConfigs);
+ }
mInjector.publishBinderService(this, mImpl);
}
+ private void registerAuthenticators() {
+ registerFingerprintSensors(mInjector.getFingerprintAidlInstances(),
+ mInjector.getFingerprintConfiguration(getContext()));
+ registerFaceSensors(mInjector.getFaceAidlInstances(),
+ mInjector.getFaceConfiguration(getContext()));
+ registerIrisSensors(mInjector.getIrisConfiguration(getContext()));
+ }
+
+ private void registerIrisSensors(String[] hidlConfigStrings) {
+ final SensorConfig[] hidlConfigs;
+ if (!mInjector.isHidlDisabled(getContext())) {
+ final int firstApiLevel = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0);
+ final int apiLevel = SystemProperties.getInt(SYSPROP_API_LEVEL, firstApiLevel);
+ if (hidlConfigStrings.length == 0 && apiLevel == Build.VERSION_CODES.R) {
+ // For backwards compatibility with R where biometrics could work without being
+ // configured in config_biometric_sensors. In the absence of a vendor provided
+ // configuration, we assume the weakest biometric strength (i.e. convenience).
+ Slog.w(TAG, "Found R vendor partition without config_biometric_sensors");
+ hidlConfigStrings = generateRSdkCompatibleConfiguration();
+ }
+ hidlConfigs = new SensorConfig[hidlConfigStrings.length];
+ for (int i = 0; i < hidlConfigStrings.length; ++i) {
+ hidlConfigs[i] = new SensorConfig(hidlConfigStrings[i]);
+ }
+ } else {
+ hidlConfigs = null;
+ }
+
+ final List<SensorPropertiesInternal> hidlIrisSensors = new ArrayList<>();
+
+ if (hidlConfigs != null) {
+ for (SensorConfig sensor : hidlConfigs) {
+ switch (sensor.modality) {
+ case TYPE_IRIS:
+ hidlIrisSensors.add(getHidlIrisSensorProps(sensor.id, sensor.strength));
+ break;
+
+ default:
+ Slog.e(TAG, "Unknown modality: " + sensor.modality);
+ }
+ }
+ }
+
+ final IIrisService irisService = mInjector.getIrisService();
+ if (irisService != null) {
+ try {
+ irisService.registerAuthenticators(hidlIrisSensors);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException when registering iris authenticators", e);
+ }
+ } else if (hidlIrisSensors.size() > 0) {
+ Slog.e(TAG, "HIDL iris configuration exists, but IrisService is null.");
+ }
+ }
+
+ private void registerFaceSensors(final String[] faceAidlInstances,
+ final String[] hidlConfigStrings) {
+ final FaceSensorConfigurations mFaceSensorConfigurations =
+ new FaceSensorConfigurations(hidlConfigStrings != null
+ && hidlConfigStrings.length > 0);
+
+ if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
+ mFaceSensorConfigurations.addHidlConfigs(
+ hidlConfigStrings, getContext());
+ }
+
+ if (faceAidlInstances != null && faceAidlInstances.length > 0) {
+ mFaceSensorConfigurations.addAidlConfigs(faceAidlInstances,
+ name -> IFace.Stub.asInterface(Binder.allowBlocking(
+ ServiceManager.waitForDeclaredService(name))));
+ }
+
+ final IFaceService faceService = mInjector.getFaceService();
+ if (faceService != null) {
+ try {
+ faceService.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException when registering face authenticators", e);
+ }
+ } else if (mFaceSensorConfigurations.hasSensorConfigurations()) {
+ Slog.e(TAG, "Face configuration exists, but FaceService is null.");
+ }
+ }
+
+ private void registerFingerprintSensors(final String[] fingerprintAidlInstances,
+ final String[] hidlConfigStrings) {
+ final FingerprintSensorConfigurations mFingerprintSensorConfigurations =
+ new FingerprintSensorConfigurations(!(hidlConfigStrings != null
+ && hidlConfigStrings.length > 0));
+
+ if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
+ mFingerprintSensorConfigurations.addHidlSensors(hidlConfigStrings, getContext());
+ }
+
+ if (fingerprintAidlInstances != null && fingerprintAidlInstances.length > 0) {
+ mFingerprintSensorConfigurations.addAidlSensors(fingerprintAidlInstances,
+ name -> IFingerprint.Stub.asInterface(Binder.allowBlocking(
+ ServiceManager.waitForDeclaredService(name))));
+ }
+
+ final IFingerprintService fingerprintService = mInjector.getFingerprintService();
+ if (fingerprintService != null) {
+ try {
+ fingerprintService.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException when registering fingerprint authenticators", e);
+ }
+ } else if (mFingerprintSensorConfigurations.hasSensorConfigurations()) {
+ Slog.e(TAG, "Fingerprint configuration exists, but FingerprintService is null.");
+ }
+ }
+
/**
* Generates an array of string configs with entries that correspond to the biometric features
* declared on the device. Returns an empty array if no biometric features are declared.
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 037ea38a2d17..89b638be3500 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -202,7 +202,7 @@ public class BiometricScheduler {
};
@VisibleForTesting
- BiometricScheduler(@NonNull String tag,
+ public BiometricScheduler(@NonNull String tag,
@NonNull Handler handler,
@SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
index 57feedc0e68e..0c3dfa7b3b84 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -387,6 +387,11 @@ public class BiometricSchedulerOperation {
return isAuthentication || isDetection;
}
+ /** If this operation is {@link StartUserClient}. */
+ public boolean isStartUserOperation() {
+ return mClientMonitor instanceof StartUserClient<?, ?>;
+ }
+
/** If this operation performs acquisition {@link AcquisitionClient}. */
public boolean isAcquisitionOperation() {
return mClientMonitor instanceof AcquisitionClient;
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 7f8f38f3e9d2..6daaad1baf83 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -54,8 +54,6 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
// is all done internally.
super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
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/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index 80754702415a..3753bbdba276 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -135,7 +135,7 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
final int currentUserId = mCurrentUserRetriever.getCurrentUserId();
final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
- if (nextUserId == currentUserId) {
+ if (nextUserId == currentUserId || mPendingOperations.getFirst().isStartUserOperation()) {
super.startNextOperationIfIdle();
} else if (currentUserId == UserHandle.USER_NULL) {
final BaseClientMonitor startClient =
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 578d9dc2aede..6af223b3660a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -36,6 +36,7 @@ import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.FaceServiceReceiver;
import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
@@ -55,6 +56,7 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
@@ -76,6 +78,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.function.Supplier;
/**
* A service to manage multiple clients that want to access the face HAL API.
@@ -86,7 +89,7 @@ public class FaceService extends SystemService {
protected static final String TAG = "FaceService";
- private final FaceServiceWrapper mServiceWrapper;
+ @VisibleForTesting final FaceServiceWrapper mServiceWrapper;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final LockPatternUtils mLockPatternUtils;
@NonNull
@@ -94,11 +97,18 @@ public class FaceService extends SystemService {
@NonNull
private final BiometricStateCallback<ServiceProvider, FaceSensorPropertiesInternal>
mBiometricStateCallback;
+ @NonNull
+ private final FaceProviderFunction mFaceProviderFunction;
+
+ interface FaceProviderFunction {
+ FaceProvider getFaceProvider(Pair<String, SensorProps[]> filteredSensorProps,
+ boolean resetLockoutRequiresChallenge);
+ }
/**
* Receives the incoming binder calls from FaceManager.
*/
- private final class FaceServiceWrapper extends IFaceService.Stub {
+ @VisibleForTesting final class FaceServiceWrapper extends IFaceService.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@@ -672,7 +682,8 @@ public class FaceService extends SystemService {
final SensorProps[] props = face.getSensorProps();
final FaceProvider provider = new FaceProvider(getContext(),
mBiometricStateCallback, props, instance, mLockoutResetDispatcher,
- BiometricContext.getInstance(getContext()));
+ BiometricContext.getInstance(getContext()),
+ false /* resetLockoutRequiresChallenge */);
providers.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
@@ -704,6 +715,55 @@ public class FaceService extends SystemService {
});
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ public void registerAuthenticatorsLegacy(
+ FaceSensorConfigurations faceSensorConfigurations) {
+ super.registerAuthenticatorsLegacy_enforcePermission();
+
+ if (!faceSensorConfigurations.hasSensorConfigurations()) {
+ Slog.d(TAG, "No face sensors to register.");
+ return;
+ }
+ mRegistry.registerAll(() -> getProviders(faceSensorConfigurations));
+ }
+
+ private List<ServiceProvider> getProviders(
+ FaceSensorConfigurations faceSensorConfigurations) {
+ final List<ServiceProvider> providers = new ArrayList<>();
+ final Pair<String, SensorProps[]> filteredSensorProps =
+ filterAvailableHalInstances(faceSensorConfigurations);
+ providers.add(mFaceProviderFunction.getFaceProvider(filteredSensorProps,
+ faceSensorConfigurations.getResetLockoutRequiresChallenge()));
+ return providers;
+ }
+
+ @NonNull
+ private Pair<String, SensorProps[]> filterAvailableHalInstances(
+ FaceSensorConfigurations faceSensorConfigurations) {
+ Pair<String, SensorProps[]> finalSensorPair = faceSensorConfigurations.getSensorPair();
+
+ if (faceSensorConfigurations.isSingleSensorConfigurationPresent()) {
+ return finalSensorPair;
+ }
+
+ final Pair<String, SensorProps[]> virtualSensorProps = faceSensorConfigurations
+ .getSensorPairForInstance("virtual");
+
+ if (Utils.isVirtualEnabled(getContext())) {
+ if (virtualSensorProps != null) {
+ return virtualSensorProps;
+ } else {
+ Slog.e(TAG, "Could not find virtual interface while it is enabled");
+ return finalSensorPair;
+ }
+ } else {
+ if (virtualSensorProps != null) {
+ return faceSensorConfigurations.getSensorPairNotForInstance("virtual");
+ }
+ }
+ return finalSensorPair;
+ }
+
private Pair<List<FaceSensorPropertiesInternal>, List<String>>
filterAvailableHalInstances(
@NonNull List<FaceSensorPropertiesInternal> hidlInstances,
@@ -752,20 +812,36 @@ public class FaceService extends SystemService {
}
public FaceService(Context context) {
+ this(context, null /* faceProviderFunction */, () -> IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
+ }
+
+ @VisibleForTesting FaceService(Context context, FaceProviderFunction faceProviderFunction,
+ Supplier<IBiometricService> biometricServiceSupplier) {
super(context);
mServiceWrapper = new FaceServiceWrapper();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
- mRegistry = new FaceServiceRegistry(mServiceWrapper,
- () -> IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
+ mRegistry = new FaceServiceRegistry(mServiceWrapper, biometricServiceSupplier);
mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
@Override
public void onAllAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) {
mBiometricStateCallback.start(mRegistry.getProviders());
}
});
+
+ if (Flags.deHidl()) {
+ mFaceProviderFunction = faceProviderFunction != null ? faceProviderFunction :
+ ((filteredSensorProps, resetLockoutRequiresChallenge) -> new FaceProvider(
+ getContext(), mBiometricStateCallback,
+ filteredSensorProps.second,
+ filteredSensorProps.first, mLockoutResetDispatcher,
+ BiometricContext.getInstance(getContext()),
+ resetLockoutRequiresChallenge));
+ } else {
+ mFaceProviderFunction = ((filteredSensorProps, resetLockoutRequiresChallenge) -> null);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java
index e5d4a635876d..ef2be790ed34 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java
@@ -24,7 +24,8 @@ import com.android.server.biometrics.sensors.LockoutTracker;
* the user changes.
*/
public class LockoutHalImpl implements LockoutTracker {
- private @LockoutMode int mCurrentUserLockoutMode;
+ @LockoutMode
+ private int mCurrentUserLockoutMode;
@Override
public int getLockoutModeForUser(int userId) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
index 57f5b34c197a..098be2120e03 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
@@ -27,6 +27,7 @@ import android.hardware.face.Face;
import android.hardware.keymaster.HardwareAuthToken;
import android.util.Slog;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
@@ -59,6 +60,20 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
void onHardwareUnavailable();
}
+ /**
+ * Interface to send results to the AidlResponseHandler's owner.
+ */
+ public interface AidlResponseHandlerCallback {
+ /**
+ * Invoked when enrollment is successful.
+ */
+ void onEnrollSuccess();
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
private static final String TAG = "AidlResponseHandler";
@NonNull
@@ -68,7 +83,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
private final int mSensorId;
private final int mUserId;
@NonNull
- private final LockoutTracker mLockoutCache;
+ private final LockoutTracker mLockoutTracker;
@NonNull
private final LockoutResetDispatcher mLockoutResetDispatcher;
@@ -76,6 +91,8 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
private final HardwareUnavailableCallback mHardwareUnavailableCallback;
+ @NonNull
+ private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
public AidlResponseHandler(@NonNull Context context,
@NonNull BiometricScheduler scheduler, int sensorId, int userId,
@@ -83,14 +100,33 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
@NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
+ this(context, scheduler, sensorId, userId, lockoutTracker, lockoutResetDispatcher,
+ authSessionCoordinator, hardwareUnavailableCallback,
+ new AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {}
+
+ @Override
+ public void onHardwareUnavailable() {}
+ });
+ }
+
+ public AidlResponseHandler(@NonNull Context context,
+ @NonNull BiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutTracker lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @NonNull HardwareUnavailableCallback hardwareUnavailableCallback,
+ @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
mContext = context;
mScheduler = scheduler;
mSensorId = sensorId;
mUserId = userId;
- mLockoutCache = lockoutTracker;
+ mLockoutTracker = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mAuthSessionCoordinator = authSessionCoordinator;
mHardwareUnavailableCallback = hardwareUnavailableCallback;
+ mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
}
@Override
@@ -106,13 +142,13 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
@Override
public void onChallengeGenerated(long challenge) {
handleResponse(FaceGenerateChallengeClient.class, (c) -> c.onChallengeGenerated(mSensorId,
- mUserId, challenge), null);
+ mUserId, challenge));
}
@Override
public void onChallengeRevoked(long challenge) {
handleResponse(FaceRevokeChallengeClient.class, (c) -> c.onChallengeRevoked(mSensorId,
- mUserId, challenge), null);
+ mUserId, challenge));
}
@Override
@@ -123,7 +159,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
return;
}
c.onAuthenticationFrame(AidlConversionUtils.toFrameworkAuthenticationFrame(frame));
- }, null);
+ });
}
@Override
@@ -134,7 +170,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
return;
}
c.onEnrollmentFrame(AidlConversionUtils.toFrameworkEnrollmentFrame(frame));
- }, null);
+ });
}
@Override
@@ -149,9 +185,13 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
handleResponse(ErrorConsumer.class, (c) -> {
c.onError(error, vendorCode);
if (error == Error.HW_UNAVAILABLE) {
- mHardwareUnavailableCallback.onHardwareUnavailable();
+ if (Flags.deHidl()) {
+ mAidlResponseHandlerCallback.onHardwareUnavailable();
+ } else {
+ mHardwareUnavailableCallback.onHardwareUnavailable();
+ }
}
- }, null);
+ });
}
@Override
@@ -167,7 +207,12 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
.getUniqueName(mContext, currentUserId);
final Face face = new Face(name, enrollmentId, mSensorId);
- handleResponse(FaceEnrollClient.class, (c) -> c.onEnrollResult(face, remaining), null);
+ handleResponse(FaceEnrollClient.class, (c) -> {
+ c.onEnrollResult(face, remaining);
+ if (remaining == 0) {
+ mAidlResponseHandlerCallback.onEnrollSuccess();
+ }
+ });
}
@Override
@@ -179,37 +224,37 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
byteList.add(b);
}
handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(face,
- true /* authenticated */, byteList), null);
+ true /* authenticated */, byteList));
}
@Override
public void onAuthenticationFailed() {
final Face face = new Face("" /* name */, 0 /* faceId */, mSensorId);
handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(face,
- false /* authenticated */, null /* hat */), null);
+ false /* authenticated */, null /* hat */));
}
@Override
public void onLockoutTimed(long durationMillis) {
- handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis), null);
+ handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis));
}
@Override
public void onLockoutPermanent() {
- handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null);
+ handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent);
}
@Override
public void onLockoutCleared() {
handleResponse(FaceResetLockoutClient.class, FaceResetLockoutClient::onLockoutCleared,
(c) -> FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
- mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- Utils.getCurrentStrength(mSensorId), -1 /* requestId */));
+ mLockoutTracker, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ Utils.getCurrentStrength(mSensorId), -1 /* requestId */));
}
@Override
public void onInteractionDetected() {
- handleResponse(FaceDetectClient.class, FaceDetectClient::onInteractionDetected, null);
+ handleResponse(FaceDetectClient.class, FaceDetectClient::onInteractionDetected);
}
@Override
@@ -219,23 +264,23 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
final int finalI = i;
handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(face,
- enrollmentIds.length - finalI - 1), null);
+ enrollmentIds.length - finalI - 1 /* remaining */));
}
} else {
handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(
- null /* identifier */, 0 /* remaining */), null);
+ null /* identifier */, 0 /* remaining */));
}
}
@Override
public void onFeaturesRetrieved(byte[] features) {
handleResponse(FaceGetFeatureClient.class, (c) -> c.onFeatureGet(true /* success */,
- features), null);
+ features));
}
@Override
public void onFeatureSet(byte feature) {
- handleResponse(FaceSetFeatureClient.class, (c) -> c.onFeatureSet(true /* success */), null);
+ handleResponse(FaceSetFeatureClient.class, (c) -> c.onFeatureSet(true /* success */));
}
@Override
@@ -245,33 +290,32 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
final int finalI = i;
handleResponse(RemovalConsumer.class,
- (c) -> c.onRemoved(face, enrollmentIds.length - finalI - 1),
- null);
+ (c) -> c.onRemoved(face,
+ enrollmentIds.length - finalI - 1 /* remaining */));
}
} else {
handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null /* identifier */,
- 0 /* remaining */), null);
+ 0 /* remaining */));
}
}
@Override
public void onAuthenticatorIdRetrieved(long authenticatorId) {
handleResponse(FaceGetAuthenticatorIdClient.class, (c) -> c.onAuthenticatorIdRetrieved(
- authenticatorId), null);
+ authenticatorId));
}
@Override
public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
handleResponse(FaceInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated(
- newAuthenticatorId), null);
+ newAuthenticatorId));
}
/**
* Handles acquired messages sent by the HAL (specifically for HIDL HAL).
*/
public void onAcquired(int acquiredInfo, int vendorCode) {
- handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode),
- null);
+ handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode));
}
/**
@@ -288,7 +332,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
lockoutMode = LockoutTracker.LOCKOUT_TIMED;
}
- mLockoutCache.setLockoutModeForUser(mUserId, lockoutMode);
+ mLockoutTracker.setLockoutModeForUser(mUserId, lockoutMode);
if (duration == 0) {
mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId);
@@ -296,6 +340,20 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
});
}
+ /**
+ * Handle clients which are not supported in HIDL HAL. For face, FaceInvalidationClient
+ * is the only AIDL client which is not supported in HIDL.
+ */
+ public void onUnsupportedClientScheduled() {
+ Slog.e(TAG, "FaceInvalidationClient is not supported in the HAL.");
+ handleResponse(FaceInvalidationClient.class, BaseClientMonitor::cancel);
+ }
+
+ private <T> void handleResponse(@NonNull Class<T> className,
+ @NonNull Consumer<T> action) {
+ handleResponse(className, action, null /* alternateAction */);
+ }
+
private <T> void handleResponse(@NonNull Class<T> className,
@NonNull Consumer<T> actionIfClassMatchesClient,
@Nullable Consumer<BaseClientMonitor> alternateAction) {
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 e70e25aebe9b..af46f441d6ce 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
@@ -21,7 +21,7 @@ import android.content.Context;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter;
+import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter;
import java.util.function.Supplier;
@@ -47,7 +47,7 @@ public class AidlSession {
public AidlSession(Context context, Supplier<IBiometricsFace> session, int userId,
AidlResponseHandler aidlResponseHandler) {
- mSession = new AidlToHidlAdapter(context, session, userId, aidlResponseHandler);
+ mSession = new HidlToAidlSessionAdapter(context, session, userId, aidlResponseHandler);
mHalInterfaceVersion = 0;
mUserId = userId;
mAidlResponseHandler = aidlResponseHandler;
@@ -64,7 +64,7 @@ public class AidlSession {
}
/** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */
- AidlResponseHandler getHalSessionCallback() {
+ public AidlResponseHandler getHalSessionCallback() {
return mAidlResponseHandler;
}
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 c15049b48bb2..c41b706555e8 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
@@ -34,7 +34,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
-import com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter;
+import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter;
import java.util.HashMap;
import java.util.Map;
@@ -75,8 +75,8 @@ public class FaceGetFeatureClient extends HalClientMonitor<AidlSession> implemen
protected void startHalOperation() {
try {
ISession session = getFreshDaemon().getSession();
- if (session instanceof AidlToHidlAdapter) {
- ((AidlToHidlAdapter) session).setFeature(mFeature);
+ if (session instanceof HidlToAidlSessionAdapter) {
+ ((HidlToAidlSessionAdapter) session).setFeature(mFeature);
}
session.getFeatures();
} catch (RemoteException e) {
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 dd9c6d50ae9e..9fa15b8ea3a1 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
@@ -24,6 +24,7 @@ import android.app.SynchronousUserSwitchObserver;
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
@@ -51,6 +52,7 @@ import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -64,11 +66,13 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.InvalidationRequesterClient;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorList;
import com.android.server.biometrics.sensors.face.FaceUtils;
import com.android.server.biometrics.sensors.face.ServiceProvider;
import com.android.server.biometrics.sensors.face.UsageStats;
+import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSensorAdapter;
import org.json.JSONArray;
import org.json.JSONException;
@@ -152,9 +156,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull SensorProps[] props,
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresChallenge) {
this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
- biometricContext, null /* daemon */);
+ biometricContext, null /* daemon */, resetLockoutRequiresChallenge,
+ false /* testHalEnabled */);
}
@VisibleForTesting FaceProvider(@NonNull Context context,
@@ -163,7 +169,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull BiometricContext biometricContext,
- IFace daemon) {
+ @Nullable IFace daemon, boolean resetLockoutRequiresChallenge,
+ boolean testHalEnabled) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
mHalInstanceName = halInstanceName;
@@ -176,48 +183,118 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mBiometricContext = biometricContext;
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
+ mTestHalEnabled = testHalEnabled;
- AuthenticationStatsBroadcastReceiver mBroadcastReceiver =
- new AuthenticationStatsBroadcastReceiver(
- mContext,
- BiometricsProtoEnums.MODALITY_FACE,
- (AuthenticationStatsCollector collector) -> {
- Slog.d(getTag(), "Initializing AuthenticationStatsCollector");
- mAuthenticationStatsCollector = collector;
- });
+ initAuthenticationBroadcastReceiver();
+ initSensors(resetLockoutRequiresChallenge, props);
+ }
- for (SensorProps prop : props) {
- final int sensorId = prop.commonProps.sensorId;
+ private void initAuthenticationBroadcastReceiver() {
+ new AuthenticationStatsBroadcastReceiver(
+ mContext,
+ BiometricsProtoEnums.MODALITY_FACE,
+ (AuthenticationStatsCollector collector) -> {
+ Slog.d(getTag(), "Initializing AuthenticationStatsCollector");
+ mAuthenticationStatsCollector = collector;
+ });
+ }
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- if (prop.commonProps.componentInfo != null) {
- for (ComponentInfo info : prop.commonProps.componentInfo) {
- componentInfo.add(new ComponentInfoInternal(info.componentId,
- info.hardwareVersion, info.firmwareVersion, info.serialNumber,
- info.softwareVersion));
+ private void initSensors(boolean resetLockoutRequiresChallenge, SensorProps[] props) {
+ if (Flags.deHidl()) {
+ if (resetLockoutRequiresChallenge) {
+ Slog.d(getTag(), "Adding HIDL configs");
+ for (SensorProps prop : props) {
+ addHidlSensors(prop, resetLockoutRequiresChallenge);
+ }
+ } else {
+ Slog.d(getTag(), "Adding AIDL configs");
+ for (SensorProps prop : props) {
+ addAidlSensors(prop, resetLockoutRequiresChallenge);
}
}
+ } else {
+ for (SensorProps prop : props) {
+ final int sensorId = prop.commonProps.sensorId;
+
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (ComponentInfo info : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
- final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
- prop.commonProps.sensorId, prop.commonProps.sensorStrength,
- prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
- prop.supportsDetectInteraction, prop.halControlsPreview,
- false /* resetLockoutRequiresChallenge */);
- final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, lockoutResetDispatcher, mBiometricContext);
- final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
- sensor.getLazySession().get().getUserId();
- mFaceSensors.addSensor(sensorId, sensor, userId,
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
- }
- });
- Slog.d(getTag(), "Added: " + internalProp);
+ final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
+ prop.commonProps.sensorId, prop.commonProps.sensorStrength,
+ prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
+ prop.supportsDetectInteraction, prop.halControlsPreview,
+ false /* resetLockoutRequiresChallenge */);
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this,
+ mContext, mHandler, internalProp, mLockoutResetDispatcher,
+ mBiometricContext);
+ sensor.init(mLockoutResetDispatcher, this);
+ final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFaceSensors.addSensor(sensorId, sensor, userId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + internalProp);
+ }
}
}
+ private void addHidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) {
+ final int sensorId = prop.commonProps.sensorId;
+ final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/" + sensorId, this,
+ mContext, mHandler, prop, mLockoutResetDispatcher,
+ mBiometricContext, resetLockoutRequiresChallenge,
+ () -> {
+ //TODO: update to make this testable
+ scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
+ null /* callback */);
+ scheduleGetFeature(sensorId, new Binder(), ActivityManager.getCurrentUser(),
+ BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, null,
+ mContext.getOpPackageName());
+ });
+ sensor.init(mLockoutResetDispatcher, this);
+ final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFaceSensors.addSensor(sensorId, sensor, userId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ scheduleGetFeature(sensorId, new Binder(), newUserId,
+ BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION,
+ null, mContext.getOpPackageName());
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFaceSensors.get(sensorId));
+ }
+
+ private void addAidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) {
+ final int sensorId = prop.commonProps.sensorId;
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext,
+ mHandler, prop, mLockoutResetDispatcher, mBiometricContext,
+ resetLockoutRequiresChallenge);
+ sensor.init(mLockoutResetDispatcher, this);
+ final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFaceSensors.addSensor(sensorId, sensor, userId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFaceSensors.get(sensorId));
+ }
+
private String getTag() {
return "FaceProvider/" + mHalInstanceName;
}
@@ -290,7 +367,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
}
- private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
+ /**
+ * Schedules FaceGetAuthenticatorIdClient for specific sensor and user.
+ */
+ protected void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
mHandler.post(() -> {
final FaceGetAuthenticatorIdClient client = new FaceGetAuthenticatorIdClient(
mContext, mFaceSensors.get(sensorId).getLazySession(), userId,
@@ -365,8 +445,12 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Override
public int getLockoutModeForUser(int sensorId, int userId) {
- return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
- Utils.getCurrentStrength(sensorId));
+ if (Flags.deHidl()) {
+ return mFaceSensors.get(sensorId).getLockoutModeForUser(userId);
+ } else {
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(sensorId));
+ }
}
@Override
@@ -376,13 +460,18 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Override
public boolean isHardwareDetected(int sensorId) {
- return hasHalInstance();
+ if (Flags.deHidl()) {
+ return mFaceSensors.get(sensorId).isHardwareDetected(mHalInstanceName);
+ } else {
+ return hasHalInstance();
+ }
}
@Override
public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFaceServiceReceiver receiver, String opPackageName) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId,
@@ -416,6 +505,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Nullable Surface previewSurface, boolean debugConsent) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final int maxTemplatesPerUser = mFaceSensors.get(
sensorId).getSensorProperties().maxEnrollmentsPerUser;
final FaceEnrollClient client = new FaceEnrollClient(mContext,
@@ -427,18 +517,23 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, maxTemplatesPerUser, debugConsent);
- scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
- mBiometricStateCallback, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- ClientMonitorCallback.super.onClientFinished(clientMonitor, success);
- if (success) {
- scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
- scheduleInvalidationRequest(sensorId, userId);
+ if (Flags.deHidl()) {
+ scheduleForSensor(sensorId, client, mBiometricStateCallback);
+ } else {
+ scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
+ mBiometricStateCallback, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ ClientMonitorCallback.super.onClientFinished(clientMonitor,
+ success);
+ if (success) {
+ scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
+ scheduleInvalidationRequest(sensorId, userId);
+ }
}
- }
- }));
+ }));
+ }
});
return id;
}
@@ -486,6 +581,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final int userId = options.getUserId();
final int sensorId = options.getSensorId();
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
+ final LockoutTracker lockoutTracker;
+ if (Flags.deHidl()) {
+ lockoutTracker = mFaceSensors.get(sensorId).getLockoutTracker(true /* forAuth */);
+ } else {
+ lockoutTracker = null;
+ }
final FaceAuthenticationClient client = new FaceAuthenticationClient(
mContext, mFaceSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
@@ -493,7 +595,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
- mUsageStats, null /* lockoutTracker */,
+ mUsageStats, lockoutTracker,
allowBackgroundAuthentication, Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
@@ -555,6 +657,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
private void scheduleRemoveSpecifiedIds(int sensorId, @NonNull IBinder token, int[] faceIds,
int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final FaceRemovalClient client = new FaceRemovalClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), faceIds, userId,
@@ -571,6 +674,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Override
public void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final FaceResetLockoutClient client = new FaceResetLockoutClient(
mContext, mFaceSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
@@ -578,8 +682,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken,
- mFaceSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
- Utils.getCurrentStrength(sensorId));
+ mFaceSensors.get(sensorId).getLockoutTracker(false/* forAuth */),
+ mLockoutResetDispatcher, Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client);
});
@@ -590,6 +694,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
boolean enabled, @NonNull byte[] hardwareAuthToken,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final List<Face> faces = FaceUtils.getInstance(sensorId)
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
@@ -610,6 +715,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public void scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final List<Face> faces = FaceUtils.getInstance(sensorId)
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
@@ -641,6 +747,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final FaceInternalCleanupClient client =
new FaceInternalCleanupClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), userId,
@@ -760,4 +867,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
biometricScheduler.startWatchdog();
}
+
+ public boolean getTestHalEnabled() {
+ return mTestHalEnabled;
+ }
}
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 77b5592c5064..d02eefaed101 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
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.ISession;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
import android.util.Slog;
@@ -34,6 +35,7 @@ import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter;
import java.util.function.Supplier;
@@ -47,7 +49,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
private static final String TAG = "FaceResetLockoutClient";
private final HardwareAuthToken mHardwareAuthToken;
- private final LockoutTracker mLockoutCache;
+ private final LockoutTracker mLockoutTracker;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final int mBiometricStrength;
@@ -60,7 +62,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, logger, biometricContext);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
- mLockoutCache = lockoutTracker;
+ mLockoutTracker = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mBiometricStrength = biometricStrength;
}
@@ -79,7 +81,11 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().getSession().resetLockout(mHardwareAuthToken);
+ final ISession session = getFreshDaemon().getSession();
+ session.resetLockout(mHardwareAuthToken);
+ if (session instanceof HidlToAidlSessionAdapter) {
+ mCallback.onClientFinished(this, true /* success */);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Unable to reset lockout", e);
mCallback.onClientFinished(this, false /* success */);
@@ -87,7 +93,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
}
void onLockoutCleared() {
- resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
+ resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutTracker,
mLockoutResetDispatcher, getBiometricContext().getAuthSessionCoordinator(),
mBiometricStrength, getRequestId());
mCallback.onClientFinished(this, true /* success */);
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 54e66eb4cca4..3e5c59914913 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
@@ -21,16 +21,20 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
+import android.hardware.biometrics.common.ComponentInfo;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
+import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
@@ -38,6 +42,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -49,12 +54,15 @@ import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.face.FaceUtils;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
@@ -71,25 +79,53 @@ public class Sensor {
@NonNull private final IBinder mToken;
@NonNull private final Handler mHandler;
@NonNull private final FaceSensorPropertiesInternal mSensorProperties;
- @NonNull private final UserAwareBiometricScheduler mScheduler;
- @NonNull private final LockoutCache mLockoutCache;
+ @NonNull private BiometricScheduler mScheduler;
+ @Nullable private LockoutTracker mLockoutTracker;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
- @NonNull private final Supplier<AidlSession> mLazySession;
+ @NonNull private Supplier<AidlSession> mLazySession;
@Nullable AidlSession mCurrentSession;
+ @NonNull BiometricContext mBiometricContext;
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull BiometricContext biometricContext, AidlSession session) {
+ @NonNull BiometricContext biometricContext, @Nullable AidlSession session) {
mTag = tag;
mProvider = provider;
mContext = context;
mToken = new Binder();
mHandler = handler;
mSensorProperties = sensorProperties;
- mScheduler = new UserAwareBiometricScheduler(tag,
+ mBiometricContext = biometricContext;
+ mAuthenticatorIds = new HashMap<>();
+ }
+
+ Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext) {
+ this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
+ biometricContext, null);
+ }
+
+ public Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresChallenge) {
+ this(tag, provider, context, handler,
+ getFaceSensorPropertiesInternal(prop, resetLockoutRequiresChallenge),
+ lockoutResetDispatcher, biometricContext, null);
+ }
+
+ /**
+ * Initialize biometric scheduler, lockout tracker and session for the sensor.
+ */
+ public void init(LockoutResetDispatcher lockoutResetDispatcher,
+ FaceProvider provider) {
+ mScheduler = new UserAwareBiometricScheduler(mTag,
BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */,
() -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
new UserAwareBiometricScheduler.UserSwitchCallback() {
@@ -98,7 +134,7 @@ public class Sensor {
public StopUserClient<?> getStopUserClient(int userId) {
return new FaceStopUserClient(mContext, mLazySession, mToken, userId,
mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), biometricContext,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
() -> mCurrentSession = null);
}
@@ -107,13 +143,36 @@ public class Sensor {
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
final int sensorId = mSensorProperties.sensorId;
- final AidlResponseHandler resultController = new AidlResponseHandler(
- mContext, mScheduler, sensorId, newUserId,
- mLockoutCache, lockoutResetDispatcher,
- biometricContext.getAuthSessionCoordinator(), () -> {
- Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
- mCurrentSession = null;
- });
+ final AidlResponseHandler resultController;
+ if (Flags.deHidl()) {
+ resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutTracker, lockoutResetDispatcher,
+ mBiometricContext.getAuthSessionCoordinator(), () -> {},
+ new AidlResponseHandler.AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {
+ mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId,
+ newUserId);
+ mProvider.scheduleInvalidationRequest(sensorId,
+ newUserId);
+ }
+
+ @Override
+ public void onHardwareUnavailable() {
+ Slog.e(mTag, "Face sensor hardware unavailable.");
+ mCurrentSession = null;
+ }
+ });
+ } else {
+ resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutTracker, lockoutResetDispatcher,
+ mBiometricContext.getAuthSessionCoordinator(), () -> {
+ Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+ mCurrentSession = null;
+ });
+ }
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
(userIdStarted, newSession, halInterfaceVersion) -> {
@@ -136,32 +195,42 @@ public class Sensor {
return new FaceStartUserClient(mContext, provider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), biometricContext,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
resultController, userStartedCallback);
}
});
- mLockoutCache = new LockoutCache();
- mAuthenticatorIds = new HashMap<>();
mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
+ mLockoutTracker = new LockoutCache();
}
- Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull BiometricContext biometricContext) {
- this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
- biometricContext, null);
+ private static FaceSensorPropertiesInternal getFaceSensorPropertiesInternal(SensorProps prop,
+ boolean resetLockoutRequiresChallenge) {
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (ComponentInfo info : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
+ final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
+ prop.commonProps.sensorId, prop.commonProps.sensorStrength,
+ prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
+ prop.supportsDetectInteraction, prop.halControlsPreview,
+ resetLockoutRequiresChallenge);
+
+ return internalProp;
}
- @NonNull Supplier<AidlSession> getLazySession() {
+ @NonNull public Supplier<AidlSession> getLazySession() {
return mLazySession;
}
- @NonNull FaceSensorPropertiesInternal getSensorProperties() {
+ @NonNull protected FaceSensorPropertiesInternal getSensorProperties() {
return mSensorProperties;
}
- @VisibleForTesting @Nullable AidlSession getSessionForUser(int userId) {
+ @VisibleForTesting @Nullable protected AidlSession getSessionForUser(int userId) {
if (mCurrentSession != null && mCurrentSession.getUserId() == userId) {
return mCurrentSession;
} else {
@@ -174,15 +243,18 @@ public class Sensor {
mProvider, this);
}
- @NonNull BiometricScheduler getScheduler() {
+ @NonNull public BiometricScheduler getScheduler() {
return mScheduler;
}
- @NonNull LockoutCache getLockoutCache() {
- return mLockoutCache;
+ @NonNull protected LockoutTracker getLockoutTracker(boolean forAuth) {
+ if (forAuth) {
+ return null;
+ }
+ return mLockoutTracker;
}
- @NonNull Map<Integer, Long> getAuthenticatorIds() {
+ @NonNull protected Map<Integer, Long> getAuthenticatorIds() {
return mAuthenticatorIds;
}
@@ -253,4 +325,49 @@ public class Sensor {
mScheduler.reset();
mCurrentSession = null;
}
+
+ protected BiometricContext getBiometricContext() {
+ return mBiometricContext;
+ }
+
+ protected Handler getHandler() {
+ return mHandler;
+ }
+
+ protected Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Schedules FaceUpdateActiveUserClient for user id.
+ */
+ public void scheduleFaceUpdateActiveUserClient(int userId) {}
+
+ /**
+ * Returns true if the sensor hardware is detected.
+ */
+ public boolean isHardwareDetected(String halInstanceName) {
+ if (mTestHalEnabled) {
+ return true;
+ }
+ return ServiceManager.checkService(IFace.DESCRIPTOR + "/" + halInstanceName) != null;
+ }
+
+ /**
+ * Returns lockout mode of this sensor.
+ */
+ @LockoutTracker.LockoutMode
+ public int getLockoutModeForUser(int userId) {
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(mSensorProperties.sensorId));
+ }
+
+ public void setScheduler(BiometricScheduler scheduler) {
+ mScheduler = scheduler;
+ }
+
+ public void setLazySession(
+ Supplier<AidlSession> lazySession) {
+ mLazySession = lazySession;
+ }
}
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 8385c3fa7103..0c34d702184a 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
@@ -27,13 +27,14 @@ 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;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.face.aidl.AidlSession;
import java.io.File;
import java.util.Map;
import java.util.function.Supplier;
-public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace> {
+public class FaceUpdateActiveUserClient extends StartUserClient<IBiometricsFace, AidlSession> {
private static final String TAG = "FaceUpdateActiveUserClient";
private static final String FACE_DATA_DIR = "facedata";
@@ -45,8 +46,18 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace
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, logger, biometricContext);
+ this(context, lazyDaemon, (newUserId, newUser, halInterfaceVersion) -> {},
+ userId, owner, sensorId, logger, biometricContext, hasEnrolledBiometrics,
+ authenticatorIds);
+ }
+
+ FaceUpdateActiveUserClient(@NonNull Context context,
+ @NonNull Supplier<IBiometricsFace> lazyDaemon, UserStartedCallback userStartedCallback,
+ int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, boolean hasEnrolledBiometrics,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, null /* token */, userId, sensorId, logger, biometricContext,
+ userStartedCallback);
mHasEnrolledBiometrics = hasEnrolledBiometrics;
mAuthenticatorIds = authenticatorIds;
}
@@ -77,6 +88,7 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace
daemon.setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
mAuthenticatorIds.put(getTargetUserId(),
mHasEnrolledBiometrics ? daemon.getAuthenticatorId().value : 0L);
+ mUserStartedCallback.onUserStarted(getTargetUserId(), null, 0);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveUser: " + e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java
index 36a9790d2d4b..7a574cecc0b1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java
@@ -112,4 +112,8 @@ public class HidlToAidlCallbackConverter extends IBiometricsFaceClientCallback.S
void onAuthenticatorIdRetrieved(long authenticatorId) {
mAidlResponseHandler.onAuthenticatorIdRetrieved(authenticatorId);
}
+
+ void onUnsupportedClientScheduled() {
+ mAidlResponseHandler.onUnsupportedClientScheduled();
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
new file mode 100644
index 000000000000..6355cb57a752
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.hidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.face.SensorProps;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.os.Handler;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+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 com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.face.FaceUtils;
+import com.android.server.biometrics.sensors.face.LockoutHalImpl;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.face.aidl.AidlSession;
+import com.android.server.biometrics.sensors.face.aidl.FaceProvider;
+import com.android.server.biometrics.sensors.face.aidl.Sensor;
+
+/**
+ * Convert HIDL sensor configurations to an AIDL Sensor.
+ */
+public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRecipient{
+
+ private static final String TAG = "HidlToAidlSensorAdapter";
+
+ private IBiometricsFace mDaemon;
+ private AidlSession mSession;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+ private final Runnable mInternalCleanupAndGetFeatureRunnable;
+ private final FaceProvider mFaceProvider;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
+ private final AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ private final StartUserClient.UserStartedCallback<AidlSession> mUserStartedCallback =
+ (newUserId, newUser, halInterfaceVersion) -> {
+ if (newUserId != mCurrentUserId) {
+ handleUserChanged(newUserId);
+ }
+ };
+ private LockoutHalImpl mLockoutTracker;
+
+ public HidlToAidlSensorAdapter(@NonNull String tag,
+ @NonNull FaceProvider provider,
+ @NonNull Context context,
+ @NonNull Handler handler,
+ @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresChallenge,
+ @NonNull Runnable internalCleanupAndGetFeatureRunnable) {
+ this(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext,
+ resetLockoutRequiresChallenge, internalCleanupAndGetFeatureRunnable,
+ new AuthSessionCoordinator(), null /* daemon */,
+ null /* onEnrollSuccessCallback */);
+ }
+
+ @VisibleForTesting
+ HidlToAidlSensorAdapter(@NonNull String tag,
+ @NonNull FaceProvider provider,
+ @NonNull Context context,
+ @NonNull Handler handler,
+ @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresChallenge,
+ @NonNull Runnable internalCleanupAndGetFeatureRunnable,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @Nullable IBiometricsFace daemon,
+ @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) {
+ super(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext,
+ resetLockoutRequiresChallenge);
+ mInternalCleanupAndGetFeatureRunnable = internalCleanupAndGetFeatureRunnable;
+ mFaceProvider = provider;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mAuthSessionCoordinator = authSessionCoordinator;
+ mDaemon = daemon;
+ mAidlResponseHandlerCallback = aidlResponseHandlerCallback == null
+ ? new AidlResponseHandler.AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {
+ scheduleFaceUpdateActiveUserClient(mCurrentUserId);
+ }
+
+ @Override
+ public void onHardwareUnavailable() {
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ } : aidlResponseHandlerCallback;
+ }
+
+ @Override
+ public void scheduleFaceUpdateActiveUserClient(int userId) {
+ getScheduler().scheduleClientMonitor(getFaceUpdateActiveUserClient(userId));
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.d(TAG, "HAL died.");
+ mDaemon = null;
+ }
+
+ @Override
+ public boolean isHardwareDetected(String halInstanceName) {
+ return getIBiometricsFace() != null;
+ }
+
+ @Override
+ @LockoutTracker.LockoutMode
+ public int getLockoutModeForUser(int userId) {
+ return mLockoutTracker.getLockoutModeForUser(userId);
+ }
+
+ @Override
+ public void init(LockoutResetDispatcher lockoutResetDispatcher,
+ FaceProvider provider) {
+ setScheduler(new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
+ null /* gestureAvailabilityTracker */));
+ setLazySession(this::getSession);
+ mLockoutTracker = new LockoutHalImpl();
+ }
+
+ @Override
+ @VisibleForTesting @Nullable protected AidlSession getSessionForUser(int userId) {
+ if (mSession != null && mSession.getUserId() == userId) {
+ return mSession;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected LockoutTracker getLockoutTracker(boolean forAuth) {
+ return mLockoutTracker;
+ }
+
+ @NonNull AidlSession getSession() {
+ if (mDaemon != null && mSession != null) {
+ return mSession;
+ } else {
+ return mSession = new AidlSession(getContext(), this::getIBiometricsFace,
+ mCurrentUserId, getAidlResponseHandler());
+ }
+ }
+
+ private AidlResponseHandler getAidlResponseHandler() {
+ return new AidlResponseHandler(getContext(), getScheduler(), getSensorProperties().sensorId,
+ mCurrentUserId, mLockoutTracker, mLockoutResetDispatcher,
+ mAuthSessionCoordinator, () -> {}, mAidlResponseHandlerCallback);
+ }
+
+ private IBiometricsFace getIBiometricsFace() {
+ if (mFaceProvider.getTestHalEnabled()) {
+ final TestHal testHal = new TestHal(getContext(), getSensorProperties().sensorId);
+ testHal.setCallback(new HidlToAidlCallbackConverter(getAidlResponseHandler()));
+ return testHal;
+ }
+
+ if (mDaemon != null) {
+ return mDaemon;
+ }
+
+ Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+ + getScheduler().getCurrentClient());
+
+ try {
+ mDaemon = IBiometricsFace.getService();
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened.
+ Slog.w(TAG, "NoSuchElementException", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get face HAL", e);
+ }
+
+ if (mDaemon == null) {
+ Slog.w(TAG, "Face HAL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
+
+ scheduleLoadAuthenticatorIds();
+ mInternalCleanupAndGetFeatureRunnable.run();
+ return mDaemon;
+ }
+
+ @VisibleForTesting void handleUserChanged(int newUserId) {
+ Slog.d(TAG, "User changed. Current user is " + newUserId);
+ mSession = null;
+ mCurrentUserId = newUserId;
+ }
+
+ private void scheduleLoadAuthenticatorIds() {
+ // Note that this can be performed on the scheduler (as opposed to being done immediately
+ // when the HAL is (re)loaded, since
+ // 1) If this is truly the first time it's being performed (e.g. system has just started),
+ // this will be run very early and way before any applications need to generate keys.
+ // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has
+ // just been reloaded), the framework already has a cache of the authenticatorIds. This
+ // is safe because authenticatorIds only change when A) new template has been enrolled,
+ // or B) all templates are removed.
+ getHandler().post(() -> {
+ for (UserInfo user : UserManager.get(getContext()).getAliveUsers()) {
+ final int targetUserId = user.id;
+ if (!getAuthenticatorIds().containsKey(targetUserId)) {
+ scheduleFaceUpdateActiveUserClient(targetUserId);
+ }
+ }
+ });
+ }
+
+ private FaceUpdateActiveUserClient getFaceUpdateActiveUserClient(int userId) {
+ return new FaceUpdateActiveUserClient(getContext(), this::getIBiometricsFace,
+ mUserStartedCallback, userId, TAG, getSensorProperties().sensorId,
+ BiometricLogger.ofUnknown(getContext()), getBiometricContext(),
+ !FaceUtils.getInstance(getSensorProperties().sensorId).getBiometricsForUser(
+ getContext(), userId).isEmpty(),
+ getAuthenticatorIds());
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
index 489b213677dd..5daf2d4fbcf4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
@@ -47,34 +47,37 @@ import java.util.List;
import java.util.function.Supplier;
/**
- * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation.
+ * Adapter to convert HIDL methods into AIDL interface {@link ISession}.
*/
-public class AidlToHidlAdapter implements ISession {
+public class HidlToAidlSessionAdapter implements ISession {
+
+ private static final String TAG = "HidlToAidlSessionAdapter";
- private final String TAG = "AidlToHidlAdapter";
private static final int CHALLENGE_TIMEOUT_SEC = 600;
@DurationMillisLong
private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000;
@DurationMillisLong
private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS = CHALLENGE_TIMEOUT_SEC * 1000;
private static final int INVALID_VALUE = -1;
+ @VisibleForTesting static final int ENROLL_TIMEOUT_SEC = 75;
+
private final Clock mClock;
private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
- @VisibleForTesting static final int ENROLL_TIMEOUT_SEC = 75;
+ private final int mUserId;
+ private final Context mContext;
+
private long mGenerateChallengeCreatedAt = INVALID_VALUE;
private long mGenerateChallengeResult = INVALID_VALUE;
@NonNull private Supplier<IBiometricsFace> mSession;
- private final int mUserId;
private HidlToAidlCallbackConverter mHidlToAidlCallbackConverter;
- private final Context mContext;
private int mFeature = INVALID_VALUE;
- public AidlToHidlAdapter(Context context, Supplier<IBiometricsFace> session,
+ public HidlToAidlSessionAdapter(Context context, Supplier<IBiometricsFace> session,
int userId, AidlResponseHandler aidlResponseHandler) {
this(context, session, userId, aidlResponseHandler, Clock.systemUTC());
}
- AidlToHidlAdapter(Context context, Supplier<IBiometricsFace> session, int userId,
+ HidlToAidlSessionAdapter(Context context, Supplier<IBiometricsFace> session, int userId,
AidlResponseHandler aidlResponseHandler, Clock clock) {
mSession = session;
mUserId = userId;
@@ -83,42 +86,11 @@ public class AidlToHidlAdapter implements ISession {
setCallback(aidlResponseHandler);
}
- private void setCallback(AidlResponseHandler aidlResponseHandler) {
- mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
- try {
- mSession.get().setCallback(mHidlToAidlCallbackConverter);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to set callback");
- }
- }
-
@Override
public IBinder asBinder() {
return null;
}
- private boolean isGeneratedChallengeCacheValid() {
- return mGenerateChallengeCreatedAt != INVALID_VALUE
- && mGenerateChallengeResult != INVALID_VALUE
- && mClock.millis() - mGenerateChallengeCreatedAt
- < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS;
- }
-
- private void incrementChallengeCount() {
- mGeneratedChallengeCount.add(0, mClock.millis());
- }
-
- private int decrementChallengeCount() {
- final long now = mClock.millis();
- // ignore values that are old in case generate/revoke calls are not matched
- // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing
- mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS);
- if (!mGeneratedChallengeCount.isEmpty()) {
- mGeneratedChallengeCount.remove(0);
- }
- return mGeneratedChallengeCount.size();
- }
-
@Override
public void generateChallenge() throws RemoteException {
incrementChallengeCount();
@@ -150,7 +122,7 @@ public class AidlToHidlAdapter implements ISession {
@Override
public EnrollmentStageConfig[] getEnrollmentConfig(byte enrollmentType) throws RemoteException {
- //unsupported in HIDL
+ Slog.e(TAG, "getEnrollmentConfig unsupported in HIDL");
return null;
}
@@ -244,19 +216,6 @@ public class AidlToHidlAdapter implements ISession {
}
}
- private int getFaceId() {
- FaceManager faceManager = mContext.getSystemService(FaceManager.class);
- List<Face> faces = faceManager.getEnrolledFaces(mUserId);
- if (faces.isEmpty()) {
- Slog.d(TAG, "No faces to get feature from.");
- mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
- BiometricFaceConstants.FACE_ERROR_NOT_ENROLLED, 0 /* vendorCode */);
- return INVALID_VALUE;
- }
-
- return faces.get(0).getBiometricId();
- }
-
@Override
public void getAuthenticatorId() throws RemoteException {
long authenticatorId = mSession.get().getAuthenticatorId().value;
@@ -265,7 +224,8 @@ public class AidlToHidlAdapter implements ISession {
@Override
public void invalidateAuthenticatorId() throws RemoteException {
- //unsupported in HIDL
+ Slog.e(TAG, "invalidateAuthenticatorId unsupported in HIDL");
+ mHidlToAidlCallbackConverter.onUnsupportedClientScheduled();
}
@Override
@@ -279,47 +239,105 @@ public class AidlToHidlAdapter implements ISession {
@Override
public void close() throws RemoteException {
- //Unsupported in HIDL
+ Slog.e(TAG, "close unsupported in HIDL");
}
@Override
public ICancellationSignal authenticateWithContext(long operationId, OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Slog.e(TAG, "authenticateWithContext unsupported in HIDL");
+ return authenticate(operationId);
}
@Override
public ICancellationSignal enrollWithContext(HardwareAuthToken hat, byte type, byte[] features,
NativeHandle previewSurface, OperationContext context) throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Slog.e(TAG, "enrollWithContext unsupported in HIDL");
+ return enroll(hat, type, features, previewSurface);
}
@Override
public ICancellationSignal detectInteractionWithContext(OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Slog.e(TAG, "detectInteractionWithContext unsupported in HIDL");
+ return detectInteraction();
}
@Override
public void onContextChanged(OperationContext context) throws RemoteException {
- //Unsupported in HIDL
+ Slog.e(TAG, "onContextChanged unsupported in HIDL");
}
@Override
public int getInterfaceVersion() throws RemoteException {
- //Unsupported in HIDL
+ Slog.e(TAG, "getInterfaceVersion unsupported in HIDL");
return 0;
}
@Override
public String getInterfaceHash() throws RemoteException {
+ Slog.e(TAG, "getInterfaceHash unsupported in HIDL");
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) {
//Unsupported in HIDL
return null;
}
+ private boolean isGeneratedChallengeCacheValid() {
+ return mGenerateChallengeCreatedAt != INVALID_VALUE
+ && mGenerateChallengeResult != INVALID_VALUE
+ && mClock.millis() - mGenerateChallengeCreatedAt
+ < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS;
+ }
+
+ private void incrementChallengeCount() {
+ mGeneratedChallengeCount.add(0, mClock.millis());
+ }
+
+ private int decrementChallengeCount() {
+ final long now = mClock.millis();
+ // ignore values that are old in case generate/revoke calls are not matched
+ // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing
+ mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS);
+ if (!mGeneratedChallengeCount.isEmpty()) {
+ mGeneratedChallengeCount.remove(0);
+ }
+ return mGeneratedChallengeCount.size();
+ }
+
+ private void setCallback(AidlResponseHandler aidlResponseHandler) {
+ mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
+ try {
+ if (mSession.get() != null) {
+ long halId = mSession.get().setCallback(mHidlToAidlCallbackConverter).value;
+ Slog.d(TAG, "Face HAL ready, HAL ID: " + halId);
+ if (halId == 0) {
+ Slog.d(TAG, "Unable to set HIDL callback.");
+ }
+ } else {
+ Slog.e(TAG, "Unable to set HIDL callback. HIDL daemon is null.");
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to set callback");
+ }
+ }
+
+ private int getFaceId() {
+ FaceManager faceManager = mContext.getSystemService(FaceManager.class);
+ List<Face> faces = faceManager.getEnrolledFaces(mUserId);
+ if (faces.isEmpty()) {
+ Slog.d(TAG, "No faces to get feature from.");
+ mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
+ BiometricFaceConstants.FACE_ERROR_NOT_ENROLLED, 0 /* vendorCode */);
+ return INVALID_VALUE;
+ }
+
+ return faces.get(0).getBiometricId();
+ }
+
/**
* Cancellation in HIDL occurs for the entire session, instead of a specific client.
*/
@@ -345,10 +363,4 @@ public class AidlToHidlAdapter implements ISession {
return null;
}
}
-
- @Override
- public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) {
- //Unsupported in HIDL
- return null;
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 83b306b07c27..e01d672d7e34 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -45,9 +45,11 @@ import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintServiceReceiver;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
@@ -80,6 +82,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
@@ -127,6 +130,8 @@ public class FingerprintService extends SystemService {
@NonNull
private final Function<String, FingerprintProvider> mFingerprintProvider;
@NonNull
+ private final FingerprintProviderFunction mFingerprintProviderFunction;
+ @NonNull
private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal>
mBiometricStateCallback;
@NonNull
@@ -136,6 +141,11 @@ public class FingerprintService extends SystemService {
@NonNull
private final FingerprintServiceRegistry mRegistry;
+ interface FingerprintProviderFunction {
+ FingerprintProvider getFingerprintProvider(Pair<String, SensorProps[]> filteredSensorProp,
+ boolean resetLockoutRequiresHardwareAuthToken);
+ }
+
/** Receives the incoming binder calls from FingerprintManager. */
@VisibleForTesting
final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() {
@@ -874,6 +884,18 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
+ public void registerAuthenticatorsLegacy(
+ @NonNull FingerprintSensorConfigurations fingerprintSensorConfigurations) {
+ super.registerAuthenticatorsLegacy_enforcePermission();
+ if (!fingerprintSensorConfigurations.hasSensorConfigurations()) {
+ Slog.d(TAG, "No fingerprint sensors available.");
+ return;
+ }
+ mRegistry.registerAll(() -> getProviders(fingerprintSensorConfigurations));
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override // Binder call
public void registerAuthenticators(
@NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) {
super.registerAuthenticators_enforcePermission();
@@ -1021,7 +1043,8 @@ public class FingerprintService extends SystemService {
() -> IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
() -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR),
- null /* fingerprintProvider */);
+ null /* fingerprintProvider */,
+ null /* fingerprintProviderFunction */);
}
@VisibleForTesting
@@ -1029,7 +1052,8 @@ public class FingerprintService extends SystemService {
BiometricContext biometricContext,
Supplier<IBiometricService> biometricServiceSupplier,
Supplier<String[]> aidlInstanceNameSupplier,
- Function<String, FingerprintProvider> fingerprintProvider) {
+ Function<String, FingerprintProvider> fingerprintProvider,
+ FingerprintProviderFunction fingerprintProviderFunction) {
super(context);
mBiometricContext = biometricContext;
mAidlInstanceNameSupplier = aidlInstanceNameSupplier;
@@ -1049,7 +1073,8 @@ public class FingerprintService extends SystemService {
return new FingerprintProvider(getContext(),
mBiometricStateCallback, mAuthenticationStateListeners,
fp.getSensorProps(), name, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher, mBiometricContext);
+ mGestureAvailabilityDispatcher, mBiometricContext,
+ true /* resetLockoutRequiresHardwareAuthToken */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
}
@@ -1059,6 +1084,22 @@ public class FingerprintService extends SystemService {
return null;
};
+ if (Flags.deHidl()) {
+ mFingerprintProviderFunction = fingerprintProviderFunction == null
+ ? (filteredSensorProps, resetLockoutRequiresHardwareAuthToken) ->
+ new FingerprintProvider(
+ getContext(), mBiometricStateCallback,
+ mAuthenticationStateListeners,
+ filteredSensorProps.second,
+ filteredSensorProps.first, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher,
+ mBiometricContext,
+ resetLockoutRequiresHardwareAuthToken)
+ : fingerprintProviderFunction;
+ } else {
+ mFingerprintProviderFunction =
+ (filteredSensorProps, resetLockoutRequiresHardwareAuthToken) -> null;
+ }
mHandler = new Handler(Looper.getMainLooper());
mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier);
mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@@ -1070,6 +1111,44 @@ public class FingerprintService extends SystemService {
});
}
+ @NonNull
+ private List<ServiceProvider> getProviders(@NonNull FingerprintSensorConfigurations
+ fingerprintSensorConfigurations) {
+ final List<ServiceProvider> providers = new ArrayList<>();
+ final Pair<String, SensorProps[]> filteredSensorProps = filterAvailableHalInstances(
+ fingerprintSensorConfigurations);
+ providers.add(mFingerprintProviderFunction.getFingerprintProvider(filteredSensorProps,
+ fingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken()));
+
+ return providers;
+ }
+
+ @NonNull
+ private Pair<String, SensorProps[]> filterAvailableHalInstances(
+ FingerprintSensorConfigurations fingerprintSensorConfigurations) {
+ Pair<String, SensorProps[]> finalSensorPair =
+ fingerprintSensorConfigurations.getSensorPair();
+ if (fingerprintSensorConfigurations.isSingleSensorConfigurationPresent()) {
+ return finalSensorPair;
+ }
+
+ final Pair<String, SensorProps[]> virtualSensorPropsPair = fingerprintSensorConfigurations
+ .getSensorPairForInstance("virtual");
+ if (Utils.isVirtualEnabled(getContext())) {
+ if (virtualSensorPropsPair != null) {
+ return virtualSensorPropsPair;
+ } else {
+ Slog.e(TAG, "Could not find virtual interface while it is enabled");
+ return finalSensorPair;
+ }
+ } else {
+ if (virtualSensorPropsPair != null) {
+ return fingerprintSensorConfigurations.getSensorPairNotForInstance("virtual");
+ }
+ }
+ return finalSensorPair;
+ }
+
private Pair<List<FingerprintSensorPropertiesInternal>, List<String>>
filterAvailableHalInstances(
@NonNull List<FingerprintSensorPropertiesInternal> hidlInstances,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
index 4a019436cf6f..bd21cf4002b0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
@@ -25,6 +25,7 @@ import android.hardware.fingerprint.Fingerprint;
import android.hardware.keymaster.HardwareAuthToken;
import android.util.Slog;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
@@ -34,9 +35,9 @@ import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.EnumerateConsumer;
import com.android.server.biometrics.sensors.ErrorConsumer;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
@@ -59,6 +60,21 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
void onHardwareUnavailable();
}
+ /**
+ * Interface to send results to the AidlResponseHandler's owner.
+ */
+ public interface AidlResponseHandlerCallback {
+ /**
+ * Invoked when enrollment is successful.
+ */
+ void onEnrollSuccess();
+
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
private static final String TAG = "AidlResponseHandler";
@NonNull
@@ -68,28 +84,49 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
private final int mSensorId;
private final int mUserId;
@NonNull
- private final LockoutCache mLockoutCache;
+ private final LockoutTracker mLockoutTracker;
@NonNull
private final LockoutResetDispatcher mLockoutResetDispatcher;
@NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
private final HardwareUnavailableCallback mHardwareUnavailableCallback;
+ @NonNull
+ private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
public AidlResponseHandler(@NonNull Context context,
@NonNull BiometricScheduler scheduler, int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
@NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
+ this(context, scheduler, sensorId, userId, lockoutTracker, lockoutResetDispatcher,
+ authSessionCoordinator, hardwareUnavailableCallback,
+ new AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {}
+
+ @Override
+ public void onHardwareUnavailable() {}
+ });
+ }
+
+ public AidlResponseHandler(@NonNull Context context,
+ @NonNull BiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutTracker lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @NonNull HardwareUnavailableCallback hardwareUnavailableCallback,
+ @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
mContext = context;
mScheduler = scheduler;
mSensorId = sensorId;
mUserId = userId;
- mLockoutCache = lockoutTracker;
+ mLockoutTracker = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mAuthSessionCoordinator = authSessionCoordinator;
mHardwareUnavailableCallback = hardwareUnavailableCallback;
+ mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
}
@Override
@@ -105,27 +142,26 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
@Override
public void onChallengeGenerated(long challenge) {
handleResponse(FingerprintGenerateChallengeClient.class, (c) -> c.onChallengeGenerated(
- mSensorId, mUserId, challenge), null);
+ mSensorId, mUserId, challenge));
}
@Override
public void onChallengeRevoked(long challenge) {
handleResponse(FingerprintRevokeChallengeClient.class, (c) -> c.onChallengeRevoked(
- challenge), null);
+ challenge));
}
/**
* Handles acquired messages sent by the HAL (specifically for HIDL HAL).
*/
public void onAcquired(int acquiredInfo, int vendorCode) {
- handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode),
- null);
+ handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode));
}
@Override
public void onAcquired(byte info, int vendorCode) {
handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(
- AidlConversionUtils.toFrameworkAcquiredInfo(info), vendorCode), null);
+ AidlConversionUtils.toFrameworkAcquiredInfo(info), vendorCode));
}
/**
@@ -135,9 +171,13 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
handleResponse(ErrorConsumer.class, (c) -> {
c.onError(error, vendorCode);
if (error == Error.HW_UNAVAILABLE) {
- mHardwareUnavailableCallback.onHardwareUnavailable();
+ if (Flags.deHidl()) {
+ mAidlResponseHandlerCallback.onHardwareUnavailable();
+ } else {
+ mHardwareUnavailableCallback.onHardwareUnavailable();
+ }
}
- }, null);
+ });
}
@Override
@@ -158,8 +198,12 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
.getUniqueName(mContext, currentUserId);
final Fingerprint fingerprint = new Fingerprint(name, currentUserId,
enrollmentId, mSensorId);
- handleResponse(FingerprintEnrollClient.class, (c) -> c.onEnrollResult(fingerprint,
- remaining), null);
+ handleResponse(FingerprintEnrollClient.class, (c) -> {
+ c.onEnrollResult(fingerprint, remaining);
+ if (remaining == 0) {
+ mAidlResponseHandlerCallback.onEnrollSuccess();
+ }
+ });
}
@Override
@@ -184,13 +228,12 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
@Override
public void onLockoutTimed(long durationMillis) {
- handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis),
- null);
+ handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis));
}
@Override
public void onLockoutPermanent() {
- handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null);
+ handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent);
}
@Override
@@ -198,7 +241,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
handleResponse(FingerprintResetLockoutClient.class,
FingerprintResetLockoutClient::onLockoutCleared,
(c) -> FingerprintResetLockoutClient.resetLocalLockoutStateToNone(
- mSensorId, mUserId, mLockoutCache, mLockoutResetDispatcher,
+ mSensorId, mUserId, mLockoutTracker, mLockoutResetDispatcher,
mAuthSessionCoordinator, Utils.getCurrentStrength(mSensorId),
-1 /* requestId */));
}
@@ -206,49 +249,74 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
@Override
public void onInteractionDetected() {
handleResponse(FingerprintDetectClient.class,
- FingerprintDetectClient::onInteractionDetected, null);
+ FingerprintDetectClient::onInteractionDetected);
}
@Override
public void onEnrollmentsEnumerated(int[] enrollmentIds) {
if (enrollmentIds.length > 0) {
for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
- int finalI = i;
- handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(fp,
- enrollmentIds.length - finalI - 1), null);
+ onEnrollmentEnumerated(enrollmentIds[i],
+ enrollmentIds.length - i - 1 /* remaining */);
}
} else {
- handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(null,
- 0), null);
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(
+ null /* identifier */,
+ 0 /* remaining */));
}
}
+ /**
+ * Handle enumerated fingerprint.
+ */
+ public void onEnrollmentEnumerated(int enrollmentId, int remaining) {
+ final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(fp, remaining));
+ }
+
+ /**
+ * Handle removal of fingerprint.
+ */
+ public void onEnrollmentRemoved(int enrollmentId, int remaining) {
+ final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
+ handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(fp, remaining));
+ }
+
@Override
public void onEnrollmentsRemoved(int[] enrollmentIds) {
if (enrollmentIds.length > 0) {
for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
- int finalI = i;
- handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(fp,
- enrollmentIds.length - finalI - 1), null);
+ onEnrollmentRemoved(enrollmentIds[i], enrollmentIds.length - i - 1 /* remaining */);
}
} else {
- handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null, 0),
- null);
+ handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null /* identifier */,
+ 0 /* remaining */));
}
}
@Override
public void onAuthenticatorIdRetrieved(long authenticatorId) {
handleResponse(FingerprintGetAuthenticatorIdClient.class,
- (c) -> c.onAuthenticatorIdRetrieved(authenticatorId), null);
+ (c) -> c.onAuthenticatorIdRetrieved(authenticatorId));
}
@Override
public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
handleResponse(FingerprintInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated(
- newAuthenticatorId), null);
+ newAuthenticatorId));
+ }
+
+ /**
+ * Handle clients which are not supported in HIDL HAL.
+ */
+ public <T extends BaseClientMonitor> void onUnsupportedClientScheduled(Class<T> className) {
+ Slog.e(TAG, className + " is not supported in the HAL.");
+ handleResponse(className, (c) -> c.cancel());
+ }
+
+ private <T> void handleResponse(@NonNull Class<T> className,
+ @NonNull Consumer<T> action) {
+ handleResponse(className, action, null /* alternateAction */);
}
private <T> void handleResponse(@NonNull Class<T> className,
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 299a310caee9..8ff105baa981 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
@@ -20,7 +20,7 @@ import android.annotation.NonNull;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import com.android.server.biometrics.sensors.fingerprint.hidl.AidlToHidlAdapter;
+import com.android.server.biometrics.sensors.fingerprint.hidl.HidlToAidlSessionAdapter;
import java.util.function.Supplier;
@@ -45,7 +45,7 @@ public class AidlSession {
public AidlSession(@NonNull Supplier<IBiometricsFingerprint> session,
int userId, AidlResponseHandler aidlResponseHandler) {
- mSession = new AidlToHidlAdapter(session, userId, aidlResponseHandler);
+ mSession = new HidlToAidlSessionAdapter(session, userId, aidlResponseHandler);
mHalInterfaceVersion = 0;
mUserId = userId;
mAidlResponseHandler = aidlResponseHandler;
@@ -62,7 +62,7 @@ public class AidlSession {
}
/** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */
- AidlResponseHandler getHalSessionCallback() {
+ public AidlResponseHandler getHalSessionCallback() {
return mAidlResponseHandler;
}
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 ea1a622c36ab..03539690c0a8 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
@@ -30,7 +30,7 @@ import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.Map;
import java.util.function.Supplier;
-class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> {
+public class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> {
private static final String TAG = "FingerprintGetAuthenticatorIdClient";
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 032ab87196f8..88a11d9c0ceb 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
@@ -59,6 +59,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -73,6 +74,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.InvalidationRequesterClient;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorList;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
@@ -80,6 +82,7 @@ import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDisp
import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.hidl.HidlToAidlSensorAdapter;
import org.json.JSONArray;
import org.json.JSONException;
@@ -165,10 +168,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@NonNull SensorProps[] props, @NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresHardwareAuthToken) {
this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName,
lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext,
- null /* daemon */);
+ null /* daemon */, resetLockoutRequiresHardwareAuthToken,
+ false /* testHalEnabled */);
}
@VisibleForTesting FingerprintProvider(@NonNull Context context,
@@ -178,7 +183,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull BiometricContext biometricContext,
- IFingerprint daemon) {
+ @Nullable IFingerprint daemon,
+ boolean resetLockoutRequiresHardwareAuthToken,
+ boolean testHalEnabled) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
mAuthenticationStateListeners = authenticationStateListeners;
@@ -191,62 +198,136 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mBiometricContext = biometricContext;
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
+ mTestHalEnabled = testHalEnabled;
- AuthenticationStatsBroadcastReceiver mBroadcastReceiver =
- new AuthenticationStatsBroadcastReceiver(
- mContext,
- BiometricsProtoEnums.MODALITY_FINGERPRINT,
- (AuthenticationStatsCollector collector) -> {
- Slog.d(getTag(), "Initializing AuthenticationStatsCollector");
- mAuthenticationStatsCollector = collector;
- });
-
- final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
+ initAuthenticationBroadcastReceiver();
+ initSensors(resetLockoutRequiresHardwareAuthToken, props, gestureAvailabilityDispatcher);
+ }
- for (SensorProps prop : props) {
- final int sensorId = prop.commonProps.sensorId;
+ private void initAuthenticationBroadcastReceiver() {
+ new AuthenticationStatsBroadcastReceiver(
+ mContext,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ (AuthenticationStatsCollector collector) -> {
+ Slog.d(getTag(), "Initializing AuthenticationStatsCollector");
+ mAuthenticationStatsCollector = collector;
+ });
+ }
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- if (prop.commonProps.componentInfo != null) {
- for (ComponentInfo info : prop.commonProps.componentInfo) {
- componentInfo.add(new ComponentInfoInternal(info.componentId,
- info.hardwareVersion, info.firmwareVersion, info.serialNumber,
- info.softwareVersion));
+ private void initSensors(boolean resetLockoutRequiresHardwareAuthToken, SensorProps[] props,
+ GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ if (Flags.deHidl()) {
+ if (!resetLockoutRequiresHardwareAuthToken) {
+ Slog.d(getTag(), "Adding HIDL configs");
+ for (SensorProps sensorConfig: props) {
+ addHidlSensors(sensorConfig, gestureAvailabilityDispatcher,
+ resetLockoutRequiresHardwareAuthToken);
+ }
+ } else {
+ Slog.d(getTag(), "Adding AIDL configs");
+ final List<SensorLocationInternal> workaroundLocations =
+ getWorkaroundSensorProps(mContext);
+ for (SensorProps prop : props) {
+ addAidlSensors(prop, gestureAvailabilityDispatcher, workaroundLocations,
+ resetLockoutRequiresHardwareAuthToken);
}
}
-
- final FingerprintSensorPropertiesInternal internalProp =
- new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
- prop.commonProps.sensorStrength,
- prop.commonProps.maxEnrollmentsPerUser,
- componentInfo,
- prop.sensorType,
- prop.halControlsIllumination,
- true /* resetLockoutRequiresHardwareAuthToken */,
- !workaroundLocations.isEmpty() ? workaroundLocations :
- Arrays.stream(prop.sensorLocations).map(location ->
- new SensorLocationInternal(
- location.display,
- location.sensorLocationX,
- location.sensorLocationY,
- location.sensorRadius))
- .collect(Collectors.toList()));
- final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher,
- mBiometricContext);
- final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
- sensor.getLazySession().get().getUserId();
- mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId,
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
- }
- });
- Slog.d(getTag(), "Added: " + internalProp);
+ } else {
+ final List<SensorLocationInternal> workaroundLocations =
+ getWorkaroundSensorProps(mContext);
+
+ for (SensorProps prop : props) {
+ final int sensorId = prop.commonProps.sensorId;
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (ComponentInfo info : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
+ final FingerprintSensorPropertiesInternal internalProp =
+ new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
+ prop.commonProps.sensorStrength,
+ prop.commonProps.maxEnrollmentsPerUser,
+ componentInfo,
+ prop.sensorType,
+ prop.halControlsIllumination,
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ !workaroundLocations.isEmpty() ? workaroundLocations :
+ Arrays.stream(prop.sensorLocations).map(
+ location -> new SensorLocationInternal(
+ location.display,
+ location.sensorLocationX,
+ location.sensorLocationY,
+ location.sensorRadius))
+ .collect(Collectors.toList()));
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext,
+ mHandler, internalProp, mLockoutResetDispatcher,
+ gestureAvailabilityDispatcher, mBiometricContext);
+ sensor.init(gestureAvailabilityDispatcher,
+ mLockoutResetDispatcher);
+ final int sessionUserId =
+ sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString());
+ }
}
}
+ private void addHidlSensors(@NonNull SensorProps prop,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ final int sensorId = prop.commonProps.sensorId;
+ final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/"
+ + sensorId, this, mContext, mHandler,
+ prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher,
+ mBiometricContext, resetLockoutRequiresHardwareAuthToken,
+ () -> scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
+ null /* callback */));
+ sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher);
+ final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString());
+ }
+
+ private void addAidlSensors(@NonNull SensorProps prop,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ List<SensorLocationInternal> workaroundLocations,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ final int sensorId = prop.commonProps.sensorId;
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId,
+ this, mContext, mHandler,
+ prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher,
+ mBiometricContext, workaroundLocations,
+ resetLockoutRequiresHardwareAuthToken);
+ sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher);
+ final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString());
+ }
+
private String getTag() {
return "FingerprintProvider/" + mHalInstanceName;
}
@@ -351,7 +432,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
}
- private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
+ /**
+ * Schedules FingerprintGetAuthenticatorIdClient for specific sensor and user.
+ */
+ protected void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
mHandler.post(() -> {
final FingerprintGetAuthenticatorIdClient client =
new FingerprintGetAuthenticatorIdClient(mContext,
@@ -387,8 +471,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken,
- mFingerprintSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
- Utils.getCurrentStrength(sensorId));
+ mFingerprintSensors.get(sensorId).getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcher, Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client);
});
}
@@ -443,18 +527,23 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mFingerprintSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController,
mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason);
- scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
- mBiometricStateCallback, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- ClientMonitorCallback.super.onClientFinished(clientMonitor, success);
- if (success) {
- scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
- scheduleInvalidationRequest(sensorId, userId);
- }
- }
- }));
+ if (Flags.deHidl()) {
+ scheduleForSensor(sensorId, client, mBiometricStateCallback);
+ } else {
+ scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
+ mBiometricStateCallback, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ ClientMonitorCallback.super.onClientFinished(
+ clientMonitor, success);
+ if (success) {
+ scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
+ scheduleInvalidationRequest(sensorId, userId);
+ }
+ }
+ }));
+ }
});
return id;
}
@@ -497,6 +586,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final int userId = options.getUserId();
final int sensorId = options.getSensorId();
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
+ final LockoutTracker lockoutTracker;
+ if (Flags.deHidl()) {
+ lockoutTracker = mFingerprintSensors.get(sensorId)
+ .getLockoutTracker(true /* forAuth */);
+ } else {
+ lockoutTracker = null;
+ }
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mFingerprintSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
@@ -510,7 +606,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mFingerprintSensors.get(sensorId).getSensorProperties(), mHandler,
Utils.getCurrentStrength(sensorId),
SystemClock.elapsedRealtimeClock(),
- null /* lockoutTracker */);
+ lockoutTracker);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
@@ -636,6 +732,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@Override
public boolean isHardwareDetected(int sensorId) {
+ if (Flags.deHidl()) {
+ return mFingerprintSensors.get(sensorId).isHardwareDetected(mHalInstanceName);
+ }
return hasHalInstance();
}
@@ -674,8 +773,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@Override
public int getLockoutModeForUser(int sensorId, int userId) {
- return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
- Utils.getCurrentStrength(sensorId));
+ if (Flags.deHidl()) {
+ return mFingerprintSensors.get(sensorId).getLockoutModeForUser(userId);
+ } else {
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(sensorId));
+ }
}
@Override
@@ -829,6 +932,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mTestHalEnabled = enabled;
}
+ public boolean getTestHalEnabled() {
+ return mTestHalEnabled;
+ }
+
// TODO(b/174868353): workaround for gaps in HAL interface (remove and get directly from HAL)
// reads values via an overlay instead of querying the HAL
@NonNull
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 ec225a60d54b..387ae12147ae 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
@@ -60,7 +60,8 @@ public class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession>
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, biometricLogger, biometricContext);
- mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
+ mHardwareAuthToken = hardwareAuthToken == null ? null :
+ HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mBiometricStrength = biometricStrength;
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 893cb8f9b4fc..dd887bb05c12 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
@@ -21,21 +21,28 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
+import android.hardware.biometrics.SensorLocationInternal;
+import android.hardware.biometrics.common.ComponentInfo;
+import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -48,15 +55,20 @@ import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
/**
* Maintains the state of a single sensor within an instance of the
@@ -73,15 +85,17 @@ public class Sensor {
@NonNull private final IBinder mToken;
@NonNull private final Handler mHandler;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
- @NonNull private final UserAwareBiometricScheduler mScheduler;
- @NonNull private final LockoutCache mLockoutCache;
+ @NonNull private BiometricScheduler mScheduler;
+ @NonNull private LockoutTracker mLockoutTracker;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
+ @NonNull private final BiometricContext mBiometricContext;
@Nullable AidlSession mCurrentSession;
- @NonNull private final Supplier<AidlSession> mLazySession;
+ @NonNull private Supplier<AidlSession> mLazySession;
- Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+ public Sensor(@NonNull String tag, @NonNull FingerprintProvider provider,
+ @NonNull Context context, @NonNull Handler handler,
+ @NonNull FingerprintSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull BiometricContext biometricContext, AidlSession session) {
@@ -91,8 +105,38 @@ public class Sensor {
mToken = new Binder();
mHandler = handler;
mSensorProperties = sensorProperties;
- mLockoutCache = new LockoutCache();
- mScheduler = new UserAwareBiometricScheduler(tag,
+ mBiometricContext = biometricContext;
+ mAuthenticatorIds = new HashMap<>();
+ mCurrentSession = session;
+ }
+
+ Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
+ this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
+ gestureAvailabilityDispatcher, biometricContext, null);
+ }
+
+ Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull SensorProps sensorProp,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext,
+ @NonNull List<SensorLocationInternal> workaroundLocation,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ this(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(sensorProp,
+ workaroundLocation, resetLockoutRequiresHardwareAuthToken),
+ lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, null);
+ }
+
+ /**
+ * Initialize biometric scheduler, lockout tracker and session for the sensor.
+ */
+ public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ LockoutResetDispatcher lockoutResetDispatcher) {
+ mScheduler = new UserAwareBiometricScheduler(mTag,
BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties),
gestureAvailabilityDispatcher,
() -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
@@ -102,7 +146,7 @@ public class Sensor {
public StopUserClient<?> getStopUserClient(int userId) {
return new FingerprintStopUserClient(mContext, mLazySession, mToken,
userId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), biometricContext,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
() -> mCurrentSession = null);
}
@@ -111,13 +155,38 @@ public class Sensor {
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
final int sensorId = mSensorProperties.sensorId;
- final AidlResponseHandler resultController = new AidlResponseHandler(
- mContext, mScheduler, sensorId, newUserId,
- mLockoutCache, lockoutResetDispatcher,
- biometricContext.getAuthSessionCoordinator(), () -> {
- Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
- mCurrentSession = null;
- });
+ final AidlResponseHandler resultController;
+
+ if (Flags.deHidl()) {
+ resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutTracker, lockoutResetDispatcher,
+ mBiometricContext.getAuthSessionCoordinator(), () -> {},
+ new AidlResponseHandler.AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {
+ mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId,
+ newUserId);
+ mProvider.scheduleInvalidationRequest(sensorId,
+ newUserId);
+ }
+
+ @Override
+ public void onHardwareUnavailable() {
+ Slog.e(mTag,
+ "Fingerprint sensor hardware unavailable.");
+ mCurrentSession = null;
+ }
+ });
+ } else {
+ resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutTracker, lockoutResetDispatcher,
+ mBiometricContext.getAuthSessionCoordinator(), () -> {
+ Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+ mCurrentSession = null;
+ });
+ }
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
(userIdStarted, newSession, halInterfaceVersion) -> {
@@ -133,40 +202,58 @@ public class Sensor {
+ "sensor: "
+ sensorId
+ ", user: " + userIdStarted);
- provider.scheduleInvalidationRequest(sensorId,
+ mProvider.scheduleInvalidationRequest(sensorId,
userIdStarted);
}
};
- return new FingerprintStartUserClient(mContext, provider::getHalInstance,
+ return new FingerprintStartUserClient(mContext, mProvider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), biometricContext,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
resultController, userStartedCallback);
}
});
- mAuthenticatorIds = new HashMap<>();
+ mLockoutTracker = new LockoutCache();
mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
- mCurrentSession = session;
}
- Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull BiometricContext biometricContext) {
- this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
- gestureAvailabilityDispatcher, biometricContext, null);
+ protected static FingerprintSensorPropertiesInternal getFingerprintSensorPropertiesInternal(
+ SensorProps prop, List<SensorLocationInternal> workaroundLocations,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (ComponentInfo info : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
+ return new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
+ prop.commonProps.sensorStrength,
+ prop.commonProps.maxEnrollmentsPerUser,
+ componentInfo,
+ prop.sensorType,
+ prop.halControlsIllumination,
+ resetLockoutRequiresHardwareAuthToken,
+ !workaroundLocations.isEmpty() ? workaroundLocations :
+ Arrays.stream(prop.sensorLocations).map(location ->
+ new SensorLocationInternal(
+ location.display,
+ location.sensorLocationX,
+ location.sensorLocationY,
+ location.sensorRadius))
+ .collect(Collectors.toList()));
}
- @NonNull Supplier<AidlSession> getLazySession() {
+ @NonNull public Supplier<AidlSession> getLazySession() {
return mLazySession;
}
- @NonNull FingerprintSensorPropertiesInternal getSensorProperties() {
+ @NonNull public FingerprintSensorPropertiesInternal getSensorProperties() {
return mSensorProperties;
}
- @Nullable AidlSession getSessionForUser(int userId) {
+ @Nullable protected AidlSession getSessionForUser(int userId) {
if (mCurrentSession != null && mCurrentSession.getUserId() == userId) {
return mCurrentSession;
} else {
@@ -180,15 +267,18 @@ public class Sensor {
biometricStateCallback, mProvider, this);
}
- @NonNull BiometricScheduler getScheduler() {
+ @NonNull public BiometricScheduler getScheduler() {
return mScheduler;
}
- @NonNull LockoutCache getLockoutCache() {
- return mLockoutCache;
+ @NonNull protected LockoutTracker getLockoutTracker(boolean forAuth) {
+ if (forAuth) {
+ return null;
+ }
+ return mLockoutTracker;
}
- @NonNull Map<Integer, Long> getAuthenticatorIds() {
+ @NonNull public Map<Integer, Long> getAuthenticatorIds() {
return mAuthenticatorIds;
}
@@ -262,4 +352,49 @@ public class Sensor {
mScheduler.reset();
mCurrentSession = null;
}
+
+ @NonNull protected Handler getHandler() {
+ return mHandler;
+ }
+
+ @NonNull protected Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Returns true if the sensor hardware is detected.
+ */
+ protected boolean isHardwareDetected(String halInstance) {
+ if (mTestHalEnabled) {
+ return true;
+ }
+ return (ServiceManager.checkService(IFingerprint.DESCRIPTOR + "/" + halInstance)
+ != null);
+ }
+
+ @NonNull protected BiometricContext getBiometricContext() {
+ return mBiometricContext;
+ }
+
+ /**
+ * Returns lockout mode of this sensor.
+ */
+ @LockoutTracker.LockoutMode
+ public int getLockoutModeForUser(int userId) {
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(mSensorProperties.sensorId));
+ }
+
+ public void setScheduler(BiometricScheduler scheduler) {
+ mScheduler = scheduler;
+ }
+
+ public void setLazySession(
+ Supplier<AidlSession> lazySession) {
+ mLazySession = lazySession;
+ }
+
+ public FingerprintProvider getProvider() {
+ return mProvider;
+ }
}
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 a4e602553101..5c5b9928f57a 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
@@ -29,7 +29,8 @@ 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;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
import java.io.File;
import java.util.Map;
@@ -38,7 +39,8 @@ import java.util.function.Supplier;
/**
* Sets the HAL's current active user, and updates the framework's authenticatorId cache.
*/
-public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometricsFingerprint> {
+public class FingerprintUpdateActiveUserClient extends
+ StartUserClient<IBiometricsFingerprint, AidlSession> {
private static final String TAG = "FingerprintUpdateActiveUserClient";
private static final String FP_DATA_DIR = "fpdata";
@@ -53,11 +55,24 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
@NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- Supplier<Integer> currentUserId,
+ @NonNull Supplier<Integer> currentUserId,
boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
boolean forceUpdateAuthenticatorId) {
- super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, logger, biometricContext);
+ this(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, currentUserId,
+ hasEnrolledBiometrics, authenticatorIds, forceUpdateAuthenticatorId,
+ (newUserId, newUser, halInterfaceVersion) -> {});
+ }
+
+ FingerprintUpdateActiveUserClient(@NonNull Context context,
+ @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull Supplier<Integer> currentUserId,
+ boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
+ boolean forceUpdateAuthenticatorId,
+ @NonNull UserStartedCallback<AidlSession> userStartedCallback) {
+ super(context, lazyDaemon, null /* token */, userId, sensorId, logger, biometricContext,
+ userStartedCallback);
mCurrentUserId = currentUserId;
mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId;
mHasEnrolledBiometrics = hasEnrolledBiometrics;
@@ -70,6 +85,7 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) {
Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning");
+ mUserStartedCallback.onUserStarted(getTargetUserId(), null, 0);
callback.onClientFinished(this, true /* success */);
return;
}
@@ -119,6 +135,7 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath());
mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics
? getFreshDaemon().getAuthenticatorId() : 0L);
+ mUserStartedCallback.onUserStarted(targetId, null, 0);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveGroup: " + e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java
index c3e5cbe7daf4..e9a48e79d245 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
import java.util.ArrayList;
@@ -73,12 +74,12 @@ public class HidlToAidlCallbackConverter extends IBiometricsFingerprintClientCal
@Override
public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
- mAidlResponseHandler.onEnrollmentsRemoved(new int[]{fingerId});
+ mAidlResponseHandler.onEnrollmentRemoved(fingerId, remaining);
}
@Override
public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
- mAidlResponseHandler.onEnrollmentsEnumerated(new int[]{fingerId});
+ mAidlResponseHandler.onEnrollmentEnumerated(fingerId, remaining);
}
void onChallengeGenerated(long challenge) {
@@ -92,4 +93,8 @@ public class HidlToAidlCallbackConverter extends IBiometricsFingerprintClientCal
void onResetLockout() {
mAidlResponseHandler.onLockoutCleared();
}
+
+ <T extends BaseClientMonitor> void unsupportedClientScheduled(Class<T> className) {
+ mAidlResponseHandler.onUnsupportedClientScheduled(className);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
new file mode 100644
index 000000000000..0bb6141583d5
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.hidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.Handler;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+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 com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.StopUserClient;
+import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
+import com.android.server.biometrics.sensors.fingerprint.aidl.Sensor;
+
+import java.util.ArrayList;
+
+/**
+ * Convert HIDL sensor configurations to an AIDL Sensor.
+ */
+public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRecipient {
+ private static final String TAG = "HidlToAidlSensorAdapter";
+
+ private final Runnable mInternalCleanupRunnable;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ private LockoutFrameworkImpl mLockoutTracker;
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
+ private final AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+ private IBiometricsFingerprint mDaemon;
+ private AidlSession mSession;
+
+ private final StartUserClient.UserStartedCallback<AidlSession> mUserStartedCallback =
+ (newUserId, newUser, halInterfaceVersion) -> {
+ if (mCurrentUserId != newUserId) {
+ handleUserChanged(newUserId);
+ }
+ };
+
+ public HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider,
+ @NonNull Context context, @NonNull Handler handler,
+ @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresHardwareAuthToken,
+ @NonNull Runnable internalCleanupRunnable) {
+ this(tag, provider, context, handler, prop, lockoutResetDispatcher,
+ gestureAvailabilityDispatcher, biometricContext,
+ resetLockoutRequiresHardwareAuthToken, internalCleanupRunnable,
+ new AuthSessionCoordinator(), null /* daemon */,
+ null /* onEnrollSuccessCallback */);
+ }
+
+ @VisibleForTesting
+ HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider,
+ @NonNull Context context, @NonNull Handler handler,
+ @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresHardwareAuthToken,
+ @NonNull Runnable internalCleanupRunnable,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @Nullable IBiometricsFingerprint daemon,
+ @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) {
+ super(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(prop,
+ new ArrayList<>(), resetLockoutRequiresHardwareAuthToken),
+ lockoutResetDispatcher,
+ gestureAvailabilityDispatcher,
+ biometricContext, null /* session */);
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mInternalCleanupRunnable = internalCleanupRunnable;
+ mAuthSessionCoordinator = authSessionCoordinator;
+ mDaemon = daemon;
+ mAidlResponseHandlerCallback = aidlResponseHandlerCallback == null
+ ? new AidlResponseHandler.AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {
+ getScheduler()
+ .scheduleClientMonitor(getFingerprintUpdateActiveUserClient(
+ mCurrentUserId, true /* forceUpdateAuthenticatorIds */));
+ }
+
+ @Override
+ public void onHardwareUnavailable() {
+ mDaemon = null;
+ mSession = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ } : aidlResponseHandlerCallback;
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.d(TAG, "HAL died.");
+ mSession = null;
+ mDaemon = null;
+ }
+
+ @Override
+ @LockoutTracker.LockoutMode
+ public int getLockoutModeForUser(int userId) {
+ return mLockoutTracker.getLockoutModeForUser(userId);
+ }
+
+ @Override
+ public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ LockoutResetDispatcher lockoutResetDispatcher) {
+ setLazySession(this::getSession);
+ setScheduler(new UserAwareBiometricScheduler(TAG,
+ BiometricScheduler.sensorTypeFromFingerprintProperties(getSensorProperties()),
+ gestureAvailabilityDispatcher, () -> mCurrentUserId, getUserSwitchCallback()));
+ mLockoutTracker = new LockoutFrameworkImpl(getContext(),
+ userId -> mLockoutResetDispatcher.notifyLockoutResetCallbacks(
+ getSensorProperties().sensorId));
+ }
+
+ @Override
+ @Nullable
+ protected AidlSession getSessionForUser(int userId) {
+ if (mSession != null && mSession.getUserId() == userId) {
+ return mSession;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected boolean isHardwareDetected(String halInstance) {
+ return getIBiometricsFingerprint() != null;
+ }
+
+ @NonNull
+ @Override
+ protected LockoutTracker getLockoutTracker(boolean forAuth) {
+ return mLockoutTracker;
+ }
+
+ private synchronized AidlSession getSession() {
+ if (mSession != null && mDaemon != null) {
+ return mSession;
+ } else {
+ return mSession = new AidlSession(this::getIBiometricsFingerprint,
+ mCurrentUserId, getAidlResponseHandler());
+ }
+ }
+
+ private AidlResponseHandler getAidlResponseHandler() {
+ return new AidlResponseHandler(getContext(),
+ getScheduler(),
+ getSensorProperties().sensorId,
+ mCurrentUserId,
+ mLockoutTracker,
+ mLockoutResetDispatcher,
+ mAuthSessionCoordinator,
+ () -> {}, mAidlResponseHandlerCallback);
+ }
+
+ @VisibleForTesting IBiometricsFingerprint getIBiometricsFingerprint() {
+ if (getProvider().getTestHalEnabled()) {
+ final TestHal testHal = new TestHal(getContext(), getSensorProperties().sensorId);
+ testHal.setNotify(new HidlToAidlCallbackConverter(getAidlResponseHandler()));
+ return testHal;
+ }
+
+ if (mDaemon != null) {
+ return mDaemon;
+ }
+
+ try {
+ mDaemon = IBiometricsFingerprint.getService();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get fingerprint HAL", e);
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened.
+ Slog.w(TAG, "NoSuchElementException", e);
+ }
+
+ if (mDaemon == null) {
+ Slog.w(TAG, "Fingerprint HAL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
+
+ Slog.d(TAG, "Fingerprint HAL ready");
+
+ scheduleLoadAuthenticatorIds();
+ mInternalCleanupRunnable.run();
+ return mDaemon;
+ }
+
+ private UserAwareBiometricScheduler.UserSwitchCallback getUserSwitchCallback() {
+ return new UserAwareBiometricScheduler.UserSwitchCallback() {
+ @NonNull
+ @Override
+ public StopUserClient<?> getStopUserClient(int userId) {
+ return new StopUserClient<IBiometricsFingerprint>(getContext(),
+ HidlToAidlSensorAdapter.this::getIBiometricsFingerprint,
+ null /* token */, userId, getSensorProperties().sensorId,
+ BiometricLogger.ofUnknown(getContext()), getBiometricContext(),
+ () -> {
+ mCurrentUserId = UserHandle.USER_NULL;
+ mSession = null;
+ }) {
+ @Override
+ public void start(@NonNull ClientMonitorCallback callback) {
+ super.start(callback);
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ onUserStopped();
+ }
+
+ @Override
+ public void unableToStart() {
+ getCallback().onClientFinished(this, false /* success */);
+ }
+ };
+ }
+
+ @NonNull
+ @Override
+ public StartUserClient<?, ?> getStartUserClient(int newUserId) {
+ return getFingerprintUpdateActiveUserClient(newUserId,
+ false /* forceUpdateAuthenticatorId */);
+ }
+ };
+ }
+
+ private FingerprintUpdateActiveUserClient getFingerprintUpdateActiveUserClient(int newUserId,
+ boolean forceUpdateAuthenticatorIds) {
+ return new FingerprintUpdateActiveUserClient(getContext(),
+ this::getIBiometricsFingerprint, newUserId, TAG,
+ getSensorProperties().sensorId, BiometricLogger.ofUnknown(getContext()),
+ getBiometricContext(), () -> mCurrentUserId,
+ !FingerprintUtils.getInstance(getSensorProperties().sensorId)
+ .getBiometricsForUser(getContext(),
+ newUserId).isEmpty(), getAuthenticatorIds(), forceUpdateAuthenticatorIds,
+ mUserStartedCallback);
+ }
+
+ private void scheduleLoadAuthenticatorIds() {
+ getHandler().post(() -> {
+ for (UserInfo user : UserManager.get(getContext()).getAliveUsers()) {
+ final int targetUserId = user.id;
+ if (!getAuthenticatorIds().containsKey(targetUserId)) {
+ getScheduler().scheduleClientMonitor(getFingerprintUpdateActiveUserClient(
+ targetUserId, true /* forceUpdateAuthenticatorIds */));
+ }
+ }
+ });
+ }
+
+ @VisibleForTesting void handleUserChanged(int newUserId) {
+ Slog.d(TAG, "User changed. Current user is " + newUserId);
+ mSession = null;
+ mCurrentUserId = newUserId;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
index b48d232e65af..2fc00e126354 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
@@ -25,20 +25,25 @@ import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintGetAuthenticatorIdClient;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintInvalidationClient;
import java.util.function.Supplier;
/**
- * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation.
+ * Adapter to convert HIDL methods into AIDL interface {@link ISession}.
*/
-public class AidlToHidlAdapter implements ISession {
- private final String TAG = "AidlToHidlAdapter";
+public class HidlToAidlSessionAdapter implements ISession {
+
+ private final String TAG = "HidlToAidlSessionAdapter";
+
@VisibleForTesting
static final int ENROLL_TIMEOUT_SEC = 60;
@NonNull
@@ -46,22 +51,13 @@ public class AidlToHidlAdapter implements ISession {
private final int mUserId;
private HidlToAidlCallbackConverter mHidlToAidlCallbackConverter;
- public AidlToHidlAdapter(Supplier<IBiometricsFingerprint> session, int userId,
+ public HidlToAidlSessionAdapter(Supplier<IBiometricsFingerprint> session, int userId,
AidlResponseHandler aidlResponseHandler) {
mSession = session;
mUserId = userId;
setCallback(aidlResponseHandler);
}
- private void setCallback(AidlResponseHandler aidlResponseHandler) {
- mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
- try {
- mSession.get().setNotify(mHidlToAidlCallbackConverter);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to set callback");
- }
- }
-
@Override
public IBinder asBinder() {
return null;
@@ -125,12 +121,16 @@ public class AidlToHidlAdapter implements ISession {
@Override
public void getAuthenticatorId() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "getAuthenticatorId unsupported in HIDL");
+ mHidlToAidlCallbackConverter.unsupportedClientScheduled(
+ FingerprintGetAuthenticatorIdClient.class);
}
@Override
public void invalidateAuthenticatorId() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "invalidateAuthenticatorId unsupported in HIDL");
+ mHidlToAidlCallbackConverter.unsupportedClientScheduled(
+ FingerprintInvalidationClient.class);
}
@Override
@@ -140,72 +140,92 @@ public class AidlToHidlAdapter implements ISession {
@Override
public void close() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "close unsupported in HIDL");
}
@Override
public void onUiReady() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onUiReady unsupported in HIDL");
}
@Override
public ICancellationSignal authenticateWithContext(long operationId, OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Log.e(TAG, "authenticateWithContext unsupported in HIDL");
+ return authenticate(operationId);
}
@Override
public ICancellationSignal enrollWithContext(HardwareAuthToken hat, OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Log.e(TAG, "enrollWithContext unsupported in HIDL");
+ return enroll(hat);
}
@Override
public ICancellationSignal detectInteractionWithContext(OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Log.e(TAG, "enrollWithContext unsupported in HIDL");
+ return detectInteraction();
}
@Override
public void onPointerDownWithContext(PointerContext context) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onPointerDownWithContext unsupported in HIDL");
+ onPointerDown(context.pointerId, (int) context.x, (int) context.y, context.minor,
+ context.major);
}
@Override
public void onPointerUpWithContext(PointerContext context) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onPointerUpWithContext unsupported in HIDL");
+ onPointerUp(context.pointerId);
}
@Override
public void onContextChanged(OperationContext context) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onContextChanged unsupported in HIDL");
}
@Override
public void onPointerCancelWithContext(PointerContext context) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onPointerCancelWithContext unsupported in HIDL");
}
@Override
public void setIgnoreDisplayTouches(boolean shouldIgnore) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "setIgnoreDisplayTouches unsupported in HIDL");
}
@Override
public int getInterfaceVersion() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "getInterfaceVersion unsupported in HIDL");
return 0;
}
@Override
public String getInterfaceHash() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "getInterfaceHash unsupported in HIDL");
return null;
}
+ private void setCallback(AidlResponseHandler aidlResponseHandler) {
+ mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
+ try {
+ if (mSession.get() != null) {
+ long halId = mSession.get().setNotify(mHidlToAidlCallbackConverter);
+ Slog.d(TAG, "Fingerprint HAL ready, HAL ID: " + halId);
+ if (halId == 0) {
+ Slog.d(TAG, "Unable to set HIDL callback.");
+ }
+ } else {
+ Slog.e(TAG, "Unable to set HIDL callback. HIDL daemon is null.");
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to set callback");
+ }
+ }
+
private class Cancellation extends ICancellationSignal.Stub {
Cancellation() {}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
index 0730c672acd9..2f77275890dd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
@@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import android.annotation.NonNull;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -31,15 +32,18 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.sensors.LockoutTracker;
+import java.util.function.Function;
+
/**
* Tracks and enforces biometric lockout for biometric sensors that do not support lockout in the
* HAL.
*/
public class LockoutFrameworkImpl implements LockoutTracker {
- private static final String TAG = "LockoutTracker";
+ private static final String TAG = "LockoutFrameworkImpl";
private static final String ACTION_LOCKOUT_RESET =
"com.android.server.biometrics.sensors.fingerprint.ACTION_LOCKOUT_RESET";
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
@@ -65,22 +69,32 @@ public class LockoutFrameworkImpl implements LockoutTracker {
void onLockoutReset(int userId);
}
- private final Context mContext;
private final LockoutResetCallback mLockoutResetCallback;
private final SparseBooleanArray mTimedLockoutCleared;
private final SparseIntArray mFailedAttempts;
private final AlarmManager mAlarmManager;
private final LockoutReceiver mLockoutReceiver;
private final Handler mHandler;
+ private final Function<Integer, PendingIntent> mLockoutResetIntent;
+
+ public LockoutFrameworkImpl(@NonNull Context context,
+ @NonNull LockoutResetCallback lockoutResetCallback) {
+ this(context, lockoutResetCallback, (userId) -> PendingIntent.getBroadcast(context, userId,
+ new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
+ }
- public LockoutFrameworkImpl(Context context, LockoutResetCallback lockoutResetCallback) {
- mContext = context;
+ @VisibleForTesting
+ LockoutFrameworkImpl(@NonNull Context context,
+ @NonNull LockoutResetCallback lockoutResetCallback,
+ @NonNull Function<Integer, PendingIntent> lockoutResetIntent) {
mLockoutResetCallback = lockoutResetCallback;
mTimedLockoutCleared = new SparseBooleanArray();
mFailedAttempts = new SparseIntArray();
mAlarmManager = context.getSystemService(AlarmManager.class);
mLockoutReceiver = new LockoutReceiver();
mHandler = new Handler(Looper.getMainLooper());
+ mLockoutResetIntent = lockoutResetIntent;
context.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
RESET_FINGERPRINT_LOCKOUT, null /* handler */, Context.RECEIVER_EXPORTED);
@@ -129,34 +143,18 @@ public class LockoutFrameworkImpl implements LockoutTracker {
return LOCKOUT_NONE;
}
- /**
- * Clears lockout for Fingerprint HIDL HAL
- */
@Override
- public void setLockoutModeForUser(int userId, int mode) {
- mFailedAttempts.put(userId, 0);
- mTimedLockoutCleared.put(userId, true);
- // If we're asked to reset failed attempts externally (i.e. from Keyguard),
- // the alarm might still be pending; remove it.
- cancelLockoutResetForUser(userId);
- mLockoutResetCallback.onLockoutReset(userId);
- }
+ public void setLockoutModeForUser(int userId, int mode) {}
private void cancelLockoutResetForUser(int userId) {
- mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
+ mAlarmManager.cancel(mLockoutResetIntent.apply(userId));
}
private void scheduleLockoutResetForUser(int userId) {
mHandler.post(() -> {
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
- getLockoutResetIntentForUser(userId));
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+ mLockoutResetIntent.apply(userId));
});
}
-
- private PendingIntent getLockoutResetIntentForUser(int userId) {
- return PendingIntent.getBroadcast(mContext, userId,
- new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- }
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index c5170585a1b3..b7ece2ea65b1 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1299,7 +1299,7 @@ public class Vpn {
}
try {
- mNms.denyProtect(mOwnerUID);
+ mNetd.networkSetProtectDeny(mOwnerUID);
} catch (Exception e) {
Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
}
@@ -1309,7 +1309,7 @@ public class Vpn {
mOwnerUID = getAppUid(mContext, newPackage, mUserId);
mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
try {
- mNms.allowProtect(mOwnerUID);
+ mNetd.networkSetProtectAllow(mOwnerUID);
} catch (Exception e) {
Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
}
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index 1bd556bdcc4f..4e341a9c19b4 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -56,6 +56,15 @@ public class DisplayOffloadSessionImpl implements DisplayManagerInternal.Display
}
}
+ @Override
+ public boolean blockScreenOn(Runnable unblocker) {
+ if (mDisplayOffloader == null) {
+ return false;
+ }
+ mDisplayOffloader.onBlockingScreenOn(unblocker);
+ return true;
+ }
+
/**
* Start the offload session. The method returns if the session is already active.
* @return Whether the session was started successfully
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 52c53f3d658e..6d09cc9d37ba 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -126,6 +126,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
// To enable these logs, run:
// 'adb shell setprop persist.log.tag.DisplayPowerController2 DEBUG && adb reboot'
private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+ private static final String SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME =
+ "Screen on blocked by displayoffload";
// If true, uses the color fade on animation.
// We might want to turn this off if we cannot get a guarantee that the screen
@@ -155,6 +157,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15;
private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16;
private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17;
+ private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18;
@@ -339,6 +342,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
// we are waiting for a callback to release it and unblock the screen.
private ScreenOnUnblocker mPendingScreenOnUnblocker;
private ScreenOffUnblocker mPendingScreenOffUnblocker;
+ private Runnable mPendingScreenOnUnblockerByDisplayOffload;
// True if we were in the process of turning off the screen.
// This allows us to recover more gracefully from situations where we abort
@@ -348,10 +352,15 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
// The elapsed real time when the screen on was blocked.
private long mScreenOnBlockStartRealTime;
private long mScreenOffBlockStartRealTime;
+ private long mScreenOnBlockByDisplayOffloadStartRealTime;
// Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields.
private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED;
+ // Used to deduplicate the displayoffload blocking screen on logic. One block per turning on.
+ // This value is reset when screen on is reported or the blocking is cancelled.
+ private boolean mScreenTurningOnWasBlockedByDisplayOffload;
+
// If the last recorded screen state was dozing or not.
private boolean mDozing;
@@ -472,7 +481,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
private boolean mBootCompleted;
private final DisplayManagerFlags mFlags;
- private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+ private DisplayOffloadSession mDisplayOffloadSession;
/**
* Creates the display power controller.
@@ -772,6 +781,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
@Override
public void setDisplayOffloadSession(DisplayOffloadSession session) {
+ if (session == mDisplayOffloadSession) {
+ return;
+ }
+ unblockScreenOnByDisplayOffload();
mDisplayOffloadSession = session;
}
@@ -1735,6 +1748,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
// reporting the display is ready because we only need to ensure the screen is in the
// right power state even as it continues to converge on the desired brightness.
final boolean ready = mPendingScreenOnUnblocker == null
+ && mPendingScreenOnUnblockerByDisplayOffload == null
&& (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted()
&& !mColorFadeOffAnimator.isStarted()))
&& mPowerState.waitUntilClean(mCleanListener);
@@ -1983,15 +1997,69 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
}
}
+ private void blockScreenOnByDisplayOffload(DisplayOffloadSession displayOffloadSession) {
+ if (mPendingScreenOnUnblockerByDisplayOffload != null || displayOffloadSession == null) {
+ return;
+ }
+ mScreenTurningOnWasBlockedByDisplayOffload = true;
+
+ Trace.asyncTraceBegin(
+ Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
+ mScreenOnBlockByDisplayOffloadStartRealTime = SystemClock.elapsedRealtime();
+
+ mPendingScreenOnUnblockerByDisplayOffload =
+ () -> onDisplayOffloadUnblockScreenOn(displayOffloadSession);
+ if (!displayOffloadSession.blockScreenOn(mPendingScreenOnUnblockerByDisplayOffload)) {
+ mPendingScreenOnUnblockerByDisplayOffload = null;
+ long delay =
+ SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime;
+ Slog.w(mTag, "Tried blocking screen on for offloading but failed. So, end trace after "
+ + delay + " ms.");
+ Trace.asyncTraceEnd(
+ Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
+ return;
+ }
+ Slog.i(mTag, "Blocking screen on for offloading.");
+ }
+
+ private void onDisplayOffloadUnblockScreenOn(DisplayOffloadSession displayOffloadSession) {
+ Message msg = mHandler.obtainMessage(MSG_OFFLOADING_SCREEN_ON_UNBLOCKED,
+ displayOffloadSession);
+ mHandler.sendMessage(msg);
+ }
+
+ private void unblockScreenOnByDisplayOffload() {
+ if (mPendingScreenOnUnblockerByDisplayOffload == null) {
+ return;
+ }
+ mPendingScreenOnUnblockerByDisplayOffload = null;
+ long delay = SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime;
+ Slog.i(mTag, "Unblocked screen on for offloading after " + delay + " ms");
+ Trace.asyncTraceEnd(
+ Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
+ }
+
private boolean setScreenState(int state) {
return setScreenState(state, false /*reportOnly*/);
}
private boolean setScreenState(int state, boolean reportOnly) {
final boolean isOff = (state == Display.STATE_OFF);
+ final boolean isOn = (state == Display.STATE_ON);
+ final boolean changed = mPowerState.getScreenState() != state;
- if (mPowerState.getScreenState() != state
- || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
+ // If the screen is turning on, give displayoffload a chance to do something before the
+ // screen actually turns on.
+ // TODO(b/316941732): add tests for this displayoffload screen-on blocker.
+ if (isOn && changed && !mScreenTurningOnWasBlockedByDisplayOffload) {
+ blockScreenOnByDisplayOffload(mDisplayOffloadSession);
+ } else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) {
+ // No longer turning screen on, so unblock previous screen on blocking immediately.
+ unblockScreenOnByDisplayOffload();
+ mScreenTurningOnWasBlockedByDisplayOffload = false;
+ }
+
+ if (changed || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
// If we are trying to turn screen off, give policy a chance to do something before we
// actually turn the screen off.
if (isOff && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
@@ -2007,8 +2075,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
}
}
- if (!reportOnly && mPowerState.getScreenState() != state
- && readyToUpdateDisplayState()) {
+ if (!reportOnly && changed && readyToUpdateDisplayState()
+ && mPendingScreenOffUnblocker == null
+ && mPendingScreenOnUnblockerByDisplayOffload == null) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
String propertyKey = "debug.tracing.screen_state";
@@ -2060,12 +2129,16 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
}
// Return true if the screen isn't blocked.
- return mPendingScreenOnUnblocker == null;
+ return mPendingScreenOnUnblocker == null
+ && mPendingScreenOnUnblockerByDisplayOffload == null;
}
private void setReportedScreenState(int state) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state);
mReportedScreenStateToPolicy = state;
+ if (state == REPORTED_TO_POLICY_SCREEN_ON) {
+ mScreenTurningOnWasBlockedByDisplayOffload = false;
+ }
}
private void loadAmbientLightSensor() {
@@ -2813,6 +2886,12 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
updatePowerState();
}
break;
+ case MSG_OFFLOADING_SCREEN_ON_UNBLOCKED:
+ if (mDisplayOffloadSession == msg.obj) {
+ unblockScreenOnByDisplayOffload();
+ updatePowerState();
+ }
+ break;
case MSG_CONFIGURE_BRIGHTNESS:
BrightnessConfiguration brightnessConfiguration =
(BrightnessConfiguration) msg.obj;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 22898a65c5de..25576ce9efd6 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.sidekick.SidekickInternal;
+import android.media.MediaDrm;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -242,6 +243,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private SurfaceControl.DisplayMode mActiveSfDisplayMode;
// The active display vsync period in SurfaceFlinger
private float mActiveRenderFrameRate;
+ // The current HDCP level supported by the display, 0 indicates unset
+ // values are defined in hardware/interfaces/drm/aidl/android/hardware/drm/HdcpLevel.aidl
+ private int mConnectedHdcpLevel;
+
private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
new DisplayEventReceiver.FrameRateOverride[0];
@@ -675,8 +680,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.yDpi = mActiveSfDisplayMode.yDpi;
mInfo.deviceProductInfo = mStaticDisplayInfo.deviceProductInfo;
- // Assume that all built-in displays that have secure output (eg. HDCP) also
- // support compositing from gralloc protected buffers.
+ if (mConnectedHdcpLevel != 0) {
+ mStaticDisplayInfo.secure = mConnectedHdcpLevel >= MediaDrm.HDCP_V1;
+ }
if (mStaticDisplayInfo.secure) {
mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
@@ -1093,6 +1099,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
+ public void onHdcpLevelsChangedLocked(int connectedLevel, int maxLevel) {
+ if (updateHdcpLevelsLocked(connectedLevel, maxLevel)) {
+ updateDeviceInfoLocked();
+ }
+ }
+
public boolean updateActiveModeLocked(int activeSfModeId, float renderFrameRate) {
if (mActiveSfDisplayMode.id == activeSfModeId
&& mActiveRenderFrameRate == renderFrameRate) {
@@ -1118,6 +1130,22 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return true;
}
+ public boolean updateHdcpLevelsLocked(int connectedLevel, int maxLevel) {
+ if (connectedLevel > maxLevel) {
+ Slog.w(TAG, "HDCP connected level: " + connectedLevel
+ + " is larger than max level: " + maxLevel
+ + ", ignoring request.");
+ return false;
+ }
+
+ if (mConnectedHdcpLevel == connectedLevel) {
+ return false;
+ }
+
+ mConnectedHdcpLevel = connectedLevel;
+ return true;
+ }
+
public void requestColorModeLocked(int colorMode) {
if (mActiveColorMode == colorMode) {
return;
@@ -1387,6 +1415,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
long renderPeriod);
void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
DisplayEventReceiver.FrameRateOverride[] overrides);
+ void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel);
}
@@ -1420,6 +1449,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
DisplayEventReceiver.FrameRateOverride[] overrides) {
mListener.onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides);
}
+
+ @Override
+ public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) {
+ mListener.onHdcpLevelsChanged(physicalDisplayId, connectedLevel, maxLevel);
+ }
}
private final class LocalDisplayEventListener implements DisplayEventListener {
@@ -1489,6 +1523,26 @@ final class LocalDisplayAdapter extends DisplayAdapter {
device.onFrameRateOverridesChanged(overrides);
}
}
+
+ @Override
+ public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) {
+ if (DEBUG) {
+ Slog.d(TAG, "onHdcpLevelsChanged(physicalDisplayId=" + physicalDisplayId
+ + ", connectedLevel=" + connectedLevel + ", maxLevel=" + maxLevel + ")");
+ }
+ synchronized (getSyncRoot()) {
+ LocalDisplayDevice device = mDevices.get(physicalDisplayId);
+ if (device == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received hdcp levels change for unhandled physical display: "
+ + "physicalDisplayId=" + physicalDisplayId);
+ }
+ return;
+ }
+
+ device.onHdcpLevelsChangedLocked(connectedLevel, maxLevel);
+ }
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 4089a81dfc20..dda50cab2cdd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -167,13 +167,17 @@ public abstract class InputMethodManagerInternal {
/**
* Indicates that the IME window has re-parented to the new target when the IME control changed.
+ *
+ * @param displayId the display hosting the IME window
*/
- public abstract void onImeParentChanged();
+ public abstract void onImeParentChanged(int displayId);
/**
- * Destroys the IME surface.
+ * Destroys the IME surface for the given display.
+ *
+ * @param displayId the display hosting the IME window
*/
- public abstract void removeImeSurface();
+ public abstract void removeImeSurface(int displayId);
/**
* Updates the IME visibility, back disposition and show IME picker status for SystemUI.
@@ -298,11 +302,11 @@ public abstract class InputMethodManagerInternal {
}
@Override
- public void onImeParentChanged() {
+ public void onImeParentChanged(int displayId) {
}
@Override
- public void removeImeSurface() {
+ public void removeImeSurface(int displayId) {
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 16e043cfb64d..0d29b7dca8d4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5671,7 +5671,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public void onImeParentChanged() {
+ public void onImeParentChanged(int displayId) {
synchronized (ImfLock.class) {
// Hide the IME method menu only when the IME surface parent is changed by the
// input target changed, in case seeing the dialog dismiss flickering during
@@ -5683,7 +5683,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public void removeImeSurface() {
+ public void removeImeSurface(int displayId) {
mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
}
diff --git a/services/core/java/com/android/server/location/altitude/AltitudeService.java b/services/core/java/com/android/server/location/altitude/AltitudeService.java
index b321e4dfdf26..97bf3548f891 100644
--- a/services/core/java/com/android/server/location/altitude/AltitudeService.java
+++ b/services/core/java/com/android/server/location/altitude/AltitudeService.java
@@ -19,6 +19,8 @@ package com.android.server.location.altitude;
import android.content.Context;
import android.frameworks.location.altitude.AddMslAltitudeToLocationRequest;
import android.frameworks.location.altitude.AddMslAltitudeToLocationResponse;
+import android.frameworks.location.altitude.GetGeoidHeightRequest;
+import android.frameworks.location.altitude.GetGeoidHeightResponse;
import android.frameworks.location.altitude.IAltitudeService;
import android.location.Location;
import android.location.altitude.AltitudeConverter;
@@ -52,15 +54,47 @@ public class AltitudeService extends IAltitudeService.Stub {
location.setLongitude(request.longitudeDegrees);
location.setAltitude(request.altitudeMeters);
location.setVerticalAccuracyMeters(request.verticalAccuracyMeters);
+
+ AddMslAltitudeToLocationResponse response = new AddMslAltitudeToLocationResponse();
try {
mAltitudeConverter.addMslAltitudeToLocation(mContext, location);
} catch (IOException e) {
- throw new RemoteException(e);
+ response.success = false;
+ return response;
}
-
- AddMslAltitudeToLocationResponse response = new AddMslAltitudeToLocationResponse();
response.mslAltitudeMeters = location.getMslAltitudeMeters();
response.mslAltitudeAccuracyMeters = location.getMslAltitudeAccuracyMeters();
+ response.success = true;
+ return response;
+ }
+
+ @Override
+ public GetGeoidHeightResponse getGeoidHeight(GetGeoidHeightRequest request)
+ throws RemoteException {
+ Location location = new Location("");
+ location.setLatitude(request.latitudeDegrees);
+ location.setLongitude(request.longitudeDegrees);
+ location.setAltitude(0.0);
+ location.setVerticalAccuracyMeters(0.0f);
+
+ GetGeoidHeightResponse response = new GetGeoidHeightResponse();
+ try {
+ mAltitudeConverter.addMslAltitudeToLocation(mContext, location);
+ } catch (IOException e) {
+ response.success = false;
+ return response;
+ }
+ // The geoid height for a location with zero WGS84 altitude is equal in value to the
+ // negative of the corresponding MSL altitude.
+ response.geoidHeightMeters = -location.getMslAltitudeMeters();
+ // The geoid height error for a location with zero vertical accuracy is equal in value to
+ // the corresponding MSL altitude accuracy.
+ response.geoidHeightErrorMeters = location.getMslAltitudeAccuracyMeters();
+ // The expiration distance and additional error are currently set to constants used by
+ // health services.
+ response.expirationDistanceMeters = 10000.0;
+ response.additionalGeoidHeightErrorMeters = 0.707f;
+ response.success = true;
return response;
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index d3eb5a95ccdc..91e6a80a5bbb 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1734,12 +1734,6 @@ public class LocationProviderManager extends
return null;
}
- // lastly - note app ops
- if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
- identity)) {
- return null;
- }
-
Location location = getPermittedLocation(
getLastLocationUnsafe(
identity.getUserId(),
@@ -1748,10 +1742,18 @@ public class LocationProviderManager extends
Long.MAX_VALUE),
permissionLevel);
- if (location != null && identity.getPid() == Process.myPid()) {
+ if (location != null) {
+ // lastly - note app ops
+ if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ identity)) {
+ return null;
+ }
+
// if delivering to the same process, make a copy of the location first (since
// location is mutable)
- location = new Location(location);
+ if (identity.getPid() == Process.myPid()) {
+ location = new Location(location);
+ }
}
return location;
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 2da1a689f69f..66e61c076030 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -234,7 +234,7 @@ public class ConditionProviders extends ManagedServices {
if (pkgList != null && (pkgList.length > 0)) {
for (String pkgName : pkgList) {
try {
- inm.removeAutomaticZenRules(pkgName);
+ inm.removeAutomaticZenRules(pkgName, /* fromUser= */ false);
inm.setNotificationPolicyAccessGranted(pkgName, false);
} catch (Exception e) {
Slog.e(TAG, "Failed to clean up rules for " + pkgName, e);
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index 885566693b9a..71a6b5ed0581 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -109,7 +109,6 @@ class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
if (origin == ZenModeConfig.UPDATE_ORIGIN_INIT
|| origin == ZenModeConfig.UPDATE_ORIGIN_INIT_USER
|| origin == ZenModeConfig.UPDATE_ORIGIN_USER
- || origin == ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
|| !mPowerManager.isInteractive()) {
unregisterScreenOffReceiver();
updateNightModeImmediately(useNightMode);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 75d3dce55abd..a919db947593 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5343,13 +5343,14 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
+ public void setZenMode(int mode, Uri conditionId, String reason, boolean fromUser) {
enforceSystemOrSystemUI("INotificationManager.setZenMode");
final int callingUid = Binder.getCallingUid();
final long identity = Binder.clearCallingIdentity();
+ enforceUserOriginOnlyFromSystem(fromUser, "setZenMode");
+
try {
- mZenModeHelper.setManualZenMode(mode, conditionId,
- ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, // Checked by enforce()
+ mZenModeHelper.setManualZenMode(mode, conditionId, computeZenOrigin(fromUser),
reason, /* caller= */ null, callingUid);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -5380,7 +5381,8 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) {
+ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg,
+ boolean fromUser) {
validateAutomaticZenRule(automaticZenRule);
checkCallerIsSameApp(pkg);
if (automaticZenRule.getZenPolicy() != null
@@ -5389,6 +5391,7 @@ public class NotificationManagerService extends SystemService {
+ "INTERRUPTION_FILTER_PRIORITY filters");
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
+ enforceUserOriginOnlyFromSystem(fromUser, "addAutomaticZenRule");
// If the calling app is the system (from any user), take the package name from the
// rule's owner rather than from the caller's package.
@@ -5400,24 +5403,18 @@ public class NotificationManagerService extends SystemService {
}
return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
- // TODO: b/308670715: Distinguish origin properly (e.g. USER if creating a rule
- // manually in Settings).
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
- "addAutomaticZenRule", Binder.getCallingUid());
+ computeZenOrigin(fromUser), "addAutomaticZenRule", Binder.getCallingUid());
}
@Override
- public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule) {
+ public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule,
+ boolean fromUser) throws RemoteException {
validateAutomaticZenRule(automaticZenRule);
enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
+ enforceUserOriginOnlyFromSystem(fromUser, "updateAutomaticZenRule");
- // TODO: b/308670715: Distinguish origin properly (e.g. USER if updating a rule
- // manually in Settings).
return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
- "updateAutomaticZenRule", Binder.getCallingUid());
+ computeZenOrigin(fromUser), "updateAutomaticZenRule", Binder.getCallingUid());
}
private void validateAutomaticZenRule(AutomaticZenRule rule) {
@@ -5445,27 +5442,24 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public boolean removeAutomaticZenRule(String id) throws RemoteException {
+ public boolean removeAutomaticZenRule(String id, boolean fromUser) throws RemoteException {
Objects.requireNonNull(id, "Id is null");
// Verify that they can modify zen rules.
enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
+ enforceUserOriginOnlyFromSystem(fromUser, "removeAutomaticZenRule");
- // TODO: b/308670715: Distinguish origin properly (e.g. USER if removing a rule
- // manually in Settings).
- return mZenModeHelper.removeAutomaticZenRule(id,
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
+ return mZenModeHelper.removeAutomaticZenRule(id, computeZenOrigin(fromUser),
"removeAutomaticZenRule", Binder.getCallingUid());
}
@Override
- public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
+ public boolean removeAutomaticZenRules(String packageName, boolean fromUser)
+ throws RemoteException {
Objects.requireNonNull(packageName, "Package name is null");
enforceSystemOrSystemUI("removeAutomaticZenRules");
+ enforceUserOriginOnlyFromSystem(fromUser, "removeAutomaticZenRules");
- return mZenModeHelper.removeAutomaticZenRules(packageName,
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
+ return mZenModeHelper.removeAutomaticZenRules(packageName, computeZenOrigin(fromUser),
packageName + "|removeAutomaticZenRules", Binder.getCallingUid());
}
@@ -5478,28 +5472,54 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void setAutomaticZenRuleState(String id, Condition condition) {
+ public void setAutomaticZenRuleState(String id, Condition condition, boolean fromUser) {
Objects.requireNonNull(id, "id is null");
Objects.requireNonNull(condition, "Condition is null");
condition.validate();
enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
- // TODO: b/308670715: Distinguish origin properly (e.g. USER if toggling a rule
- // manually in Settings).
- mZenModeHelper.setAutomaticZenRuleState(id, condition,
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
+ if (android.app.Flags.modesApi()) {
+ if (fromUser != (condition.source == Condition.SOURCE_USER_ACTION)) {
+ throw new IllegalArgumentException(String.format(
+ "Mismatch between fromUser (%s) and condition.source (%s)",
+ fromUser, Condition.sourceToString(condition.source)));
+ }
+ }
+
+ mZenModeHelper.setAutomaticZenRuleState(id, condition, computeZenOrigin(fromUser),
Binder.getCallingUid());
}
+ @ZenModeConfig.ConfigChangeOrigin
+ private int computeZenOrigin(boolean fromUser) {
+ // "fromUser" is introduced with MODES_API, so only consider it in that case.
+ // (Non-MODES_API behavior should also not depend at all on UPDATE_ORIGIN_USER).
+ if (android.app.Flags.modesApi() && fromUser) {
+ return ZenModeConfig.UPDATE_ORIGIN_USER;
+ } else if (isCallerSystemOrSystemUi()) {
+ return ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI;
+ } else {
+ return ZenModeConfig.UPDATE_ORIGIN_APP;
+ }
+ }
+
+ private void enforceUserOriginOnlyFromSystem(boolean fromUser, String method) {
+ if (android.app.Flags.modesApi()
+ && fromUser
+ && !isCallerSystemOrSystemUiOrShell()) {
+ throw new SecurityException(String.format(
+ "Calling %s with fromUser == true is only allowed for system", method));
+ }
+ }
+
@Override
- public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
+ public void setInterruptionFilter(String pkg, int filter, boolean fromUser) {
enforcePolicyAccess(pkg, "setInterruptionFilter");
final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
final int callingUid = Binder.getCallingUid();
- final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
+ enforceUserOriginOnlyFromSystem(fromUser, "setInterruptionFilter");
if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, callingUid, zen);
@@ -5508,9 +5528,7 @@ public class NotificationManagerService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
- mZenModeHelper.setManualZenMode(zen, null,
- isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
+ mZenModeHelper.setManualZenMode(zen, null, computeZenOrigin(fromUser),
/* reason= */ "setInterruptionFilter", /* caller= */ pkg,
callingUid);
} finally {
@@ -5825,10 +5843,11 @@ public class NotificationManagerService extends SystemService {
* {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd
*/
@Override
- public void setNotificationPolicy(String pkg, Policy policy) {
+ public void setNotificationPolicy(String pkg, Policy policy, boolean fromUser) {
enforcePolicyAccess(pkg, "setNotificationPolicy");
+ enforceUserOriginOnlyFromSystem(fromUser, "setNotificationPolicy");
int callingUid = Binder.getCallingUid();
- boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
+ @ZenModeConfig.ConfigChangeOrigin int origin = computeZenOrigin(fromUser);
boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
&& !canManageGlobalZenPolicy(pkg, callingUid);
@@ -5873,14 +5892,12 @@ public class NotificationManagerService extends SystemService {
newVisualEffects, policy.priorityConversationSenders);
if (shouldApplyAsImplicitRule) {
- mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy,
+ origin);
} else {
ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion,
policy);
- mZenModeHelper.setNotificationPolicy(policy,
- isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
- callingUid);
+ mZenModeHelper.setNotificationPolicy(policy, origin, callingUid);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set notification policy", e);
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index dc0cf4e09207..9f3104cbd7b0 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -117,7 +117,6 @@ public class NotificationShellCmd extends ShellCommand {
private final NotificationManagerService mDirectService;
private final INotificationManager mBinderService;
private final PackageManager mPm;
- private NotificationChannel mChannel;
public NotificationShellCmd(NotificationManagerService service) {
mDirectService = service;
@@ -183,7 +182,13 @@ public class NotificationShellCmd extends ShellCommand {
interruptionFilter = INTERRUPTION_FILTER_ALL;
}
final int filter = interruptionFilter;
- mBinderService.setInterruptionFilter(callingPackage, filter);
+ if (android.app.Flags.modesApi()) {
+ mBinderService.setInterruptionFilter(callingPackage, filter,
+ /* fromUser= */ true);
+ } else {
+ mBinderService.setInterruptionFilter(callingPackage, filter,
+ /* fromUser= */ false);
+ }
}
break;
case "allow_dnd": {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 3f8b5952a1bc..d1de9b086c5d 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -561,7 +561,7 @@ public class ZenModeHelper {
* {@link Global#ZEN_MODE_IMPORTANT_INTERRUPTIONS}.
*/
void applyGlobalPolicyAsImplicitZenRule(String callingPkg, int callingUid,
- NotificationManager.Policy policy) {
+ NotificationManager.Policy policy, @ConfigChangeOrigin int origin) {
if (!android.app.Flags.modesApi()) {
Log.wtf(TAG, "applyGlobalPolicyAsImplicitZenRule called with flag off!");
return;
@@ -579,7 +579,7 @@ public class ZenModeHelper {
}
// TODO: b/308673679 - Keep user customization of this rule!
rule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
- setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP,
+ setConfigLocked(newConfig, /* triggeringComponent= */ null, origin,
"applyGlobalPolicyAsImplicitZenRule", callingUid);
}
}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index 9ce3cb3abe4a..f6e7ef3d50e9 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -41,13 +41,14 @@ import android.system.Os;
import android.system.StructStat;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoParseException;
import com.android.internal.annotations.GuardedBy;
import com.android.server.BootReceiver;
import com.android.server.ServiceThread;
import com.android.server.os.TombstoneProtos.Cause;
import com.android.server.os.TombstoneProtos.Tombstone;
-import com.android.server.os.protobuf.CodedInputStream;
import libcore.io.IoUtils;
@@ -129,21 +130,18 @@ public final class NativeTombstoneManager {
return;
}
+ String processName = "UNKNOWN";
final boolean isProtoFile = filename.endsWith(".pb");
- if (!isProtoFile) {
- return;
- }
+ File protoPath = isProtoFile ? path : new File(path.getAbsolutePath() + ".pb");
- Optional<ParsedTombstone> parsedTombstone = handleProtoTombstone(path, true);
+ Optional<TombstoneFile> parsedTombstone = handleProtoTombstone(protoPath, isProtoFile);
if (parsedTombstone.isPresent()) {
- BootReceiver.addTombstoneToDropBox(
- mContext, path, parsedTombstone.get().getTombstone(),
- parsedTombstone.get().getProcessName(), mTmpFileLock);
+ processName = parsedTombstone.get().getProcessName();
}
+ BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName, mTmpFileLock);
}
- private Optional<ParsedTombstone> handleProtoTombstone(
- File path, boolean addToList) {
+ private Optional<TombstoneFile> handleProtoTombstone(File path, boolean addToList) {
final String filename = path.getName();
if (!filename.endsWith(".pb")) {
Slog.w(TAG, "unexpected tombstone name: " + path);
@@ -173,7 +171,7 @@ public final class NativeTombstoneManager {
return Optional.empty();
}
- final Optional<ParsedTombstone> parsedTombstone = TombstoneFile.parse(pfd);
+ final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
if (!parsedTombstone.isPresent()) {
IoUtils.closeQuietly(pfd);
return Optional.empty();
@@ -186,7 +184,7 @@ public final class NativeTombstoneManager {
previous.dispose();
}
- mTombstones.put(number, parsedTombstone.get().getTombstoneFile());
+ mTombstones.put(number, parsedTombstone.get());
}
}
@@ -334,27 +332,6 @@ public final class NativeTombstoneManager {
}
}
- static class ParsedTombstone {
- TombstoneFile mTombstoneFile;
- Tombstone mTombstone;
- ParsedTombstone(TombstoneFile tombstoneFile, Tombstone tombstone) {
- mTombstoneFile = tombstoneFile;
- mTombstone = tombstone;
- }
-
- public String getProcessName() {
- return mTombstoneFile.getProcessName();
- }
-
- public TombstoneFile getTombstoneFile() {
- return mTombstoneFile;
- }
-
- public Tombstone getTombstone() {
- return mTombstone;
- }
- }
-
static class TombstoneFile {
final ParcelFileDescriptor mPfd;
@@ -437,21 +414,67 @@ public final class NativeTombstoneManager {
}
}
- static Optional<ParsedTombstone> parse(ParcelFileDescriptor pfd) {
- Tombstone tombstoneProto;
- try (FileInputStream is = new FileInputStream(pfd.getFileDescriptor())) {
- final byte[] tombstoneBytes = is.readAllBytes();
+ static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+ final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+ final ProtoInputStream stream = new ProtoInputStream(is);
+
+ int pid = 0;
+ int uid = 0;
+ String processName = null;
+ String crashReason = "";
+ String selinuxLabel = "";
+
+ try {
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Tombstone.PID:
+ pid = stream.readInt(Tombstone.PID);
+ break;
+
+ case (int) Tombstone.UID:
+ uid = stream.readInt(Tombstone.UID);
+ break;
+
+ case (int) Tombstone.COMMAND_LINE:
+ if (processName == null) {
+ processName = stream.readString(Tombstone.COMMAND_LINE);
+ }
+ break;
- tombstoneProto = Tombstone.parseFrom(
- CodedInputStream.newInstance(tombstoneBytes));
- } catch (IOException ex) {
+ case (int) Tombstone.CAUSES:
+ if (!crashReason.equals("")) {
+ // Causes appear in decreasing order of likelihood. For now we only
+ // want the most likely crash reason here, so ignore all others.
+ break;
+ }
+ long token = stream.start(Tombstone.CAUSES);
+ cause:
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Cause.HUMAN_READABLE:
+ crashReason = stream.readString(Cause.HUMAN_READABLE);
+ break cause;
+
+ default:
+ break;
+ }
+ }
+ stream.end(token);
+ break;
+
+ case (int) Tombstone.SELINUX_LABEL:
+ selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } catch (IOException | ProtoParseException ex) {
Slog.e(TAG, "Failed to parse tombstone", ex);
return Optional.empty();
}
- int pid = tombstoneProto.getPid();
- int uid = tombstoneProto.getUid();
-
if (!UserHandle.isApp(uid)) {
Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
return Optional.empty();
@@ -468,7 +491,6 @@ public final class NativeTombstoneManager {
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
- String selinuxLabel = tombstoneProto.getSelinuxLabel();
if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
return Optional.empty();
@@ -480,30 +502,11 @@ public final class NativeTombstoneManager {
result.mAppId = appId;
result.mPid = pid;
result.mUid = uid;
- result.mProcessName = getCmdLineProcessName(tombstoneProto);
+ result.mProcessName = processName == null ? "" : processName;
result.mTimestampMs = timestampMs;
- result.mCrashReason = getCrashReason(tombstoneProto);
+ result.mCrashReason = crashReason;
- return Optional.of(new ParsedTombstone(result, tombstoneProto));
- }
-
- private static String getCmdLineProcessName(Tombstone tombstoneProto) {
- for (String cmdline : tombstoneProto.getCommandLineList()) {
- if (cmdline != null) {
- return cmdline;
- }
- }
- return "";
- }
-
- private static String getCrashReason(Tombstone tombstoneProto) {
- for (Cause cause : tombstoneProto.getCausesList()) {
- if (cause.getHumanReadable() != null
- && !cause.getHumanReadable().equals("")) {
- return cause.getHumanReadable();
- }
- }
- return "";
+ return Optional.of(result);
}
public IParcelFileDescriptorRetriever getPfdRetriever() {
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 82622d9a4ea8..f3df4244c47f 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -1042,7 +1042,9 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
existingSettings, forceQueryable, protectedBroadcasts);
SparseSetArray<Integer> queriesViaComponent = computer.execute();
synchronized (mQueriesViaComponentLock) {
- mQueriesViaComponent.copyFrom(queriesViaComponent);
+ mQueriesViaComponent = new WatchedSparseSetArray<>(queriesViaComponent);
+ mQueriesViaComponentSnapshot = new SnapshotCache.Auto<>(
+ mQueriesViaComponent, mQueriesViaComponent, "AppsFilter.mQueriesViaComponent");
}
mQueriesViaComponentRequireRecompute.set(false);
diff --git a/services/core/java/com/android/server/pm/CleanUpArgs.java b/services/core/java/com/android/server/pm/CleanUpArgs.java
new file mode 100644
index 000000000000..9da76d6f044f
--- /dev/null
+++ b/services/core/java/com/android/server/pm/CleanUpArgs.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+
+import java.io.File;
+
+final class CleanUpArgs {
+ @NonNull
+ private final String mPackageName;
+ @NonNull
+ private final File mCodeFile;
+ @NonNull
+ private final String[] mInstructionSets;
+
+ /**
+ * Create args that describe an existing installed package. Typically used
+ * when cleaning up old installs.
+ */
+ CleanUpArgs(@NonNull String packageName, @NonNull String codePath,
+ @NonNull String[] instructionSets) {
+ mPackageName = packageName;
+ mCodeFile = new File(codePath);
+ mInstructionSets = instructionSets;
+ }
+
+ @NonNull
+ String getPackageName() {
+ return mPackageName;
+ }
+
+ @NonNull
+ File getCodeFile() {
+ return mCodeFile;
+ }
+
+ @NonNull
+ /** @see PackageSetting#getPath() */
+ String getCodePath() {
+ return mCodeFile.getAbsolutePath();
+ }
+
+ @NonNull
+ String[] getInstructionSets() {
+ return mInstructionSets;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 27f4e11c53ad..482807c397ea 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -518,7 +518,9 @@ public interface Computer extends PackageDataSnapshot {
boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId)
throws PackageManager.NameNotFoundException;
- boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId);
+ /** Check if the package is suspending any package. */
+ boolean isSuspendingAnyPackages(@NonNull String suspendingPackage,
+ @UserIdInt int suspendingUserId, int targetUserId);
@NonNull
ParceledListSlice<IntentFilter> getAllIntentFilters(@NonNull String packageName);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 744c94661067..3cb2420cd223 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -96,6 +96,7 @@ import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
import android.content.pm.VersionedPackage;
import android.os.Binder;
import android.os.Build;
@@ -5008,11 +5009,13 @@ public class ComputerEngine implements Computer {
@Override
public boolean isSuspendingAnyPackages(@NonNull String suspendingPackage,
- @UserIdInt int userId) {
+ @UserIdInt int suspendingUserId, int targetUserId) {
+ final UserPackage suspender = UserPackage.of(suspendingUserId, suspendingPackage);
for (final PackageStateInternal packageState : getPackageStates().values()) {
- final PackageUserStateInternal state = packageState.getUserStateOrDefault(userId);
+ final PackageUserStateInternal state =
+ packageState.getUserStateOrDefault(targetUserId);
if (state.getSuspendParams() != null
- && state.getSuspendParams().containsKey(suspendingPackage)) {
+ && state.getSuspendParams().containsKey(suspender)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index aa7f0d3c668a..b96b70419b74 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -282,8 +282,8 @@ final class DeletePackageHelper {
// other processes clean up before deleting resources.
synchronized (mPm.mInstallLock) {
if (info.mArgs != null) {
- mRemovePackageHelper.cleanUpResources(info.mArgs.mCodeFile,
- info.mArgs.mInstructionSets);
+ mRemovePackageHelper.cleanUpResources(info.mArgs.getPackageName(),
+ info.mArgs.getCodeFile(), info.mArgs.getInstructionSets());
}
boolean reEnableStub = false;
@@ -571,7 +571,7 @@ final class DeletePackageHelper {
// Delete application code and resources only for parent packages
if (deleteCodeAndResources) {
- outInfo.mArgs = new InstallArgs(
+ outInfo.mArgs = new CleanUpArgs(ps.getName(),
ps.getPathString(), getAppDexInstructionSets(
ps.getPrimaryCpuAbiLegacy(), ps.getSecondaryCpuAbiLegacy()));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.mArgs);
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index cc6bb007411e..46f9732a88e1 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -16,14 +16,9 @@
package com.android.server.pm;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.DataLoaderType;
import android.content.pm.IPackageInstallObserver2;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -102,22 +97,4 @@ final class InstallArgs {
mPackageSource = packageSource;
mApplicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
}
-
- /**
- * Create args that describe an existing installed package. Typically used
- * when cleaning up old installs, or used as a move source.
- */
- InstallArgs(String codePath, String[] instructionSets) {
- this(OriginInfo.fromNothing(), null, null, 0, 0, InstallSource.EMPTY, null, null,
- instructionSets, null, new ArrayMap<>(), null, MODE_DEFAULT, null, 0,
- SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN,
- PackageManager.INSTALL_SCENARIO_DEFAULT, false, DataLoaderType.NONE,
- PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, false);
- mCodeFile = (codePath != null) ? new File(codePath) : null;
- }
-
- /** @see PackageSettingBase#getPath() */
- String getCodePath() {
- return (mCodeFile != null) ? mCodeFile.getAbsolutePath() : null;
- }
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 0fa7aa5473b2..65bfb2f258eb 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2111,7 +2111,8 @@ final class InstallPackageHelper {
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
- installRequest.getRemovedInfo().mArgs = new InstallArgs(
+ installRequest.getRemovedInfo().mArgs = new CleanUpArgs(
+ packageName,
oldPackage.getPath(),
getAppDexInstructionSets(
deletedPkgSetting.getPrimaryCpuAbi(),
@@ -2820,7 +2821,7 @@ final class InstallPackageHelper {
request.setReturnMessage("Package was removed before install could complete.");
// Remove the update failed package's older resources safely now
- mRemovePackageHelper.cleanUpResources(request.getOldCodeFile(),
+ mRemovePackageHelper.cleanUpResources(packageName, request.getOldCodeFile(),
request.getOldInstructionSet());
mPm.notifyInstallObserver(request);
return;
@@ -2872,7 +2873,7 @@ final class InstallPackageHelper {
getUnknownSourcesSettings());
// Remove the replaced package's older resources safely now
- InstallArgs args = request.getRemovedInfo() != null
+ CleanUpArgs args = request.getRemovedInfo() != null
? request.getRemovedInfo().mArgs : null;
if (args != null) {
if (!killApp) {
@@ -2883,7 +2884,8 @@ final class InstallPackageHelper {
// propagated to all application threads.
mPm.scheduleDeferredNoKillPostDelete(args);
} else {
- mRemovePackageHelper.cleanUpResources(args.mCodeFile, args.mInstructionSets);
+ mRemovePackageHelper.cleanUpResources(packageName, args.getCodeFile(),
+ args.getInstructionSets());
}
} else {
// Force a gc to clear up things. Ask for a background one, it's fine to go on
@@ -4040,7 +4042,7 @@ final class InstallPackageHelper {
+ "; " + pkgSetting.getPathString()
+ " --> " + parsedPackage.getPath());
- mRemovePackageHelper.cleanUpResources(
+ mRemovePackageHelper.cleanUpResources(pkgSetting.getPackageName(),
new File(pkgSetting.getPathString()),
getAppDexInstructionSets(pkgSetting.getPrimaryCpuAbiLegacy(),
pkgSetting.getSecondaryCpuAbiLegacy()));
@@ -4146,7 +4148,8 @@ final class InstallPackageHelper {
+ parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.getPathString() + " --> "
+ parsedPackage.getPath());
- mRemovePackageHelper.cleanUpResources(new File(pkgSetting.getPathString()),
+ mRemovePackageHelper.cleanUpResources(pkgSetting.getPackageName(),
+ new File(pkgSetting.getPathString()),
getAppDexInstructionSets(
pkgSetting.getPrimaryCpuAbiLegacy(), pkgSetting.getSecondaryCpuAbiLegacy()));
} else {
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index ee780d99b6b6..ebaf516f4066 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -294,13 +294,13 @@ final class InstallRequest {
@Nullable
public File getOldCodeFile() {
return (mRemovedInfo != null && mRemovedInfo.mArgs != null)
- ? mRemovedInfo.mArgs.mCodeFile : null;
+ ? mRemovedInfo.mArgs.getCodeFile() : null;
}
@Nullable
public String[] getOldInstructionSet() {
return (mRemovedInfo != null && mRemovedInfo.mArgs != null)
- ? mRemovedInfo.mArgs.mInstructionSets : null;
+ ? mRemovedInfo.mArgs.getInstructionSets() : null;
}
public UserHandle getUser() {
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 2864a8b42445..dcfc855da855 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -132,6 +132,11 @@ public class PackageArchiver {
private static final String EXTRA_INSTALLER_TITLE =
"com.android.content.pm.extra.UNARCHIVE_INSTALLER_TITLE";
+ private static final PorterDuffColorFilter OPACITY_LAYER_FILTER =
+ new PorterDuffColorFilter(
+ Color.argb(0.5f /* alpha */, 0f /* red */, 0f /* green */, 0f /* blue */),
+ PorterDuff.Mode.SRC_ATOP);
+
private final Context mContext;
private final PackageManagerService mPm;
@@ -746,11 +751,7 @@ public class PackageArchiver {
return bitmap;
}
BitmapDrawable appIconDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
- PorterDuffColorFilter colorFilter =
- new PorterDuffColorFilter(
- Color.argb(0.32f /* alpha */, 0f /* red */, 0f /* green */, 0f /* blue */),
- PorterDuff.Mode.SRC_ATOP);
- appIconDrawable.setColorFilter(colorFilter);
+ appIconDrawable.setColorFilter(OPACITY_LAYER_FILTER);
appIconDrawable.setBounds(
0 /* left */,
0 /* top */,
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 4ecbd154cab5..ee5875ee9497 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -101,9 +101,10 @@ final class PackageHandler extends Handler {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
} break;
case DEFERRED_NO_KILL_POST_DELETE: {
- InstallArgs args = (InstallArgs) msg.obj;
+ CleanUpArgs args = (CleanUpArgs) msg.obj;
if (args != null) {
- mPm.cleanUpResources(args.mCodeFile, args.mInstructionSets);
+ mPm.cleanUpResources(args.getPackageName(), args.getCodeFile(),
+ args.getInstructionSets());
}
} break;
case DEFERRED_NO_KILL_INSTALL_OBSERVER:
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index c737b45ae885..8da168375447 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -19,6 +19,8 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.RESTRICTION_NONE;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -37,6 +39,7 @@ import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserPackage;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -250,7 +253,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
getSuspendPackageHelper().removeSuspensionsBySuspendingPackage(snapshot(),
new String[]{packageName},
(suspendingPackage) -> !PackageManagerService.PLATFORM_PACKAGE_NAME.equals(
- suspendingPackage),
+ suspendingPackage.packageName),
userId);
}
@@ -269,7 +272,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
@Override
@Deprecated
- public final String getSuspendingPackage(String suspendedPackage, int userId) {
+ public final UserPackage getSuspendingPackage(String suspendedPackage, int userId) {
return getSuspendPackageHelper().getSuspendingPackage(snapshot(), suspendedPackage, userId,
Binder.getCallingUid());
}
@@ -277,7 +280,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
@Override
@Deprecated
public final SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
- String suspendingPackage, int userId) {
+ UserPackage suspendingPackage, int userId) {
return getSuspendPackageHelper().getSuspendedDialogInfo(snapshot(), suspendedPackage,
suspendingPackage, userId, Binder.getCallingUid());
}
@@ -683,14 +686,16 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
@Override
@Deprecated
- public final void unsuspendForSuspendingPackage(final String packageName, int affectedUser) {
- mService.unsuspendForSuspendingPackage(snapshot(), packageName, affectedUser);
+ public final void unsuspendAdminSuspendedPackages(int affectedUser) {
+ final int suspendingUserId = affectedUser;
+ mService.unsuspendForSuspendingPackage(snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId);
}
@Override
@Deprecated
- public final boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
- return snapshot().isSuspendingAnyPackages(suspendingPackage, userId);
+ public final boolean isAdminSuspendingAnyPackages(int userId) {
+ final int suspendingUserId = userId;
+ return snapshot().isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, suspendingUserId, userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 56365b676618..1d5b8c3ed22a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -292,6 +292,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* Keep track of all those APKs everywhere.
@@ -1293,7 +1294,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
}
- void scheduleDeferredNoKillPostDelete(InstallArgs args) {
+ void scheduleDeferredNoKillPostDelete(CleanUpArgs args) {
Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
// If the feature flag is on, retain the old files for a day. Otherwise, delete the old
// files after a few seconds.
@@ -3137,7 +3138,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
private void enforceCanSetPackagesSuspendedAsUser(@NonNull Computer snapshot,
- boolean quarantined, String callingPackage, int callingUid, int userId,
+ boolean quarantined, UserPackage suspender, int callingUid, int targetUserId,
String callingMethod) {
if (callingUid == Process.ROOT_UID
// Need to compare app-id to allow system dialogs access on secondary users
@@ -3145,9 +3146,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService
return;
}
- final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
+ final String ownerPackage =
+ mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId);
if (ownerPackage != null) {
- final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, userId);
+ final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, targetUserId);
if (ownerUid == callingUid) {
return;
}
@@ -3168,25 +3170,27 @@ public class PackageManagerService implements PackageSender, TestUtilityService
callingMethod);
}
- final int packageUid = snapshot.getPackageUid(callingPackage, 0, userId);
+ final int packageUid = snapshot.getPackageUid(suspender.packageName, 0, targetUserId);
final boolean allowedPackageUid = packageUid == callingUid;
// TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL
final boolean allowedShell = callingUid == SHELL_UID
&& UserHandle.isSameApp(packageUid, callingUid);
if (!allowedShell && !allowedPackageUid) {
- throw new SecurityException("Calling package " + callingPackage + " in user "
- + userId + " does not belong to calling uid " + callingUid);
+ throw new SecurityException("Suspending package " + suspender.packageName
+ + " in user " + targetUserId + " does not belong to calling uid " + callingUid);
}
}
void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage,
- @UserIdInt int userId) {
+ @UserIdInt int suspendingUserId) {
// TODO: This can be replaced by a special parameter to iterate all packages, rather than
// this weird pre-collect of all packages.
final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]);
+ final Predicate<UserPackage> suspenderPredicate =
+ UserPackage.of(suspendingUserId, suspendingPackage)::equals;
mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer,
- allPackages, suspendingPackage::equals, userId);
+ allPackages, suspenderPredicate, suspendingUserId);
}
void removeAllDistractingPackageRestrictions(@NonNull Computer snapshot, int userId) {
@@ -5259,8 +5263,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (!snapshot.isPackageSuspendedForUser(packageName, userId)) {
return null;
}
- return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId,
- callingUid);
+ final UserPackage suspender = mSuspendPackageHelper.getSuspendingPackage(
+ snapshot, packageName, userId, callingUid);
+ return suspender != null ? suspender.packageName : null;
} catch (PackageManager.NameNotFoundException e) {
return null;
}
@@ -6198,7 +6203,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@Override
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
- SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId) {
+ SuspendDialogInfo dialogInfo, int flags, String suspendingPackage,
+ int suspendingUserId, int targetUserId) {
final int callingUid = Binder.getCallingUid();
boolean quarantined = false;
if (Flags.quarantinedEnabled()) {
@@ -6207,14 +6213,15 @@ public class PackageManagerService implements PackageSender, TestUtilityService
} else if (FeatureFlagUtils.isEnabled(mContext,
SETTINGS_TREAT_PAUSE_AS_QUARANTINE)) {
final String wellbeingPkg = mContext.getString(R.string.config_systemWellbeing);
- quarantined = callingPackage.equals(wellbeingPkg);
+ quarantined = suspendingPackage.equals(wellbeingPkg);
}
}
final Computer snapshot = snapshotComputer();
- enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, callingPackage, callingUid,
- userId, "setPackagesSuspendedAsUser");
+ final UserPackage suspender = UserPackage.of(targetUserId, suspendingPackage);
+ enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, suspender, callingUid,
+ targetUserId, "setPackagesSuspendedAsUser");
return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
- appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
+ appExtras, launcherExtras, dialogInfo, suspender, targetUserId, callingUid,
quarantined);
}
@@ -6653,7 +6660,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
final Computer computer = snapshotComputer();
final String[] allPackages = computer.getAllAvailablePackageNames();
mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, allPackages,
- (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
+ (suspender) -> !PLATFORM_PACKAGE_NAME.equals(suspender.packageName),
userId);
}
@@ -6667,8 +6674,13 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@Override
public String[] setPackagesSuspendedByAdmin(
@UserIdInt int userId, @NonNull String[] packageNames, boolean suspended) {
- return mSuspendPackageHelper.setPackagesSuspendedByAdmin(
- snapshotComputer(), userId, packageNames, suspended);
+ final int suspendingUserId = userId;
+ final UserPackage suspender = UserPackage.of(
+ suspendingUserId, PackageManagerService.PLATFORM_PACKAGE_NAME);
+ return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames,
+ suspended, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, suspender, userId, Process.SYSTEM_UID,
+ false /* quarantined */);
}
@Override
@@ -7974,8 +7986,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mRemovePackageHelper.removeCodePath(codePath);
}
- void cleanUpResources(@Nullable File codeFile, @Nullable String[] instructionSets) {
- mRemovePackageHelper.cleanUpResources(codeFile, instructionSets);
+ void cleanUpResources(@NonNull String packageName, @NonNull File codeFile,
+ @NonNull String[] instructionSets) {
+ mRemovePackageHelper.cleanUpResources(packageName, codeFile, instructionSets);
}
void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 322557b9e496..243fb16b19ae 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2827,7 +2827,7 @@ class PackageManagerShellCommand extends ShellCommand {
mInterface.setPackagesSuspendedAsUser(packageNames.toArray(new String[] {}),
suspendedState, ((appExtras.size() > 0) ? appExtras : null),
((launcherExtras.size() > 0) ? launcherExtras : null),
- info, flags, callingPackage, translatedUserId);
+ info, flags, callingPackage, UserHandle.USER_SYSTEM, translatedUserId);
for (int i = 0; i < packageNames.size(); i++) {
final String packageName = packageNames.get(i);
pw.println("Package " + packageName + " new suspended state: "
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index 881b0b398f9a..6f57538b581d 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -43,7 +43,7 @@ final class PackageRemovedInfo {
// of package changes
SparseArray<int[]> mBroadcastAllowList;
// Clean up resources deleted packages.
- InstallArgs mArgs = null;
+ CleanUpArgs mArgs = null;
private static final int[] EMPTY_INT_ARRAY = new int[0];
public void populateBroadcastUsers(PackageSetting deletedPackageSetting) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 7d0a1f6afe1d..02ce15922272 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -32,6 +32,7 @@ import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
import android.content.pm.overlay.OverlayPaths;
import android.os.UserHandle;
import android.os.incremental.IncrementalManager;
@@ -71,6 +72,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -172,6 +174,8 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
private File mPath;
@NonNull
private String mPathString;
+ @Nullable
+ private LinkedHashSet<File> mOldPaths;
private float mLoadingProgress;
private long mLoadingCompletedTime;
@@ -682,6 +686,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
pkg = other.pkg;
mPath = other.mPath;
mPathString = other.mPathString;
+ mOldPaths = other.mOldPaths == null ? null : new LinkedHashSet<>(other.mOldPaths);
mPrimaryCpuAbi = other.mPrimaryCpuAbi;
mSecondaryCpuAbi = other.mSecondaryCpuAbi;
mCpuAbiOverride = other.mCpuAbiOverride;
@@ -952,7 +957,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
void setUserState(int userId, long ceDataInode, long deDataInode, int enabled,
boolean installed, boolean stopped, boolean notLaunched, boolean hidden,
- int distractionFlags, ArrayMap<String, SuspendParams> suspendParams,
+ int distractionFlags, ArrayMap<UserPackage, SuspendParams> suspendParams,
boolean instantApp, boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int installReason, int uninstallReason,
@@ -1182,7 +1187,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
if (state.isSuspended()) {
for (int j = 0; j < state.getSuspendParams().size(); j++) {
proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE,
- state.getSuspendParams().keyAt(j));
+ state.getSuspendParams().keyAt(j).packageName);
}
}
proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.isStopped());
@@ -1237,6 +1242,27 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
return this;
}
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public PackageSetting addOldPath(@NonNull File path) {
+ if (mOldPaths == null) {
+ mOldPaths = new LinkedHashSet<>();
+ }
+ mOldPaths.add(path);
+ onChanged();
+ return this;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public PackageSetting removeOldPath(@Nullable File path) {
+ if (path == null || mOldPaths == null) {
+ return this;
+ }
+ if (mOldPaths.remove(path)) {
+ onChanged();
+ }
+ return this;
+ }
+
/**
* @param userId the specific user to change the label/icon for
* @see PackageUserStateImpl#overrideLabelAndIcon(ComponentName, String, Integer)
@@ -1667,6 +1693,11 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
@DataClass.Generated.Member
+ public @Nullable LinkedHashSet<File> getOldPaths() {
+ return mOldPaths;
+ }
+
+ @DataClass.Generated.Member
public float getLoadingProgress() {
return mLoadingProgress;
}
@@ -1748,10 +1779,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
@DataClass.Generated(
- time = 1700251133016L,
+ time = 1702666890740L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.internal.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic boolean isRequestLegacyExternalStorage()\npublic boolean isUserDataFragile()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n int[] queryUsersInstalledOrHasData(int[])\n long getCeDataInode(int)\n long getDeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\npublic com.android.server.pm.PackageSetting setScannedAsStoppedSystemApp(boolean)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\npublic @java.lang.Override boolean isScannedAsStoppedSystemApp()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int SCANNED_AS_STOPPED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable boolean[] usesSdkLibrariesOptional\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.internal.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate @android.annotation.Nullable java.util.LinkedHashSet<java.io.File> mOldPaths\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic boolean isRequestLegacyExternalStorage()\npublic boolean isUserDataFragile()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n void setArchiveState(com.android.server.pm.pkg.ArchiveState,int)\n boolean getInstalled(int)\n boolean isArchived(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n int[] queryUsersInstalledOrHasData(int[])\n long getCeDataInode(int)\n long getDeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\npublic com.android.server.pm.PackageSetting setScannedAsStoppedSystemApp(boolean)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting addOldPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting removeOldPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override boolean[] getUsesSdkLibrariesOptional()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesOptional(boolean[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\npublic @java.lang.Override boolean isScannedAsStoppedSystemApp()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int SCANNED_AS_STOPPED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index f42b43f5aeef..285445361a29 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -30,6 +30,7 @@ import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX;
import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
@@ -456,15 +457,25 @@ final class RemovePackageHelper {
}
}
- void cleanUpResources(File codeFile, String[] instructionSets) {
+ void cleanUpResources(@Nullable String packageName, @Nullable File codeFile,
+ @Nullable String[] instructionSets) {
synchronized (mPm.mInstallLock) {
cleanUpResourcesLI(codeFile, instructionSets);
}
+ if (packageName == null) {
+ return;
+ }
+ synchronized (mPm.mLock) {
+ PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ ps.removeOldPath(codeFile);
+ }
+ }
}
// Need installer lock especially for dex file removal.
@GuardedBy("mPm.mInstallLock")
- private void cleanUpResourcesLI(File codeFile, String[] instructionSets) {
+ private void cleanUpResourcesLI(@Nullable File codeFile, @Nullable String[] instructionSets) {
// Try enumerating all code paths before deleting
List<String> allCodePaths = Collections.EMPTY_LIST;
if (codeFile != null && codeFile.exists()) {
@@ -482,7 +493,8 @@ final class RemovePackageHelper {
}
@GuardedBy("mPm.mInstallLock")
- private void removeDexFilesLI(List<String> allCodePaths, String[] instructionSets) {
+ private void removeDexFilesLI(@NonNull List<String> allCodePaths,
+ @Nullable String[] instructionSets) {
if (!allCodePaths.isEmpty()) {
if (instructionSets == null) {
throw new IllegalStateException("instructionSet == null");
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 5c6d61e3eaf9..2e67b2f41520 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -37,6 +37,7 @@ import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
@@ -227,6 +228,7 @@ final class ScanPackageUtils {
// make a deep copy to avoid modifying any existing system state.
pkgSetting = new PackageSetting(pkgSetting);
pkgSetting.setPkg(parsedPackage);
+ final boolean isDontKill = (scanFlags & SCAN_DONT_KILL_APP) != 0;
// REMOVE SharedUserSetting from method; update in a separate call.
//
@@ -244,7 +246,8 @@ final class ScanPackageUtils {
parsedPackage.getUsesSdkLibrariesOptional(),
usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
parsedPackage.getMimeGroups(), newDomainSetId,
- parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash());
+ parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash(),
+ isDontKill);
}
if (createNewPackage && originalPkgSetting != null) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 460bbb7e4b41..edae2734c0db 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -41,6 +41,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
@@ -51,6 +52,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.overlay.OverlayPaths;
import android.net.Uri;
@@ -1227,9 +1229,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
@Nullable boolean[] usesSdkLibrariesOptional,
@Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
@Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId,
- int targetSdkVersion, byte[] restrictUpdatedHash)
+ int targetSdkVersion, byte[] restrictUpdatedHash, boolean isDontKill)
throws PackageManagerException {
final String pkgName = pkgSetting.getPackageName();
+ final File oldCodePath = pkgSetting.getPath();
if (sharedUser != null) {
if (!Objects.equals(existingSharedUserSetting, sharedUser)) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -1246,7 +1249,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
pkgSetting.setSharedUserAppId(INVALID_UID);
}
- if (!pkgSetting.getPath().equals(codePath)) {
+ if (!oldCodePath.equals(codePath)) {
final boolean isSystem = pkgSetting.isSystem();
Slog.i(PackageManagerService.TAG,
"Update" + (isSystem ? " system" : "")
@@ -1274,6 +1277,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
pkgSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPath);
}
pkgSetting.setPath(codePath);
+ if (isDontKill && Flags.improveInstallDontKill()) {
+ // We retain old code paths for DONT_KILL installs. Keep a record of old paths until
+ // they are removed.
+ pkgSetting.addOldPath(oldCodePath);
+ }
}
pkgSetting.setPrimaryCpuAbi(primaryCpuAbi)
@@ -1956,7 +1964,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
ArchiveState archiveState = null;
int packageDepth = parser.getDepth();
- ArrayMap<String, SuspendParams> suspendParamsMap = null;
+ ArrayMap<UserPackage, SuspendParams> suspendParamsMap = null;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > packageDepth)) {
@@ -1983,18 +1991,15 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
parser);
break;
case TAG_SUSPEND_PARAMS:
- final String suspendingPackage = parser.getAttributeValue(null,
- ATTR_SUSPENDING_PACKAGE);
- if (suspendingPackage == null) {
- Slog.wtf(TAG, "No suspendingPackage found inside tag "
- + TAG_SUSPEND_PARAMS);
+ Map.Entry<UserPackage, SuspendParams> entry =
+ readSuspensionParamsLPr(userId, parser);
+ if (entry == null) {
continue;
}
if (suspendParamsMap == null) {
suspendParamsMap = new ArrayMap<>();
}
- suspendParamsMap.put(suspendingPackage,
- SuspendParams.restoreFromXml(parser));
+ suspendParamsMap.put(entry.getKey(), entry.getValue());
break;
case TAG_ARCHIVE_STATE:
archiveState = parseArchiveState(parser);
@@ -2016,7 +2021,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
oldSuspendedLauncherExtras,
false /* quarantined */);
suspendParamsMap = new ArrayMap<>();
- suspendParamsMap.put(oldSuspendingPackage, suspendParams);
+ suspendParamsMap.put(
+ UserPackage.of(userId, oldSuspendingPackage), suspendParams);
}
if (blockUninstall) {
@@ -2058,6 +2064,20 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
}
+ @Nullable
+ private static Map.Entry<UserPackage, SuspendParams> readSuspensionParamsLPr(
+ int userId, TypedXmlPullParser parser) throws IOException {
+ final String suspendingPackage = parser.getAttributeValue(null, ATTR_SUSPENDING_PACKAGE);
+ if (suspendingPackage == null) {
+ Slog.wtf(TAG, "No suspendingPackage found inside tag " + TAG_SUSPEND_PARAMS);
+ return null;
+ }
+ final int suspendingUserId = userId;
+ return Map.entry(
+ UserPackage.of(suspendingUserId, suspendingPackage),
+ SuspendParams.restoreFromXml(parser));
+ }
+
private static ArchiveState parseArchiveState(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
String installerTitle = parser.getAttributeValue(null,
@@ -2414,10 +2434,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
if (ustate.isSuspended()) {
for (int i = 0; i < ustate.getSuspendParams().size(); i++) {
- final String suspendingPackage = ustate.getSuspendParams().keyAt(i);
+ final UserPackage suspendingPackage =
+ ustate.getSuspendParams().keyAt(i);
serializer.startTag(null, TAG_SUSPEND_PARAMS);
serializer.attribute(null, ATTR_SUSPENDING_PACKAGE,
- suspendingPackage);
+ suspendingPackage.packageName);
final SuspendParams params =
ustate.getSuspendParams().valueAt(i);
if (params != null) {
@@ -4955,6 +4976,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
pw.print(prefix); pw.print(" pkg="); pw.println(pkg);
pw.print(prefix); pw.print(" codePath="); pw.println(ps.getPathString());
+ if (ps.getOldPaths() != null && ps.getOldPaths().size() > 0) {
+ for (File oldPath : ps.getOldPaths()) {
+ pw.print(prefix); pw.println(" oldCodePath=" + oldPath.getAbsolutePath());
+ }
+ }
if (permissionNames == null) {
pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.getPathString());
pw.print(prefix); pw.print(" legacyNativeLibraryDir=");
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index c2a960a95394..4e70cc52ef90 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -27,10 +27,10 @@ import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.Intent;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserPackage;
import android.os.Binder;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
@@ -88,8 +88,8 @@ public final class SuspendPackageHelper {
* @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
* should be shown to the user when they try to launch a suspended app.
* Ignored if {@code suspended} is false.
- * @param callingPackage The caller's package name.
- * @param userId The user where packages reside.
+ * @param suspendingPackage The caller's package name.
+ * @param targetUserId The user where packages reside.
* @param callingUid The caller's uid.
* @return The names of failed packages.
*/
@@ -97,14 +97,14 @@ public final class SuspendPackageHelper {
String[] setPackagesSuspended(@NonNull Computer snapshot, @Nullable String[] packageNames,
boolean suspended, @Nullable PersistableBundle appExtras,
@Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo,
- @NonNull String callingPackage, @UserIdInt int userId, int callingUid,
+ @NonNull UserPackage suspendingPackage, @UserIdInt int targetUserId, int callingUid,
boolean quarantined) {
if (ArrayUtils.isEmpty(packageNames)) {
return packageNames;
}
- if (suspended && !quarantined && !isSuspendAllowedForUser(snapshot, userId,
- callingUid)) {
- Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
+ if (suspended && !quarantined
+ && !isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) {
+ Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId);
return packageNames;
}
@@ -119,19 +119,21 @@ public final class SuspendPackageHelper {
final IntArray changedUids = new IntArray(packageNames.length);
final boolean[] canSuspend = suspended
- ? canSuspendPackageForUser(snapshot, packageNames, userId, callingUid)
+ ? canSuspendPackageForUser(snapshot, packageNames, targetUserId, callingUid)
: null;
for (int i = 0; i < packageNames.length; i++) {
final String packageName = packageNames[i];
- if (callingPackage.equals(packageName)) {
- Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+ if (suspendingPackage.packageName.equals(packageName)
+ && suspendingPackage.userId == targetUserId) {
+ Slog.w(TAG, "Suspending package: " + suspendingPackage + " trying to "
+ (suspended ? "" : "un") + "suspend itself. Ignoring");
unmodifiablePackages.add(packageName);
continue;
}
final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
- if (packageState == null || !packageState.getUserStateOrDefault(userId).isInstalled()
- || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
+ if (packageState == null
+ || !packageState.getUserStateOrDefault(targetUserId).isInstalled()
+ || snapshot.shouldFilterApplication(packageState, callingUid, targetUserId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
+ ". Skipping suspending/un-suspending.");
unmodifiablePackages.add(packageName);
@@ -142,34 +144,34 @@ public final class SuspendPackageHelper {
continue;
}
- final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
- packageState.getUserStateOrDefault(userId).getSuspendParams();
+ final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
+ packageState.getUserStateOrDefault(targetUserId).getSuspendParams();
final SuspendParams oldSuspendParams = suspendParamsMap == null
- ? null : suspendParamsMap.get(callingPackage);
+ ? null : suspendParamsMap.get(suspendingPackage);
boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams);
if (suspended && !changed) {
// Carried over API behavior, must notify change even if no change
notifyPackagesList.add(packageName);
notifyUids.add(
- UserHandle.getUid(userId, packageState.getAppId()));
+ UserHandle.getUid(targetUserId, packageState.getAppId()));
continue;
}
- // If only the callingPackage is suspending this package,
+ // If only the suspendingPackage is suspending this package,
// it will be unsuspended when this change is committed
boolean packageUnsuspended = !suspended
&& CollectionUtils.size(suspendParamsMap) == 1
- && suspendParamsMap.containsKey(callingPackage);
+ && suspendParamsMap.containsKey(suspendingPackage);
if (suspended || packageUnsuspended) {
// Always notify of a suspend call + notify when fully unsuspended
notifyPackagesList.add(packageName);
- notifyUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+ notifyUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
}
if (changed) {
changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+ changedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
} else {
Slog.w(TAG, "No change is needed for package: " + packageName
+ ". Skipping suspending/un-suspending.");
@@ -181,11 +183,11 @@ public final class SuspendPackageHelper {
for (int index = 0; index < size; index++) {
final String packageName = changedPackagesList.valueAt(index);
final PackageUserStateWrite userState = mutator.forPackage(packageName)
- .userState(userId);
+ .userState(targetUserId);
if (suspended) {
- userState.putSuspendParams(callingPackage, newSuspendParams);
+ userState.putSuspendParams(suspendingPackage, newSuspendParams);
} else {
- userState.removeSuspension(callingPackage);
+ userState.removeSuspension(suspendingPackage);
}
}
});
@@ -197,17 +199,17 @@ public final class SuspendPackageHelper {
mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
- changedPackages, notifyUids.toArray(), quarantined, userId);
+ changedPackages, notifyUids.toArray(), quarantined, targetUserId);
mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages,
- suspended, userId);
- mPm.scheduleWritePackageRestrictions(userId);
+ suspended, targetUserId);
+ mPm.scheduleWritePackageRestrictions(targetUserId);
}
// Send the suspension changed broadcast to ensure suspension state is not stale.
if (!changedPackagesList.isEmpty()) {
mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
- userId);
+ targetUserId);
}
return unmodifiablePackages.toArray(new String[0]);
}
@@ -216,19 +218,19 @@ public final class SuspendPackageHelper {
* Returns the names in the {@code packageNames} which can not be suspended by the caller.
*
* @param packageNames The names of packages to check.
- * @param userId The user where packages reside.
+ * @param targetUserId The user where packages reside.
* @param callingUid The caller's uid.
* @return The names of packages which are Unsuspendable.
*/
@NonNull
String[] getUnsuspendablePackagesForUser(@NonNull Computer snapshot,
- @NonNull String[] packageNames, @UserIdInt int userId, int callingUid) {
- if (!isSuspendAllowedForUser(snapshot, userId, callingUid)) {
- Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
+ @NonNull String[] packageNames, @UserIdInt int targetUserId, int callingUid) {
+ if (!isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) {
+ Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId);
return packageNames;
}
final ArraySet<String> unactionablePackages = new ArraySet<>();
- final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, userId,
+ final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, targetUserId,
callingUid);
for (int i = 0; i < packageNames.length; i++) {
if (!canSuspend[i]) {
@@ -237,7 +239,7 @@ public final class SuspendPackageHelper {
}
final PackageStateInternal packageState =
snapshot.getPackageStateForInstalledAndFiltered(
- packageNames[i], callingUid, userId);
+ packageNames[i], callingUid, targetUserId);
if (packageState == null) {
Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
unactionablePackages.add(packageNames[i]);
@@ -285,30 +287,31 @@ public final class SuspendPackageHelper {
* @param packagesToChange The packages on which the suspension are to be removed.
* @param suspendingPackagePredicate A predicate identifying the suspending packages whose
* suspensions will be removed.
- * @param userId The user for which the changes are taking place.
+ * @param targetUserId The user for which the changes are taking place.
*/
void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot,
@NonNull String[] packagesToChange,
- @NonNull Predicate<String> suspendingPackagePredicate, int userId) {
+ @NonNull Predicate<UserPackage> suspendingPackagePredicate, int targetUserId) {
final List<String> unsuspendedPackages = new ArrayList<>();
final IntArray unsuspendedUids = new IntArray();
- final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>();
+ final ArrayMap<String, ArraySet<UserPackage>> pkgToSuspendingPkgsToCommit =
+ new ArrayMap<>();
for (String packageName : packagesToChange) {
final PackageStateInternal packageState =
snapshot.getPackageStateInternal(packageName);
final PackageUserStateInternal packageUserState = packageState == null
- ? null : packageState.getUserStateOrDefault(userId);
+ ? null : packageState.getUserStateOrDefault(targetUserId);
if (packageUserState == null || !packageUserState.isSuspended()) {
continue;
}
- WatchedArrayMap<String, SuspendParams> suspendParamsMap =
+ WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
packageUserState.getSuspendParams();
int countRemoved = 0;
for (int index = 0; index < suspendParamsMap.size(); index++) {
- String suspendingPackage = suspendParamsMap.keyAt(index);
+ UserPackage suspendingPackage = suspendParamsMap.keyAt(index);
if (suspendingPackagePredicate.test(suspendingPackage)) {
- ArraySet<String> suspendingPkgsToCommit =
+ ArraySet<UserPackage> suspendingPkgsToCommit =
pkgToSuspendingPkgsToCommit.get(packageName);
if (suspendingPkgsToCommit == null) {
suspendingPkgsToCommit = new ArraySet<>();
@@ -322,31 +325,33 @@ public final class SuspendPackageHelper {
// Everything would be removed and package unsuspended
if (countRemoved == suspendParamsMap.size()) {
unsuspendedPackages.add(packageState.getPackageName());
- unsuspendedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+ unsuspendedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
}
}
mPm.commitPackageStateMutation(null, mutator -> {
for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) {
String packageName = pkgToSuspendingPkgsToCommit.keyAt(mapIndex);
- ArraySet<String> packagesToRemove = pkgToSuspendingPkgsToCommit.valueAt(mapIndex);
- PackageUserStateWrite userState = mutator.forPackage(packageName).userState(userId);
+ ArraySet<UserPackage> packagesToRemove =
+ pkgToSuspendingPkgsToCommit.valueAt(mapIndex);
+ PackageUserStateWrite userState =
+ mutator.forPackage(packageName).userState(targetUserId);
for (int setIndex = 0; setIndex < packagesToRemove.size(); setIndex++) {
userState.removeSuspension(packagesToRemove.valueAt(setIndex));
}
}
});
- mPm.scheduleWritePackageRestrictions(userId);
+ mPm.scheduleWritePackageRestrictions(targetUserId);
final Computer newSnapshot = mPm.snapshotComputer();
if (!unsuspendedPackages.isEmpty()) {
final String[] packageArray = unsuspendedPackages.toArray(
new String[unsuspendedPackages.size()]);
mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray,
- false, userId);
+ false, targetUserId);
mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
Intent.ACTION_PACKAGES_UNSUSPENDED,
- packageArray, unsuspendedUids.toArray(), false, userId);
+ packageArray, unsuspendedUids.toArray(), false, targetUserId);
}
}
@@ -404,7 +409,7 @@ public final class SuspendPackageHelper {
* @return The name of suspending package.
*/
@Nullable
- String getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage,
+ UserPackage getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage,
int userId, int callingUid) {
final PackageStateInternal packageState = snapshot.getPackageStateInternal(
suspendedPackage, callingUid);
@@ -417,13 +422,13 @@ public final class SuspendPackageHelper {
return null;
}
- String suspendingPackage = null;
- String suspendedBySystem = null;
- String qasPackage = null;
+ UserPackage suspendingPackage = null;
+ UserPackage suspendedBySystem = null;
+ UserPackage qasPackage = null;
for (int i = 0; i < userState.getSuspendParams().size(); i++) {
suspendingPackage = userState.getSuspendParams().keyAt(i);
var suspendParams = userState.getSuspendParams().valueAt(i);
- if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+ if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) {
suspendedBySystem = suspendingPackage;
}
if (suspendParams.isQuarantined() && qasPackage == null) {
@@ -451,7 +456,7 @@ public final class SuspendPackageHelper {
*/
@Nullable
SuspendDialogInfo getSuspendedDialogInfo(@NonNull Computer snapshot,
- @NonNull String suspendedPackage, @NonNull String suspendingPackage, int userId,
+ @NonNull String suspendedPackage, @NonNull UserPackage suspendingPackage, int userId,
int callingUid) {
final PackageStateInternal packageState = snapshot.getPackageStateInternal(
suspendedPackage, callingUid);
@@ -464,7 +469,7 @@ public final class SuspendPackageHelper {
return null;
}
- final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
+ final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
userState.getSuspendParams();
if (suspendParamsMap == null) {
return null;
@@ -493,34 +498,36 @@ public final class SuspendPackageHelper {
* be suspended or not.
*
* @param packageNames The package names to check suspendability for.
- * @param userId The user to check in
+ * @param targetUserId The user to check in
* @param callingUid The caller's uid.
* @return An array containing results of the checks
*/
@NonNull
boolean[] canSuspendPackageForUser(@NonNull Computer snapshot, @NonNull String[] packageNames,
- int userId, int callingUid) {
+ int targetUserId, int callingUid) {
final boolean[] canSuspend = new boolean[packageNames.length];
- final boolean isCallerOwner = isCallerDeviceOrProfileOwner(snapshot, userId, callingUid);
+ final boolean isCallerOwner =
+ isCallerDeviceOrProfileOwner(snapshot, targetUserId, callingUid);
final long token = Binder.clearCallingIdentity();
try {
final DefaultAppProvider defaultAppProvider = mInjector.getDefaultAppProvider();
- final String activeLauncherPackageName = defaultAppProvider.getDefaultHome(userId);
- final String dialerPackageName = defaultAppProvider.getDefaultDialer(userId);
+ final String activeLauncherPackageName =
+ defaultAppProvider.getDefaultHome(targetUserId);
+ final String dialerPackageName = defaultAppProvider.getDefaultDialer(targetUserId);
final String requiredInstallerPackage =
- getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, userId);
+ getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, targetUserId);
final String requiredUninstallerPackage =
- getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, userId);
+ getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, targetUserId);
final String requiredVerifierPackage =
- getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, userId);
+ getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, targetUserId);
final String requiredPermissionControllerPackage =
getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
- userId);
+ targetUserId);
for (int i = 0; i < packageNames.length; i++) {
canSuspend[i] = false;
final String packageName = packageNames[i];
- if (mPm.isPackageDeviceAdmin(packageName, userId)) {
+ if (mPm.isPackageDeviceAdmin(packageName, targetUserId)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": has an active device admin");
continue;
@@ -555,12 +562,12 @@ public final class SuspendPackageHelper {
+ "\": required for permissions management");
continue;
}
- if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ if (mProtectedPackages.isPackageStateProtected(targetUserId, packageName)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": protected package");
continue;
}
- if (!isCallerOwner && snapshot.getBlockUninstall(userId, packageName)) {
+ if (!isCallerOwner && snapshot.getBlockUninstall(targetUserId, packageName)) {
Slog.w(TAG, "Cannot suspend package \"" + packageName
+ "\": blocked by admin");
continue;
@@ -572,7 +579,7 @@ public final class SuspendPackageHelper {
PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
if (pkg != null) {
- final int uid = UserHandle.getUid(userId, packageState.getAppId());
+ final int uid = UserHandle.getUid(targetUserId, packageState.getAppId());
// Cannot suspend SDK libs as they are controlled by SDK manager.
if (pkg.isSdkLibrary()) {
Slog.w(TAG, "Cannot suspend package: " + packageName
@@ -614,20 +621,6 @@ public final class SuspendPackageHelper {
== AppOpsManager.MODE_ALLOWED;
}
- /**
- * Suspends packages on behalf of an admin.
- *
- * @return array of packages that are unsuspendable, either because admin is not allowed to
- * suspend them (e.g. current dialer) or there was other problem (e.g. package not found).
- */
- public String[] setPackagesSuspendedByAdmin(
- Computer snapshot, int userId, String[] packageNames, boolean suspend) {
- return setPackagesSuspended(snapshot, packageNames, suspend,
- null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
- PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
- false /* quarantined */);
- }
-
private String getKnownPackageName(@NonNull Computer snapshot,
@KnownPackages.KnownPackage int knownPackage, int userId) {
final String[] knownPackages =
@@ -635,14 +628,15 @@ public final class SuspendPackageHelper {
return knownPackages.length > 0 ? knownPackages[0] : null;
}
- private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int userId,
+ private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int targetUserId,
int callingUid) {
if (callingUid == SYSTEM_UID) {
return true;
}
- final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
+ final String ownerPackage =
+ mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId);
if (ownerPackage != null) {
- return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, userId,
+ return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, targetUserId,
callingUid);
}
return false;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c48eccf2aac5..2305d6c9fba9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -7237,7 +7237,8 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean isUserInitialized(@UserIdInt int userId) {
- return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0;
+ final UserInfo userInfo = getUserInfo(userId);
+ return userInfo != null && (userInfo.flags & UserInfo.FLAG_INITIALIZED) != 0;
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 671e031b546b..3afba39ad4af 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -3291,7 +3291,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
final PermissionAllowlist permissionAllowlist =
SystemConfig.getInstance().getPermissionAllowlist();
final String packageName = packageState.getPackageName();
- if (packageState.isVendor()) {
+ if (packageState.isVendor() || packageState.isOdm()) {
return permissionAllowlist.getVendorPrivilegedAppAllowlistState(packageName,
permissionName);
} else if (packageState.isProduct()) {
@@ -3386,7 +3386,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
// the permission's protectionLevel does not have the extra 'vendorPrivileged'
// flag.
if (allowed && isPrivilegedPermission && !bp.isVendorPrivileged()
- && pkgSetting.isVendor()) {
+ && (pkgSetting.isVendor() || pkgSetting.isOdm())) {
Slog.w(TAG, "Permission " + permissionName
+ " cannot be granted to privileged vendor apk " + pkg.getPackageName()
+ " because it isn't a 'vendorPrivileged' permission.");
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index 2f4ad2d8fcc6..15b693cf72f8 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
+import android.content.pm.UserPackage;
import android.content.pm.overlay.OverlayPaths;
import android.util.ArraySet;
import android.util.Pair;
@@ -173,7 +174,7 @@ class PackageUserStateDefault implements PackageUserStateInternal {
@Nullable
@Override
- public WatchedArrayMap<String, SuspendParams> getSuspendParams() {
+ public WatchedArrayMap<UserPackage, SuspendParams> getSuspendParams() {
return null;
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index c5ef5257ddd5..7a5a14d8d3c2 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
+import android.content.pm.UserPackage;
import android.content.pm.overlay.OverlayPaths;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -121,7 +122,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
* Suspending package to suspend params
*/
@Nullable
- private WatchedArrayMap<String, SuspendParams> mSuspendParams;
+ private WatchedArrayMap<UserPackage, SuspendParams> mSuspendParams;
@Nullable
private WatchedArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap;
@@ -369,7 +370,10 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
return !CollectionUtils.isEmpty(mSuspendParams);
}
- public PackageUserStateImpl putSuspendParams(@NonNull String suspendingPackage,
+ /**
+ * Adds or updates suspension params by the given package.
+ */
+ public PackageUserStateImpl putSuspendParams(@NonNull UserPackage suspendingPackage,
@Nullable SuspendParams suspendParams) {
if (mSuspendParams == null) {
mSuspendParams = new WatchedArrayMap<>();
@@ -384,7 +388,10 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
return this;
}
- public PackageUserStateImpl removeSuspension(@NonNull String suspendingPackage) {
+ /**
+ * Removes suspension by the given package.
+ */
+ public PackageUserStateImpl removeSuspension(@NonNull UserPackage suspendingPackage) {
if (mSuspendParams != null) {
mSuspendParams.remove(suspendingPackage);
onChanged();
@@ -565,7 +572,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
* Suspending package to suspend params
*/
public @NonNull PackageUserStateImpl setSuspendParams(
- @NonNull ArrayMap<String, SuspendParams> value) {
+ @NonNull ArrayMap<UserPackage, SuspendParams> value) {
if (value == null) {
return this;
}
@@ -778,7 +785,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
* Suspending package to suspend params
*/
@DataClass.Generated.Member
- public @Nullable WatchedArrayMap<String,SuspendParams> getSuspendParams() {
+ public @Nullable WatchedArrayMap<UserPackage,SuspendParams> getSuspendParams() {
return mSuspendParams;
}
@@ -830,7 +837,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
* Suspending package to suspend params
*/
@DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setSuspendParams(@NonNull WatchedArrayMap<String,SuspendParams> value) {
+ public @NonNull PackageUserStateImpl setSuspendParams(@NonNull WatchedArrayMap<UserPackage,SuspendParams> value) {
mSuspendParams = value;
return this;
}
@@ -909,10 +916,10 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
}
@DataClass.Generated(
- time = 1701470095849L,
+ time = 1701864813354L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate long mDeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate long mDeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(android.content.pm.UserPackage)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
index 46cc830130ef..f8d745cb7fbf 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
@@ -19,6 +19,7 @@ package com.android.server.pm.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.content.pm.UserPackage;
import android.content.pm.pkg.FrameworkPackageUserState;
import android.util.Pair;
@@ -38,7 +39,7 @@ public interface PackageUserStateInternal extends PackageUserState, FrameworkPac
// TODO: Make non-null with emptyMap()
@Nullable
- WatchedArrayMap<String, SuspendParams> getSuspendParams();
+ WatchedArrayMap<UserPackage, SuspendParams> getSuspendParams();
@Nullable
WatchedArraySet<String> getDisabledComponentsNoCopy();
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
index 8430cf7a0d11..253eb4006122 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.UserPackage;
import android.content.pm.overlay.OverlayPaths;
import android.util.ArraySet;
@@ -349,7 +350,7 @@ public class PackageStateMutator {
@NonNull
@Override
- public PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
+ public PackageUserStateWrite putSuspendParams(@NonNull UserPackage suspendingPackage,
@Nullable SuspendParams suspendParams) {
if (mUserState != null) {
mUserState.putSuspendParams(suspendingPackage, suspendParams);
@@ -359,7 +360,7 @@ public class PackageStateMutator {
@NonNull
@Override
- public PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage) {
+ public PackageUserStateWrite removeSuspension(@NonNull UserPackage suspendingPackage) {
if (mUserState != null) {
mUserState.removeSuspension(suspendingPackage);
}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
index 0c6c6723b79b..f6b21045a8bb 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
+import android.content.pm.UserPackage;
import android.content.pm.overlay.OverlayPaths;
import com.android.server.pm.pkg.PackageUserStateImpl;
@@ -38,11 +39,11 @@ public interface PackageUserStateWrite {
@PackageManager.DistractionRestriction int restrictionFlags);
@NonNull
- PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
+ PackageUserStateWrite putSuspendParams(@NonNull UserPackage suspendingPackage,
@Nullable SuspendParams suspendParams);
@NonNull
- PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage);
+ PackageUserStateWrite removeSuspension(@NonNull UserPackage suspendingPackage);
@NonNull
PackageUserStateWrite setHidden(boolean hidden);
diff --git a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
index b8850afdf3ad..ff426ccf9083 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
@@ -47,6 +47,17 @@ public class WatchedSparseSetArray<T> extends WatchableImpl implements Snappable
}
/**
+ * Create a new WatchedSparseSetArray from an existing SparseSetArray without copying.
+ * <p>
+ * Use with caution: Callers must ensure that no reference to {@code sparseSetArray} exists
+ * anywhere else in the system. If such a reference does exist, then changes to the storage via
+ * that reference will not be noticed by the new WatchedSpareSetArray.
+ */
+ public WatchedSparseSetArray(@NonNull SparseSetArray<T> sparseSetArray) {
+ mStorage = sparseSetArray;
+ }
+
+ /**
* Return the underlying storage. This breaks the wrapper but is necessary when
* passing the array to distant methods.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f3922f9917ba..1ce87a7b6af3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -565,7 +565,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean idle; // has the activity gone idle?
boolean hasBeenLaunched;// has this activity ever been launched?
boolean immersive; // immersive mode (don't interrupt if possible)
- boolean forceNewConfig; // force re-create with new config next time
boolean supportsEnterPipOnTaskSwitch; // This flag is set by the system to indicate that the
// activity can enter picture in picture while pausing (only when switching to another task)
// The PiP params used when deferring the entering of picture-in-picture.
@@ -9600,7 +9599,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// configurations because there are cases (like moving a task to the root pinned task) where
// the combine configurations are equal, but would otherwise differ in the override config
mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
- if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
+ if (getConfiguration().equals(mTmpConfig) && !displayChanged) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
+ "unchanged in %s", this);
return true;
@@ -9627,7 +9626,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return true;
}
- if (changes == 0 && !forceNewConfig) {
+ if (changes == 0) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration no differences in %s",
this);
// There are no significant differences, so we won't relaunch but should still deliver
@@ -9649,7 +9648,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// pick that up next time it starts.
if (!attachedToProcess()) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration doesn't matter not running %s", this);
- forceNewConfig = false;
return true;
}
@@ -9659,11 +9657,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
Integer.toHexString(changes), Integer.toHexString(info.getRealConfigChanged()),
mLastReportedConfiguration);
- if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
+ if (shouldRelaunchLocked(changes, mTmpConfig)) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
startFreezingScreenLocked(globalChanges);
- forceNewConfig = false;
// Do not preserve window if it is freezing screen because the original window won't be
// able to update drawn state that causes freeze timeout.
preserveWindow &= isResizeOnlyChange(changes) && !mFreezingScreen;
@@ -9883,7 +9880,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
try {
ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
(andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
- forceNewConfig = false;
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(token,
pendingResults, pendingNewIntents, configChangeFlags,
new MergedConfiguration(getProcessGlobalConfiguration(),
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 86be6ba5e6ad..7af494c296de 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -33,6 +33,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
+import com.android.window.flags.Flags;
import java.io.File;
import java.util.ArrayList;
@@ -121,7 +122,8 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
// TODO remove when enabled
static boolean isSnapshotEnabled() {
- return SystemProperties.getInt("persist.wm.debug.activity_screenshot", 0) != 0;
+ return SystemProperties.getInt("persist.wm.debug.activity_screenshot", 0) != 0
+ || Flags.activitySnapshotByDefault();
}
static PersistInfoProvider createPersistInfoProvider(
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index f9d344bd7e31..e7621ffe8e3c 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -48,6 +48,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -223,7 +224,7 @@ class ActivityStartInterceptor {
// before issuing the work challenge.
return true;
}
- if (interceptLockedManagedProfileIfNeeded()) {
+ if (interceptLockedProfileIfNeeded()) {
return true;
}
if (interceptHomeIfNeeded()) {
@@ -335,19 +336,19 @@ class ActivityStartInterceptor {
return false;
}
final String suspendedPackage = mAInfo.applicationInfo.packageName;
- final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId);
- if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+ final UserPackage suspender = pmi.getSuspendingPackage(suspendedPackage, mUserId);
+ if (suspender != null && PLATFORM_PACKAGE_NAME.equals(suspender.packageName)) {
return interceptSuspendedByAdminPackage();
}
final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage,
- suspendingPackage, mUserId);
+ suspender, mUserId);
final Bundle crossProfileOptions = hasCrossProfileAnimation()
? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
: null;
final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
FLAG_IMMUTABLE);
mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
- suspendingPackage, dialogInfo, crossProfileOptions, target, mUserId);
+ suspender, dialogInfo, crossProfileOptions, target, mUserId);
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mResolvedType = null;
@@ -377,7 +378,7 @@ class ActivityStartInterceptor {
return true;
}
- private boolean interceptLockedManagedProfileIfNeeded() {
+ private boolean interceptLockedProfileIfNeeded() {
final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
if (interceptingIntent == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 630b9e139456..cb2adbcf460a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -579,30 +579,11 @@ class ActivityStarter {
computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid),
realCallingPid);
if (resolveInfo == null) {
- final UserInfo userInfo = supervisor.getUserInfo(userId);
- if (userInfo != null && userInfo.isManagedProfile()) {
- // Special case for managed profiles, if attempting to launch non-cryto aware
- // app in a locked managed profile from an unlocked parent allow it to resolve
- // as user will be sent via confirm credentials to unlock the profile.
- final UserManager userManager = UserManager.get(supervisor.mService.mContext);
- boolean profileLockedAndParentUnlockingOrUnlocked = false;
- final long token = Binder.clearCallingIdentity();
- try {
- final UserInfo parent = userManager.getProfileParent(userId);
- profileLockedAndParentUnlockingOrUnlocked = (parent != null)
- && userManager.isUserUnlockingOrUnlocked(parent.id)
- && !userManager.isUserUnlockingOrUnlocked(userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- if (profileLockedAndParentUnlockingOrUnlocked) {
- resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- computeResolveFilterUid(callingUid, realCallingUid,
- filterCallingUid), realCallingPid);
- }
- }
+ // Special case for profiles: If attempting to launch non-crypto aware app in a
+ // locked profile or launch an app in a profile that is stopped by quiet mode from
+ // an unlocked parent, allow it to resolve as user will be sent via confirm
+ // credentials to unlock the profile.
+ resolveInfo = resolveIntentForLockedOrStoppedProfiles(supervisor);
}
// Collect information about the target of the Intent.
@@ -616,6 +597,36 @@ class ActivityStarter {
UserHandle.getUserId(activityInfo.applicationInfo.uid));
}
}
+
+ /**
+ * Resolve intent for locked or stopped profiles if the parent profile is unlocking or
+ * unlocked.
+ */
+ ResolveInfo resolveIntentForLockedOrStoppedProfiles(
+ ActivityTaskSupervisor supervisor) {
+ final UserInfo userInfo = supervisor.getUserInfo(userId);
+ if (userInfo != null && userInfo.isProfile()) {
+ final UserManager userManager = UserManager.get(supervisor.mService.mContext);
+ boolean profileLockedAndParentUnlockingOrUnlocked = false;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final UserInfo parent = userManager.getProfileParent(userId);
+ profileLockedAndParentUnlockingOrUnlocked = (parent != null)
+ && userManager.isUserUnlockingOrUnlocked(parent.id)
+ && !userManager.isUserUnlockingOrUnlocked(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (profileLockedAndParentUnlockingOrUnlocked) {
+ return supervisor.resolveIntent(intent, resolvedType, userId,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ computeResolveFilterUid(callingUid, realCallingUid,
+ filterCallingUid), realCallingPid);
+ }
+ }
+ return null;
+ }
}
ActivityStarter(ActivityStartController controller, ActivityTaskManagerService service,
@@ -2767,10 +2778,7 @@ class ActivityStarter {
}
}
- // If the target task is not in the front, then we need to bring it to the front...
- // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
- // the same behavior as if a new instance was being started, which means not bringing it
- // to the front if the caller is not itself in the front.
+ // If the target task is not in the front, then we need to bring it to the front.
final boolean differentTopTask;
if (mTargetRootTask.getDisplayArea() == mPreferredTaskDisplayArea) {
final Task focusRootTask = mTargetRootTask.mDisplayContent.getFocusedRootTask();
@@ -2787,49 +2795,47 @@ class ActivityStarter {
if (differentTopTask && !avoidMoveToFront()) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- if (mSourceRecord == null || inTopNonFinishingTask(mSourceRecord)) {
- // We really do want to push this one into the user's face, right now.
- if (mLaunchTaskBehind && mSourceRecord != null) {
- intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
- }
-
- if (intentActivity.isDescendantOf(mTargetRootTask)) {
- // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
- // tasks hierarchies.
- if (mTargetRootTask != intentTask
- && mTargetRootTask != intentTask.getParent().asTask()) {
- intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
- false /* includingParents */);
- intentTask = intentTask.getParent().asTaskFragment().getTask();
- }
- // If the activity is visible in multi-windowing mode, it may already be on
- // the top (visible to user but not the global top), then the result code
- // should be START_DELIVERED_TO_TOP instead of START_TASK_TO_FRONT.
- final boolean wasTopOfVisibleRootTask = intentActivity.isVisibleRequested()
- && intentActivity.inMultiWindowMode()
- && intentActivity == mTargetRootTask.topRunningActivity()
- && !intentActivity.mTransitionController.isTransientHide(
- mTargetRootTask);
- // We only want to move to the front, if we aren't going to launch on a
- // different root task. If we launch on a different root task, we will put the
- // task on top there.
- // Defer resuming the top activity while moving task to top, since the
- // current task-top activity may not be the activity that should be resumed.
- mTargetRootTask.moveTaskToFront(intentTask, mNoAnimation, mOptions,
- mStartActivity.appTimeTracker, DEFER_RESUME,
- "bringingFoundTaskToFront");
- mMovedToFront = !wasTopOfVisibleRootTask;
- } else if (intentActivity.getWindowingMode() != WINDOWING_MODE_PINNED) {
- // Leaves reparenting pinned task operations to task organizer to make sure it
- // dismisses pinned task properly.
- // TODO(b/199997762): Consider leaving all reparent operation of organized tasks
- // to task organizer.
- intentTask.reparent(mTargetRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
- ANIMATE, DEFER_RESUME, "reparentToTargetRootTask");
- mMovedToFront = true;
+ // We really do want to push this one into the user's face, right now.
+ if (mLaunchTaskBehind && mSourceRecord != null) {
+ intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
+ }
+
+ if (intentActivity.isDescendantOf(mTargetRootTask)) {
+ // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
+ // tasks hierarchies.
+ if (mTargetRootTask != intentTask
+ && mTargetRootTask != intentTask.getParent().asTask()) {
+ intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
+ false /* includingParents */);
+ intentTask = intentTask.getParent().asTaskFragment().getTask();
}
- mOptions = null;
- }
+ // If the activity is visible in multi-windowing mode, it may already be on
+ // the top (visible to user but not the global top), then the result code
+ // should be START_DELIVERED_TO_TOP instead of START_TASK_TO_FRONT.
+ final boolean wasTopOfVisibleRootTask = intentActivity.isVisibleRequested()
+ && intentActivity.inMultiWindowMode()
+ && intentActivity == mTargetRootTask.topRunningActivity()
+ && !intentActivity.mTransitionController.isTransientHide(
+ mTargetRootTask);
+ // We only want to move to the front, if we aren't going to launch on a
+ // different root task. If we launch on a different root task, we will put the
+ // task on top there.
+ // Defer resuming the top activity while moving task to top, since the
+ // current task-top activity may not be the activity that should be resumed.
+ mTargetRootTask.moveTaskToFront(intentTask, mNoAnimation, mOptions,
+ mStartActivity.appTimeTracker, DEFER_RESUME,
+ "bringingFoundTaskToFront");
+ mMovedToFront = !wasTopOfVisibleRootTask;
+ } else if (intentActivity.getWindowingMode() != WINDOWING_MODE_PINNED) {
+ // Leaves reparenting pinned task operations to task organizer to make sure it
+ // dismisses pinned task properly.
+ // TODO(b/199997762): Consider leaving all reparent operation of organized tasks
+ // to task organizer.
+ intentTask.reparent(mTargetRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+ ANIMATE, DEFER_RESUME, "reparentToTargetRootTask");
+ mMovedToFront = true;
+ }
+ mOptions = null;
}
if (differentTopTask) {
logPIOnlyCreatorAllowsBAL();
@@ -2850,20 +2856,6 @@ class ActivityStarter {
mRootWindowContainer.getDefaultTaskDisplayArea(), mTargetRootTask);
}
- private boolean inTopNonFinishingTask(ActivityRecord r) {
- if (r == null || r.getTask() == null) {
- return false;
- }
-
- final Task rTask = r.getTask();
- final Task parent = rTask.getCreatedByOrganizerTask() != null
- ? rTask.getCreatedByOrganizerTask() : r.getRootTask();
- final ActivityRecord topNonFinishingActivity = parent != null
- ? parent.getTopNonFinishingActivity() : null;
-
- return topNonFinishingActivity != null && topNonFinishingActivity.getTask() == rTask;
- }
-
private void resumeTargetRootTaskIfNeeded() {
if (mDoResume) {
final ActivityRecord next = mTargetRootTask.topRunningActivity(
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 38682caaa036..908c49eb8580 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4777,6 +4777,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (DEBUG_ALL && !mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
Slog.i(TAG, "continueWindowLayout reason=" + mLayoutReasons);
}
+
+ // ClientTransactions is queued during #deferWindowLayout() for performance.
+ // Notify to continue.
+ mLifecycleManager.onLayoutContinued();
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e59601c69cd8..10efb9491bed 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -906,7 +906,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
mService.getPackageManagerInternalLocked().notifyPackageUse(
r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
- r.forceNewConfig = false;
mService.getAppWarningsLocked().onStartActivity(r);
// Because we could be starting an Activity in the system process this may not go
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 6b6388b10ce7..2e476772f85b 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -105,7 +105,7 @@ class ClientLifecycleManager {
final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
clientTransaction.addTransactionItem(transactionItem);
- onClientTransactionItemScheduledLocked(clientTransaction);
+ onClientTransactionItemScheduled(clientTransaction);
} else {
// TODO(b/260873529): cleanup after launch.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
@@ -134,7 +134,7 @@ class ClientLifecycleManager {
clientTransaction.addTransactionItem(transactionItem);
clientTransaction.addTransactionItem(lifecycleItem);
- onClientTransactionItemScheduledLocked(clientTransaction);
+ onClientTransactionItemScheduled(clientTransaction);
} else {
// TODO(b/260873529): cleanup after launch.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
@@ -146,6 +146,9 @@ class ClientLifecycleManager {
/** Executes all the pending transactions. */
void dispatchPendingTransactions() {
+ if (!Flags.bundleClientTransactionFlag()) {
+ return;
+ }
final int size = mPendingTransactions.size();
for (int i = 0; i < size; i++) {
final ClientTransaction transaction = mPendingTransactions.valueAt(i);
@@ -158,6 +161,20 @@ class ClientLifecycleManager {
mPendingTransactions.clear();
}
+ /**
+ * Called to when {@link WindowSurfacePlacer#continueLayout}.
+ * Dispatches all pending transactions unless there is an ongoing/scheduled layout, in which
+ * case the pending transactions will be dispatched in
+ * {@link RootWindowContainer#performSurfacePlacementNoTrace}.
+ */
+ void onLayoutContinued() {
+ if (shouldDispatchPendingTransactionsImmediately()) {
+ // Dispatch the pending transactions immediately if there is no ongoing/scheduled layout
+ dispatchPendingTransactions();
+ }
+ }
+
+ /** Must only be called with WM lock. */
@NonNull
private ClientTransaction getOrCreatePendingTransaction(@NonNull IApplicationThread client) {
final IBinder clientBinder = client.asBinder();
@@ -173,20 +190,28 @@ class ClientLifecycleManager {
}
/** Must only be called with WM lock. */
- private void onClientTransactionItemScheduledLocked(
+ private void onClientTransactionItemScheduled(
@NonNull ClientTransaction clientTransaction) throws RemoteException {
- // TODO(b/260873529): make sure WindowSurfacePlacer#requestTraversal is called before
- // ClientTransaction scheduled when needed.
-
- if (mWms != null && (mWms.mWindowPlacerLocked.isInLayout()
- || mWms.mWindowPlacerLocked.isTraversalScheduled())) {
- // The pending transactions will be dispatched when
- // RootWindowContainer#performSurfacePlacementNoTrace.
- return;
+ if (shouldDispatchPendingTransactionsImmediately()) {
+ // Dispatch the pending transaction immediately.
+ mPendingTransactions.remove(clientTransaction.getClient().asBinder());
+ scheduleTransaction(clientTransaction);
}
+ }
- // Dispatch the pending transaction immediately.
- mPendingTransactions.remove(clientTransaction.getClient().asBinder());
- scheduleTransaction(clientTransaction);
+ /** Must only be called with WM lock. */
+ private boolean shouldDispatchPendingTransactionsImmediately() {
+ if (mWms == null) {
+ return true;
+ }
+ // Do not dispatch when
+ // 1. Layout deferred.
+ // 2. Layout requested.
+ // 3. Layout in process.
+ // The pending transactions will be dispatched during layout in
+ // RootWindowContainer#performSurfacePlacementNoTrace.
+ return !mWms.mWindowPlacerLocked.isLayoutDeferred()
+ && !mWms.mWindowPlacerLocked.isTraversalScheduled()
+ && !mWms.mWindowPlacerLocked.isInLayout();
}
}
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 73bcc8d94252..1a8927ecf564 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -19,7 +19,6 @@ package com.android.server.wm;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.CompatScaleProvider.COMPAT_SCALE_MODE_SYSTEM_FIRST;
import static com.android.server.wm.CompatScaleProvider.COMPAT_SCALE_MODE_SYSTEM_LAST;
@@ -47,6 +46,7 @@ import android.util.AtomicFile;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.Xml;
import com.android.internal.protolog.common.ProtoLog;
@@ -60,6 +60,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -347,6 +348,7 @@ public final class CompatModePackages {
private GameManagerInternal mGameManager;
private final AtomicFile mFile;
private final HashMap<String, Integer> mPackages = new HashMap<>();
+ private final SparseBooleanArray mLegacyScreenCompatPackages = new SparseBooleanArray();
private final CompatHandler mHandler;
private final SparseArray<CompatScaleProvider> mProviders = new SparseArray<>();
@@ -427,6 +429,7 @@ public final class CompatModePackages {
mPackages.remove(packageName);
scheduleWrite();
}
+ mLegacyScreenCompatPackages.delete(packageName.hashCode());
}
public void handlePackageAddedLocked(String packageName, boolean updated) {
@@ -458,6 +461,17 @@ public final class CompatModePackages {
mHandler.sendMessageDelayed(msg, 10000);
}
+ /**
+ * Returns {@code true} if the windows belonging to the package should be scaled with
+ * {@link DisplayContent#mCompatibleScreenScale}.
+ */
+ boolean useLegacyScreenCompatMode(String packageName) {
+ if (mLegacyScreenCompatPackages.size() == 0) {
+ return false;
+ }
+ return mLegacyScreenCompatPackages.get(packageName.hashCode());
+ }
+
public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
final boolean forceCompat = getPackageCompatModeEnabledLocked(ai);
final CompatScale compatScale = getCompatScaleFromProvider(ai.packageName, ai.uid);
@@ -466,8 +480,18 @@ public final class CompatModePackages {
: getCompatScale(ai.packageName, ai.uid, /* checkProvider= */ false);
final float densityScale = compatScale != null ? compatScale.mDensityScaleFactor : appScale;
final Configuration config = mService.getGlobalConfiguration();
- return new CompatibilityInfo(ai, config.screenLayout, config.smallestScreenWidthDp,
- forceCompat, appScale, densityScale);
+ final CompatibilityInfo info = new CompatibilityInfo(ai, config.screenLayout,
+ config.smallestScreenWidthDp, forceCompat, appScale, densityScale);
+ // Ignore invalid info which may be a placeholder of isolated process.
+ if (ai.flags != 0 && ai.sourceDir != null) {
+ if (!info.supportsScreen() && !"android".equals(ai.packageName)) {
+ Slog.i(TAG, "Use legacy screen compat mode: " + ai.packageName);
+ mLegacyScreenCompatPackages.put(ai.packageName.hashCode(), true);
+ } else if (mLegacyScreenCompatPackages.size() > 0) {
+ mLegacyScreenCompatPackages.delete(ai.packageName.hashCode());
+ }
+ }
+ return info;
}
float getCompatScale(String packageName, int uid) {
@@ -718,14 +742,23 @@ public final class CompatModePackages {
scheduleWrite();
- final Task rootTask = mService.getTopDisplayFocusedRootTask();
- ActivityRecord starting = rootTask.restartPackage(packageName);
-
+ final ArrayList<WindowProcessController> restartedApps = new ArrayList<>();
+ mService.mRootWindowContainer.forAllWindows(w -> {
+ final ActivityRecord ar = w.mActivityRecord;
+ if (ar != null) {
+ if (ar.packageName.equals(packageName) && !restartedApps.contains(ar.app)) {
+ ar.restartProcessIfVisible();
+ restartedApps.add(ar.app);
+ }
+ } else if (w.getProcess().mInfo.packageName.equals(packageName)) {
+ w.updateGlobalScale();
+ }
+ }, true /* traverseTopToBottom */);
// Tell all processes that loaded this package about the change.
SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap();
for (int i = pidMap.size() - 1; i >= 0; i--) {
final WindowProcessController app = pidMap.valueAt(i);
- if (!app.containsPackage(packageName)) {
+ if (!app.containsPackage(packageName) || restartedApps.contains(app)) {
continue;
}
try {
@@ -737,14 +770,6 @@ public final class CompatModePackages {
} catch (Exception e) {
}
}
-
- if (starting != null) {
- starting.ensureActivityConfiguration(0 /* globalChanges */,
- false /* preserveWindow */);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
- rootTask.ensureActivitiesVisible(starting, 0, !PRESERVE_WINDOWS);
- }
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ae10ce3690aa..f8dc9c79dda7 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -512,7 +512,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics();
- /** The desired scaling factor for compatible apps. */
+ /**
+ * The desired scaling factor for compatible apps. It limits the size of the window to be
+ * original size ([320x480] x density). Used to scale window for applications running under
+ * legacy compatibility mode.
+ */
float mCompatibleScreenScale;
/** @see #getCurrentOverrideConfigurationChanges */
@@ -4794,7 +4798,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
assignRelativeLayerForIme(getSyncTransaction(), true /* forceUpdate */);
scheduleAnimation();
- mWmService.mH.post(() -> InputMethodManagerInternal.get().onImeParentChanged());
+ mWmService.mH.post(
+ () -> InputMethodManagerInternal.get().onImeParentChanged(getDisplayId()));
} else if (mImeControlTarget != null && mImeControlTarget == mImeLayeringTarget) {
// Even if the IME surface parent is not changed, the layer target belonging to the
// parent may have changes. Then attempt to reassign if the IME control target is
@@ -7090,7 +7095,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@Override
- public void notifyInsetsControlChanged() {
+ public void notifyInsetsControlChanged(int displayId) {
final InsetsStateController stateController = getInsetsStateController();
try {
mRemoteInsetsController.insetsControlChanged(stateController.getRawInsetsState(),
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index 8ecbc177896c..b74eb56ebdca 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -29,8 +29,10 @@ interface InsetsControlTarget {
/**
* Notifies the control target that the insets control has changed.
+ *
+ * @param displayId the display hosting the window of this target
*/
- default void notifyInsetsControlChanged() {
+ default void notifyInsetsControlChanged(int displayId) {
};
/**
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 781567990235..3c556bf7b126 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -728,7 +728,7 @@ class InsetsPolicy {
}
@Override
- public void notifyInsetsControlChanged() {
+ public void notifyInsetsControlChanged(int displayId) {
mHandler.post(this);
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index c4d01291f558..6b9fcf411ce1 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -72,7 +72,7 @@ class InsetsStateController {
};
private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() {
@Override
- public void notifyInsetsControlChanged() {
+ public void notifyInsetsControlChanged(int displayId) {
InsetsSourceControl[] controls = getControlsForDispatch(this);
if (controls == null) {
return;
@@ -80,7 +80,7 @@ class InsetsStateController {
for (InsetsSourceControl control : controls) {
if (control.getType() == WindowInsets.Type.ime()) {
mDisplayContent.mWmService.mH.post(() ->
- InputMethodManagerInternal.get().removeImeSurface());
+ InputMethodManagerInternal.get().removeImeSurface(displayId));
}
}
}
@@ -370,9 +370,10 @@ class InsetsStateController {
provider.onSurfaceTransactionApplied();
}
final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
+ int displayId = mDisplayContent.getDisplayId();
for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i);
- controlTarget.notifyInsetsControlChanged();
+ controlTarget.notifyInsetsControlChanged(displayId);
if (mControlTargetProvidersMap.containsKey(controlTarget)) {
// We only collect targets who get controls, not lose controls.
newControlTargets.add(controlTarget);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 5518de7b64fd..9305396caa19 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
@@ -170,6 +171,8 @@ final class LetterboxUiController {
// Corresponds to OVERRIDE_ANY_ORIENTATION
private final boolean mIsOverrideAnyOrientationEnabled;
+ // Corresponds to OVERRIDE_ANY_ORIENTATION_TO_USER
+ private final boolean mIsOverrideToUserOrientationEnabled;
// Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
private final boolean mIsOverrideToPortraitOrientationEnabled;
// Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR
@@ -254,9 +257,8 @@ final class LetterboxUiController {
// Counter for ActivityRecord#setRequestedOrientation
private int mSetOrientationRequestCounter = 0;
- // The min aspect ratio override set by user. Stores the last selected aspect ratio after
- // {@link #shouldApplyUserFullscreenOverride} or {@link #shouldApplyUserMinAspectRatioOverride}
- // have been invoked.
+ // TODO(b/315140179): Make mUserAspectRatio final
+ // The min aspect ratio override set by user
@PackageManager.UserMinAspectRatio
private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET;
@@ -355,6 +357,8 @@ final class LetterboxUiController {
PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
+ mIsOverrideToUserOrientationEnabled =
+ isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER);
mIsOverrideToPortraitOrientationEnabled =
isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT);
mIsOverrideToReverseLandscapeOrientationEnabled =
@@ -663,9 +667,11 @@ final class LetterboxUiController {
@ScreenOrientation
int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
+ final DisplayContent displayContent = mActivityRecord.mDisplayContent;
+ final boolean isIgnoreOrientationRequestEnabled = displayContent != null
+ && displayContent.getIgnoreOrientationRequest();
if (shouldApplyUserFullscreenOverride()
- && mActivityRecord.mDisplayContent != null
- && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
+ && isIgnoreOrientationRequestEnabled) {
Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ mActivityRecord + " is overridden to "
+ screenOrientationToString(SCREEN_ORIENTATION_USER)
@@ -690,7 +696,6 @@ final class LetterboxUiController {
return candidate;
}
- DisplayContent displayContent = mActivityRecord.mDisplayContent;
if (mIsOverrideOrientationOnlyForCameraEnabled && displayContent != null
&& (displayContent.mDisplayRotationCompatPolicy == null
|| !displayContent.mDisplayRotationCompatPolicy
@@ -698,6 +703,17 @@ final class LetterboxUiController {
return candidate;
}
+ // mUserAspectRatio is always initialized first in shouldApplyUserFullscreenOverride(),
+ // which will always come first before this check as user override > device
+ // manufacturer override.
+ if (mUserAspectRatio == PackageManager.USER_MIN_ASPECT_RATIO_UNSET
+ && mIsOverrideToUserOrientationEnabled && isIgnoreOrientationRequestEnabled) {
+ Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_USER));
+ return SCREEN_ORIENTATION_USER;
+ }
+
if (mIsOverrideToReverseLandscapeOrientationEnabled
&& (isFixedOrientationLandscape(candidate) || mIsOverrideAnyOrientationEnabled)) {
Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 57939bc4f348..7995028b1ec7 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -89,6 +89,7 @@ import com.android.server.wm.WindowManagerService.H;
import com.android.window.flags.Flags;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
@@ -106,7 +107,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowProcessController mProcess;
private final String mStringName;
SurfaceSession mSurfaceSession;
- private int mNumWindow = 0;
+ private final ArrayList<WindowState> mAddedWindows = new ArrayList<>();
// Set of visible application overlay window surfaces connected to this session.
private final ArraySet<WindowSurfaceController> mAppOverlaySurfaces = new ArraySet<>();
// Set of visible alert window surfaces connected to this session.
@@ -192,8 +193,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
- // The caller has died, so we can just forget about this.
- // Hmmm, should we call killSessionLocked()??
+ mClientDead = true;
}
}
@@ -211,12 +211,27 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
}
+ boolean isClientDead() {
+ return mClientDead;
+ }
+
@Override
public void binderDied() {
synchronized (mService.mGlobalLock) {
mCallback.asBinder().unlinkToDeath(this, 0);
mClientDead = true;
- killSessionLocked();
+ try {
+ for (int i = mAddedWindows.size() - 1; i >= 0; i--) {
+ final WindowState w = mAddedWindows.get(i);
+ Slog.i(TAG_WM, "WIN DEATH: " + w);
+ if (w.mActivityRecord != null && w.mActivityRecord.findMainWindow() == w) {
+ mService.mSnapshotController.onAppDied(w.mActivityRecord);
+ }
+ w.removeIfPossible();
+ }
+ } finally {
+ killSessionLocked();
+ }
}
}
@@ -741,7 +756,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
}
- void windowAddedLocked() {
+ void onWindowAdded(WindowState w) {
if (mPackageName == null) {
mPackageName = mProcess.mInfo.packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
@@ -757,12 +772,14 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
mService.dispatchNewAnimatorScaleLocked(this);
}
}
- mNumWindow++;
+ mAddedWindows.add(w);
}
- void windowRemovedLocked() {
- mNumWindow--;
- killSessionLocked();
+ void onWindowRemoved(WindowState w) {
+ mAddedWindows.remove(w);
+ if (mAddedWindows.isEmpty()) {
+ killSessionLocked();
+ }
}
@@ -829,7 +846,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
private void killSessionLocked() {
- if (mNumWindow > 0 || !mClientDead) {
+ if (!mClientDead) {
return;
}
@@ -838,10 +855,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
return;
}
- if (DEBUG) {
- Slog.v(TAG_WM, "Last window removed from " + this
- + ", destroying " + mSurfaceSession);
- }
ProtoLog.i(WM_SHOW_TRANSACTIONS, " KILL SURFACE SESSION %s", mSurfaceSession);
try {
mSurfaceSession.kill();
@@ -850,6 +863,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
+ " in session " + this + ": " + e.toString());
}
mSurfaceSession = null;
+ mAddedWindows.clear();
mAlertWindowSurfaces.clear();
mAppOverlaySurfaces.clear();
setHasOverlayUi(false);
@@ -869,7 +883,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
+ pw.print(prefix); pw.print("numWindow="); pw.print(mAddedWindows.size());
pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
pw.print(" mAppOverlaySurfaces="); pw.print(mAppOverlaySurfaces);
pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 671acfc697e4..dbfcc22c6903 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -34,7 +34,6 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
@@ -5917,22 +5916,6 @@ class Task extends TaskFragment {
return activities;
}
- ActivityRecord restartPackage(String packageName) {
- ActivityRecord starting = topRunningActivity();
-
- // All activities that came from the package must be
- // restarted as if there was a config change.
- forAllActivities(r -> {
- if (!r.info.packageName.equals(packageName)) return;
- r.forceNewConfig = true;
- if (starting != null && r == starting && r.isVisibleRequested()) {
- r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
- }
- });
-
- return starting;
- }
-
Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) {
return reuseOrCreateTask(info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
toTop, null /*activity*/, null /*source*/, null /*options*/);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2125c63eed34..c1310a6880fd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1445,6 +1445,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
+ if (session.isClientDead()) {
+ ProtoLog.w(WM_ERROR, "Attempted to add window with a client %s "
+ + "that is dead. Aborting.", session);
+ return WindowManagerGlobal.ADD_APP_EXITING;
+ }
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
@@ -1629,19 +1634,6 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
- if (win.mDeathRecipient == null) {
- // Client has apparently died, so there is no reason to
- // continue.
- ProtoLog.w(WM_ERROR, "Adding window client %s"
- + " that is dead, aborting.", client.asBinder());
- return WindowManagerGlobal.ADD_APP_EXITING;
- }
-
- if (win.getDisplayContent() == null) {
- ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
- return WindowManagerGlobal.ADD_INVALID_DISPLAY;
- }
-
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);
@@ -1725,7 +1717,7 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.mTapExcludedWindows.add(win);
}
- win.attach();
+ win.mSession.onWindowAdded(win);
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4e9d23c88db4..9c21e4c0121e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -52,7 +52,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NOT_MAGNIFIABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY;
@@ -310,7 +309,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// mAttrs.flags is tested in animation without being locked. If the bits tested are ever
// modified they will need to be locked.
final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
- final DeathRecipient mDeathRecipient;
private boolean mIsChildWindow;
final int mBaseLayer;
final int mSubLayer;
@@ -1108,7 +1106,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mViewVisibility = viewVisibility;
mPolicy = mWmService.mPolicy;
mContext = mWmService.mContext;
- DeathRecipient deathRecipient = new DeathRecipient();
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle(
@@ -1128,22 +1125,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
}
- try {
- c.asBinder().linkToDeath(deathRecipient, 0);
- } catch (RemoteException e) {
- mDeathRecipient = null;
- mIsChildWindow = false;
- mLayoutAttached = false;
- mIsImWindow = false;
- mIsWallpaper = false;
- mIsFloatingLayer = false;
- mBaseLayer = 0;
- mSubLayer = 0;
- mWinAnimator = null;
- mOverrideScale = 1f;
- return;
- }
- mDeathRecipient = deathRecipient;
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
// The multiplier here is to reserve space for multiple
@@ -1238,11 +1219,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return TouchOcclusionMode.BLOCK_UNTRUSTED;
}
- void attach() {
- if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
- mSession.windowAddedLocked();
- }
-
void updateGlobalScale() {
if (hasCompatScale()) {
mCompatScale = (mOverrideScale == 1f || mToken.hasSizeCompatBounds())
@@ -1270,13 +1246,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* @see ActivityRecord#hasSizeCompatBounds()
*/
boolean hasCompatScale() {
- if ((mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0) {
- return true;
- }
if (mAttrs.type == TYPE_APPLICATION_STARTING) {
// Exclude starting window because it is not displayed by the application.
return false;
}
+ if (mWmService.mAtmService.mCompatModePackages.useLegacyScreenCompatMode(
+ mSession.mProcess.mInfo.packageName)) {
+ return true;
+ }
return mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
|| mOverrideScale != 1f;
}
@@ -2398,14 +2375,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
disposeInputChannel();
mOnBackInvokedCallbackInfo = null;
- mSession.windowRemovedLocked();
- try {
- mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
- } catch (RuntimeException e) {
- // Ignore if it has already been removed (usually because
- // we are doing this as part of processing a death note.)
- }
-
+ mSession.onWindowRemoved(this);
mWmService.postWindowRemoveCleanupLocked(this);
mWmService.mTrustedPresentationListenerController.removeIgnoredWindowTokens(
@@ -2935,31 +2905,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
- private class DeathRecipient implements IBinder.DeathRecipient {
- @Override
- public void binderDied() {
- try {
- synchronized (mWmService.mGlobalLock) {
- final WindowState win = mWmService
- .windowForClientLocked(mSession, mClient, false);
- Slog.i(TAG, "WIN DEATH: " + win);
- if (win != null) {
- if (win.mActivityRecord != null
- && win.mActivityRecord.findMainWindow() == win) {
- mWmService.mSnapshotController.onAppDied(win.mActivityRecord);
- }
- win.removeIfPossible();
- } else if (mHasSurface) {
- Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
- WindowState.this.removeIfPossible();
- }
- }
- } catch (IllegalArgumentException ex) {
- // This will happen if the window has already been removed.
- }
- }
- }
-
/** Returns {@code true} if this window desires key events. */
boolean canReceiveKeys() {
return canReceiveKeys(false /* fromUserTouch */);
@@ -3830,7 +3775,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
- public void notifyInsetsControlChanged() {
+ public void notifyInsetsControlChanged(int displayId) {
ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsControlChanged for %s ", this);
if (mRemoved) {
return;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index c34e70107a2f..b19f3d813985 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -37,7 +37,6 @@ cc_library_static {
"com_android_server_adb_AdbDebuggingManager.cpp",
"com_android_server_am_BatteryStatsService.cpp",
"com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp",
- "com_android_server_BootReceiver.cpp",
"com_android_server_ConsumerIrService.cpp",
"com_android_server_companion_virtual_InputController.cpp",
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
@@ -94,16 +93,6 @@ cc_library_static {
header_libs: [
"bionic_libc_platform_headers",
],
-
- static_libs: [
- "libunwindstack",
- ],
-
- whole_static_libs: [
- "libdebuggerd_tombstone_proto_to_text",
- ],
-
- runtime_libs: ["libdexfile"],
}
cc_defaults {
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 0d762dfb29ea..061fe0fc88d9 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -33,4 +33,3 @@ per-file com_android_server_companion_virtual_InputController.cpp = file:/servic
# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java
per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS
-per-file com_android_server_BootReceiver.cpp = file:/STABILITY_OWNERS
diff --git a/services/core/jni/com_android_server_BootReceiver.cpp b/services/core/jni/com_android_server_BootReceiver.cpp
deleted file mode 100644
index 3892d284dafb..000000000000
--- a/services/core/jni/com_android_server_BootReceiver.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <libdebuggerd/tombstone.h>
-#include <nativehelper/JNIHelp.h>
-
-#include <sstream>
-
-#include "jni.h"
-#include "tombstone.pb.h"
-
-namespace android {
-
-static void writeToString(std::stringstream& ss, const std::string& line, bool should_log) {
- ss << line << std::endl;
-}
-
-static jstring com_android_server_BootReceiver_getTombstoneText(JNIEnv* env, jobject,
- jbyteArray tombstoneBytes) {
- Tombstone tombstone;
- tombstone.ParseFromArray(env->GetByteArrayElements(tombstoneBytes, 0),
- env->GetArrayLength(tombstoneBytes));
-
- std::stringstream tombstoneString;
-
- tombstone_proto_to_text(tombstone,
- std::bind(&writeToString, std::ref(tombstoneString),
- std::placeholders::_1, std::placeholders::_2));
-
- return env->NewStringUTF(tombstoneString.str().c_str());
-}
-
-static const JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- {"getTombstoneText", "([B)Ljava/lang/String;",
- (jstring*)com_android_server_BootReceiver_getTombstoneText},
-};
-
-int register_com_android_server_BootReceiver(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "com/android/server/BootReceiver", sMethods,
- NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index deae82c696e6..11734da5b1ac 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -65,7 +65,6 @@ int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env);
int register_android_server_companion_virtual_InputController(JNIEnv* env);
int register_android_server_app_GameManagerService(JNIEnv* env);
-int register_com_android_server_BootReceiver(JNIEnv* env);
int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env);
int register_com_android_server_display_DisplayControl(JNIEnv* env);
int register_com_android_server_SystemClockTime(JNIEnv* env);
@@ -127,7 +126,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_sensor_SensorService(vm, env);
register_android_server_companion_virtual_InputController(env);
register_android_server_app_GameManagerService(env);
- register_com_android_server_BootReceiver(env);
register_com_android_server_wm_TaskFpsCallbackController(env);
register_com_android_server_display_DisplayControl(env);
register_com_android_server_SystemClockTime(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e0a2f30b1831..a490013303e9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2469,7 +2469,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void migratePersonalAppSuspensionLocked(
int doUserId, int poUserId, ActiveAdmin poAdmin) {
final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
- if (!pmi.isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, doUserId)) {
+ if (!pmi.isAdminSuspendingAnyPackages(doUserId)) {
Slogf.i(LOG_TAG, "DO is not suspending any apps.");
return;
}
@@ -2480,7 +2480,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
poAdmin.mSuspendPersonalApps = true;
} else {
Slogf.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps.");
- pmi.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, doUserId);
+ pmi.unsuspendAdminSuspendedPackages(doUserId);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 6570ce1cd500..506dbe8c48c4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -16,8 +16,6 @@
package com.android.server.devicepolicy;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
@@ -287,7 +285,7 @@ final class PolicyEnforcerCallbacks {
suspendPersonalAppsInPackageManager(context, userId);
} else {
LocalServices.getService(PackageManagerInternal.class)
- .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, userId);
+ .unsuspendAdminSuspendedPackages(userId);
}
});
return true;
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
index eae159fe9e89..8ff1a7dfa37b 100644
--- a/services/manifest_services.xml
+++ b/services/manifest_services.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="framework">
<hal format="aidl">
<name>android.frameworks.location.altitude</name>
- <version>1</version>
+ <version>2</version>
<fqname>IAltitudeService/default</fqname>
</hal>
<hal format="aidl">
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index f69f6283f968..022268df4a63 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1262,7 +1262,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
val apexModuleName = packageState.apexModuleName
val packageName = packageState.packageName
return when {
- packageState.isVendor ->
+ packageState.isVendor || packageState.isOdm ->
permissionAllowlist.getVendorPrivilegedAppAllowlistState(
packageName,
permissionName
@@ -1471,12 +1471,15 @@ class AppIdPermissionPolicy : SchemePolicy() {
// In any case, don't grant a privileged permission to privileged vendor apps,
// if the permission's protectionLevel does not have the extra vendorPrivileged
// flag.
- if (packageState.isVendor && !permission.isVendorPrivileged) {
+ if (
+ (packageState.isVendor || packageState.isOdm) &&
+ !permission.isVendorPrivileged
+ ) {
Slog.w(
LOG_TAG,
"Permission $permissionName cannot be granted to privileged" +
- " vendor app $packageName because it isn't a vendorPrivileged" +
- " permission"
+ " vendor (or odm) app $packageName because it isn't a" +
+ " vendorPrivileged permission"
)
return false
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 3d0b389aa171..6fff012cd9ca 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -36,6 +36,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -50,6 +51,7 @@ import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
import android.os.BaseBundle;
import android.os.Message;
import android.os.PersistableBundle;
@@ -57,6 +59,9 @@ import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -122,6 +127,8 @@ public class PackageManagerSettingsTests {
private static final String PACKAGE_NAME_3 = "com.android.app3";
private static final int TEST_RESOURCE_ID = 2131231283;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Mock
RuntimePermissionsPersistence mRuntimePermissionsPersistence;
@Mock
@@ -425,7 +432,7 @@ public class PackageManagerSettingsTests {
PackageUserStateInternal packageUserState1 = ps1.readUserState(0);
assertThat(packageUserState1.isSuspended(), is(true));
assertThat(packageUserState1.getSuspendParams().size(), is(1));
- assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
+ assertThat(packageUserState1.getSuspendParams().keyAt(0), is(UserPackage.of(0, "android")));
assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
is(nullValue()));
@@ -437,7 +444,7 @@ public class PackageManagerSettingsTests {
packageUserState1 = ps1.readUserState(0);
assertThat(packageUserState1.isSuspended(), is(true));
assertThat(packageUserState1.getSuspendParams().size(), is(1));
- assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
+ assertThat(packageUserState1.getSuspendParams().keyAt(0), is(UserPackage.of(0, "android")));
assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
is(nullValue()));
@@ -472,7 +479,8 @@ public class PackageManagerSettingsTests {
watcher.verifyNoChangeReported("readUserState");
assertThat(packageUserState1.isSuspended(), is(true));
assertThat(packageUserState1.getSuspendParams().size(), is(1));
- assertThat(packageUserState1.getSuspendParams().keyAt(0), is(PACKAGE_NAME_3));
+ assertThat(packageUserState1.getSuspendParams().keyAt(0),
+ is(UserPackage.of(0, PACKAGE_NAME_3)));
final SuspendParams params = packageUserState1.getSuspendParams().valueAt(0);
watcher.verifyNoChangeReported("fetch user state");
assertThat(params, is(notNullValue()));
@@ -523,19 +531,24 @@ public class PackageManagerSettingsTests {
.setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND)
.build();
- ps1.modifyUserState(0).putSuspendParams("suspendingPackage1",
+ UserPackage suspender1 = UserPackage.of(0, "suspendingPackage1");
+ UserPackage suspender2 = UserPackage.of(0, "suspendingPackage2");
+ UserPackage suspender3 = UserPackage.of(0, "suspendingPackage3");
+ UserPackage irrelevantSuspender = UserPackage.of(0, "irrelevant");
+
+ ps1.modifyUserState(0).putSuspendParams(suspender1,
new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
- ps1.modifyUserState(0).putSuspendParams("suspendingPackage2",
+ ps1.modifyUserState(0).putSuspendParams(suspender2,
new SuspendParams(dialogInfo2, appExtras2, launcherExtras2));
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
watcher.verifyChangeReported("put package 1");
- ps2.modifyUserState(0).putSuspendParams("suspendingPackage3",
+ ps2.modifyUserState(0).putSuspendParams(suspender3,
new SuspendParams(null, appExtras1, null));
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
watcher.verifyChangeReported("put package 2");
- ps3.modifyUserState(0).removeSuspension("irrelevant");
+ ps3.modifyUserState(0).removeSuspension(irrelevantSuspender);
settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
watcher.verifyChangeReported("put package 3");
@@ -560,7 +573,7 @@ public class PackageManagerSettingsTests {
assertThat(readPus1.getSuspendParams().size(), is(2));
watcher.verifyNoChangeReported("read package param");
- assertThat(readPus1.getSuspendParams().keyAt(0), is("suspendingPackage1"));
+ assertThat(readPus1.getSuspendParams().keyAt(0), is(suspender1));
final SuspendParams params11 = readPus1.getSuspendParams().valueAt(0);
watcher.verifyNoChangeReported("read package param");
assertThat(params11, is(notNullValue()));
@@ -570,7 +583,7 @@ public class PackageManagerSettingsTests {
is(true));
watcher.verifyNoChangeReported("read package param");
- assertThat(readPus1.getSuspendParams().keyAt(1), is("suspendingPackage2"));
+ assertThat(readPus1.getSuspendParams().keyAt(1), is(suspender2));
final SuspendParams params12 = readPus1.getSuspendParams().valueAt(1);
assertThat(params12, is(notNullValue()));
assertThat(params12.getDialogInfo(), is(dialogInfo2));
@@ -583,7 +596,7 @@ public class PackageManagerSettingsTests {
.readUserState(0);
assertThat(readPus2.isSuspended(), is(true));
assertThat(readPus2.getSuspendParams().size(), is(1));
- assertThat(readPus2.getSuspendParams().keyAt(0), is("suspendingPackage3"));
+ assertThat(readPus2.getSuspendParams().keyAt(0), is(suspender3));
final SuspendParams params21 = readPus2.getSuspendParams().valueAt(0);
assertThat(params21, is(notNullValue()));
assertThat(params21.getDialogInfo(), is(nullValue()));
@@ -1087,6 +1100,8 @@ public class PackageManagerSettingsTests {
new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-1");
private static final File UPDATED_CODE_PATH =
new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-2");
+ private static final File UPDATED_CODE_PATH2 =
+ new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-3");
private static final long INITIAL_VERSION_CODE = 10023L;
private static final long UPDATED_VERSION_CODE = 10025L;
@@ -1134,7 +1149,8 @@ public class PackageManagerSettingsTests {
.setNeutralButtonText(0x11220003)
.setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS)
.build();
- origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1",
+ origPkgSetting01.modifyUserState(0).putSuspendParams(
+ UserPackage.of(0, "suspendingPackage1"),
new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
origPkgSetting01.setPkg(mockAndroidPackage(origPkgSetting01));
final PackageSetting testPkgSetting01 = new PackageSetting(
@@ -1182,13 +1198,16 @@ public class PackageManagerSettingsTests {
null /*mimeGroups*/,
UUID.randomUUID(),
34 /*targetSdkVersion*/,
- null /*restrictUpdateHash*/);
+ null /*restrictUpdateHash*/,
+ false /*isDontKill*/);
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("arm64-v8a"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
assertThat(testPkgSetting01.getSecondaryCpuAbiLegacy(), is("armeabi"));
assertThat(testPkgSetting01.getFlags(), is(0));
assertThat(testPkgSetting01.getPrivateFlags(), is(0));
+ assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
+ assertNull(testPkgSetting01.getOldPaths());
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, false /*notLaunched*/,
false /*stopped*/, false /*installed*/);
@@ -1223,7 +1242,8 @@ public class PackageManagerSettingsTests {
null /*mimeGroups*/,
UUID.randomUUID(),
34 /*targetSdkVersion*/,
- null /*restrictUpdateHash*/);
+ null /*restrictUpdateHash*/,
+ false /*isDontKill*/);
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getPrimaryCpuAbiLegacy(), is("arm64-v8a"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
@@ -1237,6 +1257,74 @@ public class PackageManagerSettingsTests {
false /*stopped*/, true /*installed*/);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_IMPROVE_INSTALL_DONT_KILL)
+ public void testUpdatePackageSettings02WithOldPaths() throws PackageManagerException {
+ final PackageSetting testPkgSetting01 =
+ createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
+ testPkgSetting01.setInstalled(false /*installed*/, 0 /*userId*/);
+ assertThat(testPkgSetting01.getFlags(), is(0));
+ assertThat(testPkgSetting01.getPrivateFlags(), is(0));
+ final PackageSetting oldPkgSetting01 = new PackageSetting(testPkgSetting01);
+ Settings.updatePackageSetting(
+ testPkgSetting01,
+ null /*disabledPkg*/,
+ null /*existingSharedUserSetting*/,
+ null /*sharedUser*/,
+ UPDATED_CODE_PATH /*codePath*/,
+ null /*legacyNativeLibraryPath*/,
+ "arm64-v8a" /*primaryCpuAbi*/,
+ "armeabi" /*secondaryCpuAbi*/,
+ ApplicationInfo.FLAG_SYSTEM /*pkgFlags*/,
+ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED /*pkgPrivateFlags*/,
+ UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
+ null /*usesStaticLibraries*/,
+ null /*usesStaticLibrariesVersions*/,
+ null /*mimeGroups*/,
+ UUID.randomUUID(),
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/,
+ true /*isDontKill*/);
+ assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
+ assertThat(testPkgSetting01.getOldPaths().size(), is(1));
+ assertTrue(testPkgSetting01.getOldPaths().contains(INITIAL_CODE_PATH));
+ Settings.updatePackageSetting(
+ testPkgSetting01,
+ null /*disabledPkg*/,
+ null /*existingSharedUserSetting*/,
+ null /*sharedUser*/,
+ UPDATED_CODE_PATH2 /*codePath*/,
+ null /*legacyNativeLibraryPath*/,
+ "arm64-v8a" /*primaryCpuAbi*/,
+ "armeabi" /*secondaryCpuAbi*/,
+ ApplicationInfo.FLAG_SYSTEM /*pkgFlags*/,
+ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED /*pkgPrivateFlags*/,
+ UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
+ null /*usesSdkLibrariesOptional*/,
+ null /*usesStaticLibraries*/,
+ null /*usesStaticLibrariesVersions*/,
+ null /*mimeGroups*/,
+ UUID.randomUUID(),
+ 34 /*targetSdkVersion*/,
+ null /*restrictUpdateHash*/,
+ true /*isDontKill*/);
+ assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH2));
+ assertThat(testPkgSetting01.getOldPaths().size(), is(2));
+ int index = 0;
+ for (File path : testPkgSetting01.getOldPaths()) {
+ switch (index) {
+ case 0 -> assertThat(path, is(INITIAL_CODE_PATH));
+ case 1 -> assertThat(path, is(UPDATED_CODE_PATH));
+ }
+ index++;
+ }
+ }
+
/** Update package; changing shared user throws exception */
@Test
public void testUpdatePackageSetting03() {
@@ -1266,7 +1354,8 @@ public class PackageManagerSettingsTests {
null /*mimeGroups*/,
UUID.randomUUID(),
34 /*targetSdkVersion*/,
- null /*restrictUpdateHash*/);
+ null /*restrictUpdateHash*/,
+ false /*isDontKill*/);
fail("Expected a PackageManagerException");
} catch (PackageManagerException expected) {
}
@@ -1672,6 +1761,63 @@ public class PackageManagerSettingsTests {
assertSame(sus1, settings.getSharedUserSettingLPr(ps1));
}
+ @Test
+ public void testAddRemoveOldPaths() {
+ final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+ ps1.addOldPath(INITIAL_CODE_PATH);
+ ps1.addOldPath(UPDATED_CODE_PATH);
+ ps1.addOldPath(UPDATED_CODE_PATH2);
+ assertThat(ps1.getOldPaths().size(), is(3));
+ int index = 0;
+ for (File path : ps1.getOldPaths()) {
+ switch (index) {
+ case 0 -> assertThat(path, is(INITIAL_CODE_PATH));
+ case 1 -> assertThat(path, is(UPDATED_CODE_PATH));
+ case 2 -> assertThat(path, is(UPDATED_CODE_PATH2));
+ }
+ index++;
+ }
+
+ ps1.removeOldPath(UPDATED_CODE_PATH);
+ assertThat(ps1.getOldPaths().size(), is(2));
+ index = 0;
+ for (File path : ps1.getOldPaths()) {
+ switch (index) {
+ case 0 -> assertThat(path, is(INITIAL_CODE_PATH));
+ case 1 -> assertThat(path, is(UPDATED_CODE_PATH2));
+ }
+ index++;
+ }
+ ps1.removeOldPath(UPDATED_CODE_PATH2);
+ // assert not throws when deleting non-existing element
+ ps1.removeOldPath(UPDATED_CODE_PATH2);
+ assertThat(ps1.getOldPaths().size(), is(1));
+ assertTrue(ps1.getOldPaths().contains(INITIAL_CODE_PATH));
+ ps1.removeOldPath(INITIAL_CODE_PATH);
+ // assert not throws when deleting from an empty list
+ ps1.removeOldPath(INITIAL_CODE_PATH);
+ assertThat(ps1.getOldPaths().size(), is(0));
+ }
+
+ @Test
+ public void testOldPathsNotPreservedAfterReboot() {
+ Settings settings = makeSettings();
+ final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+ ps1.setAppId(Process.FIRST_APPLICATION_UID);
+ ps1.setPkg(PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()
+ .setUid(ps1.getAppId())
+ .hideAsFinal());
+ ps1.addOldPath(INITIAL_CODE_PATH);
+ ps1.addOldPath(UPDATED_CODE_PATH);
+ settings.mPackages.put(PACKAGE_NAME_1, ps1);
+
+ settings.writeLPr(computer, /*sync=*/true);
+ settings.mPackages.clear();
+
+ assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+ assertNull(settings.getPackageLPr(PACKAGE_NAME_1).getOldPaths());
+ }
+
private void verifyUserState(PackageUserState userState,
boolean notLaunched, boolean stopped, boolean installed) {
assertThat(userState.getEnabledState(), is(0));
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java
index c0c70321c79b..978044045ab3 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserPackage;
import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
@@ -89,7 +90,7 @@ public class PackageUserStateTest {
assertThat(testUserState.equals(oldUserState), is(false));
oldUserState = new PackageUserStateImpl();
- oldUserState.putSuspendParams("suspendingPackage",
+ oldUserState.putSuspendParams(UserPackage.of(0, "suspendingPackage"),
new SuspendParams(null, new PersistableBundle(), null));
assertThat(testUserState.equals(oldUserState), is(false));
@@ -220,6 +221,8 @@ public class PackageUserStateTest {
final PersistableBundle launcherExtras2 = createPersistableBundle(null, 0, "name",
"launcherExtras2", null, 0);
+ final int suspendingUser1 = 0;
+ final int suspendingUser2 = 10;
final String suspendingPackage1 = "package1";
final String suspendingPackage2 = "package2";
@@ -230,12 +233,12 @@ public class PackageUserStateTest {
.setMessage("dialogMessage2")
.build();
- final ArrayMap<String, SuspendParams> paramsMap1 = new ArrayMap<>();
- paramsMap1.put(suspendingPackage1, createSuspendParams(dialogInfo1, appExtras1,
- launcherExtras1));
- final ArrayMap<String, SuspendParams> paramsMap2 = new ArrayMap<>();
- paramsMap2.put(suspendingPackage2, createSuspendParams(dialogInfo2,
- appExtras2, launcherExtras2));
+ final ArrayMap<UserPackage, SuspendParams> paramsMap1 = new ArrayMap<>();
+ paramsMap1.put(UserPackage.of(suspendingUser1, suspendingPackage1),
+ createSuspendParams(dialogInfo1, appExtras1, launcherExtras1));
+ final ArrayMap<UserPackage, SuspendParams> paramsMap2 = new ArrayMap<>();
+ paramsMap2.put(UserPackage.of(suspendingUser2, suspendingPackage2),
+ createSuspendParams(dialogInfo2, appExtras2, launcherExtras2));
final PackageUserStateImpl testUserState1 = new PackageUserStateImpl();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
index dea838d3763d..fbb14c3db9f9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
@@ -19,6 +19,7 @@ package com.android.server.display;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -88,4 +89,12 @@ public class DisplayOffloadSessionImplTest {
verify(mDisplayPowerController).setBrightnessFromOffload(brightness);
}
+
+ @Test
+ public void testBlockScreenOn() {
+ Runnable unblocker = () -> {};
+ mSession.blockScreenOn(unblocker);
+
+ verify(mDisplayOffloader).onBlockingScreenOn(eq(unblocker));
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 2f1944d05db5..00f98924eff3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1238,6 +1238,9 @@ public class LocalDisplayAdapterTest {
@Override
public void stopOffload() {}
+
+ @Override
+ public void onBlockingScreenOn(Runnable unblocker) {}
});
mDisplayOffloadSession = new DisplayOffloadSessionImpl(mDisplayOffloader,
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index ae53e707a7cc..7444403f88c8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -19,6 +19,7 @@ package com.android.server.pm
import android.app.AppOpsManager
import android.content.Intent
import android.content.pm.SuspendDialogInfo
+import android.content.pm.UserPackage
import android.os.Binder
import android.os.PersistableBundle
import com.android.server.testutils.any
@@ -41,12 +42,18 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
.thenReturn(AppOpsManager.MODE_DEFAULT)
}
+ companion object {
+ val doUserPackage = UserPackage.of(TEST_USER_ID, DEVICE_OWNER_PACKAGE)
+ val platformUserPackage = UserPackage.of(TEST_USER_ID, PLATFORM_PACKAGE_NAME)
+ val testUserPackage1 = UserPackage.of(TEST_USER_ID, TEST_PACKAGE_1)
+ }
+
@Test
fun setPackagesSuspended() {
val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+ null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
testHandler.flush()
@@ -63,14 +70,14 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
fun setPackagesSuspended_emptyPackageName() {
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
null /* packageNames */, true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+ null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
assertThat(failedNames).isNull()
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOfNulls(0), true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+ null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
assertThat(failedNames).isEmpty()
@@ -80,7 +87,8 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
fun setPackagesSuspended_callerIsNotAllowed() {
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID,
+ null /* launcherExtras */, null /* dialogInfo */,
+ testUserPackage1, TEST_USER_ID,
Binder.getCallingUid(), false /* quarantined */)
assertThat(failedNames).asList().hasSize(1)
@@ -91,7 +99,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
fun setPackagesSuspended_callerSuspendItself() {
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(DEVICE_OWNER_PACKAGE), true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+ null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
assertThat(failedNames).asList().hasSize(1)
@@ -102,7 +110,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
fun setPackagesSuspended_nonexistentPackage() {
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(NONEXISTENT_PACKAGE), true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+ null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
assertThat(failedNames).asList().hasSize(1)
@@ -115,7 +123,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
knownPackages, true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+ null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
TEST_USER_ID, deviceOwnerUid, false /* quarantined */)!!
assertThat(failedNames.size).isEqualTo(knownPackages.size)
@@ -129,14 +137,14 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+ null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
testHandler.flush()
Mockito.clearInvocations(broadcastHelper)
assertThat(failedNames).isEmpty()
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, false /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+ null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
testHandler.flush()
@@ -184,7 +192,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_1)
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_1), true /* suspended */, appExtras, null /* launcherExtras */,
- null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+ null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -202,22 +210,22 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, appExtras, null /* launcherExtras */,
- null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+ null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
false /* quarantined */)
testHandler.flush()
Mockito.clearInvocations(broadcastHelper)
assertThat(failedNames).isEmpty()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
- TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+ TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
- TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull()
assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull()
suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
- targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE },
+ targetPackages, { suspender -> suspender.packageName == DEVICE_OWNER_PACKAGE },
TEST_USER_ID)
testHandler.flush()
@@ -243,7 +251,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
- null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+ null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -258,7 +266,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
fun isPackageSuspended() {
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+ null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -273,13 +281,13 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
- null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+ null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
- TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
}
@Test
@@ -290,57 +298,57 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
// Suspend.
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, null /* appExtras */, launcherExtras,
- null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+ null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
false /* quarantined */)
assertThat(failedNames).isEmpty()
testHandler.flush()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
- TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
// Suspend by system.
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, null /* appExtras */, launcherExtras,
- null /* dialogInfo */, PLATFORM_PACKAGE_NAME, TEST_USER_ID, deviceOwnerUid,
+ null /* dialogInfo */, platformUserPackage, TEST_USER_ID, deviceOwnerUid,
false /* quarantined */)
assertThat(failedNames).isEmpty()
testHandler.flush()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
- TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME)
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(platformUserPackage)
// QAS by package1.
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, null /* appExtras */, launcherExtras,
- null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid,
+ null /* dialogInfo */, testUserPackage1, TEST_USER_ID, deviceOwnerUid,
true /* quarantined */)
assertThat(failedNames).isEmpty()
testHandler.flush()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
- TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(TEST_PACKAGE_1)
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(testUserPackage1)
// Un-QAS by package1.
suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
- targetPackages, { suspendingPackage -> suspendingPackage == TEST_PACKAGE_1 },
+ targetPackages, { suspendingPackage -> suspendingPackage == testUserPackage1 },
TEST_USER_ID)
testHandler.flush()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
- TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME)
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(platformUserPackage)
// Un-suspend by system.
suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
- targetPackages, { suspendingPackage -> suspendingPackage == PLATFORM_PACKAGE_NAME },
+ targetPackages, { suspender -> suspender.packageName == PLATFORM_PACKAGE_NAME },
TEST_USER_ID)
testHandler.flush()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
- TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
// Unsuspend.
suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
- targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE },
+ targetPackages, { suspendingPackage -> suspendingPackage == doUserPackage },
TEST_USER_ID)
testHandler.flush()
@@ -354,13 +362,13 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
.setTitle(TEST_PACKAGE_1).build()
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
+ null /* launcherExtras */, dialogInfo, doUserPackage, TEST_USER_ID,
deviceOwnerUid, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
val result = suspendPackageHelper.getSuspendedDialogInfo(pms.snapshotComputer(),
- TEST_PACKAGE_1, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)!!
+ TEST_PACKAGE_1, doUserPackage, TEST_USER_ID, deviceOwnerUid)!!
assertThat(result.title).isEqualTo(TEST_PACKAGE_1)
}
diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
deleted file mode 100644
index 523c5c060cf5..000000000000
--- a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.test.AndroidTestCase;
-
-import com.android.server.os.TombstoneProtos;
-import com.android.server.os.TombstoneProtos.Tombstone;
-
-public class BootReceiverTest extends AndroidTestCase {
- private static final String TAG = "BootReceiverTest";
-
- public void testRemoveMemoryFromTombstone() {
- Tombstone tombstoneBase = Tombstone.newBuilder()
- .setBuildFingerprint("build_fingerprint")
- .setRevision("revision")
- .setPid(123)
- .setTid(23)
- .setUid(34)
- .setSelinuxLabel("selinux_label")
- .addCommandLine("cmd1")
- .addCommandLine("cmd2")
- .addCommandLine("cmd3")
- .setProcessUptime(300)
- .setAbortMessage("abort")
- .addCauses(TombstoneProtos.Cause.newBuilder()
- .setHumanReadable("cause1")
- .setMemoryError(TombstoneProtos.MemoryError.newBuilder()
- .setTool(TombstoneProtos.MemoryError.Tool.SCUDO)
- .setType(TombstoneProtos.MemoryError.Type.DOUBLE_FREE)))
- .addLogBuffers(TombstoneProtos.LogBuffer.newBuilder().setName("name").addLogs(
- TombstoneProtos.LogMessage.newBuilder()
- .setTimestamp("123")
- .setMessage("message")))
- .addOpenFds(TombstoneProtos.FD.newBuilder().setFd(1).setPath("path"))
- .build();
-
- Tombstone tombstoneWithoutMemory = tombstoneBase.toBuilder()
- .putThreads(1, TombstoneProtos.Thread.newBuilder()
- .setId(1)
- .setName("thread1")
- .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1))
- .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2))
- .addBacktraceNote("backtracenote1")
- .addUnreadableElfFiles("files1")
- .setTaggedAddrCtrl(1)
- .setPacEnabledKeys(10)
- .build())
- .build();
-
- Tombstone tombstoneWithMemory = tombstoneBase.toBuilder()
- .addMemoryMappings(TombstoneProtos.MemoryMapping.newBuilder()
- .setBeginAddress(1)
- .setEndAddress(100)
- .setOffset(10)
- .setRead(true)
- .setWrite(true)
- .setExecute(false)
- .setMappingName("mapping")
- .setBuildId("build")
- .setLoadBias(70))
- .putThreads(1, TombstoneProtos.Thread.newBuilder()
- .setId(1)
- .setName("thread1")
- .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1))
- .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2))
- .addBacktraceNote("backtracenote1")
- .addUnreadableElfFiles("files1")
- .addMemoryDump(TombstoneProtos.MemoryDump.newBuilder()
- .setRegisterName("register1")
- .setMappingName("mapping")
- .setBeginAddress(10))
- .setTaggedAddrCtrl(1)
- .setPacEnabledKeys(10)
- .build())
- .build();
-
- assertThat(BootReceiver.removeMemoryFromTombstone(tombstoneWithMemory))
- .isEqualTo(tombstoneWithoutMemory);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java
index 5fd28f57c7c3..332b1a2b77ca 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility.magnification;
+import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT;
import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE;
@@ -44,6 +45,7 @@ import android.testing.TestableContext;
import androidx.test.InstrumentationRegistry;
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -70,6 +72,13 @@ public class WindowMagnificationPromptControllerTest {
private WindowMagnificationPromptController mWindowMagnificationPromptController;
private BroadcastReceiver mReceiver;
+ /**
+ * return whether window magnification is supported for current test context.
+ */
+ private boolean isWindowModeSupported() {
+ return mTestableContext.getPackageManager().hasSystemFeature(FEATURE_WINDOW_MAGNIFICATION);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -78,6 +87,9 @@ public class WindowMagnificationPromptControllerTest {
setWindowMagnificationPromptSettings(true);
mWindowMagnificationPromptController = new WindowMagnificationPromptController(
mTestableContext, TEST_USER);
+
+ // skip test if window magnification is not supported to prevent fail results.
+ Assume.assumeTrue(isWindowModeSupported());
}
@After
@@ -111,8 +123,8 @@ public class WindowMagnificationPromptControllerTest {
final Intent intent = new Intent(ACTION_TURN_ON_IN_SETTINGS);
mReceiver.onReceive(mTestableContext, intent);
- assertThat(Settings.Secure.getInt(mResolver, ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
- -1)).isEqualTo(0);
+ assertThat(Settings.Secure.getIntForUser(mResolver,
+ ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, -1, TEST_USER)).isEqualTo(0);
verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver);
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 3b5cae328b3c..88b2ed4f79c9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -50,14 +50,22 @@ import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.iris.IIrisService;
import android.os.Binder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
@@ -89,6 +97,9 @@ public class AuthServiceTest {
@Rule
public MockitoRule mockitorule = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -118,6 +129,10 @@ public class AuthServiceTest {
@Captor
private ArgumentCaptor<List<FingerprintSensorPropertiesInternal>> mFingerprintPropsCaptor;
@Captor
+ private ArgumentCaptor<FingerprintSensorConfigurations> mFingerprintSensorConfigurationsCaptor;
+ @Captor
+ private ArgumentCaptor<FaceSensorConfigurations> mFaceSensorConfigurationsCaptor;
+ @Captor
private ArgumentCaptor<List<FaceSensorPropertiesInternal>> mFacePropsCaptor;
@Before
@@ -143,6 +158,9 @@ public class AuthServiceTest {
when(mContext.getResources()).thenReturn(mResources);
when(mInjector.getBiometricService()).thenReturn(mBiometricService);
when(mInjector.getConfiguration(any())).thenReturn(config);
+ when(mInjector.getFaceConfiguration(any())).thenReturn(config);
+ when(mInjector.getFingerprintConfiguration(any())).thenReturn(config);
+ when(mInjector.getIrisConfiguration(any())).thenReturn(config);
when(mInjector.getFingerprintService()).thenReturn(mFingerprintService);
when(mInjector.getFaceService()).thenReturn(mFaceService);
when(mInjector.getIrisService()).thenReturn(mIrisService);
@@ -173,12 +191,13 @@ public class AuthServiceTest {
}
@Test
+ @RequiresFlagsDisabled(com.android.server.biometrics.Flags.FLAG_DE_HIDL)
public void testRegisterAuthenticator_registerAuthenticators() throws Exception {
final int fingerprintId = 0;
final int fingerprintStrength = 15;
final int faceId = 1;
- final int faceStrength = 4095;
+ final int faceStrength = 15;
final String[] config = {
// ID0:Fingerprint:Strong
@@ -206,6 +225,51 @@ public class AuthServiceTest {
Utils.authenticatorStrengthToPropertyStrength(faceStrength));
}
+ @Test
+ @RequiresFlagsEnabled(com.android.server.biometrics.Flags.FLAG_DE_HIDL)
+ public void testRegisterAuthenticator_registerAuthenticatorsLegacy() throws RemoteException {
+ final int fingerprintId = 0;
+ final int fingerprintStrength = 15;
+
+ final int faceId = 1;
+ final int faceStrength = 4095;
+
+ final String[] config = {
+ // ID0:Fingerprint:Strong
+ String.format("%d:2:%d", fingerprintId, fingerprintStrength),
+ // ID2:Face:Convenience
+ String.format("%d:8:%d", faceId, faceStrength)
+ };
+
+ when(mInjector.getFingerprintConfiguration(any())).thenReturn(config);
+ when(mInjector.getFaceConfiguration(any())).thenReturn(config);
+ when(mInjector.getFingerprintAidlInstances()).thenReturn(new String[]{});
+ when(mInjector.getFaceAidlInstances()).thenReturn(new String[]{});
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ verify(mFingerprintService).registerAuthenticatorsLegacy(
+ mFingerprintSensorConfigurationsCaptor.capture());
+
+ final SensorProps[] fingerprintProp = mFingerprintSensorConfigurationsCaptor.getValue()
+ .getSensorPairForInstance("defaultHIDL").second;
+
+ assertEquals(fingerprintProp[0].commonProps.sensorId, fingerprintId);
+ assertEquals(fingerprintProp[0].commonProps.sensorStrength,
+ Utils.authenticatorStrengthToPropertyStrength(fingerprintStrength));
+
+ verify(mFaceService).registerAuthenticatorsLegacy(
+ mFaceSensorConfigurationsCaptor.capture());
+
+ final android.hardware.biometrics.face.SensorProps[] faceProp =
+ mFaceSensorConfigurationsCaptor.getValue()
+ .getSensorPairForInstance("defaultHIDL").second;
+
+ assertEquals(faceProp[0].commonProps.sensorId, faceId);
+ assertEquals(faceProp[0].commonProps.sensorStrength,
+ Utils.authenticatorStrengthToPropertyStrength(faceStrength));
+ }
// TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
new file mode 100644
index 000000000000..c9e1c4a8bfc5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.face.FaceSensorProperties.TYPE_UNKNOWN;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.SensorProps;
+import android.hardware.face.FaceSensorConfigurations;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.server.biometrics.Flags;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.face.aidl.FaceProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+@SmallTest
+public class FaceServiceTest {
+ private static final int ID_DEFAULT = 2;
+ private static final int ID_VIRTUAL = 6;
+ private static final String NAME_DEFAULT = "default";
+ private static final String NAME_VIRTUAL = "virtual";
+
+ @Rule
+ public final MockitoRule mMockito = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ @Rule
+ public final FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule();
+ @Mock
+ FaceProvider mFaceProviderDefault;
+ @Mock
+ FaceProvider mFaceProviderVirtual;
+ @Mock
+ IFace mDefaultFaceDaemon;
+ @Mock
+ IFace mVirtualFaceDaemon;
+ @Mock
+ IBiometricService mIBiometricService;
+
+ private final SensorProps mDefaultSensorProps = new SensorProps();
+ private final SensorProps mVirtualSensorProps = new SensorProps();
+ private FaceService mFaceService;
+ private final FaceSensorPropertiesInternal mSensorPropsDefault =
+ new FaceSensorPropertiesInternal(ID_DEFAULT, STRENGTH_STRONG,
+ 2 /* maxEnrollmentsPerUser */,
+ List.of(),
+ TYPE_UNKNOWN,
+ true /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */);
+ private final FaceSensorPropertiesInternal mSensorPropsVirtual =
+ new FaceSensorPropertiesInternal(ID_VIRTUAL, STRENGTH_STRONG,
+ 2 /* maxEnrollmentsPerUser */,
+ List.of(),
+ TYPE_UNKNOWN,
+ true /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */);
+ private FaceSensorConfigurations mFaceSensorConfigurations;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mDefaultFaceDaemon.getSensorProps()).thenReturn(
+ new SensorProps[]{mDefaultSensorProps});
+ when(mVirtualFaceDaemon.getSensorProps()).thenReturn(
+ new SensorProps[]{mVirtualSensorProps});
+ when(mFaceProviderDefault.getSensorProperties()).thenReturn(List.of(mSensorPropsDefault));
+ when(mFaceProviderVirtual.getSensorProperties()).thenReturn(List.of(mSensorPropsVirtual));
+
+ mFaceSensorConfigurations = new FaceSensorConfigurations(false);
+ mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_DEFAULT, NAME_VIRTUAL},
+ (name) -> {
+ if (name.equals(IFace.DESCRIPTOR + "/" + NAME_DEFAULT)) {
+ return mDefaultFaceDaemon;
+ } else if (name.equals(IFace.DESCRIPTOR + "/" + NAME_VIRTUAL)) {
+ return mVirtualFaceDaemon;
+ }
+ return null;
+ });
+ }
+
+ private void initService() {
+ mFaceService = new FaceService(mContext,
+ (filteredSensorProps, resetLockoutRequiresChallenge) -> {
+ if (NAME_DEFAULT.equals(filteredSensorProps.first)) return mFaceProviderDefault;
+ if (NAME_VIRTUAL.equals(filteredSensorProps.first)) return mFaceProviderVirtual;
+ return null;
+ }, () -> mIBiometricService);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_defaultOnly() throws Exception {
+ initService();
+
+ mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT),
+ eq(BiometricAuthenticator.TYPE_FACE),
+ eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)),
+ any());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_virtualOnly() throws Exception {
+ initService();
+ Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
+ Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
+
+ mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL),
+ eq(BiometricAuthenticator.TYPE_FACE),
+ eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)), any());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception {
+ mFaceSensorConfigurations = new FaceSensorConfigurations(false);
+ mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_VIRTUAL},
+ (name) -> {
+ if (name.equals(IFace.DESCRIPTOR + "/" + NAME_DEFAULT)) {
+ return mDefaultFaceDaemon;
+ } else if (name.equals(IFace.DESCRIPTOR + "/" + NAME_VIRTUAL)) {
+ return mVirtualFaceDaemon;
+ }
+ return null;
+ });
+ initService();
+
+ mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL),
+ eq(BiometricAuthenticator.TYPE_FACE),
+ eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)), any());
+ }
+
+ private void waitForRegistration() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mFaceService.mServiceWrapper.addAuthenticatorsRegisteredCallback(
+ new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> sensors) {
+ latch.countDown();
+ }
+ });
+ latch.await(10, TimeUnit.SECONDS);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index f43120d1a755..8b1a2915820a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -16,6 +16,9 @@
package com.android.server.biometrics.sensors.face.aidl;
+import static android.os.UserHandle.USER_NULL;
+import static android.os.UserHandle.USER_SYSTEM;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -32,15 +35,19 @@ import android.hardware.biometrics.common.CommonProps;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.SensorProps;
+import android.hardware.face.HidlFaceSensorConfig;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -49,6 +56,7 @@ import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -59,6 +67,10 @@ import java.util.ArrayList;
@SmallTest
public class FaceProviderTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final String TAG = "FaceProviderTest";
private static final float FRR_THRESHOLD = 0.2f;
@@ -109,7 +121,7 @@ public class FaceProviderTest {
mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback,
mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext,
- mDaemon);
+ mDaemon, false /* resetLockoutRequiresChallenge */, false /* testHalEnabled */);
}
@Test
@@ -124,10 +136,38 @@ public class FaceProviderTest {
assertThat(currentClient).isInstanceOf(FaceInternalCleanupClient.class);
assertThat(currentClient.getSensorId()).isEqualTo(prop.commonProps.sensorId);
- assertThat(currentClient.getTargetUserId()).isEqualTo(UserHandle.USER_SYSTEM);
+ assertThat(currentClient.getTargetUserId()).isEqualTo(USER_SYSTEM);
}
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void testAddingHidlSensors() {
+ when(mResources.getIntArray(anyInt())).thenReturn(new int[]{});
+ when(mResources.getBoolean(anyInt())).thenReturn(false);
+
+ final int faceId = 0;
+ final int faceStrength = 15;
+ final String config = String.format("%d:8:%d", faceId, faceStrength);
+ final HidlFaceSensorConfig faceSensorConfig = new HidlFaceSensorConfig();
+ faceSensorConfig.parse(config, mContext);
+ final HidlFaceSensorConfig[] hidlFaceSensorConfig =
+ new HidlFaceSensorConfig[]{faceSensorConfig};
+ mFaceProvider = new FaceProvider(mContext,
+ mBiometricStateCallback, hidlFaceSensorConfig, TAG,
+ mLockoutResetDispatcher, mBiometricContext, mDaemon,
+ true /* resetLockoutRequiresChallenge */,
+ true /* testHalEnabled */);
+
+ assertThat(mFaceProvider.mFaceSensors.get(faceId)
+ .getLazySession().get().getUserId()).isEqualTo(USER_NULL);
+
+ waitForIdle();
+
+ assertThat(mFaceProvider.mFaceSensors.get(faceId)
+ .getLazySession().get().getUserId()).isEqualTo(USER_SYSTEM);
+ }
+
@SuppressWarnings("rawtypes")
@Test
public void halServiceDied_resetsAllSchedulers() {
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 7a293e80c183..e7f7195588ff 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
@@ -157,7 +157,7 @@ public class SensorTest {
sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext,
null /* handler */, internalProp, mLockoutResetDispatcher, mBiometricContext);
-
+ sensor.init(mLockoutResetDispatcher, mFaceProvider);
mScheduler.reset();
assertNull(mScheduler.getCurrentClient());
@@ -185,6 +185,7 @@ public class SensorTest {
sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null,
internalProp, mLockoutResetDispatcher, mBiometricContext, mCurrentSession);
+ sensor.init(mLockoutResetDispatcher, mFaceProvider);
mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler();
sensor.mCurrentSession = new AidlSession(0, mock(ISession.class),
USER_ID, mHalCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
new file mode 100644
index 000000000000..4e43332ab52c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.hidl;
+
+import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalUint64;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.hardware.face.Face;
+import android.hardware.face.HidlFaceSensorConfig;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.testing.TestableContext;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient;
+import com.android.server.biometrics.sensors.face.aidl.FaceProvider;
+import com.android.server.biometrics.sensors.face.aidl.FaceResetLockoutClient;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+public class HidlToAidlSensorAdapterTest {
+ private static final String TAG = "HidlToAidlSensorAdapterTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final byte[] HAT = new byte[69];
+ @Rule
+ public final MockitoRule mMockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcherForSensor;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcherForClient;
+ @Mock
+ private BiometricLogger mLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+ @Mock
+ private FaceProvider mFaceProvider;
+ @Mock
+ private Runnable mInternalCleanupAndGetFeatureRunnable;
+ @Mock
+ private IBiometricsFace mDaemon;
+ @Mock
+ AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ @Mock
+ BiometricUtils<Face> mBiometricUtils;
+
+ private final TestLooper mLooper = new TestLooper();
+ private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter;
+ private final TestableContext mContext = new TestableContext(
+ ApplicationProvider.getApplicationContext());
+
+ @Before
+ public void setUp() throws RemoteException {
+ final OptionalUint64 result = new OptionalUint64();
+ result.status = Status.OK;
+
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ when(mDaemon.setCallback(any())).thenReturn(result);
+ doAnswer((answer) -> {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onLockoutChanged(0);
+ return null;
+ }).when(mDaemon).resetLockout(any());
+ doAnswer((answer) -> {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onEnrollmentProgress(1, 0);
+ return null;
+ }).when(mDaemon).enroll(any(), anyInt(), any());
+
+ mContext.getOrCreateTestableResources();
+
+ final String config = String.format("%d:8:15", SENSOR_ID);
+ final BiometricScheduler scheduler = new BiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
+ BiometricScheduler.SENSOR_TYPE_FACE,
+ null /* gestureAvailabilityTracker */,
+ mBiometricService, 10 /* recentOperationsLimit */);
+ final HidlFaceSensorConfig faceSensorConfig = new HidlFaceSensorConfig();
+ faceSensorConfig.parse(config, mContext);
+ mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG, mFaceProvider,
+ mContext, new Handler(mLooper.getLooper()), faceSensorConfig,
+ mLockoutResetDispatcherForSensor, mBiometricContext,
+ false /* resetLockoutRequiresChallenge */, mInternalCleanupAndGetFeatureRunnable,
+ mAuthSessionCoordinator, mDaemon, mAidlResponseHandlerCallback);
+ mHidlToAidlSensorAdapter.init(mLockoutResetDispatcherForSensor, mFaceProvider);
+ mHidlToAidlSensorAdapter.setScheduler(scheduler);
+ mHidlToAidlSensorAdapter.handleUserChanged(USER_ID);
+ }
+
+ @Test
+ public void lockoutTimedResetViaClient() {
+ setLockoutTimed();
+
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(
+ new FaceResetLockoutClient(mContext, mHidlToAidlSensorAdapter.getLazySession(),
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT,
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcherForClient, 0 /* biometricStrength */));
+ mLooper.dispatchAll();
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false/* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mLockoutResetDispatcherForClient, never()).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutTimedResetViaCallback() {
+ setLockoutTimed();
+
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutChanged(0);
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(eq(
+ SENSOR_ID));
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID))
+ .isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutPermanentResetViaCallback() {
+ setLockoutPermanent();
+
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutChanged(0);
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(eq(
+ SENSOR_ID));
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutPermanentResetViaClient() {
+ setLockoutPermanent();
+
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(
+ new FaceResetLockoutClient(mContext,
+ mHidlToAidlSensorAdapter.getLazySession(),
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT,
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcherForClient, 0 /* biometricStrength */));
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForClient, never()).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID);
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void verifyOnEnrollSuccessCallback() {
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(new FaceEnrollClient(mContext,
+ mHidlToAidlSensorAdapter.getLazySession(), null /* token */, null /* listener */,
+ USER_ID, HAT, TAG, 1 /* requestId */, mBiometricUtils,
+ new int[]{} /* disabledFeatures */, ENROLL_TIMEOUT_SEC, null /* previewSurface */,
+ SENSOR_ID, mLogger, mBiometricContext, 1 /* maxTemplatesPerUser */,
+ false /* debugConsent */));
+ mLooper.dispatchAll();
+
+ verify(mAidlResponseHandlerCallback).onEnrollSuccess();
+ }
+
+ private void setLockoutTimed() {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onLockoutChanged(1);
+ mLooper.dispatchAll();
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID))
+ .isEqualTo(LockoutTracker.LOCKOUT_TIMED);
+ }
+
+ private void setLockoutPermanent() {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onLockoutChanged(-1);
+ mLooper.dispatchAll();
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_PERMANENT);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
index 9a40e8a7201a..b9a4fb4e0939 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
@@ -16,7 +16,7 @@
package com.android.server.biometrics.sensors.face.hidl;
-import static com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter.ENROLL_TIMEOUT_SEC;
+import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC;
import static com.android.server.biometrics.sensors.face.hidl.FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC;
import static com.google.common.truth.Truth.assertThat;
@@ -68,7 +68,7 @@ import java.util.List;
@Presubmit
@SmallTest
-public class AidlToHidlAdapterTest {
+public class HidlToAidlSessionAdapterTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -84,25 +84,32 @@ public class AidlToHidlAdapterTest {
private Clock mClock;
private final long mChallenge = 100L;
- private AidlToHidlAdapter mAidlToHidlAdapter;
+ private HidlToAidlSessionAdapter mHidlToAidlSessionAdapter;
private final Face mFace = new Face("face" /* name */, 1 /* faceId */, 0 /* deviceId */);
private final int mFeature = BiometricFaceConstants.FEATURE_REQUIRE_REQUIRE_DIVERSITY;
private final byte[] mFeatures = new byte[]{Feature.REQUIRE_ATTENTION};
@Before
public void setUp() throws RemoteException {
+ final OptionalUint64 setCallbackResult = new OptionalUint64();
+ setCallbackResult.value = 1;
+
+ when(mSession.setCallback(any())).thenReturn(setCallbackResult);
+
TestableContext testableContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
testableContext.addMockSystemService(FaceManager.class, mFaceManager);
- mAidlToHidlAdapter = new AidlToHidlAdapter(testableContext, () -> mSession, 0 /* userId */,
- mAidlResponseHandler, mClock);
+
+ mHidlToAidlSessionAdapter = new HidlToAidlSessionAdapter(testableContext, () -> mSession,
+ 0 /* userId */, mAidlResponseHandler, mClock);
mHardwareAuthToken.timestamp = new Timestamp();
mHardwareAuthToken.mac = new byte[10];
- final OptionalUint64 result = new OptionalUint64();
- result.status = Status.OK;
- result.value = mChallenge;
- when(mSession.generateChallenge(anyInt())).thenReturn(result);
+ final OptionalUint64 generateChallengeResult = new OptionalUint64();
+ generateChallengeResult.status = Status.OK;
+ generateChallengeResult.value = mChallenge;
+
+ when(mSession.generateChallenge(anyInt())).thenReturn(generateChallengeResult);
when(mFaceManager.getEnrolledFaces(anyInt())).thenReturn(List.of(mFace));
}
@@ -112,16 +119,16 @@ public class AidlToHidlAdapterTest {
final ArgumentCaptor<Long> challengeCaptor = ArgumentCaptor.forClass(Long.class);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
verify(mSession).generateChallenge(CHALLENGE_TIMEOUT_SEC);
verify(mAidlResponseHandler).onChallengeGenerated(challengeCaptor.capture());
assertThat(challengeCaptor.getValue()).isEqualTo(mChallenge);
forwardTime(10 /* seconds */);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
forwardTime(20 /* seconds */);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
//Confirms that the challenge is cached and the hal method is not called again
verifyNoMoreInteractions(mSession);
@@ -129,7 +136,7 @@ public class AidlToHidlAdapterTest {
.onChallengeGenerated(mChallenge);
forwardTime(60 /* seconds */);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
//HAL method called after challenge has timed out
verify(mSession, times(2)).generateChallenge(CHALLENGE_TIMEOUT_SEC);
@@ -138,11 +145,11 @@ public class AidlToHidlAdapterTest {
@Test
public void testRevokeChallenge_waitsUntilEmpty() throws RemoteException {
for (int i = 0; i < 3; i++) {
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
forwardTime(10 /* seconds */);
}
for (int i = 0; i < 3; i++) {
- mAidlToHidlAdapter.revokeChallenge(0);
+ mHidlToAidlSessionAdapter.revokeChallenge(0);
forwardTime((i + 1) * 10 /* seconds */);
}
@@ -151,20 +158,19 @@ public class AidlToHidlAdapterTest {
@Test
public void testRevokeChallenge_timeout() throws RemoteException {
- mAidlToHidlAdapter.generateChallenge();
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
forwardTime(700);
- mAidlToHidlAdapter.generateChallenge();
- mAidlToHidlAdapter.revokeChallenge(0);
+ mHidlToAidlSessionAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.revokeChallenge(0);
verify(mSession).revokeChallenge();
}
@Test
public void testEnroll() throws RemoteException {
- ICancellationSignal cancellationSignal = mAidlToHidlAdapter.enroll(mHardwareAuthToken,
- EnrollmentType.DEFAULT, mFeatures,
- null /* previewSurface */);
+ ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.enroll(
+ mHardwareAuthToken, EnrollmentType.DEFAULT, mFeatures, null /* previewSurface */);
ArgumentCaptor<ArrayList<Integer>> featureCaptor = ArgumentCaptor.forClass(ArrayList.class);
verify(mSession).enroll(any(), eq(ENROLL_TIMEOUT_SEC), featureCaptor.capture());
@@ -182,7 +188,8 @@ public class AidlToHidlAdapterTest {
@Test
public void testAuthenticate() throws RemoteException {
final int operationId = 2;
- ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId);
+ ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.authenticate(
+ operationId);
verify(mSession).authenticate(operationId);
@@ -193,7 +200,7 @@ public class AidlToHidlAdapterTest {
@Test
public void testDetectInteraction() throws RemoteException {
- ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction();
+ ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.detectInteraction();
verify(mSession).authenticate(0);
@@ -204,7 +211,7 @@ public class AidlToHidlAdapterTest {
@Test
public void testEnumerateEnrollments() throws RemoteException {
- mAidlToHidlAdapter.enumerateEnrollments();
+ mHidlToAidlSessionAdapter.enumerateEnrollments();
verify(mSession).enumerate();
}
@@ -212,7 +219,7 @@ public class AidlToHidlAdapterTest {
@Test
public void testRemoveEnrollment() throws RemoteException {
final int[] enrollments = new int[]{1};
- mAidlToHidlAdapter.removeEnrollments(enrollments);
+ mHidlToAidlSessionAdapter.removeEnrollments(enrollments);
verify(mSession).remove(enrollments[0]);
}
@@ -226,8 +233,8 @@ public class AidlToHidlAdapterTest {
when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
- mAidlToHidlAdapter.setFeature(mFeature);
- mAidlToHidlAdapter.getFeatures();
+ mHidlToAidlSessionAdapter.setFeature(mFeature);
+ mHidlToAidlSessionAdapter.getFeatures();
verify(mSession).getFeature(eq(mFeature), anyInt());
verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture());
@@ -244,8 +251,8 @@ public class AidlToHidlAdapterTest {
when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
- mAidlToHidlAdapter.setFeature(mFeature);
- mAidlToHidlAdapter.getFeatures();
+ mHidlToAidlSessionAdapter.setFeature(mFeature);
+ mHidlToAidlSessionAdapter.getFeatures();
verify(mSession).getFeature(eq(mFeature), anyInt());
verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture());
@@ -260,8 +267,8 @@ public class AidlToHidlAdapterTest {
when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
- mAidlToHidlAdapter.setFeature(mFeature);
- mAidlToHidlAdapter.getFeatures();
+ mHidlToAidlSessionAdapter.setFeature(mFeature);
+ mHidlToAidlSessionAdapter.getFeatures();
verify(mSession).getFeature(eq(mFeature), anyInt());
verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any());
@@ -270,7 +277,7 @@ public class AidlToHidlAdapterTest {
@Test
public void testGetFeatures_featureNotSet() throws RemoteException {
- mAidlToHidlAdapter.getFeatures();
+ mHidlToAidlSessionAdapter.getFeatures();
verify(mSession, never()).getFeature(eq(mFeature), anyInt());
verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any());
@@ -283,7 +290,7 @@ public class AidlToHidlAdapterTest {
when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt())).thenReturn(Status.OK);
- mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled);
+ mHidlToAidlSessionAdapter.setFeature(mHardwareAuthToken, feature, enabled);
verify(mAidlResponseHandler).onFeatureSet(feature);
}
@@ -296,7 +303,7 @@ public class AidlToHidlAdapterTest {
when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt()))
.thenReturn(Status.INTERNAL_ERROR);
- mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled);
+ mHidlToAidlSessionAdapter.setFeature(mHardwareAuthToken, feature, enabled);
verify(mAidlResponseHandler).onError(BiometricFaceConstants.FACE_ERROR_UNKNOWN,
0 /* vendorCode */);
@@ -311,7 +318,7 @@ public class AidlToHidlAdapterTest {
when(mSession.getAuthenticatorId()).thenReturn(result);
- mAidlToHidlAdapter.getAuthenticatorId();
+ mHidlToAidlSessionAdapter.getAuthenticatorId();
verify(mSession).getAuthenticatorId();
verify(mAidlResponseHandler).onAuthenticatorIdRetrieved(authenticatorId);
@@ -319,7 +326,7 @@ public class AidlToHidlAdapterTest {
@Test
public void testResetLockout() throws RemoteException {
- mAidlToHidlAdapter.resetLockout(mHardwareAuthToken);
+ mHidlToAidlSessionAdapter.resetLockout(mHardwareAuthToken);
ArgumentCaptor<ArrayList> hatCaptor = ArgumentCaptor.forClass(ArrayList.class);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index 2aa62d96168d..f570ba23441d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -42,13 +42,19 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.TestableContext;
@@ -58,6 +64,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
@@ -89,6 +96,9 @@ public class FingerprintServiceTest {
@Rule
public final MockitoRule mMockito = MockitoJUnit.rule();
@Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
public final TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Rule
@@ -110,6 +120,10 @@ public class FingerprintServiceTest {
private IBinder mToken;
@Mock
private VirtualDeviceManagerInternal mVdmInternal;
+ @Mock
+ private IFingerprint mDefaultFingerprintDaemon;
+ @Mock
+ private IFingerprint mVirtualFingerprintDaemon;
@Captor
private ArgumentCaptor<FingerprintAuthenticateOptions> mAuthenticateOptionsCaptor;
@@ -126,7 +140,10 @@ public class FingerprintServiceTest {
List.of(),
TYPE_UDFPS_OPTICAL,
false /* resetLockoutRequiresHardwareAuthToken */);
+ private FingerprintSensorConfigurations mFingerprintSensorConfigurations;
private FingerprintService mService;
+ private final SensorProps mDefaultSensorProps = new SensorProps();
+ private final SensorProps mVirtualSensorProps = new SensorProps();
@Before
public void setup() throws Exception {
@@ -139,6 +156,10 @@ public class FingerprintServiceTest {
.thenAnswer(i -> i.getArguments()[0].equals(ID_DEFAULT));
when(mFingerprintVirtual.containsSensor(anyInt()))
.thenAnswer(i -> i.getArguments()[0].equals(ID_VIRTUAL));
+ when(mDefaultFingerprintDaemon.getSensorProps()).thenReturn(
+ new SensorProps[]{mDefaultSensorProps});
+ when(mVirtualFingerprintDaemon.getSensorProps()).thenReturn(
+ new SensorProps[]{mVirtualSensorProps});
mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager);
for (int permission : List.of(OP_USE_BIOMETRIC, OP_USE_FINGERPRINT)) {
@@ -150,6 +171,18 @@ public class FingerprintServiceTest {
mContext.getTestablePermissions().setPermission(
permission, PackageManager.PERMISSION_GRANTED);
}
+
+ mFingerprintSensorConfigurations = new FingerprintSensorConfigurations(
+ true /* resetLockoutRequiresHardwareAuthToken */);
+ mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_DEFAULT, NAME_VIRTUAL},
+ (name) -> {
+ if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_DEFAULT)) {
+ return mDefaultFingerprintDaemon;
+ } else if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_VIRTUAL)) {
+ return mVirtualFingerprintDaemon;
+ }
+ return null;
+ });
}
private void initServiceWith(String... aidlInstances) {
@@ -160,6 +193,10 @@ public class FingerprintServiceTest {
if (NAME_DEFAULT.equals(name)) return mFingerprintDefault;
if (NAME_VIRTUAL.equals(name)) return mFingerprintVirtual;
return null;
+ }, (sensorPropsPair, resetLockoutRequiresHardwareAuthToken) -> {
+ if (NAME_DEFAULT.equals(sensorPropsPair.first)) return mFingerprintDefault;
+ if (NAME_VIRTUAL.equals(sensorPropsPair.first)) return mFingerprintVirtual;
+ return null;
});
}
@@ -180,6 +217,17 @@ public class FingerprintServiceTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_defaultOnly() throws Exception {
+ initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
+
+ mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any());
+ }
+
+ @Test
public void registerAuthenticators_virtualOnly() throws Exception {
initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
@@ -192,6 +240,19 @@ public class FingerprintServiceTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_virtualOnly() throws Exception {
+ initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
+ Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
+ Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
+
+ mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
+ }
+
+ @Test
public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception {
initServiceWith(NAME_VIRTUAL);
@@ -201,6 +262,28 @@ public class FingerprintServiceTest {
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception {
+ mFingerprintSensorConfigurations =
+ new FingerprintSensorConfigurations(true);
+ mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_VIRTUAL},
+ (name) -> {
+ if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_DEFAULT)) {
+ return mDefaultFingerprintDaemon;
+ } else if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_VIRTUAL)) {
+ return mVirtualFingerprintDaemon;
+ }
+ return null;
+ });
+ initServiceWith(NAME_VIRTUAL);
+
+ mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
+ }
+
private void waitForRegistration() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mService.mServiceWrapper.addAuthenticatorsRegisteredCallback(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 4cfb83fa1c69..bf5986c1d0f3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -16,6 +16,9 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static android.os.UserHandle.USER_NULL;
+import static android.os.UserHandle.USER_SYSTEM;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -34,17 +37,20 @@ import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.SensorLocation;
import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.fingerprint.HidlFingerprintSensorConfig;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -52,6 +58,7 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -64,6 +71,10 @@ public class FingerprintProviderTest {
private static final String TAG = "FingerprintProviderTest";
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Mock
private Context mContext;
@Mock
@@ -115,7 +126,8 @@ public class FingerprintProviderTest {
mFingerprintProvider = new FingerprintProvider(mContext,
mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext,
- mDaemon);
+ mDaemon, false /* resetLockoutRequiresHardwareAuthToken */,
+ true /* testHalEnabled */);
}
@Test
@@ -123,17 +135,42 @@ public class FingerprintProviderTest {
waitForIdle();
for (SensorProps prop : mSensorProps) {
- final BiometricScheduler scheduler =
- mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId)
- .getScheduler();
- BaseClientMonitor currentClient = scheduler.getCurrentClient();
-
- assertThat(currentClient).isInstanceOf(FingerprintInternalCleanupClient.class);
- assertThat(currentClient.getSensorId()).isEqualTo(prop.commonProps.sensorId);
- assertThat(currentClient.getTargetUserId()).isEqualTo(UserHandle.USER_SYSTEM);
+ final Sensor sensor =
+ mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId);
+ assertThat(sensor.getLazySession().get().getUserId()).isEqualTo(USER_SYSTEM);
}
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void testAddingHidlSensors() {
+ when(mResources.getIntArray(anyInt())).thenReturn(new int[]{});
+ when(mResources.getBoolean(anyInt())).thenReturn(false);
+
+ final int fingerprintId = 0;
+ final int fingerprintStrength = 15;
+ final String config = String.format("%d:2:%d", fingerprintId, fingerprintStrength);
+ final HidlFingerprintSensorConfig fingerprintSensorConfig =
+ new HidlFingerprintSensorConfig();
+ fingerprintSensorConfig.parse(config, mContext);
+ HidlFingerprintSensorConfig[] hidlFingerprintSensorConfigs =
+ new HidlFingerprintSensorConfig[]{fingerprintSensorConfig};
+ mFingerprintProvider = new FingerprintProvider(mContext,
+ mBiometricStateCallback, mAuthenticationStateListeners,
+ hidlFingerprintSensorConfigs, TAG, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher, mBiometricContext, mDaemon,
+ false /* resetLockoutRequiresHardwareAuthToken */,
+ true /* testHalEnabled */);
+
+ assertThat(mFingerprintProvider.mFingerprintSensors.get(fingerprintId)
+ .getLazySession().get().getUserId()).isEqualTo(USER_NULL);
+
+ waitForIdle();
+
+ assertThat(mFingerprintProvider.mFingerprintSensors.get(fingerprintId)
+ .getLazySession().get().getUserId()).isEqualTo(USER_SYSTEM);
+ }
+
@SuppressWarnings("rawtypes")
@Test
public void halServiceDied_resetsAllSchedulers() {
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 410260074fbd..126a05e12628 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
@@ -96,7 +96,7 @@ public class SensorTest {
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
- private UserAwareBiometricScheduler mScheduler;
+ private BiometricScheduler mScheduler;
private AidlResponseHandler mHalCallback;
@Before
@@ -164,7 +164,8 @@ public class SensorTest {
final Sensor sensor = new Sensor("SensorTest", mFingerprintProvider, mContext,
null /* handler */, internalProp, mLockoutResetDispatcher,
mGestureAvailabilityDispatcher, mBiometricContext, mCurrentSession);
- mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler();
+ sensor.init(mGestureAvailabilityDispatcher, mLockoutResetDispatcher);
+ mScheduler = sensor.getScheduler();
sensor.mCurrentSession = new AidlSession(0, mock(ISession.class),
USER_ID, mHalCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
new file mode 100644
index 000000000000..89a49615dbe1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.hidl;
+
+import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlarmManager;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.HidlFingerprintSensorConfig;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.annotation.NonNull;
+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.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.StopUserClient;
+import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient;
+
+import org.junit.Before;
+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 HidlToAidlSensorAdapterTest {
+
+ private static final String TAG = "HidlToAidlSensorAdapterTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final byte[] HAT = new byte[69];
+
+ @Rule
+ public final MockitoRule mMockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcherForSensor;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcherForClient;
+ @Mock
+ private BiometricLogger mLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+ @Mock
+ private FingerprintProvider mFingerprintProvider;
+ @Mock
+ private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
+ @Mock
+ private Runnable mInternalCleanupRunnable;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private IBiometricsFingerprint mDaemon;
+ @Mock
+ private AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ @Mock
+ private BiometricUtils<Fingerprint> mBiometricUtils;
+ @Mock
+ private AuthenticationStateListeners mAuthenticationStateListeners;
+
+ private final TestLooper mLooper = new TestLooper();
+ private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter;
+ private final TestableContext mContext = new TestableContext(
+ ApplicationProvider.getApplicationContext());
+
+ private final UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback =
+ new UserAwareBiometricScheduler.UserSwitchCallback() {
+ @NonNull
+ @Override
+ public StopUserClient<?> getStopUserClient(int userId) {
+ return new StopUserClient<IBiometricsFingerprint>(mContext,
+ mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null, USER_ID,
+ SENSOR_ID, mLogger, mBiometricContext, () -> {}) {
+ @Override
+ protected void startHalOperation() {
+ getCallback().onClientFinished(this, true /* success */);
+ }
+
+ @Override
+ public void unableToStart() {}
+ };
+ }
+
+ @NonNull
+ @Override
+ public StartUserClient<?, ?> getStartUserClient(int newUserId) {
+ return new StartUserClient<IBiometricsFingerprint, AidlSession>(mContext,
+ mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null,
+ USER_ID, SENSOR_ID,
+ mLogger, mBiometricContext,
+ (newUserId1, newUser, halInterfaceVersion) ->
+ mHidlToAidlSensorAdapter.handleUserChanged(newUserId1)) {
+ @Override
+ public void start(@NonNull ClientMonitorCallback callback) {
+ super.start(callback);
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ mUserStartedCallback.onUserStarted(USER_ID, null, 0);
+ getCallback().onClientFinished(this, true /* success */);
+ }
+
+ @Override
+ public void unableToStart() {}
+ };
+ }
+ };;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ doAnswer((answer) -> {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onEnrollmentProgress(1 /* enrollmentId */, 0 /* remaining */);
+ return null;
+ }).when(mDaemon).enroll(any(), anyInt(), anyInt());
+
+ mContext.addMockSystemService(AlarmManager.class, mAlarmManager);
+ mContext.getOrCreateTestableResources();
+
+ final String config = String.format("%d:2:15", SENSOR_ID);
+ final UserAwareBiometricScheduler scheduler = new UserAwareBiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
+ BiometricScheduler.SENSOR_TYPE_FP_OTHER,
+ null /* gestureAvailabilityDispatcher */,
+ mBiometricService,
+ () -> USER_ID,
+ mUserSwitchCallback);
+ final HidlFingerprintSensorConfig fingerprintSensorConfig =
+ new HidlFingerprintSensorConfig();
+ fingerprintSensorConfig.parse(config, mContext);
+ mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG,
+ mFingerprintProvider, mContext, new Handler(mLooper.getLooper()),
+ fingerprintSensorConfig, mLockoutResetDispatcherForSensor,
+ mGestureAvailabilityDispatcher, mBiometricContext,
+ false /* resetLockoutRequiresHardwareAuthToken */,
+ mInternalCleanupRunnable, mAuthSessionCoordinator, mDaemon,
+ mAidlResponseHandlerCallback);
+ mHidlToAidlSensorAdapter.init(mGestureAvailabilityDispatcher,
+ mLockoutResetDispatcherForSensor);
+ mHidlToAidlSensorAdapter.setScheduler(scheduler);
+ mHidlToAidlSensorAdapter.handleUserChanged(USER_ID);
+ }
+
+ @Test
+ public void lockoutTimedResetViaClient() {
+ setLockoutTimed();
+
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(
+ new FingerprintResetLockoutClient(mContext,
+ mHidlToAidlSensorAdapter.getLazySession(),
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT,
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcherForClient, 0 /* biometricStrength */));
+ mLooper.dispatchAll();
+
+ verify(mAlarmManager).setExact(anyInt(), anyLong(), any());
+ verify(mLockoutResetDispatcherForClient).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID);
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutTimedResetViaCallback() {
+ setLockoutTimed();
+
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutCleared();
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForSensor, times(2)).notifyLockoutResetCallbacks(eq(
+ SENSOR_ID));
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutPermanentResetViaCallback() {
+ setLockoutPermanent();
+
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutCleared();
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForSensor, times(2)).notifyLockoutResetCallbacks(eq(
+ SENSOR_ID));
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutPermanentResetViaClient() {
+ setLockoutPermanent();
+
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(
+ new FingerprintResetLockoutClient(mContext,
+ mHidlToAidlSensorAdapter.getLazySession(),
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT,
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcherForClient, 0 /* biometricStrength */));
+ mLooper.dispatchAll();
+
+ verify(mAlarmManager, atLeast(1)).setExact(anyInt(), anyLong(), any());
+ verify(mLockoutResetDispatcherForClient).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID);
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void verifyOnEnrollSuccessCallback() {
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(new FingerprintEnrollClient(
+ mContext, mHidlToAidlSensorAdapter.getLazySession(), null /* token */,
+ 1 /* requestId */, null /* listener */, USER_ID, HAT, TAG, mBiometricUtils,
+ SENSOR_ID, mLogger, mBiometricContext,
+ mHidlToAidlSensorAdapter.getSensorProperties(), null, null,
+ mAuthenticationStateListeners, 5 /* maxTemplatesPerUser */, ENROLL_ENROLL));
+ mLooper.dispatchAll();
+
+ verify(mAidlResponseHandlerCallback).onEnrollSuccess();
+ }
+
+ private void setLockoutPermanent() {
+ for (int i = 0; i < 20; i++) {
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .addFailedAttemptForUser(USER_ID);
+ }
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_PERMANENT);
+ }
+
+ private void setLockoutTimed() {
+ for (int i = 0; i < 5; i++) {
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .addFailedAttemptForUser(USER_ID);
+ }
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_TIMED);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java
index b78ba82bd8fe..d723e87a62ad 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java
@@ -41,7 +41,7 @@ import org.mockito.junit.MockitoRule;
@Presubmit
@SmallTest
-public class AidlToHidlAdapterTest {
+public class HidlToAidlSessionAdapterTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -54,11 +54,11 @@ public class AidlToHidlAdapterTest {
private final long mChallenge = 100L;
private final int mUserId = 0;
- private AidlToHidlAdapter mAidlToHidlAdapter;
+ private HidlToAidlSessionAdapter mHidlToAidlSessionAdapter;
@Before
public void setUp() {
- mAidlToHidlAdapter = new AidlToHidlAdapter(() -> mSession, mUserId,
+ mHidlToAidlSessionAdapter = new HidlToAidlSessionAdapter(() -> mSession, mUserId,
mAidlResponseHandler);
mHardwareAuthToken.timestamp = new Timestamp();
mHardwareAuthToken.mac = new byte[10];
@@ -67,7 +67,7 @@ public class AidlToHidlAdapterTest {
@Test
public void testGenerateChallenge() throws RemoteException {
when(mSession.preEnroll()).thenReturn(mChallenge);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
verify(mSession).preEnroll();
verify(mAidlResponseHandler).onChallengeGenerated(mChallenge);
@@ -75,7 +75,7 @@ public class AidlToHidlAdapterTest {
@Test
public void testRevokeChallenge() throws RemoteException {
- mAidlToHidlAdapter.revokeChallenge(mChallenge);
+ mHidlToAidlSessionAdapter.revokeChallenge(mChallenge);
verify(mSession).postEnroll();
verify(mAidlResponseHandler).onChallengeRevoked(0L);
@@ -84,9 +84,9 @@ public class AidlToHidlAdapterTest {
@Test
public void testEnroll() throws RemoteException {
final ICancellationSignal cancellationSignal =
- mAidlToHidlAdapter.enroll(mHardwareAuthToken);
+ mHidlToAidlSessionAdapter.enroll(mHardwareAuthToken);
- verify(mSession).enroll(any(), anyInt(), eq(AidlToHidlAdapter.ENROLL_TIMEOUT_SEC));
+ verify(mSession).enroll(any(), anyInt(), eq(HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC));
cancellationSignal.cancel();
@@ -96,7 +96,8 @@ public class AidlToHidlAdapterTest {
@Test
public void testAuthenticate() throws RemoteException {
final int operationId = 2;
- final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId);
+ final ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.authenticate(
+ operationId);
verify(mSession).authenticate(operationId, mUserId);
@@ -107,7 +108,8 @@ public class AidlToHidlAdapterTest {
@Test
public void testDetectInteraction() throws RemoteException {
- final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction();
+ final ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter
+ .detectInteraction();
verify(mSession).authenticate(0 /* operationId */, mUserId);
@@ -118,7 +120,7 @@ public class AidlToHidlAdapterTest {
@Test
public void testEnumerateEnrollments() throws RemoteException {
- mAidlToHidlAdapter.enumerateEnrollments();
+ mHidlToAidlSessionAdapter.enumerateEnrollments();
verify(mSession).enumerate();
}
@@ -126,7 +128,7 @@ public class AidlToHidlAdapterTest {
@Test
public void testRemoveEnrollment() throws RemoteException {
final int[] enrollmentIds = new int[]{1};
- mAidlToHidlAdapter.removeEnrollments(enrollmentIds);
+ mHidlToAidlSessionAdapter.removeEnrollments(enrollmentIds);
verify(mSession).remove(mUserId, enrollmentIds[0]);
}
@@ -134,14 +136,14 @@ public class AidlToHidlAdapterTest {
@Test
public void testRemoveMultipleEnrollments() throws RemoteException {
final int[] enrollmentIds = new int[]{1, 2};
- mAidlToHidlAdapter.removeEnrollments(enrollmentIds);
+ mHidlToAidlSessionAdapter.removeEnrollments(enrollmentIds);
verify(mSession).remove(mUserId, 0);
}
@Test
public void testResetLockout() throws RemoteException {
- mAidlToHidlAdapter.resetLockout(mHardwareAuthToken);
+ mHidlToAidlSessionAdapter.resetLockout(mHardwareAuthToken);
verify(mAidlResponseHandler).onLockoutCleared();
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index edfe1b416f22..071d571adabe 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,10 +24,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.companion.virtual.camera.VirtualCameraCallback;
import android.companion.virtual.camera.VirtualCameraConfig;
-import android.companion.virtual.camera.VirtualCameraMetadata;
import android.companion.virtual.camera.VirtualCameraStreamConfig;
import android.companion.virtualcamera.IVirtualCameraService;
import android.companion.virtualcamera.VirtualCameraConfiguration;
@@ -156,10 +154,6 @@ public class VirtualCameraControllerTest {
@NonNull VirtualCameraStreamConfig streamConfig) {}
@Override
- public void onProcessCaptureRequest(
- int streamId, long frameId, @Nullable VirtualCameraMetadata metadata) {}
-
- @Override
public void onStreamClosed(int streamId) {}
};
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 943a9c4759c4..1dd64ffa5dde 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -18,7 +18,6 @@ package com.android.server.devicepolicy;
import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -222,21 +221,21 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R);
// Pretend some packages are suspended.
- when(getServices().packageManagerInternal.isSuspendingAnyPackages(
- PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true);
+ when(getServices().packageManagerInternal.isAdminSuspendingAnyPackages(
+ USER_SYSTEM)).thenReturn(true);
final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
verify(getServices().packageManagerInternal, never())
- .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+ .unsuspendAdminSuspendedPackages(USER_SYSTEM);
sendBroadcastWithUser(dpms, Intent.ACTION_USER_STARTED, USER_SYSTEM);
// Verify that actual package suspension state is not modified after user start
verify(getServices().packageManagerInternal, never())
- .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+ .unsuspendAdminSuspendedPackages(USER_SYSTEM);
verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser(
- any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt());
+ any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt(), anyInt());
final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
@@ -255,14 +254,14 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.Q);
// Pretend some packages are suspended.
- when(getServices().packageManagerInternal.isSuspendingAnyPackages(
- PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true);
+ when(getServices().packageManagerInternal.isAdminSuspendingAnyPackages(
+ USER_SYSTEM)).thenReturn(true);
final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
// Verify that apps get unsuspended.
verify(getServices().packageManagerInternal)
- .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+ .unsuspendAdminSuspendedPackages(USER_SYSTEM);
final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
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 f4dac2c10d0f..24704034ae0c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -63,7 +63,6 @@ import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE;
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_TURN_PROFILE_ON_NOTIFICATION;
import static com.android.server.devicepolicy.DpmMockContext.CALLER_USER_HANDLE;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.testutils.TestUtils.assertExpectException;
import static com.google.common.truth.Truth.assertThat;
@@ -5080,7 +5079,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(getServices().iwindowManager).refreshScreenCaptureDisabled();
// Unsuspend personal apps
verify(getServices().packageManagerInternal)
- .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM);
+ .unsuspendAdminSuspendedPackages(UserHandle.USER_SYSTEM);
verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false);
@@ -7535,7 +7534,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED));
// Verify that the apps are NOT unsuspeded.
verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser(
- any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt());
+ any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt(), anyInt());
// Verify that DPC is invoked to check policy compliance.
verify(mContext.spiedContext).startActivityAsUser(
MockUtils.checkIntentAction(ACTION_CHECK_POLICY_COMPLIANCE),
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index b4281d63c5e2..101ea6d81a16 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -183,6 +183,13 @@ public class UserManagerServiceTest {
}
@Test
+ public void testIsUserInitialized_NonExistentUserReturnsFalse() {
+ int nonExistentUserId = UserHandle.USER_NULL;
+ assertThat(mUserManagerService.isUserInitialized(nonExistentUserId))
+ .isFalse();
+ }
+
+ @Test
public void testSetUserRestrictionWithIncorrectID() throws Exception {
int incorrectId = 1;
while (mUserManagerService.userExists(incorrectId)) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 260ee39656ea..30843d222742 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -245,8 +245,8 @@ public class DefaultDeviceEffectsApplierTest {
}
@Test
- @TestParameters({"{origin: ORIGIN_USER}", "{origin: ORIGIN_INIT}", "{origin: ORIGIN_INIT_USER}",
- "{origin: ORIGIN_SYSTEM_OR_SYSTEMUI}"})
+ @TestParameters({"{origin: ORIGIN_USER}", "{origin: ORIGIN_INIT}",
+ "{origin: ORIGIN_INIT_USER}"})
public void apply_nightModeWithScreenOn_appliedImmediatelyBasedOnOrigin(ChangeOrigin origin) {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
@@ -263,7 +263,7 @@ public class DefaultDeviceEffectsApplierTest {
@Test
@TestParameters({"{origin: ORIGIN_APP}", "{origin: ORIGIN_RESTORE_BACKUP}",
- "{origin: ORIGIN_UNKNOWN}"})
+ "{origin: ORIGIN_SYSTEM_OR_SYSTEMUI}", "{origin: ORIGIN_UNKNOWN}"})
public void apply_nightModeWithScreenOn_willBeAppliedLaterBasedOnOrigin(ChangeOrigin origin) {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 56c75b52cf18..9a1359591890 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -80,6 +80,9 @@ import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
+import static android.service.notification.Condition.SOURCE_CONTEXT;
+import static android.service.notification.Condition.SOURCE_USER_ACTION;
+import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
@@ -224,6 +227,7 @@ import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.provider.Settings;
import android.service.notification.Adjustment;
+import android.service.notification.Condition;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.DeviceEffectsApplier;
import android.service.notification.INotificationListener;
@@ -342,6 +346,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final String SCHEME_TIMEOUT = "timeout";
private static final String REDACTED_TEXT = "redacted text";
+ private static final AutomaticZenRule SOME_ZEN_RULE =
+ new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+ .setOwner(new ComponentName("pkg", "cls"))
+ .build();
+
private final int mUid = Binder.getCallingUid();
private final @UserIdInt int mUserId = UserHandle.getUserId(mUid);
@@ -9048,7 +9057,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
zenPolicy, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled);
try {
- mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName(), false);
fail("Zen policy only applies to priority only mode");
} catch (IllegalArgumentException e) {
// yay
@@ -9056,11 +9065,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName(), false);
rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
null, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled);
- mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName(), false);
}
@Test
@@ -9075,7 +9084,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
boolean isEnabled = true;
AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "com.android.settings");
+ mBinderService.addAutomaticZenRule(rule, "com.android.settings", false);
// verify that zen mode helper gets passed in a package name of "android"
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule),
@@ -9097,7 +9106,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
boolean isEnabled = true;
AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "com.android.settings");
+ mBinderService.addAutomaticZenRule(rule, "com.android.settings", false);
// verify that zen mode helper gets passed in a package name of "android"
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule),
@@ -9117,7 +9126,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
boolean isEnabled = true;
AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "another.package");
+ mBinderService.addAutomaticZenRule(rule, "another.package", false);
// verify that zen mode helper gets passed in the package name from the arg, not the owner
verify(mockZenModeHelper).addAutomaticZenRule(
@@ -9128,10 +9137,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testAddAutomaticZenRule_typeManagedCanBeUsedByDeviceOwners() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
mService.setCallerIsNormalPackage();
- mService.setZenHelper(mock(ZenModeHelper.class));
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
.setType(AutomaticZenRule.TYPE_MANAGED)
@@ -9139,8 +9146,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.build();
when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(true);
- mBinderService.addAutomaticZenRule(rule, "pkg");
- // No exception!
+ mBinderService.addAutomaticZenRule(rule, "pkg", /* fromUser= */ false);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(rule), anyInt(), any(), anyInt());
}
@Test
@@ -9158,7 +9166,144 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(false);
assertThrows(IllegalArgumentException.class,
- () -> mBinderService.addAutomaticZenRule(rule, "pkg"));
+ () -> mBinderService.addAutomaticZenRule(rule, "pkg", /* fromUser= */ false));
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void addAutomaticZenRule_fromUser_mappedToOriginUser() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ true);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(SOME_ZEN_RULE),
+ eq(ZenModeConfig.UPDATE_ORIGIN_USER), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void addAutomaticZenRule_fromSystemNotUser_mappedToOriginSystem() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ false);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(SOME_ZEN_RULE),
+ eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void addAutomaticZenRule_fromApp_mappedToOriginApp() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ false);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(SOME_ZEN_RULE),
+ eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void addAutomaticZenRule_fromAppFromUser_blocked() throws Exception {
+ setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ assertThrows(SecurityException.class, () ->
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ true));
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void updateAutomaticZenRule_fromUserFromSystem_allowed() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ mBinderService.updateAutomaticZenRule("id", SOME_ZEN_RULE, /* fromUser= */ true);
+
+ verify(zenModeHelper).updateAutomaticZenRule(eq("id"), eq(SOME_ZEN_RULE),
+ eq(ZenModeConfig.UPDATE_ORIGIN_USER), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void updateAutomaticZenRule_fromUserFromApp_blocked() throws Exception {
+ setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ assertThrows(SecurityException.class, () ->
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ true));
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void removeAutomaticZenRule_fromUserFromSystem_allowed() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ mBinderService.removeAutomaticZenRule("id", /* fromUser= */ true);
+
+ verify(zenModeHelper).removeAutomaticZenRule(eq("id"),
+ eq(ZenModeConfig.UPDATE_ORIGIN_USER), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void removeAutomaticZenRule_fromUserFromApp_blocked() throws Exception {
+ setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ assertThrows(SecurityException.class, () ->
+ mBinderService.removeAutomaticZenRule("id", /* fromUser= */ true));
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void setAutomaticZenRuleState_fromUserMatchesConditionSource_okay() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ Condition withSourceContext = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
+ SOURCE_CONTEXT);
+ mBinderService.setAutomaticZenRuleState("id", withSourceContext, /* fromUser= */ false);
+ verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceContext),
+ eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyInt());
+
+ Condition withSourceUser = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
+ SOURCE_USER_ACTION);
+ mBinderService.setAutomaticZenRuleState("id", withSourceUser, /* fromUser= */ true);
+ verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceUser),
+ eq(ZenModeConfig.UPDATE_ORIGIN_USER), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void setAutomaticZenRuleState_fromUserDoesNotMatchConditionSource_blocked()
+ throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ Condition withSourceContext = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
+ SOURCE_CONTEXT);
+ assertThrows(IllegalArgumentException.class,
+ () -> mBinderService.setAutomaticZenRuleState("id", withSourceContext,
+ /* fromUser= */ true));
+
+ Condition withSourceUser = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
+ SOURCE_USER_ACTION);
+ assertThrows(IllegalArgumentException.class,
+ () -> mBinderService.setAutomaticZenRuleState("id", withSourceUser,
+ /* fromUser= */ false));
+ }
+
+ private ZenModeHelper setUpMockZenTest() {
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.setZenHelper(zenModeHelper);
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ return zenModeHelper;
}
@Test
@@ -9184,7 +9329,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
});
mService.getBinderService().setZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS, null,
- "testing!");
+ "testing!", false);
waitForIdle();
InOrder inOrder = inOrder(mContext);
@@ -13441,9 +13586,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(true);
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
- mBinderService.setNotificationPolicy("package", policy);
+ mBinderService.setNotificationPolicy("package", policy, false);
- verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy));
+ verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy),
+ eq(ZenModeConfig.UPDATE_ORIGIN_APP));
}
@Test
@@ -13457,7 +13603,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.isSystemUid = true;
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
- mBinderService.setNotificationPolicy("package", policy);
+ mBinderService.setNotificationPolicy("package", policy, false);
verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
}
@@ -13479,7 +13625,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.build()));
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
- mBinderService.setNotificationPolicy("package", policy);
+ mBinderService.setNotificationPolicy("package", policy, false);
verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
}
@@ -13495,7 +13641,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(true);
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
- mBinderService.setNotificationPolicy("package", policy);
+ mBinderService.setNotificationPolicy("package", policy, false);
verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
}
@@ -13525,7 +13671,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
.thenReturn(true);
- mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
verify(zenHelper).applyGlobalZenModeAsImplicitZenRule(eq("package"), anyInt(),
eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
@@ -13542,7 +13688,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(true);
mService.isSystemUid = true;
- mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), eq("package"),
@@ -13565,7 +13711,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
.build()));
- mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 1aea56cd8f9f..bc63c29e9955 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -112,6 +112,7 @@ import android.net.Uri;
import android.os.Parcel;
import android.os.Process;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -211,7 +212,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
private static final ZenDeviceEffects NO_EFFECTS = new ZenDeviceEffects.Builder().build();
@Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+ SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
ConditionProviders mConditionProviders;
@Mock NotificationManager mNotificationManager;
@@ -3372,6 +3374,29 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void removeAutomaticZenRule_propagatesOriginToEffectsApplier() {
+ mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
+ reset(mDeviceEffectsApplier);
+
+ String ruleId = addRuleWithEffects(new ZenDeviceEffects.Builder()
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .build());
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ mTestableLooper.processAllMessages();
+ verify(mDeviceEffectsApplier).apply(any(), eq(UPDATE_ORIGIN_APP));
+
+ // Now delete the (currently active!) rule. For example, assume this is done from settings.
+ mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_USER, "remove",
+ Process.SYSTEM_UID);
+ mTestableLooper.processAllMessages();
+
+ verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_USER));
+ }
+
+ @Test
public void testDeviceEffects_applied() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
@@ -3511,8 +3536,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
.setDeviceEffects(effects)
.build();
- return mZenModeHelper.addAutomaticZenRule("pkg", rule, UPDATE_ORIGIN_APP, "",
- CUSTOM_PKG_UID);
+ return mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+ UPDATE_ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
}
@Test
@@ -3619,7 +3644,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
- mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy,
+ UPDATE_ORIGIN_APP);
ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
.disallowAllSounds()
@@ -3643,13 +3669,14 @@ public class ZenModeHelperTest extends UiServiceTestCase {
PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
- original);
+ original, UPDATE_ORIGIN_APP);
// Change priorityCallSenders: contacts -> starred.
Policy updated = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
PRIORITY_SENDERS_STARRED, PRIORITY_SENDERS_STARRED,
Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
- mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated,
+ UPDATE_ORIGIN_APP);
ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
.disallowAllSounds()
@@ -3671,7 +3698,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
withoutWtfCrash(
() -> mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME,
- CUSTOM_PKG_UID, new Policy(0, 0, 0)));
+ CUSTOM_PKG_UID, new Policy(0, 0, 0), UPDATE_ORIGIN_APP));
assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
}
@@ -3684,7 +3711,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
Policy.getAllSuppressedVisualEffects(), STATE_FALSE,
CONVERSATION_SENDERS_IMPORTANT);
mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
- writtenPolicy);
+ writtenPolicy, UPDATE_ORIGIN_APP);
Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
CUSTOM_PKG_NAME);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 526201f9c1c6..670f9f697a5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -49,6 +49,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -195,7 +196,7 @@ public class ActivityStartInterceptorTest {
mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
- .thenReturn(PLATFORM_PACKAGE_NAME);
+ .thenReturn(UserPackage.of(TEST_USER_ID, PLATFORM_PACKAGE_NAME));
// THEN calling intercept returns true
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
@@ -227,9 +228,10 @@ public class ActivityStartInterceptorTest {
.setMessage("Test Message")
.setIcon(0x11110001)
.build();
+ UserPackage suspender = UserPackage.of(TEST_USER_ID, suspendingPackage);
when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
- .thenReturn(suspendingPackage);
- when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage,
+ .thenReturn(suspender);
+ when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspender,
TEST_USER_ID)).thenReturn(dialogInfo);
return dialogInfo;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index d2c731c3f8ad..ed99108e0da3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -1087,4 +1087,16 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
assertTrue(homeActivity.getTask().isFocused());
assertFalse(pinnedTask.isFocused());
}
+
+ @Test
+ public void testContinueWindowLayout_notifyClientLifecycleManager() {
+ clearInvocations(mClientLifecycleManager);
+ mAtm.deferWindowLayout();
+
+ verify(mClientLifecycleManager, never()).onLayoutContinued();
+
+ mAtm.continueWindowLayout();
+
+ verify(mClientLifecycleManager).onLayoutContinued();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index 7fdc5fc2cff6..c757457a55ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -52,14 +54,12 @@ import org.mockito.MockitoAnnotations;
* Build/Install/Run:
* atest WmTests:ClientLifecycleManagerTests
*/
-// Suppress GuardedBy warning on unit tests
-@SuppressWarnings("GuardedBy")
@SmallTest
@Presubmit
public class ClientLifecycleManagerTests {
@Rule(order = 0)
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@Rule(order = 1)
public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule();
@@ -225,4 +225,30 @@ public class ClientLifecycleManagerTests {
verify(mTransaction).schedule();
verify(mTransaction).recycle();
}
+
+ @Test
+ public void testLayoutDeferred() throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+ spyOn(mWms.mWindowPlacerLocked);
+ doReturn(false).when(mWms.mWindowPlacerLocked).isInLayout();
+ doReturn(false).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
+ doReturn(true).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
+
+ // Queue transactions during layout deferred.
+ mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem);
+
+ verify(mLifecycleManager, never()).scheduleTransaction(any());
+
+ // Continue queueing when there are multi-level defer.
+ mLifecycleManager.onLayoutContinued();
+
+ verify(mLifecycleManager, never()).scheduleTransaction(any());
+
+ // Immediately dispatch when layout continue without ongoing/scheduled layout.
+ doReturn(false).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
+
+ mLifecycleManager.onLayoutContinued();
+
+ verify(mLifecycleManager).scheduleTransaction(any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
index 1f7b65e8b701..c18726350d81 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
@@ -20,10 +20,13 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import android.app.GameManagerInternal;
+import android.content.pm.ApplicationInfo;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -90,6 +93,14 @@ public class CompatModePackagesTests extends SystemServiceTestsBase {
public void testGetCompatScale_noGameManager() {
assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
0.01f);
- }
+ final ApplicationInfo info = new ApplicationInfo();
+ // Any non-zero value without FLAG_SUPPORTS_*_SCREENS.
+ info.flags = ApplicationInfo.FLAG_HAS_CODE;
+ info.packageName = info.sourceDir = "legacy.app";
+ mAtm.mCompatModePackages.compatibilityInfoForPackageLocked(info);
+ assertTrue(mAtm.mCompatModePackages.useLegacyScreenCompatMode(info.packageName));
+ mAtm.mCompatModePackages.handlePackageUninstalledLocked(info.packageName);
+ assertFalse(mAtm.mCompatModePackages.useLegacyScreenCompatMode(info.packageName));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index c6796dc9e90d..985be429d42b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
@@ -673,6 +674,47 @@ public class LetterboxUiControllerTest extends WindowTestsBase {
}
@Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUser()
+ throws Exception {
+ mDisplayContent.setIgnoreOrientationRequest(true);
+ assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_optOut_returnsUnchanged()
+ throws Exception {
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, /* value */ false);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+ mDisplayContent.setIgnoreOrientationRequest(true);
+
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUnchanged()
+ throws Exception {
+ mDisplayContent.setIgnoreOrientationRequest(false);
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenAndUserOverrideEnabled_returnsUnchanged()
+ throws Exception {
+ prepareActivityThatShouldApplyUserMinAspectRatioOverride();
+
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ }
+
+ @Test
@EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsPortrait()
throws Exception {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index f99b489720b9..8bf4833bc2ca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -499,11 +499,16 @@ public class WindowManagerServiceTests extends WindowTestsBase {
public void testAddWindowWithSubWindowTypeByWindowContext() {
spyOn(mWm.mWindowContextListenerController);
- final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay);
- final Session session = getTestSession();
+ final WindowState parentWin = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final IBinder parentToken = parentWin.mToken.token;
+ parentWin.mAttrs.token = parentToken;
+ mWm.mWindowMap.put(parentToken, parentWin);
+ final Session session = parentWin.mSession;
+ session.onWindowAdded(parentWin);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
TYPE_APPLICATION_ATTACHED_DIALOG);
- params.token = windowToken.token;
+ params.token = parentToken;
+ params.setTitle("attached-dialog");
final IBinder windowContextToken = new Binder();
params.setWindowContextToken(windowContextToken);
doReturn(true).when(mWm.mWindowContextListenerController)
@@ -517,6 +522,12 @@ public class WindowManagerServiceTests extends WindowTestsBase {
verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(),
any(), any(), anyInt(), any(), anyBoolean());
+
+ assertTrue(parentWin.hasChild());
+ assertTrue(parentWin.isAttached());
+ session.binderDied();
+ assertFalse(parentWin.hasChild());
+ assertFalse(parentWin.isAttached());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index df4af112c087..616a23e7ab5b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -294,7 +294,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
*/
static void suppressInsetsAnimation(InsetsControlTarget target) {
spyOn(target);
- Mockito.doNothing().when(target).notifyInsetsControlChanged();
+ Mockito.doNothing().when(target).notifyInsetsControlChanged(anyInt());
}
@After
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 72db7fecb8ac..ccd4ce038367 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -2395,7 +2395,19 @@ public class UsageStatsService extends SystemService implements
return null;
}
- return queryEventsHelper(UserHandle.getCallingUserId(), query.getBeginTimeMillis(),
+ final int callingUserId = UserHandle.getCallingUserId();
+ int userId = query.getUserId();
+ if (userId == UserHandle.USER_NULL) {
+ // Convert userId to actual user Id if not specified in the query object.
+ userId = callingUserId;
+ }
+ if (userId != callingUserId) {
+ getContext().enforceCallingPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "No permission to query usage stats for user " + userId);
+ }
+
+ return queryEventsHelper(userId, query.getBeginTimeMillis(),
query.getEndTimeMillis(), callingPackage, query.getEventTypeFilter());
}
diff --git a/services/voiceinteraction/Android.bp b/services/voiceinteraction/Android.bp
index de8d1440e6ac..9e425b0420f3 100644
--- a/services/voiceinteraction/Android.bp
+++ b/services/voiceinteraction/Android.bp
@@ -65,12 +65,14 @@ java_library_static {
java_library_static {
name: "services.voiceinteraction",
- defaults: ["platform_service_defaults"],
+ defaults: [
+ "platform_service_defaults",
+ "android.hardware.power-java_shared",
+ ],
srcs: [":services.voiceinteraction-sources"],
libs: [
"services.core",
"app-compat-annotations",
- "android.hardware.power-V1-java",
"android.hardware.power-V1.0-java",
],
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
index 4720d279a676..f9fa9b7a9524 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
@@ -106,96 +106,104 @@ final class VisualQueryDetectorSession extends DetectorSession {
@Override
public void onAttentionGained() {
Slog.v(TAG, "BinderCallback#onAttentionGained");
- mEgressingData = true;
- if (mAttentionListener == null) {
- return;
- }
- try {
- mAttentionListener.onAttentionGained();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error delivering attention gained event.", e);
+ synchronized (mLock) {
+ mEgressingData = true;
+ if (mAttentionListener == null) {
+ return;
+ }
try {
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_ATTENTION_STATE,
- "Attention listener failed to switch to GAINED state."));
- } catch (RemoteException ex) {
- Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
+ mAttentionListener.onAttentionGained();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering attention gained event.", e);
+ try {
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_ATTENTION_STATE,
+ "Attention listener fails to switch to GAINED state."));
+ } catch (RemoteException ex) {
+ Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
+ }
}
- return;
}
}
@Override
public void onAttentionLost() {
Slog.v(TAG, "BinderCallback#onAttentionLost");
- mEgressingData = false;
- if (mAttentionListener == null) {
- return;
- }
- try {
- mAttentionListener.onAttentionLost();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error delivering attention lost event.", e);
+ synchronized (mLock) {
+ mEgressingData = false;
+ if (mAttentionListener == null) {
+ return;
+ }
try {
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_ATTENTION_STATE,
- "Attention listener failed to switch to LOST state."));
- } catch (RemoteException ex) {
- Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
+ mAttentionListener.onAttentionLost();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering attention lost event.", e);
+ try {
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_ATTENTION_STATE,
+ "Attention listener fails to switch to LOST state."));
+ } catch (RemoteException ex) {
+ Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
+ }
}
- return;
}
}
@Override
public void onQueryDetected(@NonNull String partialQuery) throws RemoteException {
- Objects.requireNonNull(partialQuery);
Slog.v(TAG, "BinderCallback#onQueryDetected");
- if (!mEgressingData) {
- Slog.v(TAG, "Query should not be egressed within the unattention state.");
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_STREAMING_STATE,
- "Cannot stream queries without attention signals."));
- return;
+ synchronized (mLock) {
+ Objects.requireNonNull(partialQuery);
+ if (!mEgressingData) {
+ Slog.v(TAG, "Query should not be egressed within the unattention state.");
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_STREAMING_STATE,
+ "Cannot stream queries without attention signals."));
+ return;
+ }
+ mQueryStreaming = true;
+ callback.onQueryDetected(partialQuery);
+ Slog.i(TAG, "Egressed from visual query detection process.");
}
- mQueryStreaming = true;
- callback.onQueryDetected(partialQuery);
- Slog.i(TAG, "Egressed from visual query detection process.");
}
@Override
public void onQueryFinished() throws RemoteException {
Slog.v(TAG, "BinderCallback#onQueryFinished");
- if (!mQueryStreaming) {
- Slog.v(TAG, "Query streaming state signal FINISHED is block since there is"
- + " no active query being streamed.");
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_STREAMING_STATE,
- "Cannot send FINISHED signal with no query streamed."));
- return;
+ synchronized (mLock) {
+ if (!mQueryStreaming) {
+ Slog.v(TAG, "Query streaming state signal FINISHED is block since there is"
+ + " no active query being streamed.");
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_STREAMING_STATE,
+ "Cannot send FINISHED signal with no query streamed."));
+ return;
+ }
+ callback.onQueryFinished();
+ mQueryStreaming = false;
}
- callback.onQueryFinished();
- mQueryStreaming = false;
}
@Override
public void onQueryRejected() throws RemoteException {
Slog.v(TAG, "BinderCallback#onQueryRejected");
- if (!mQueryStreaming) {
- Slog.v(TAG, "Query streaming state signal REJECTED is block since there is"
- + " no active query being streamed.");
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_STREAMING_STATE,
- "Cannot send REJECTED signal with no query streamed."));
- return;
+ synchronized (mLock) {
+ if (!mQueryStreaming) {
+ Slog.v(TAG, "Query streaming state signal REJECTED is block since there is"
+ + " no active query being streamed.");
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_STREAMING_STATE,
+ "Cannot send REJECTED signal with no query streamed."));
+ return;
+ }
+ callback.onQueryRejected();
+ mQueryStreaming = false;
}
- callback.onQueryRejected();
- mQueryStreaming = false;
}
};
return mRemoteDetectionService.run(
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index b167f1bdfe20..d05eb5cd2d4c 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1349,6 +1349,24 @@ public class TelecomManager {
}
/**
+ * Returns a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
+ * calls. The returned list includes those accounts which have been explicitly enabled by
+ * the user or other users visible to the user.
+ *
+ * @see #EXTRA_PHONE_ACCOUNT_HANDLE
+ * @return A list of {@code PhoneAccountHandle} objects.
+ *
+ * @throws IllegalStateException if telecom service is null.
+ */
+ @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_WORK_PROFILE_API_SPLIT)
+ @RequiresPermission(allOf = {android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES})
+ public @NonNull List<PhoneAccountHandle> getCallCapablePhoneAccountsAcrossProfiles() {
+ return getCallCapablePhoneAccountsAcrossProfiles(false);
+ }
+
+
+ /**
* Returns a list of {@link PhoneAccountHandle}s for all self-managed
* {@link ConnectionService}s owned by the calling {@link UserHandle}.
* <p>
@@ -1423,7 +1441,7 @@ public class TelecomManager {
if (service != null) {
try {
return service.getCallCapablePhoneAccounts(includeDisabledAccounts,
- mContext.getOpPackageName(), mContext.getAttributionTag()).getList();
+ mContext.getOpPackageName(), mContext.getAttributionTag(), false).getList();
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts("
+ includeDisabledAccounts + ")", e);
@@ -1433,6 +1451,37 @@ public class TelecomManager {
}
/**
+ * Returns a list of {@link PhoneAccountHandle}s visible to current user including those which
+ * have not been enabled by the user.
+ *
+ * @param includeDisabledAccounts When {@code true}, disabled phone accounts will be included,
+ * when {@code false}, only enabled phone accounts will be
+ * included.
+ * @return A list of {@code PhoneAccountHandle} objects.
+ *
+ * @throws IllegalStateException if telecom service is null.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_WORK_PROFILE_API_SPLIT)
+ @RequiresPermission(allOf = {android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES})
+ public @NonNull List<PhoneAccountHandle> getCallCapablePhoneAccountsAcrossProfiles(
+ boolean includeDisabledAccounts) {
+ ITelecomService service = getTelecomService();
+ if (service == null) {
+ throw new IllegalStateException("telecom service is null.");
+ }
+
+ try {
+ return service.getCallCapablePhoneAccounts(includeDisabledAccounts,
+ mContext.getOpPackageName(), mContext.getAttributionTag(), true).getList();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Returns a list of all {@link PhoneAccount}s registered for the calling package.
*
* @deprecated Use {@link #getSelfManagedPhoneAccounts()} instead to get only self-managed
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 53154e6063b2..f1bfd227298c 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -61,7 +61,8 @@ interface ITelecomService {
* @see TelecomServiceImpl#getCallCapablePhoneAccounts
*/
ParceledListSlice<PhoneAccountHandle> getCallCapablePhoneAccounts(
- boolean includeDisabledAccounts, String callingPackage, String callingFeatureId);
+ boolean includeDisabledAccounts, String callingPackage,
+ String callingFeatureId, boolean acrossProfiles);
/**
* @see TelecomServiceImpl#getSelfManagedPhoneAccounts
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7cb2cc398c46..bcd99295b605 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9577,14 +9577,20 @@ public class CarrierConfigManager {
"satellite_attach_supported_bool";
/**
- * The carrier-enabled satellite connection hysteresis time in seconds to determine whether to
- * recommend Dialer to prompt users to use satellite emergency messaging.
+ * The carrier-enabled satellite connection hysteresis time in seconds for which the device
+ * continues in satellite mode after it loses the connection with the satellite network.
* <p>
- * A timer is started when there is an ongoing emergency call, and the IMS is not registered,
- * and cellular service is not available, and the device was connected to a satellite network
- * within this time in the past. When the timer expires, Telephony will send the event
+ * If the device is in satellite mode, the following actions will be taken by the device:
+ * <ul>
+ * <li>System UI will continue showing the satellite icon.</li>
+ * <li>When there is an ongoing emergency call, and the IMS is not registered, and cellular
+ * service is not available, and the device is in satellite mode, a timer with a duration
+ * defined by the overlay config
+ * {@code config_emergency_call_wait_for_connection_timeout_millis} will be started. When the
+ * timer expires, Telephony will send the event
* {@link TelephonyManager#EVENT_DISPLAY_EMERGENCY_MESSAGE} to Dialer, which will then prompt
- * users to switch to using satellite emergency messaging.
+ * users to switch to using satellite emergency messaging.</li>
+ * </ul>
* <p>
* The default value is 300 seconds.
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 55a3da5ad89d..f8166e58cd2f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1207,7 +1207,8 @@ public class TelephonyManager {
* The dialer app receives this event via
* {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
* <p>
- * The {@link Bundle} parameter is expected to include the following extras:
+ * The {@link Bundle} parameter is guaranteed to include the following extras if the below
+ * conditions are met:
* <ul>
* <li>{@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE} - the recommending handover
* type.</li>
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4d1db12e0b80..84777c9441a1 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3045,8 +3045,8 @@ interface ITelephony {
*
* @param handoverType The type of handover from emergency call to satellite messaging. Use one
* of the following values to enable the override:
- * 0 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS
- * 1 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911
+ * 1 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS
+ * 2 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911
* To disable the override, use -1 for handoverType.
* @param delaySeconds The event EVENT_DISPLAY_EMERGENCY_MESSAGE will be sent to Dialer
* delaySeconds after the emergency call starts.
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 50590177f791..b7dd4df1c843 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -548,6 +548,7 @@ public interface RILConstants {
int RIL_REQUEST_SET_CELLULAR_IDENTIFIER_DISCLOSED_ENABLED = 247;
int RIL_REQUEST_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED = 248;
int RIL_REQUEST_IS_SECURITY_ALGORITHMS_UPDATED_ENABLED = 249;
+ int RIL_REQUEST_GET_SIMULTANEOUS_CALLING_SUPPORT = 250;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -625,4 +626,5 @@ public interface RILConstants {
int RIL_UNSOL_CONNECTION_SETUP_FAILURE = 1108;
int RIL_UNSOL_NOTIFY_ANBR = 1109;
int RIL_UNSOL_IMEI_MAPPING_CHANGED = 1110;
+ int RIL_UNSOL_SIMULTANEOUS_CALLING_SUPPORT_CHANGED = 1111;
}
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 0e2e158c327e..4eac361d6e53 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -268,6 +268,9 @@ java_library_host {
srcs: [
"helper-framework-runtime-src/**/*.java",
],
+ exclude_srcs: [
+ "helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java",
+ ],
libs: [
"hoststubgen-helper-runtime",
"framework-all-hidden-api-host-impl",
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java
new file mode 100644
index 000000000000..631fc0273c94
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen.nativesubstitution;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.util.Base64;
+
+import java.text.DecimalFormat;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+public class CursorWindow_host {
+
+ private static final HashMap<Long, CursorWindow_host> sInstances = new HashMap<>();
+ private static long sNextId = 1;
+
+ private int mColumnNum;
+ private static class Row {
+ String[] fields;
+ int[] types;
+ }
+
+ private final List<Row> mRows = new ArrayList<>();
+
+ public static long nativeCreate(String name, int cursorWindowSize) {
+ CursorWindow_host instance = new CursorWindow_host();
+ long instanceId = sNextId++;
+ sInstances.put(instanceId, instance);
+ return instanceId;
+ }
+
+ public static void nativeDispose(long windowPtr) {
+ sInstances.remove(windowPtr);
+ }
+
+ public static boolean nativeSetNumColumns(long windowPtr, int columnNum) {
+ sInstances.get(windowPtr).mColumnNum = columnNum;
+ return true;
+ }
+
+ public static int nativeGetNumRows(long windowPtr) {
+ return sInstances.get(windowPtr).mRows.size();
+ }
+
+ public static boolean nativeAllocRow(long windowPtr) {
+ CursorWindow_host instance = sInstances.get(windowPtr);
+ Row row = new Row();
+ row.fields = new String[instance.mColumnNum];
+ row.types = new int[instance.mColumnNum];
+ Arrays.fill(row.types, Cursor.FIELD_TYPE_NULL);
+ instance.mRows.add(row);
+ return true;
+ }
+
+ private static boolean put(long windowPtr, String value, int type, int row, int column) {
+ CursorWindow_host instance = sInstances.get(windowPtr);
+ if (row >= instance.mRows.size() || column >= instance.mColumnNum) {
+ return false;
+ }
+ Row r = instance.mRows.get(row);
+ r.fields[column] = value;
+ r.types[column] = type;
+ return true;
+ }
+
+ public static int nativeGetType(long windowPtr, int row, int column) {
+ CursorWindow_host instance = sInstances.get(windowPtr);
+ if (row >= instance.mRows.size() || column >= instance.mColumnNum) {
+ return Cursor.FIELD_TYPE_NULL;
+ }
+
+ return instance.mRows.get(row).types[column];
+ }
+
+ public static boolean nativePutString(long windowPtr, String value,
+ int row, int column) {
+ return put(windowPtr, value, Cursor.FIELD_TYPE_STRING, row, column);
+ }
+
+ public static String nativeGetString(long windowPtr, int row, int column) {
+ CursorWindow_host instance = sInstances.get(windowPtr);
+ if (row >= instance.mRows.size() || column >= instance.mColumnNum) {
+ return null;
+ }
+
+ return instance.mRows.get(row).fields[column];
+ }
+
+ public static boolean nativePutLong(long windowPtr, long value, int row, int column) {
+ return put(windowPtr, Long.toString(value), Cursor.FIELD_TYPE_INTEGER, row, column);
+ }
+
+ public static long nativeGetLong(long windowPtr, int row, int column) {
+ String value = nativeGetString(windowPtr, row, column);
+ if (value == null) {
+ return 0;
+ }
+
+ Number number = new DecimalFormat().parse(value, new ParsePosition(0));
+ return number == null ? 0 : number.longValue();
+ }
+
+ public static boolean nativePutDouble(long windowPtr, double value, int row, int column) {
+ return put(windowPtr, Double.toString(value), Cursor.FIELD_TYPE_FLOAT, row, column);
+ }
+
+ public static double nativeGetDouble(long windowPtr, int row, int column) {
+ String value = nativeGetString(windowPtr, row, column);
+ if (value == null) {
+ return 0;
+ }
+
+ Number number = new DecimalFormat().parse(value, new ParsePosition(0));
+ return number == null ? 0 : number.doubleValue();
+ }
+
+ public static boolean nativePutBlob(long windowPtr, byte[] value, int row, int column) {
+ return put(windowPtr, value == null ? null : Base64.encodeToString(value, 0),
+ Cursor.FIELD_TYPE_BLOB, row, column);
+ }
+
+ public static byte[] nativeGetBlob(long windowPtr, int row, int column) {
+ int type = nativeGetType(windowPtr, row, column);
+ switch (type) {
+ case Cursor.FIELD_TYPE_BLOB: {
+ String value = nativeGetString(windowPtr, row, column);
+ return value == null ? null : Base64.decode(value, 0);
+ }
+ case Cursor.FIELD_TYPE_STRING: {
+ String value = nativeGetString(windowPtr, row, column);
+ return value == null ? null : value.getBytes();
+ }
+ case Cursor.FIELD_TYPE_FLOAT:
+ throw new SQLiteException();
+ case Cursor.FIELD_TYPE_INTEGER:
+ throw new SQLiteException();
+ case Cursor.FIELD_TYPE_NULL:
+ default:
+ return null;
+ }
+ }
+}